例外処理(DataType Misalignment)について

[戻る]
M 2005/07/06(水) 16:06:14
Mです。

現在開発しているプロジェクトで、通常出るはずのない実行文で、
「例外処理(初回)は hoge.exeにあります :0x80000002:DataType Misalignment」
が表示されます。

アウトプットには、
SENDING command id 0x0BC1 to CDialog target.
Exception 004 Thread=81b88a28 Proc=61a46daa
AKY=00001001 PC=0002496e RA=0002491c BVA=0002496e
Process 'hoge.exe'
と表示されます。

アウトプットに表示された内容の意味(AKY,PC,RA,BVA,Thread,Proc)を教えていただけないで
しょうか?
これが手がかりになる気がするのです。


# 「通常出るはずの無い実行文」というのは、switchによる評価で、同様の処理を
# if文で評価すると、例外は発生しません。
# ですが、何としても原因をでっち上げたいです。
#
# また、DataType Misalignmentはアライメントが不正の時に発生するとの事ですが、
# 「メニュー」「プロジェクト設定」「c/c++」カテゴリ「コード生成」
# から設定できるアライメントサイズも8バイトという事で、領域がズレているという事も
# 考えにくいです。

環境は(板違いではあると思うのですが) eMbedded Visual C++4.0 SP2、Win2Kです。
よろしくお願いいたします。

Ban 2005/07/07(木) 00:56:58
> アウトプットに表示された内容の意味(AKY,PC,RA,BVA,Thread,Proc)を
> 教えていただけないでしょうか?
> これが手がかりになる気がするのです。

eMbedded は知りませんので適当かもしれませんが、
名前から推測するにPCやRAはレジスタ情報でexceptionは割り込み番号では
ないでしょうか。であればマップやりストと付き合わせれば落ちた位置と
呼び元くらいは分かりそうに思いますが。(要アセンブラ基礎知識)
スレッドとかプロセスとかはハンドル値か何かでは。

> # 「通常出るはずの無い実行文」というのは、switchによる評価で、同様の処理を
> # if文で評価すると、例外は発生しません。

break が抜けてたりして...。

> # ですが、何としても原因をでっち上げたいです。

.......。

> # また、DataType Misalignmentはアライメントが不正の時に発生するとの事ですが、
> # 「メニュー」「プロジェクト設定」「c/c++」カテゴリ「コード生成」
> # から設定できるアライメントサイズも8バイトという事で、
> 領域がズレているという事も
> # 考えにくいです。

恐らく石はARMとかですよね。データのアライメントとコード生成は多分無関係では?
Intel以外の多くのRISC系CPU(含むARM)は奇数アドレスからワードアクセスなどすると
アライメント違反で落ちますから、それではないかという気がしますが。

近くに char* からのポインタのキャストとかやってるところがあったりしませんか?
C++ で reinterpret_cast を要求されるような所は要注意で、Intelでは落ちないので
PCから入った人がよくはまったりしますけど。

M 2005/07/07(木) 13:16:26
Mです。

Banさん>

PCやRA等の意味を教えてくださいましてありがとうございます。
私はアセンブラの知識は持ち合わせていないので、
意味がわかったところで、特定は出来なさそうです。

> break が抜けてたりして...。
switch()文では、下記に示すようなコーディングをしています。
return はすぐ戻ると認識していますので、特にbreakは記述していません。
# もちろん、return しない場合は breakを記述しています。

>Intel以外の多くのRISC系CPU(含むARM)は奇数アドレスからワードアクセスなどすると
当方、CPUはMIPSです。
DataType Misalignment はIntel等のx86系では発生しないという事で、
今まで気づきませんでした。

あれから色々と試しており、この問題が再現する最低限のソースを特定できましたので、
以下に公開します。
結局「丸投げ」になってしまうのですが、識者の方、何卒ご助言よろしくお願いいたします。



------------------------------------------------------
再現方法
------------------------------------------------------

1. eMbedded Visual C++4.0(当方SP2です)を起動します。
2.「80000002」というプロジェクトで作成します。
3. 以下に示す修正を行います。

--------------------------
80000002 アプリクラス
--------------------------

