From c53e775af5199ef7e2526ce6b33ec579df0ee4eb Mon Sep 17 00:00:00 2001 From: joost Date: Sat, 25 Apr 2015 19:15:09 +0000 Subject: [PATCH] FpDebug: The developers at Apple were not able to build a linker with the ability to deal with Dwarf-debug info. Added the ability to read the Dwarf-debug info from the object files and to map the corresponding addresses to their position in the final executable. git-svn-id: trunk@48864 - --- components/fpdebug/fpdbgdarwinclasses.pas | 28 +- components/fpdebug/fpdbgdwarf.pas | 2 + components/fpdebug/fpdbgdwarfdataclasses.pas | 51 +++- components/fpdebug/fpdbgloader.pp | 30 +- components/fpdebug/fpimgreaderelf.pas | 6 +- components/fpdebug/fpimgreadermacho.pas | 281 ++++++++++++++++++- components/fpdebug/fpimgreaderwinpe.pas | 6 +- 7 files changed, 379 insertions(+), 25 deletions(-) diff --git a/components/fpdebug/fpdbgdarwinclasses.pas b/components/fpdebug/fpdbgdarwinclasses.pas index d7991b7723..06dc24b339 100644 --- a/components/fpdebug/fpdbgdarwinclasses.pas +++ b/components/fpdebug/fpdbgdarwinclasses.pas @@ -16,6 +16,7 @@ uses DbgIntfBaseTypes, FpDbgLinuxExtra, FpDbgDwarfDataClasses, + FpImgReaderMacho, FpDbgInfo, MacOSAll, FpDbgUtil, @@ -639,9 +640,12 @@ end; procedure TDbgDarwinProcess.InitializeLoaders; var dSYMFilename: string; + PrimaryLoader: TDbgImageLoader; ALoader: TDbgImageLoader; begin - LoaderList.Add(TDbgImageLoader.Create(FExecutableFilename)); + ALoader:=nil; + PrimaryLoader := TDbgImageLoader.Create(FExecutableFilename); + LoaderList.Add(PrimaryLoader); // JvdS: Mach-O binaries do not contain DWARF-debug info. Instead this info // is stored inside the .o files, and the executable contains a map (in stabs- @@ -657,13 +661,23 @@ begin if FileExists(dSYMFilename) then begin ALoader := TDbgImageLoader.Create(dSYMFilename); - if GUIDToString(ALoader.UUID)<>GUIDToString(LoaderList[0].UUID) then - log('The unique UUID''s of the executable and the dSYM bundle with debug-info ('+dSYMFilename+') do not match. This can lead to problems during debugging.', dllInfo); + if GUIDToString(ALoader.UUID)<>GUIDToString(PrimaryLoader.UUID) then + begin + log('The unique UUID''s of the executable and the dSYM bundle with debug-info ('+dSYMFilename+') do not match.', dllDebug); + FreeAndNil(ALoader); + end + else + begin + log('Load debug-info from dSYM bundle ('+dSYMFilename+').', dllDebug); + LoaderList.Add(ALoader); + end; + end; - LoaderList.Add(ALoader); - end - else - log('No dSYM bundle ('+dSYMFilename+') found.', dllInfo); + if not assigned(ALoader) then + begin + log('Read debug-info from separate object files.', dllDebug); + TDbgMachoDataSource.LoadSubFiles(PrimaryLoader.SubFiles, LoaderList); + end; end; function TDbgDarwinProcess.CreateThread(AthreadIdentifier: THandle; out IsMainThread: boolean): TDbgThread; diff --git a/components/fpdebug/fpdbgdwarf.pas b/components/fpdebug/fpdbgdwarf.pas index e143aec383..fb7056341e 100644 --- a/components/fpdebug/fpdbgdwarf.pas +++ b/components/fpdebug/fpdbgdwarf.pas @@ -2897,6 +2897,8 @@ begin if LocationParser.ResultKind in [lseValue] then begin AnAddress := TargetLoc(LocationParser.ResultData); + if ATag=DW_AT_location then + AnAddress.Address :=CompilationUnit.MapAddressToNewValue(AnAddress.Address); Result := True; end else diff --git a/components/fpdebug/fpdbgdwarfdataclasses.pas b/components/fpdebug/fpdbgdwarfdataclasses.pas index 5966f4be87..44ad0c5f61 100644 --- a/components/fpdebug/fpdbgdwarfdataclasses.pas +++ b/components/fpdebug/fpdbgdwarfdataclasses.pas @@ -465,6 +465,7 @@ type TDwarfDebugFile = record Sections: array[TDwarfSection] of TDwarfSectionInfo; + AddressMapList: TDbgAddressMapList; end; PDwarfDebugFile = ^TDwarfDebugFile; @@ -544,6 +545,8 @@ type function ReadValue(AAttribute: Pointer; AForm: Cardinal; out AValue: String): Boolean; function ReadValue(AAttribute: Pointer; AForm: Cardinal; out AValue: PChar): Boolean; function ReadValue(AAttribute: Pointer; AForm: Cardinal; out AValue: TByteDynArray): Boolean; + // Read a value that contains an address. The address is evaluated using MapAddressToNewValue + function ReadAddressValue(AAttribute: Pointer; AForm: Cardinal; out AValue: QWord): Boolean; public constructor Create(AOwner: TFpDwarfInfo; ADebugFile: PDwarfDebugFile; ADataOffset: QWord; ALength: QWord; AVersion: Word; AAbbrevOffset: QWord; AAddressSize: Byte; AIsDwarf64: Boolean); virtual; @@ -554,6 +557,9 @@ type function GetLineAddress(const AFileName: String; ALine: Cardinal): TDbgPtr; procedure BuildLineInfo(AAddressInfo: PDwarfAddressInfo; ADoAll: Boolean); function FullFileName(const AFileName:string): String; + // On Darwin it could be that the debug-information is not included into the executable by the linker. + // This function is to map object-file addresses into the corresponding addresses in the executable. + function MapAddressToNewValue(AValue: QWord): QWord; property Valid: Boolean read FValid; property FileName: String read FFileName; @@ -2677,7 +2683,10 @@ begin end else if (Form = DW_FORM_ref_addr) then begin - Result := FCompUnit.ReadValue(InfoData, Form, Offs); + if FCompUnit.Version=2 then + Result := FCompUnit.ReadAddressValue(InfoData, Form, Offs) + else + Result := FCompUnit.ReadValue(InfoData, Form, Offs); if not Result then exit; AValue := FCompUnit.DebugFile^.Sections[dsInfo].RawData + Offs; @@ -2819,6 +2828,7 @@ begin SetLength(FFiles, ALoaderList.Count); for i := 0 to ALoaderList.Count-1 do begin + FFiles[i].AddressMapList:=ALoaderList[i].AddressMapList; for Section := Low(Section) to High(Section) do begin p := ALoaderList[i].Section[DWARF_SECTION_NAME[Section]]; @@ -3191,6 +3201,7 @@ begin if FOwner.FLineInfo.Addr64 then FAddress := PQWord(pbyte(FLineInfoPtr)+1)^ else FAddress := PLongWord(pbyte(FLineInfoPtr)+1)^; + FAddress:=FOwner.MapAddressToNewValue(FAddress); end; DW_LNE_define_file: begin // don't move pb, it's done at the end by instruction length @@ -3437,10 +3448,10 @@ begin if (dafHasLowAddr in AttribList.Abbrev^.flags) and LocateAttribute(Scope.Entry, DW_AT_low_pc, AttribList, Attrib, Form) then begin - ReadValue(Attrib, Form, Info.StartPC); + ReadAddressValue(Attrib, Form, Info.StartPC); if LocateAttribute(Scope.Entry, DW_AT_high_pc, AttribList, Attrib, Form) - then ReadValue(Attrib, Form, Info.EndPC) + then ReadAddressValue(Attrib, Form, Info.EndPC) else Info.EndPC := Info.StartPC; // TODO (dafHasName in Abbrev.flags) @@ -3652,10 +3663,10 @@ begin end; if LocateAttribute(Scope.Entry, DW_AT_low_pc, AttribList, Attrib, Form) - then ReadValue(Attrib, Form, FMinPC); + then ReadAddressValue(Attrib, Form, FMinPC); if LocateAttribute(Scope.Entry, DW_AT_high_pc, AttribList, Attrib, Form) - then ReadValue(Attrib, Form, FMaxPC); + then ReadAddressValue(Attrib, Form, FMaxPC); if FMinPC = 0 then FMinPC := FMaxPC; if FMaxPC = 0 then FMAxPC := FMinPC; @@ -3984,6 +3995,29 @@ begin if AIncPointer then inc(AData, FAddressSize); end; +function TDwarfCompilationUnit.MapAddressToNewValue(AValue: QWord): QWord; +var + i: Integer; + AddrMap: TDbgAddressMap; +begin + result := avalue; + if assigned(DebugFile^.AddressMapList) then + for i := 0 to DebugFile^.AddressMapList.Count-1 do + begin + AddrMap:=DebugFile^.AddressMapList[i]; + if AddrMap.OrgAddr=AValue then + begin + result:=AddrMap.NewAddr; + break; + end + else if (AddrMap.OrgAddr DwarfDebugMap.ObjFileAge then + debugln(Format('The timestamp of the object-file "%s" does not correspond to the timestamp (%s) stored inside the executable. The debug-info in this file is not loaded.', [DwarfDebugMap.ObjectFile, DateTimeToStr(FileDatetoDateTime(DwarfDebugMap.ObjFileAge))])) + else + begin + Loader := TDbgImageLoader.Create(DwarfDebugMap.ObjectFile, DwarfDebugMap); + ALoaderList.Add(Loader); + end; + end + else + DebugLn('File with debug-info "'+DwarfDebugMap.ObjectFile+'" does not exist. This could lead to missing debug-information.'); + end; + end; +end; + procedure TDbgMachoDataSource.ParseSymbolTable(AfpSymbolInfo: TfpSymbolList); var p: PDbgImageSection; @@ -138,6 +229,25 @@ begin fileRead := true; end; +function TDbgMachoDataSource.GetSubFiles: TStrings; +begin + if not assigned(fSubFiles) then + begin + fSubFiles:=TStringList.Create; + fSubFiles.OwnsObjects:=true; + end; + Result:=fSubFiles; +end; + +function TDbgMachoDataSource.GetAddressMapList: TDbgAddressMapList; +begin + if not assigned(fAddressMapList) then + begin + fAddressMapList:=TDbgAddressMapList.Create; + end; + Result:=fAddressMapList; +end; + function TDbgMachoDataSource.GetSymTableSectionInfo(StabStr: Boolean; var SectionOffset, SectionSize: Int64): Boolean; begin @@ -188,7 +298,7 @@ begin fSource.LoadMemory(ex^.Offs, Result^.Size, Result^.RawData); end; -constructor TDbgMachoDataSource.Create(ASource: TDbgFileLoader; OwnSource: Boolean); +constructor TDbgMachoDataSource.Create(ASource: TDbgFileLoader; ADebugMap: TObject; OwnSource: Boolean); const SymbolsSectionName : array [Boolean] of AnsiString = (_symbol, _symbolstrings); var @@ -248,11 +358,20 @@ DebugLn(Name); FSections.AddObject(SymbolsSectionName[true], TObject(p)); end; - inherited Create(ASource, OwnSource); + if assigned(ADebugMap) then + ParseSubAppleDwarfDataMap(ADebugMap) + else + ParseMainAppleDwarfDataMap; + + inherited Create(ASource, ADebugMap, OwnSource); end; destructor TDbgMachoDataSource.Destroy; begin + if assigned(fSubFiles) then + fSubFiles.Free; + if assigned(fAddressMapList) then + fAddressMapList.Free; if Assigned(fFile) then fFile.Free; if fOwnSource then fSource.Free; while FSections.Count > 0 do begin @@ -387,6 +506,160 @@ begin Result := fSource.Read(sofs + Offset, sz, @Buf[0]); end; +procedure TDbgMachoDataSource.ParseMainAppleDwarfDataMap; +var + p: PDbgImageSection; + ps: PDbgImageSection; + SymbolArr: PnlistArray; + SymbolStr: pointer; + i: integer; + SymbolCount: integer; + State: TDebugTableState; + AddressMap: TDbgAddressMap; + ProcName: string; + DwarfDebugMap: TAppleDwarfDebugMap; +begin + DwarfDebugMap:=nil; + p := Section[_symbol]; + ps := Section[_symbolstrings]; + if assigned(p) and assigned(ps) then + begin + SymbolArr:=PDbgImageSectionEx(p)^.Sect.RawData; + SymbolStr:=PDbgImageSectionEx(ps)^.Sect.RawData; + SymbolCount := PDbgImageSectionEx(p)^.Sect.Size div sizeof(nlist); + state := dtsEnd; + for i := 0 to SymbolCount-1 do + begin + case state of + dtsEnd: + begin + if SymbolArr^[i].n_type = N_SO then + begin + if assigned(DwarfDebugMap) then + DwarfDebugMap.Free; + DwarfDebugMap := TAppleDwarfDebugMap.Create; + DwarfDebugMap.Dir := pchar(SymbolStr+SymbolArr^[i].n_un.n_strx); + state := dtsDir; + end; + end; + dtsDir: + begin + if SymbolArr^[i].n_type = N_SO then + begin + DwarfDebugMap.SourceFile:=pchar(SymbolStr+SymbolArr^[i].n_un.n_strx); + inc(state); + end + else + state := dtsEnd; + end; + dtsSource: + begin + if SymbolArr^[i].n_type = N_OSO then + begin + DwarfDebugMap.ObjectFile:=pchar(SymbolStr+SymbolArr^[i].n_un.n_strx); + DwarfDebugMap.ObjFileAge:=SymbolArr^[i].n_value; + inc(state); + end; + end; + dtsObjectFile: + begin + if (SymbolArr^[i].n_type = N_BNSYM) then + begin + inc(state); + end + else if (SymbolArr^[i].n_type = N_STSYM) then + begin + AddressMap.NewAddr:=SymbolArr^[i].n_value; + AddressMap.OrgAddr:=0; + AddressMap.Length:=0; + DwarfDebugMap.GlobalList.Add(pchar(SymbolStr+SymbolArr^[i].n_un.n_strx), AddressMap); + end + else if (SymbolArr^[i].n_type = N_SO) and (SymbolArr^[i].n_sect=1) then + begin + state := dtsEnd; + SubFiles.AddObject(DwarfDebugMap.ObjectFile, DwarfDebugMap); + DwarfDebugMap:=nil; + end; + end; + dtsProc: + begin + if (SymbolArr^[i].n_type = N_FUN) and (SymbolArr^[i].n_sect=1) then + begin + AddressMap.NewAddr:=SymbolArr^[i].n_value; + ProcName:=pchar(SymbolStr+SymbolArr^[i].n_un.n_strx); + inc(state); + end; + end; + dtsProcLen: + begin + if (SymbolArr^[i].n_type = N_FUN) and (SymbolArr^[i].n_sect=0) then + begin + AddressMap.Length:=SymbolArr^[i].n_value; + inc(state); + end; + end; + dtsProcEnd: + begin + if (SymbolArr^[i].n_type = N_ENSYM) and (SymbolArr^[i].n_sect=1) then + begin + DwarfDebugMap.GlobalList.Add(ProcName, AddressMap); + state := dtsObjectFile; + end; + end; + end; + end; + end; +end; + +procedure TDbgMachoDataSource.ParseSubAppleDwarfDataMap(ADebugMap: TObject); +var + p: PDbgImageSection; + ps: PDbgImageSection; + SymbolArr: PnlistArray; + SymbolStr: pointer; + i: integer; + SymbolCount: integer; + MainDwarfDebugMap: TAppleDwarfDebugMap; + ind: THTCustomNode; + AddressMap: TDbgAddressMap; + s: string; +begin + MainDwarfDebugMap:=TAppleDwarfDebugMap(ADebugMap); + p := Section[_symbol]; + ps := Section[_symbolstrings]; + if assigned(p) and assigned(ps) then + begin + SymbolArr:=PDbgImageSectionEx(p)^.Sect.RawData; + SymbolStr:=PDbgImageSectionEx(ps)^.Sect.RawData; + SymbolCount := PDbgImageSectionEx(p)^.Sect.Size div sizeof(nlist); + for i := 0 to SymbolCount-1 do + begin + if SymbolArr^[i].n_type = N_SECT then + begin + s := pchar(SymbolStr+SymbolArr^[i].n_un.n_strx); + ind := MainDwarfDebugMap.GlobalList.Find(s); + if assigned(ind) then + begin + AddressMap:=MainDwarfDebugMap.GlobalList.Items[s]; + AddressMap.OrgAddr:=SymbolArr^[i].n_value; + AddressMapList.Add(AddressMap); + end; + end; + if SymbolArr^[i].n_type = N_SECT+N_EXT then + begin + s := pchar(SymbolStr+SymbolArr^[i].n_un.n_strx); + ind := MainDwarfDebugMap.GlobalList.Find(s); + if assigned(ind) then + begin + AddressMap:=MainDwarfDebugMap.GlobalList.Items[s]; + AddressMap.OrgAddr:=SymbolArr^[i].n_value; + AddressMapList.Add(AddressMap); + end; + end; + end; + end; +end; + initialization RegisterImageReaderClass( TDbgMachoDataSource ); diff --git a/components/fpdebug/fpimgreaderwinpe.pas b/components/fpdebug/fpimgreaderwinpe.pas index e7806b84d1..bc5c05619f 100644 --- a/components/fpdebug/fpimgreaderwinpe.pas +++ b/components/fpdebug/fpimgreaderwinpe.pas @@ -60,7 +60,7 @@ type class function isValid(ASource: TDbgFileLoader): Boolean; override; class function UserName: AnsiString; override; public - constructor Create(ASource: TDbgFileLoader; OwnSource: Boolean); override; + constructor Create(ASource: TDbgFileLoader; ADebugMap: TObject; OwnSource: Boolean); override; destructor Destroy; override; procedure ParseSymbolTable(AfpSymbolInfo: TfpSymbolList); override; end; @@ -92,7 +92,7 @@ begin end; end; -constructor TPEFileSource.Create(ASource: TDbgFileLoader; OwnSource: Boolean); +constructor TPEFileSource.Create(ASource: TDbgFileLoader; ADebugMap: TObject; OwnSource: Boolean); begin FSections := TStringList.Create; FSections.Sorted := True; @@ -102,7 +102,7 @@ begin FFileLoader:=ASource; FOwnLoader:=OwnSource; LoadSections; - inherited Create(ASource, OwnSource); + inherited Create(ASource, ADebugMap, OwnSource); end; destructor TPEFileSource.Destroy;