開発環境:WinXP(SP1) VC++6.0
はじめまして。
現在、ソケット通信アプリケーションを作成しています。
そのアプリケーションが、例外処理を起こし、その現象が再現せずに困っています。発生したエラーというのは、
「例外unknown software exception (0x80000003) がアプリケーションの0x0046fcd0で発生しました」
という内容です。
0x80000003というアドレスは、レジストリのルートキーであるHKEY_USERSを指し示すことは分かりました。
では、アプリケーションの0x0046fcd0とは、どんなところだろう?と思い、コンパイル時にmapファイルを出力するようにし、見てみました。
そして、0x0046fcd0が含まれているアドレスを見たのですが、どうもよく分かりませんでした。書いてある内容は、
Preferred load address is 00400000
Start Length Name Class
0001:00000000 0005a65dH .text CODE
0001:0005a660 000077eaH .text$x CODE
0001:00061e4a 0002f406H .textbss CODE ← ここが問題
0002:00000000 00012b24H .rdata DATA
0002:00012b28 00005b63H .xdata$x DATA
(中略)
0001:00059848 ?AfxGetModuleState@@YGPAVAFX_MODULE_STATE@@XZ 0045a848 fmfc42d:MFC42D.DLL
0001:00061e50 __enc$textbss$begin 00462e50 <linker-defined>←ここ
0001:00091250 __enc$textbss$end 00492250 <linker-defined>
0002:0000001c ??_C@_0CD@KNKL@?$CK?$CK?$CK?$CK?$CK?$CK?$CK?$CK?$CK?$CK?5CAccept?3?3CAccept?$CI?$CJ?5Ou@ 0049301c Accept.obj
0002:00000048 ??_C@_0CC@MALE@?$CK?$CK?$CK?$CK?$CK?$CK?$CK?$CK?$CK?$CK?5CAccept?3?3CAccept?$CI?$CJ?5In@ 00493048 Accept.obj
0002:00000070 ??_7CAccept@@6B@ 00493070 Accept.obj
このように書かれていたのですが、自分が分かるのは、
1.何かしらのコード(プログラム)領域である
2.リンカが何やらdefinedしている
といった、どうでもいい事くらいです。
長くなってしまいましたが、自分が教えていただきたいことは、
1.<linker-defined>とは、どういう意味なのか?
2.CODEクラスが3つあるが、なぜ3つに分かれているのか?1つではないのか?
と言うことです。
すると、例外が発生した原因が何か分かるかなと思っています。
ネットや本で色々調べてみたのですが、あまり情報が載っていなくて、こまり、書き込ませていただきました。
どうか、みなさんの知恵をお貸しください!
宜しくお願いします!
PE Header に関する文献を探してください。
MSDN Library 技術仕様書->プラットフォーム->PE/COFF で見つかります。
でもこれを知っても問題解決には全く無関係でしょう。
プログラムのロードアドレスがわからないと、
0x0046fcd0番地がマップ上のどのアドレスであるか判断できません。
...っていうか、デバッガに引っかかってるんですよね?
デバッガ上で単にバックトレース表示するだけで解決しませんか?
tetrapodさん、ご返答ありがとうございます。
早速調べてみます!
>プログラムのロードアドレスがわからないと、
>0x0046fcd0番地がマップ上のどのアドレスであるか判断できません。
プログラムのロードアドレスは、最初に書いたように、
[Preferred load address is 00400000]となっていましたので、0x00400000
だと思います。
>...っていうか、デバッガに引っかかってるんですよね?
>デバッガ上で単にバックトレース表示するだけで解決しませんか?
この現象が発生したのは、デバッガ上ではなくて、exe起動して動作試験をしていた最中でした。その現象が出たときは、何も操作をしないで、ほったらかしにしていた時です(動作試験をしていたのは、他の人です)。
そして、トレースをログファイルに出していたので、例外が発生した関数は追うことが出来ました。その、発生した関数というのが、OnTimerなのです。
例外発生時は、タイマが十数個動いていて、他のスレッドも十個近くあり、OnTimerの中ではあるけれども、どのイベントで発生したかまでは、分からなかったのです。
なので、更に詳しいトレースを出すようにして、再度試みているのですが、再現しないのです。再現しないのならば、原因追及しようと思い、現在に至ります。
まずは、tetrapodさんに教えていただいたところを調べてみます。
tetrapodさん
先ほどは、アドバイスありがとうございました。
それで、教えていただいたよう、PE/COFFについて調べてみようとしました。
が・・・「MSDN Library 技術仕様書->プラットフォーム->PE/COFF」といった流れでは、発見できませんでした(2003年4月バージョンのMSDN)。
なので、PEやCOFFをそれぞれ調べてみたのですが、「linker-defined」等の説明が書かれていませんでした。
引き続き、PE HEADERについて調べていますが、どうも良い情報が得られません。
そろそろ煮詰まって参りました・・・・(泣)
一体、どんな理由で例外が起こったのか・・・・・大雑把な場所しか把握できないようなログを出力したとしても、意味がないと、今回は一つ利口になっております(大泣)
何か良い「例外原因解明方法」は、無いものでしょうか・・・・??
引き続き、何か良いアドバイスがございましたら、ご口授下さいませ。
宜しくお願いいたしますm(_ _)m
ウチの MSDN-Library 2001/Apr で検索に textbss を入力すると
PE/COFF 仕様書の特殊セクションがヒットしますが、ダメですか?
textbss は「コンパイラが要求することで、リンカが生成する、
実際のコードを含まないコード領域である。実行時にコード領域に
追加スペースを必要とするツールをサポートするための領域」
と解説があります。
エディトコンティニューを無効にすると生成されません。
要するに次回のリンクを高速にするための埋め草領域です。
あと preferred load address は load address ではありません。
# 辞書引きましょう
真のロードアドレスがわからないと無意味です。
textbss 領域にはコードは存在しないので、例外が発生したアドレスが
真に textbss 領域であるなら、スタック上の戻りアドレスが破壊された
のだと推測できます (が、真のロードアドレス以下略)
tetrapodさん
アドバイスありがとうございます!
load addressに関しては、お恥ずかしい限りです・・・。
それでまずMSDNの件ですが、2003年版だと見れなかったのですが、2001年 10月版で確かめてみると、きちんとtextbssで検索ヒットしました(説明はtetrapodさんの方が理解しやすかったです・・・)。2001年版に載っているとは、盲点でした。ありがとうございました。
そして、load addressの件ですが、preferred load addressとは違うのですね。「理想的なロードアドレス」ですか・・・。実際のロードアドレスではないものを、MAPファイルに書かれる意味あるのですかね?
それで、実際のload addressを求めるため、コンストラクタの先頭で実行を止め、その時の実行行のアドレスをコールスタックウインドウで見てみたのですが、やはり0x00400000でした。これもまた、仮想的なアドレスですか?
お恥ずかしい話ですが、自分の力量が足りない為、正しいload addressを求める簡単な手法が思いつきません。申し訳ないのですが、ご口授願えませんか?
また、textbssという領域の件ですが(load address=0x00400000が前提として)、コードが書かれていない領域に飛んでしまうという事であれば、確かにスタックが怪しいですね。当然何かしらの理由がないと、スタック上のアドレスは破壊されませんよね(以前組み込み系の仕事をしていたとき、関数を、ひたすらネストしていったら、ある階層でスタック上の内容が破壊されたという経験はありますが・・・)。
まあ、だから「例外」なのでしょうが・・・・・。
色々アドバイスありがとうございます!
今回も、新たに質問してしまいましたが、どうか助言下さいますよう、宜しくお願い致します。
一般的に Win32 EXE は 0x00400000 Win32 DLL は 0x10000000 を
preferred load address としますが、実際にロードされるアドレスは
違う可能性があります (特に DLL)
ただ Win32EXE はほとんどの場合 0x00400000 にロードされるようです。
# そこ以外にロードされたの見たこと無い...
ロードアドレスを知る簡単な方法は、実行されたとおりブレークするか
関数へのポインタを表示するとか、GetModuleInformation() です。
MODULEINFO minfo;
GetModuleInformation(GetCurrentProcess(),GetModuleHandle(0),
&minfo,sizeof minfo);
ただしロードアドレスからマップアドレスとの対応を取るのは面倒です。
戻りアドレスが壊れるのは釈迦に説法くさいですが
スタックオーバーフロー、バッファオーバーフローが主因で、
その他には可変引数の処理間違いとか、無効ポインタが指す先への
書き込みとか、__stdcall の使い間違いとか、まあいろいろありますわな。
マルチスレッドしているのなら、ロックが必要なところに正しいロックが
なされていないとかで、非常にわかりにくいシビアなタイミングで
無効なデータをアクセスしたとかがありえます。
組み込み系で経験されたというスタック破壊は単なるスタックオーバーで
しょう。
tetrapodさん
事細かな説明ありがとうございます!
大変よく分かりました。
load addressは、tetrapodさんがおっしゃっていた通り、
調べてみた結果、0x00400000で間違いなさそうです。
ありがとうございましたm(_ _)m
また、DLLについての知識まで頂き、重ね重ね
ありがとうございますm(_ _)m
例外の原因は、やはりその類に収まってしまいますね。
しかし、今回のやりとりで、原因を追う下積みとなりましたので
大変感謝しております。
「ロックが必要なところに正しいロックがなされていない」という所を
重点的に追っていこうと思います(少々厳しいですが・・・)。
本当にありがとうございました。
tetrapodさんのアドバイスにより解決したこの問題が、
他の人の参考になればと思います。
自分も含め、他のプログラマー達にも、
今回のような的確なアドバイスを、
今後とも宜しくお願いいたします!
PS.
>組み込み系で経験されたというスタック破壊は単なるスタックオーバーで
>しょう。
その通りです。確保したスタック領域が小さすぎたため、起こってしまったということです。現在のPCで動くWindowsアプリでは、そうそうありえないでしょうね。。。
Win32 のデフォルトスタックは 1MByte ですから、
スタックオーバーは簡単には起こりませんが、
再帰でバグらせるとやはり発生します。
再帰を使ったつもりが無くても、メッセージハンドラ内で
Dispatch すると再帰させちゃうことがあります。
ちなみにここでの 0x80000003 は例外コード。
よく見る例外コードです。 HKEY_USERS とは多分無関係。
知らぬ間にもりもり話が進んでたので、参加できなくて残念なり。
#define E_INVALIDARG 0x80000003
俗に「プロシージャ・パラメータが不正です」系。
知ったからといって、何の特にもならない情報ですな。
んで、アドレスから関数の求め方だけど、でけた?
実行したBinaryのmapファイルがあるなら一瞬だす。
mapファイルの30行目付近に
「Address Publics by Value Rva+Base Lib:Object」
てのが、あるでしょう?
このRva+Baseが実際の仮想メモリアドレス配置になりませう。
だから、そのすぐ下から始まる定義で0x0046fcd0を含む0x0046f??? fを探せば、
となりに関数名が記述されて。そこから相対位置(0x0046fcd0-0x0046f???)を
計算すれば、どこで落ちたか一目瞭然。
しかし、今時マップファイルを元に追うなんて通だね〜。
ちなみにオイラは面倒なので、逆アセして直接コードを読み。そっちの方が早い〜
tetrapodさん AUT`sさん
レスが増えていたので見てみると、更なるアドバイスが・・・。
解決してからのアドバイス、ありがとうございます!
tetrapodさんのレスより
>ちなみにここでの 0x80000003 は例外コード。
>よく見る例外コードです。 HKEY_USERS とは多分無関係。
そのようですね。
冷静に見てみると、0x80000003がアドレスでないことは、分かりますね。
お恥ずかしい限りです・・・・。
AUT`sさんのレスより
>#define E_INVALIDARG 0x80000003
>俗に「プロシージャ・パラメータが不正です」系。
>知ったからといって、何の特にもならない情報ですな。
はじめまして。
レスありがとうございます!
自分も「0x80000003はエラーコードである」という事に気づいてから、結構調べてみましたが、あまり有力な情報が得られませんでした。
AUT`sさんがおっしゃる内容を見て、「なるほど!」と思いました。
そういう意味であるなら、調べても見つかりにくいわけですね・・・。
大変、助かりました。
>このRva+Baseが実際の仮想メモリアドレス配置になりませう。
解決した後で申し訳ないのですが、このレスについて質問です。
この、「Rva」と「Base」とは、何を指し示しているのですか?mapファイル内には、「Rva」と「Base」が持つだろう何かしらの値が書かれていないのですが、指し示す何かがあるのですか?
何やら、前に見た記憶のある単語ですが・・・・。
>だから、そのすぐ下から始まる定義で0x0046fcd0を含む0x0046f??? fを
>探せば、となりに関数名が記述されて。そこから相対位置(0x0046fcd0->0x0046f???)を計算すれば、どこで落ちたか一目瞭然。
そうですね。
今回で言うと、アドレス0x0046fcd0は、関数ではなくて、「.textbss」という領域に存在するということが分かりました。
>しかし、今時マップファイルを元に追うなんて通だね〜。
>ちなみにオイラは面倒なので、逆アセして直接コードを読み。そっちの方が>早い〜
とんでもないです!
逆に言うと、ただマップファイルを追うことしか出来ていないだけです。
アセンブルがあまり得意ではないので、このような手法をとっているのです(恥)
それにしても、お二人とも、解決後なのに気にしてくださって、ありがとうございます!
そして、またもや質問してしまいましたが、どうぞアドバイスの程、宜しくお願いいたしますm(_ _)m
質問の内容が、少々甘えていました。
普通に考えれば、自分で調べられる範囲でした。
申し訳ないです。
最後の質問に限っては、皆さんの知識量が多いのをいい事に、
あてにしまくっていたような気がします。
本来の目的は、みなさんのおかげで解決しましたので、
この辺でやめておきます。
tetrapodさん AUT`sさん
色々ありがとうございましたm(_ _)m
また、何かにつまづいたとき、宜しくお願いします。