我有完全相同的源代码,所以我可以创建一个MAP文件.但我不知道如何在MAP文件中找到AccessViolation提供的地址.
(将来,我们希望使用像JclDebug这样的框架来创建可用的堆栈跟踪).
我已经设置了一个例子:
procedure CrashMe; var k: TMemo; a: TButton; begin k.Text := 'abc'; k.Color := clBlack; k.Assign(a); end; procedure TForm1.Button1Click(Sender: TObject); begin CrashMe; end;
访问冲突是:
Address 004146CF. Reading from address B2D88B53 .
Start Length Name Class 0001:00401000 000556A8H .text CODE 0002:00457000 00000770H .itext ICODE 0003:00458000 00001B0CH .data DATA 0004:0045A000 00004CCCH .bss BSS 0005:00000000 00000038H .tls TLS .... 0001:000552F0 Unit1..TForm1 0001:00055498 Unit1.CrashMe 0004:00004CC8 Unit1.Form1 0001:000554C8 Unit1.TForm1.Button1Click
为什么AV说地址004146CF,而MAP文件说0001:00055498?
即使我减去CODE段的起始地址(0001),我仍然得到004146CF-00401000 = 136CF,这也不是我想要的.
我也试图通过搜索字符串“:00414”找到错误地址,但它没有找到任何内容.
如何在MAP文件中查找AV中的地址?
解决方法
Why does the AV say address 004146CF,while the MAP file says 0001:00055498 ?
004146CF是在运行时崩溃的代码指令的实际内存地址,而.map文件中的地址是相对的,因为在编译时不知道进程的实际加载地址.
Even if I subtract the start address of the CODE segment (0001)
0001不是地址,更不用说起始地址了.它只是在给定段的.map文件顶部定义的ID号. 0001:00055498指的是被标识为0001的段内的相对地址00055498.
I still get 004146CF-00401000 = 136CF,which is not what I am looking for either.
通常,进程的加载地址为$400000(实际值在Project Options中定义,默认为$400000),但由于各种原因(例如重新编译),在运行时可能会有所不同.确定实际加载地址后,需要在进程中包含代码段的实际偏移量.该偏移量通常为1000美元(实际值在已编译的可执行文件的PE头中定义).因此,要将运行时的内存地址映射到.map文件中的地址,通常会从运行时内存地址中减去$401000.价值观可能不同!
在这种情况下,结果值136CF将是您要在.map文件中使用0001代码段查找的项目.您不太可能找到完全匹配,因为崩溃的代码很可能在函数的中间,很少在函数的最开始.所以你会寻找一个起始地址最接近136CF而不超过它的.map项目.
您没有显示整个.map文件,因此您的代码段中没有接近136CF的项目.但实际的崩溃并不像你期望的那样在CrashMe本身.它实际上是CrashMe()内部调用的另一个函数内部.设置TMemo.Text属性调用TWinControl.SetText(),它调用TControl.GetText(),它调用TWinControl.GetTextLen(),当尝试访问无效TMemo对象的FHandle或FText数据成员时崩溃:
procedure TWinControl.SetText(const Value: TCaption); begin if GetText <> Value then // <-- here begin if WindowHandle <> 0 then Perform(WM_SETTEXT,string(Value)) else FText := Value; Perform(CM_TEXTCHANGED,0); end; end; function TControl.GetText: TCaption; {$IF DEFINED(CLR)} begin Result := GetTextPiece(GetTextLen); end; {$ELSE} var Len: Integer; begin Len := GetTextLen; // <-- here SetString(Result,PChar(nil),Len); if Len <> 0 then begin Len := Len - GetTextBuf(PChar(Result),Len + 1); if Len > 0 then SetLength(Result,Length(Result) - Len); end; end; {$IFEND} function TWinControl.GetTextLen: Integer; begin if WindowHandle <> 0 then // <-- here Result := Perform(WM_GETTEXTLENGTH,0) else Result := Length(FText); // <-- or here end;
在诊断AV时,如果要将崩溃映射到CrashMe(),则仅具有AV的内存地址是不够的,因为该内存地址不在CrashMe()内部.您需要一个完整的堆栈跟踪通向AV,以显示CrashMe()在某个时刻被调用并进行后续调用,从而导致实际的AV. .map文件无法帮助您获得堆栈跟踪,您需要一个在崩溃时处理它的运行时库,例如JclDebug,MadExcept,EurekaLog等.