<< .h >>
・以下を記述
#define RC_STATUS_OK    (int)500001        // リターンコード
#define RC_STATUS_NG3    (int)500002        //    〃

#define    WUM_TEST    WM_APP + 1        // ユーザーメッセージ

・グローバル変数として追加
public:
    bool    g_bMode;


--------------------------
80000002Dlg ダイアログクラス
--------------------------
<< .h >>
・以下を宣言
 class        CMy80000002App*    m_pApp;
 int        m_nRet;
 LRESULT    OnMessageTest(WPARAM wParam, LPARAM lParam);


<< .cpp >>
・コンストラクタ内に記述
 m_pApp = (CMy80000002App*)AfxGetApp();

・BEGIN_MESSAGE_MAP()内に以下を追加
 ON_MESSAGE(WUM_TEST, OnMessageTest)

・以下の関数を記述
LRESULT CMy80000002Dlg::OnMessageTest(WPARAM wParam, LPARAM lParam)
{
    return (lParam);
}

・OnInitDialog()内に追加
    //////// 現象を再現する為のソース ////////
    m_pApp->g_bMode = true;
    m_nRet            = 0;

    int nRet;

    nRet = 0;                            
        // ←これを有効にすると、Access Violation
//    nRet = 3;                            
        // ←これを有効にすると、DataType Misalignment


    switch (nRet)                        
        // ←ここにブレイクポイントを張り、
    {                            
                //   「ステップ実行」すると現象が発生します。
        case 0:
            m_nRet = RC_STATUS_OK;
            EndDialog(IDOK);
            return TRUE;

        case 1:
            return TRUE;
        
        case 2:
            if (!m_pApp->g_bMode) {            
    // ←このIF文を無効にすると現象は発生しません
                PostMessage(WUM_TEST,0,0);
            }
            return TRUE;

        case 3:
            m_nRet = RC_STATUS_NG3;
            EndDialog(IDOK);
            return TRUE;
    }

※ユーザーメッセージにより、一定の条件を満たした時(上記文では case2の時)、
 OnMessageTest()関数が実行されます。
※アプリケーションクラスにグローバル変数を持たせ、それを参照しています。
※結果コードは予め DEFINEで宣言し、 m_nRet = 500001等とは記述しません。
※ユーザーメッセージによる関数を辞めて、通常のメンバ関数 Func1()等に変更すると
 現象は発生しません。
※板違いであることは認知しておりますが、日頃この掲示板はよく利用しており、
 皆様とても親切にご回答してくださいますので、こちらの掲示板に掲載させていただきまし
た。


長文、失礼いたしました。

M 2005/07/07(木) 13:24:23
すみません、注釈がおかしくなってしまいました。

    nRet = 0;   // ←これを有効にすると、Access Violation
//  nRet = 3;   // ←これを有効にすると、DataType Misalignment


