リリース後のアプリケーションで例外が発生した場合、codファイルを使用して例外の発生した行を特定することができます。
コンパイル時に生成された、アセンブリ言語のコードと、コンピュータ語のコードを保存することができます。プロジェクトのプロパティを開いて、C/C++の出力ファイルにある「アセンブリの出力」を変更して、「アセンブリコード、コンピュータ語コード、ソースコード」を選択します。これでビルド時に拡張子codのファイルが生成されます。
マップファイルを使用することで、例外オフセットから障害が発生したアドレスを調べられることは「マップファイルを使用したデバッグ」で解説しました。
Address Publics by Value Rva+Base Lib:Object
0000:00000001 ___safe_se_handler_count 00000001
0000:00000000 ___ImageBase 00400000
0001:00000000 _wmain 00401000 f crashTest.obj
0001:00000020 ?funcA@@YAHXZ 00401020 f crashTest.obj
0001:00000050 ?funcB@@YAHH@Z 00401050 f crashTest.obj
0001:00000090 ?funcC@@YAXXZ 00401090 f crashTest.obj
ここで例外の発生している関数funcBと例外オフセットアドレスの差を求めます。この場合、例外オフセットが0x1064、 関数の先頭アドレスが0x1050で、差分は0x14(20)バイトです。ここでcodファイルのfuncBの場所を参照します。
PUBLIC ?funcB@@YAHH@Z ; funcB
; Function compile flags: /Odtp
; COMDAT ?funcB@@YAHH@Z
_TEXT SEGMENT
_val$ = -4 ; size = 4
_arg1$ = 8 ; size = 4
?funcB@@YAHH@Z PROC ; funcB, COMDAT
; 29 : {
00000 55 push ebp
00001 8b ec mov ebp, esp
00003 51 push ecx
; 30 : char *val = NULL;
00004 c7 45 fc 00 00
00 00 mov DWORD PTR _val$[ebp], 0
; 31 : strcpy(val, “Hello World”); // 1.ソースコードの31行目で例外が発生している
0000b 8b 45 fc mov eax, DWORD PTR _val$[ebp]
0000e 8b 0d 00 00 00
00 mov ecx, DWORD PTR $SG-6
00014 89 08 mov DWORD PTR [eax], ecx // 2.ここで例外が発生している
00016 8b 15 04 00 00
00 mov edx, DWORD PTR $SG-6+4
0001c 89 50 04 mov DWORD PTR [eax+4], edx
0001f 8b 0d 08 00 00
00 mov ecx, DWORD PTR $SG-6+8
00025 89 48 08 mov DWORD PTR [eax+8], ecx
; 32 : printf(“call funcB”);
00028 68 00 00 00 00 push OFFSET $SG-7
0002d ff 15 00 00 00
00 call DWORD PTR __imp__printf
00033 83 c4 04 add esp, 4
; 33 : return 0;
00036 33 c0 xor eax, eax
; 34 : }
00038 8b e5 mov esp, ebp
0003a 5d pop ebp
0003b c3 ret 0
?funcB@@YAHH@Z ENDP ; funcB
_TEXT ENDS
0x0014バイトの個所を探すと、2.の個所で例外が発生していることがわかります。これは1.で示されるソースコード上の31行目にあたります。この作業でリリース後のアプリケーションでもソースコード状の何処で例外が発生しているのか特定することができます。