2016-07-05 1 views
4

내 사용자 중 일부가 내가 분석하고자하는 드문 AccessViolations를보고했습니다.지도 파일에서 AccessViolation의 주소 확인

나는 정확히 그 Build의 소스 코드를 가지고 있으므로 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; 

액세스 위반은 다음과 같습니다 :

주소 004146CF

나는 설정을 예를 들어 있습니다. 주소 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 

맵 파일이 0001라고하면서 왜 AV는 주소 004146CF은 말하는가 : 00,055,498를?

CODE 세그먼트 (0001)의 시작 주소를 뺄 경우에도 여전히 004146CF-00401000 = 136CF를 얻습니다. 이는 내가 찾고있는 것이 아닙니다.

또한 ": 00414"문자열을 검색하여 오류 주소를 찾으려고했지만 아무것도 찾지 못했습니다.

어떻게 MAP 파일의 AV에서 주소를 조회 할 수 있습니까?

+3

사람들이 질문을 downvote 할 때 너무나 싫어합니다. 질문에 무엇이 잘못되었는지 알려줍니다. 나는 몇 시간 봤 거든이 메모리 주소 사이에 올바른 변환 기법을 찾지 못했습니다. –

+0

Jedi Code Library에서 코드를 검색 할 수 있습니다.그러나 실제로지도 파일에 제공된 주소 범위를 검색하는 종류입니다. 예외가 발생한 장소에 대해 얼마나 정확하게 말할 수 있는지지도 파일 (라인 번호 또는 루틴에 대한)의 detalization에 달려 있습니다 ... 하지만 주로 JvDebugHandler를 사용할 수 있습니다 - 이미 완료된 모든 것이 있습니다! –

+1

앱에 중단 점을 넣고 중단 점에 도달 할 때까지 ot를 실행합니다. 그런 다음 IDE에서 검색 | 오류를 찾아 주소를 입력하십시오. – MartynA

답변

4

왜 AV에서 주소가 004146CF이고 MAP 파일은 0001 : 00055498입니까? .map 파일의 어드레스 프로세스의 실제로드 어드레스는 컴파일 시간에 공지되지 않기 때문에, 상대을 반면

004146CF는 런타임에 충돌 코드 명령의 실제 메모리 어드레스이다.내가 코드 세그먼트 (0001)

0001의 시작 주소를 빼더라도

는 주소는 커녕 시작 주소가 아닙니다. 주어진 세그먼트에 대해 .map 파일 상단에 정의 된 ID 번호 일뿐입니다. 0001:000554980001으로 식별 된 세그먼트 내의 상대 주소 00055498을 나타냅니다.

나는 여전히 004146CF-00401000 = 136CF를 얻었는데, 이는 내가 찾고있는 것이 아닙니다.

보통 프로세스의로드 주소는 $400000입니다 (실제 값이 프로젝트 옵션에 정의와 기본적으로 $400000입니다)하지만, 그 때문에 같은 재 등 다양한 이유에 런타임시 다를 수있다 기초. 실제 주소 인을 확인한 후에 프로세스 내에서 코드 세그먼트의 실제 오프셋을 포함시켜야합니다. 그 오프셋은 보통$1000입니다 (실제 값은 컴파일 된 실행 파일의 PE 헤더에 정의됩니다). 따라서 런타임에 메모리 주소를 .map 파일의 주소로 매핑하려면 보통을 런타임 메모리 주소에서 $401000 빼십시오. 값 일 수 있습니다!

이 경우 결과 값 136CF.map 파일의 0001 코드 세그먼트를 검색하려는 항목이됩니다. 크래시 된 코드가 기능의 시작 부분에있는 경우가 거의없고 중간 일 가능성이 높으므로 EXACT과 일치 할 가능성이 없습니다. 따라서 시작 주소가 136CF에 가장 근접하지 않는 .map 항목을 찾습니다.

전체 .map 파일을 표시하지 않았으므로 코드 단편에 136CF에 가까운 항목이 없습니다. 그러나 실제 충돌은 CrashMe 자체가 아니므로 예상하고 있습니다. 실제로 CrashMe()이 내부적으로 호출하는 다른 함수 안에 있습니다. 하기 AV를 진단 할 때 매핑 할 경우,

procedure TWinControl.SetText(const Value: TCaption); 
begin 
    if GetText <> Value then // <-- here 
    begin 
    if WindowHandle <> 0 then 
     Perform(WM_SETTEXT, 0, string(Value)) 
    else 
     FText := Value; 
    Perform(CM_TEXTCHANGED, 0, 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, 0) 
    else 
    Result := Length(FText); // <-- or here 
end; 

다음 TMemo.Text 속성을 설정 잘못된 TMemo 객체의 FHandle 또는 FText 데이터 멤버에 액세스하려고 할 때 충돌하는 TWinControl.GetTextLen()를 호출하는 TControl.GetText()를 호출하는 TWinControl.SetText() 호출 충돌은 CrashMe()으로, 그 메모리 주소가 CrashMe() 내부에 없기 때문에 AV의 메모리 주소를 갖는 것만으로는 충분하지 않습니다. 어떤 시점에서 CrashMe()이 호출되었고 실제 AV를 유발하는 후속 호출을했다는 것을 보여주기 위해 AV까지 이어지는 전체 스택 추적이 필요합니다. A .map 파일은 스택 추적을 얻는 데 도움이되지 않습니다. JclDebug, MadExcept, EurekaLog 등과 같이 충돌시이를 처리하는 런타임 라이브러리가 필요합니다.

1

세부 맵 파일에는 식별자 이름별로 정렬 된 섹션과 주소별로 정렬 된 섹션 (실제로 RVA, 상대 가상 주소 별)이 포함되어야합니다. 실제 주소를 RVA로 변환하는 기술이 정확합니다. 주소별로 정렬 된 섹션으로 이동하여 가장 가까운 주소를 찾지 만 $ 136cf 이하의 주소를 찾으십시오. 그것은 충돌이 일어나고있는 기능이어야합니다.

그러나 디버그 DCU로 빌드하고 있는지 확인해야합니다. 그렇지 않으면 코드의 프로그램 부분에 대한 주소 만 표시됩니다.

DLL/패키지의 경우 PE 파일에 지정된 기본값 이외의 주소로로드 될 가능성이 높습니다. 이 경우 특정 모듈의 기본 주소를 찾아야합니다. 모듈보기 (Ctrl-Alt-M 또는보기 | 디버그보기 | 모듈 ...)를 엽니 다. 기본 주소가 크래시 주소와 가장 비슷하지만 크래시 주소보다 큰 모듈을 찾으십시오. 해당 모듈의 주소는 "기본 주소"가됩니다. 이 값을 사용하여 RVA를 계산 한 다음 해당 모듈의 MAP 파일로 이동하여 위치를 찾습니다.