switch (nRet)   // ←ここにブレイクポイントを張り、
{               //   「ステップ実行」すると現象が発生します。
case 0:
  (以下省略)


if (!m_pApp->g_bMode) {   // ←このIF文を無効にすると現象は発生しません
    PostMessage(WUM_TEST,0,0);
}

と読み替えてください。

K 2005/07/07(木) 20:03:49
いつの間にか「Access Violation」が出るパターンも増えてるんですか?

ステップ実行で確実に発生するということなら、私ならレジスタ表示させて
混合モードで見ます。ARM(?) のアセンブラなんか全く読み書き経験ありませんが、
混合モードなら気合で読めるんじゃないかな?と思います。

しょせんは「高級アセンブラ」で、いくら C/C++ のコードに問題なくても、
コンパイラが妙なコード吐いてたら動くわけがありませんから。

Ban 2005/07/08(金) 02:07:58
# 再現コードがあっても環境がないわけですが>eMbedded

MIPSだそうですがARMでも基本は一緒ですし、どちらか読めればだいたい分かりますね。
奇数アドレスからワードアクセスすると落ちるのはMIPSも一緒ですが、
ぱっと見そういうコードはなさそうです。
# m_pApp のアドレスは妥当ですか。

K さんの言われるようにコンパイラがバグってる可能性も勿論ありますし、
例えばスタックオーバフローとか、範囲外参照で this とかを壊すとかすると
こんな感じの現象が出たりとか....簡単にでいいので勉強されてみては?>MIPS
# 書けなくてもマニュアル片手に混合モードが読めれば十分。

ロード命令とストア命令(読み書き)に絞ってレジスタ内のアドレスを見ていけば、
不定値(?)が入ってたりどこかで壊してたりしませんか。
>    nRet = 0;   // ←これを有効にすると、Access Violation
> //  nRet = 3;   // ←これを有効にすると、DataType Misalignment
その結果、偶々アクセス可能な範囲の、でも奇数なアドレスになるのが 3,
アクセスも許可されないようなアドレスになってるのが 0なのではないかというのが、
見える情報から考えられる仮説その1でしょうか。

ketamifu 2005/07/08(金) 13:08:19
#define RC_STATUS_OK    (int)500001        // リターンコード
#define RC_STATUS_NG3    (int)500002        //    〃
↑ここがおかしいのでは?
#define RC_STATUS_OK    ((int)500001)
#define RC_STATUS_NG3    ((int)500002)
こうしてみてはいかがでしょうか。
はずしてたらごめんなさい。

M 2005/07/08(金) 17:57:57
Mです。
皆様、アドバイスありがとうございます。

Kさん>
仰る通り、何故か Access Violationも表示されます。ですが、ポインタを使用している個所
は、
Appクラスへの参照と値代入くらいで、コーディングミスによるポインタの不正アクセスは考え
られません。

Kさん、Banさん>
ステップ実行をしている時に混合モードで表示してみました。
混合モードで表示されるアセンブラの意味は現時点では分かりませんが、
ただ結論として1つ、混合モードでステップ実行中は再現しません。
# 同時にステップ実行しないで実機よりエクスプローラーから直接実行しても、
# 再現しません。
# アセンブラについては、時間に余裕ができましたら独学してみたいと思います。

Banさん>
m_pAppのアドレスは妥当です。
また、switch中のcase句内でreturn していますが、returnを辞め、breakを記述すると
再現はしません。
switchの中でreturnするのは、コーディング的には良くないのでしょうか?

ketamifuさん>
ご指摘の通りに試してみましたが、残念ながら該当しませんでした。
ですが、確かにint型で32767(4byte)を超える値というのも変ですね。
32767以下の値に変更しましたが、ダメでした。


eMbedded Visual C++は現在 SP4までサービスパックが出ています。
モバイルメーカーの提示する環境が SP2なので、SP2までしか適用していませんが、
これで回避できるか(即ちコンパイラのバグでこれが改善されているか)試してみます。


もう少々お付き合いいただければ幸いです。

ketamifu 2005/07/08(金) 19:07:03
if (!m_pApp->g_bMode) {   // ←このIF文を無効にすると現象は発生しません
ここを
if (!(m_pApp->g_bMode)){
または、
if ( m_pApp->g_bMode != 0 ){
としてみてはいかがでしょうか。
しつこかったらごめんなさい。

Ban 2005/07/08(金) 19:17:11
> switchの中でreturnするのは、コーディング的には良くないのでしょうか?

コーディング規約などで禁止されている場合もあるでしょうが(常に出口は一つとか.....)
言語的には問題ないはずです。

ブレイクすると条件が変わるなどの症状から推測される一般的な原因は、
例えばこのあたりでしょうか。
# 一般論でしか回答できないのであまり役には立たなそう...。

・コードとは非同期な原因がある。
  別スレッドがいるとかドライバがいるなどで、排他制御に穴があるとか。
  これは単純にコードのバグなので探して直すしかありません。

・コンパイラがバグっている(例えば不正な最適化など)。
  eMbedded は知りませんが、組み込み用のコンパイラ出力は妄信すると危険だと思ってます。
  アセンブラ出力を確認してトレースすればコンパイラのバグは切り分けできますし、
  最初に確認して切り分けを行う方がいいと思います。(基本的な確認項目だと思います)
  
・デバッガが対応できない。ハードがバグってるなどの外的要因。
 お使いの環境でどうかは知りませんが、アセンブラのコードも正しいのに
 ブレイクした後に落ちるということもまぁありえる話ではないかと思います。
 # eMbedded は知りませんが、以前 MIPS の評価ボードに iTRON OS のポーティングを
 # した時にも某デバッガでそんなことがありましたし.....。
 # (デバッガ自体も石に対応直後(無実績)だったのであまり参考にならない事例なのかも...)


> ですが、確かにint型で32767(4byte)を超える値というのも変ですね。

32767 は符号付の2byte ですね。

M 2005/07/12(火) 09:05:16
Mです。
皆様、アドバイスありがとうございます。

ketamifuさん>
情報ありがとうございました。ですが、やはりダメでした。
if文を無効にすると現象が出ない上、
if文単体でステップ実行しても現象が出ませんので、やはりこの線ではない気がします。

Banさん>
>言語的には問題ないはずです。
前回書き込みした後、C++の入門書等も調べていましたが、
どこにも switch 文の中に return している例が無いので
自分の知識に疑いを持ち始めていたところです。安心いたしました。

ご教授していただいた
(1)非同期な原因がある
については、掲示しました捨てプロでも発生することから、とりあえず除外するとしまして、

(2)コンパイラがバグっている
(3)デバッガが対応できない。
私もこちらが怪しいのかなと思っています。
eVC4での他のCPU(STANDARD SDK Emulator)や、VC6では発生しないからです。
混合モードでも発生しない・・どういうことなのかさっぱり分からない状態です。

またBan様のご意見、
> 某デバッガでそんなことがありましたし
が非常に気になります。しかもMIPSという事で、
もう少々詳しくお聞かせ願えませんでしょうか?

今までは例外が発生すれば、まず間違いなくソースを疑い、
全てコーディングミス(ポインタの初期値設定ミスや、構造体要素の最大値オーバーなど)
でしたが、この様にデバッガにもバグがある事を意識した方が良いのかもしれませんね。

尚、先日 同条件のPCを準備し、SP4を当てて試行してみましたが、
ステップ実行中、デバッガはやはり例外を発して停止しました。

また何か進展がありましたらご連絡します。

Ban 2005/07/12(火) 14:07:17
> 混合モードでも発生しない・・どういうことなのかさっぱり分からない状態です。

恐らくどこかのタイミング問題の可能性が高そうですけど、
それがMさんのコードなのか、デバッガなのか、OSやドライバやハードの問題なのかは
勿論私には分かりません。

> もう少々詳しくお聞かせ願えませんでしょうか?

私のケースは単に開発環境が信用できなかった過去の一事例ですし、
詳細は参考にならないと思いますが。

新しい石の評価ボード(初期ロッド)に、そのボードに対応したばかりのデバッガ
(Jtag)をつないでましたが、やはりブレイクをかけてトレースをはじめると、
簡単なテストコードでも落ちました。(現象だけなら似たような感じです)

勿論アセンブラも読みましたが特に悪くなさそうなので、原因はデバッガと見ました。
# 原因がコンパイラの場合には問合せ先が変わる。

OS自体のポーティングをやってるような新規ボードでしたし、
この時点ではまだ、OS屋の他にデバッガ屋、ドライバ屋など限られた一部の
会社にしかそのボードを使った実績はありません。
時期的に、デバッガ側もこのボードでの実績がほぼないことは分かっていましたので、
デバッガのメーカに連絡して確認してもらい、結局ソフトをアップデートしました。
原因はどうもブレイク後の制御に不具合があったようです。

> 今までは例外が発生すれば、まず間違いなくソースを疑い、
  -- snip --
> この様にデバッガにもバグがある事を意識した方が良いのかもしれませんね。

まず自分を疑うのは当然ですが、いかにコンパイラやデバッガであろうと
問題がまったくないということはありえません。
利用者が少なかったり、新しい石だったりすると、コンパイラやデバッガにしても
実績が少ないわけで、バグが潜んでいる可能性は通常のPCアプリよりは当然
高くなります。
何事も自分の目で確認する姿勢は大事だと思い知らされたり...。

散々コードを調べまわった挙句にハードの問題とかいうこともありますし、
問題を切り分ける上でも、足元から地道に確認していくことをお勧めします。

古いスレッドには追加発言できません。