mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-04-05 10:37:58 +02:00
857 lines
25 KiB
ObjectPascal
857 lines
25 KiB
ObjectPascal
unit FpImgReaderMacho;
|
|
|
|
{$mode objfpc}{$H+}
|
|
{$IFDEF INLINE_OFF}{$INLINE OFF}{$ENDIF}
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes, SysUtils, contnrs,
|
|
LazFglHash, {$ifdef FORCE_LAZLOGGER_DUMMY} LazLoggerDummy {$else} LazLoggerBase {$endif}, LazFileUtils, LazUTF8,
|
|
DbgIntfBaseTypes,
|
|
// FpDebug
|
|
macho, FpImgReaderMachoFile, FpImgReaderBase, fpDbgSymTable, FpDbgUtil, FpDbgLoader, FpDbgCommon;
|
|
|
|
type
|
|
|
|
{ TDbgMachoDataSource }
|
|
|
|
TDbgMachoDataSource = class(TDbgImageReader)
|
|
private
|
|
fSource : TDbgFileLoader;
|
|
FSections: TStringListUTF8Fast;
|
|
fSubFiles: TStringList;
|
|
fAddressMapList: TDbgAddressMapList;
|
|
fOwnSource : Boolean;
|
|
fFile : TMachoFile;
|
|
hasSymTable : Boolean;
|
|
StabsCmd : symtab_command;
|
|
fileRead : Boolean;
|
|
function GetSectionInfo(const SectionName: AnsiString; var Size: int64): Boolean;
|
|
function GetSectionData(const SectionName: AnsiString; Offset, {%H-}Size: Int64; var Buf: array of byte): Int64;
|
|
procedure ParseMainAppleDwarfDataMap;
|
|
procedure ParseSubAppleDwarfDataMap(ADebugMap: TObject);
|
|
protected
|
|
procedure ReadFile;
|
|
function GetSubFiles: TStrings; override;
|
|
function GetAddressMapList: TDbgAddressMapList; override;
|
|
function GetSymTableSectionInfo({%H-}StabStr: Boolean; var {%H-}SectionOffset, {%H-}SectionSize: Int64): Boolean;
|
|
function GetSectionIndex(const SectionName: AnsiString): Integer;
|
|
|
|
function GetSection(const AName: String): PDbgImageSection; override;
|
|
function GetSection(const ID: integer): PDbgImageSection; override;
|
|
public
|
|
class function isValid(ASource: TDbgFileLoader): Boolean; override;
|
|
class function UserName: AnsiString; override;
|
|
class procedure LoadSubFiles(ASubFiles: TStrings; ALoaderList: TDbgImageLoaderList);
|
|
procedure AddSubFilesToLoaderList(ALoaderList: TObject; PrimaryLoader: TObject); override;
|
|
procedure ParseSymbolTable(AfpSymbolInfo: TfpSymbolList); override;
|
|
public
|
|
constructor Create(ASource: TDbgFileLoader; ADebugMap: TObject; ALoadedTargetImageAddr: TDbgPtr; OwnSource: Boolean); override;
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
implementation
|
|
|
|
var
|
|
DBG_VERBOSE, DBG_WARNINGS: PLazLoggerLogGroup;
|
|
|
|
type
|
|
PnlistArray = ^nlist; // ^array[0..infinite] of nlist;
|
|
PnlistArray64 = ^nlist_64; // ^array[0..infinite] of nlist_64;
|
|
|
|
const
|
|
// Symbol-map section name
|
|
_symbol = '.symbols';
|
|
_symbolstrings = '.symbolsstrings';
|
|
|
|
type
|
|
TDebugTableState = (dtsDir, dtsSource, dtsObjectFile, dtsProc, dtsProcLen, dtsProcEnd, dtsEnd);
|
|
TDebugTableEntry = record
|
|
Dir: string;
|
|
SourceFile: string;
|
|
ObjectFile: string;
|
|
ObjFileAge: longint;
|
|
Offset: TDBGPtr;
|
|
end;
|
|
|
|
{ TAppleDwarfDebugMap }
|
|
|
|
TAppleDwarfDebugMap = class(TObject)
|
|
private
|
|
//FAddressMap: TDbgAddressMapList;
|
|
FDir: string;
|
|
FGlobalList: TDbgAddressMapHashList;
|
|
FObjectFile: string;
|
|
FObjFileAge: longint;
|
|
FOffset: TDBGPtr;
|
|
FSourceFile: string;
|
|
public
|
|
constructor create;
|
|
destructor destroy; override;
|
|
procedure clear;
|
|
property Offset: TDBGPtr read FOffset write FOffset;
|
|
property Dir: string read FDir write FDir;
|
|
property ObjectFile: string read FObjectFile write FObjectFile;
|
|
property SourceFile: string read FSourceFile write FSourceFile;
|
|
property ObjFileAge: longint read FObjFileAge write FObjFileAge;
|
|
// This list contains the locations for all symbols in the executable,
|
|
// parsed from the 'stabs'-debuginfo in the executable.
|
|
// This property is only available in the debug-map for the main
|
|
// executable.
|
|
property GlobalList: TDbgAddressMapHashList read FGlobalList;
|
|
// This list maps addresses in an object-file to the addresses
|
|
// in the main executable.
|
|
// This property is only available in the debug-map for a specific
|
|
// object-file.
|
|
//property AddressMap: TDbgAddressMapList read FAddressMap;
|
|
end;
|
|
|
|
function isValidMachoStream(ASource: TDbgFileLoader): Boolean;
|
|
var
|
|
header : mach_header;
|
|
begin
|
|
try
|
|
Result := Assigned(ASource);
|
|
if not Result then Exit;
|
|
Result := ASource.Read(0, sizeof(header), @header) = sizeof(header);
|
|
if not Result then Exit;
|
|
Result := (header.magic = MH_CIGAM) or (header.magic = MH_MAGIC) or
|
|
(header.magic = MH_CIGAM_64) or (header.magic = MH_MAGIC_64);
|
|
except
|
|
Result := false;
|
|
end;
|
|
end;
|
|
|
|
function FixMachoName(const macsectionname: String): String;
|
|
begin
|
|
if Copy(macsectionName, 1, 2) = '__' then
|
|
Result:= '.'+Copy(macsectionName, 3, length(macsectionName)-2)
|
|
else
|
|
Result := macsectionname;
|
|
end;
|
|
|
|
constructor TAppleDwarfDebugMap.create;
|
|
begin
|
|
FGlobalList := TDbgAddressMapHashList.Create;
|
|
//FAddressMap := TDbgAddressMapList.Create;
|
|
end;
|
|
|
|
destructor TAppleDwarfDebugMap.destroy;
|
|
begin
|
|
//FAddressMap.Free;
|
|
FGlobalList.Free;
|
|
inherited destroy;
|
|
end;
|
|
|
|
procedure TAppleDwarfDebugMap.clear;
|
|
begin
|
|
//FAddressMap.Clear;
|
|
FDir := '';
|
|
FGlobalList.Clear;
|
|
FObjectFile := '';
|
|
FObjFileAge := 0;
|
|
FOffset := 0;
|
|
FSourceFile := '';
|
|
end;
|
|
|
|
{ TDbgMachoDataSource }
|
|
|
|
class function TDbgMachoDataSource.isValid(ASource: TDbgFileLoader): Boolean;
|
|
begin
|
|
Result := isValidMachoStream(ASource);
|
|
end;
|
|
|
|
class function TDbgMachoDataSource.UserName: AnsiString;
|
|
begin
|
|
Result:='mach-o file';
|
|
end;
|
|
|
|
class procedure TDbgMachoDataSource.LoadSubFiles(ASubFiles: TStrings; ALoaderList: TDbgImageLoaderList);
|
|
var
|
|
DwarfDebugMap: TAppleDwarfDebugMap;
|
|
Loader: TDbgImageLoader;
|
|
i: Integer;
|
|
begin
|
|
if assigned(ASubFiles) then
|
|
begin
|
|
for i := 0 to ASubFiles.Count -1 do
|
|
begin
|
|
if (ASubFiles.Objects[i] is TAppleDwarfDebugMap) and FileExists(ASubFiles[i]) then
|
|
begin
|
|
DwarfDebugMap:=TAppleDwarfDebugMap(ASubFiles.Objects[i]);
|
|
if FileAge(ASubFiles[i]) <> 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 "'+ASubFiles[i]+'" does not exist. This could lead to missing debug-information.');
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TDbgMachoDataSource.ParseSymbolTable(AfpSymbolInfo: TfpSymbolList);
|
|
var
|
|
p: PDbgImageSection;
|
|
ps: PDbgImageSection;
|
|
SymbolArr: PnlistArray;
|
|
SymbolArr64: PnlistArray64;
|
|
SymbolStr: pointer;
|
|
SymbolType: uint8_t;
|
|
StringOffset: uint32_t;
|
|
SymbolValue: TDBGPtr;
|
|
i: integer;
|
|
SymbolCount: integer;
|
|
begin
|
|
p := Section[_symbol];
|
|
ps := Section[_symbolstrings];
|
|
if assigned(p) and assigned(ps) then
|
|
begin
|
|
SymbolStr:=PDbgImageSectionEx(ps)^.Sect.RawData;
|
|
if TargetInfo.Bitness = b64 then
|
|
begin
|
|
SymbolArr64:=PDbgImageSectionEx(p)^.Sect.RawData;
|
|
SymbolCount := PDbgImageSectionEx(p)^.Sect.Size div sizeof(nlist_64);
|
|
end
|
|
else
|
|
begin
|
|
SymbolArr:=PDbgImageSectionEx(p)^.Sect.RawData;
|
|
SymbolCount := PDbgImageSectionEx(p)^.Sect.Size div sizeof(nlist);
|
|
end;
|
|
for i := 0 to SymbolCount-1 do
|
|
begin
|
|
if TargetInfo.Bitness = b64 then
|
|
begin
|
|
SymbolType := SymbolArr64[i].n_type;
|
|
StringOffset := SymbolArr64[i].n_un.n_strx;
|
|
SymbolValue := SymbolArr64[i].n_value;
|
|
end
|
|
else
|
|
begin
|
|
SymbolType := SymbolArr[i].n_type;
|
|
StringOffset := SymbolArr[i].n_un.n_strx;
|
|
SymbolValue := SymbolArr[i].n_value;
|
|
end;
|
|
if (SymbolType and $e0)<>0 then
|
|
// This is a stabs-entry. Ignore.
|
|
Continue;
|
|
if (SymbolType and $0e)=$e then
|
|
begin
|
|
// Section-index is ignored for now...
|
|
AfpSymbolInfo.Add(pchar(SymbolStr+StringOffset), SymbolValue);
|
|
end
|
|
end;
|
|
end;
|
|
|
|
end;
|
|
|
|
procedure TDbgMachoDataSource.ReadFile;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
if Assigned(fFile) then fFile.Free;
|
|
fFile:=TMachOFile.Create;
|
|
fFile.LoadFromFile(fSource);
|
|
for i := 0 to fFile.header.ncmds - 1 do begin
|
|
hasSymTable := fFile.commands[i]^.cmd = LC_SYMTAB;
|
|
if hasSymTable then begin
|
|
StabsCmd := psymtab_command(fFile.commands[i])^;
|
|
Break;
|
|
end;
|
|
end;
|
|
FTargetInfo := fFile.TargetInfo;
|
|
SetUUID(fFile.UUID);
|
|
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
|
|
Result := hasSymTable;
|
|
if not Result then Exit;
|
|
if StabStr then begin
|
|
SectionOffset := StabsCmd.stroff;
|
|
SectionSize := StabsCmd.strsize;
|
|
end else begin
|
|
SectionOffset := StabsCmd.symoff;
|
|
if TargetInfo.Bitness = b64 then
|
|
SectionSize := Int64(StabsCmd.nsyms * sizeof(nlist_64))
|
|
else
|
|
SectionSize := Int64(StabsCmd.nsyms * sizeof(nlist));
|
|
end;
|
|
end;
|
|
|
|
function TDbgMachoDataSource.GetSectionIndex(const SectionName: AnsiString): Integer;
|
|
var
|
|
i : Integer;
|
|
Name : AnsiString;
|
|
begin
|
|
//todo: hash-table
|
|
for i := 0 to fFile.Sections.Count - 1 do begin
|
|
with TMachoSection(fFile.sections[i]) do
|
|
if is32
|
|
then Name := FixMachoName(sec32.sectname)
|
|
else Name := FixMachoName(sec64.sectname);
|
|
if Name = SectionName then begin
|
|
Result := i;
|
|
Exit;
|
|
end;
|
|
end;
|
|
Result := -1;
|
|
end;
|
|
|
|
function TDbgMachoDataSource.GetSection(const AName: String): PDbgImageSection;
|
|
var
|
|
i: Integer;
|
|
ex: PDbgImageSectionEx;
|
|
begin
|
|
Result := nil;
|
|
i := FSections.IndexOf(AName);
|
|
if i < 0 then
|
|
exit;
|
|
ex := PDbgImageSectionEx(FSections.Objects[i]);
|
|
Result := @ex^.Sect;
|
|
if ex^.Loaded then
|
|
exit;
|
|
ex^.Loaded := True;
|
|
fSource.LoadMemory(ex^.Offs, Result^.Size, Result^.RawData);
|
|
end;
|
|
|
|
function TDbgMachoDataSource.GetSection(const ID: integer): PDbgImageSection;
|
|
var
|
|
ex: PDbgImageSectionEx;
|
|
begin
|
|
if (ID >= 0) and (ID < FSections.Count) then
|
|
begin
|
|
ex := PDbgImageSectionEx(FSections.Objects[ID]);
|
|
Result := @ex^.Sect;
|
|
Result^.Name := FSections[ID];
|
|
if not ex^.Loaded then
|
|
begin
|
|
ex^.Loaded := True;
|
|
fSource.LoadMemory(ex^.Offs, Result^.Size, Result^.RawData);
|
|
end;
|
|
end
|
|
else
|
|
Result := nil;
|
|
end;
|
|
|
|
procedure TDbgMachoDataSource.AddSubFilesToLoaderList(ALoaderList: TObject;
|
|
PrimaryLoader: TObject);
|
|
var
|
|
PLoader: TDbgImageLoader absolute PrimaryLoader;
|
|
LList: TDbgImageLoaderList absolute ALoaderList;
|
|
ALoader: TDbgImageLoader;
|
|
fname, dSYMFilename: String;
|
|
i: SizeInt;
|
|
begin
|
|
// 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-
|
|
// format) of all these .o files. An alternative to parsing this map and reading
|
|
// those .o files a dSYM-bundle could be used, which could be generated
|
|
// with dsymutil.
|
|
|
|
// PLoader.FileName in Contents/MacOS
|
|
ALoader:=nil;
|
|
|
|
fname := PLoader.FileName;
|
|
i := pos('/Contents/MacOS', fname);
|
|
if i > 0 then delete(fname, i, Length(fname));
|
|
dSYMFilename:=ChangeFileExt(PLoader.FileName, '.dSYM');
|
|
dSYMFilename:=dSYMFilename+'/Contents/Resources/DWARF/'+ExtractFileName(fname); // TDbgProcess.Name
|
|
|
|
if FilenameExtIs(dSYMFilename,'.app',true) then
|
|
dSYMFilename := ChangeFileExt(dSYMFilename,'');
|
|
|
|
if FileExists(dSYMFilename) then
|
|
begin
|
|
ALoader := TDbgImageLoader.Create(dSYMFilename);
|
|
if GUIDToString(ALoader.UUID)<>GUIDToString(PLoader.UUID) then
|
|
begin
|
|
AddReaderError('The unique UUID''s of the executable and the dSYM bundle with debug-info ('+dSYMFilename+') do not match.');
|
|
debugln(DBG_WARNINGS, 'The unique UUID''s of the executable and the dSYM bundle with debug-info ('+dSYMFilename+') do not match.');
|
|
FreeAndNil(ALoader);
|
|
end
|
|
else
|
|
begin
|
|
debugln(DBG_VERBOSE, 'Load debug-info from dSYM bundle ('+dSYMFilename+').');
|
|
LList.Add(ALoader);
|
|
end;
|
|
end;
|
|
|
|
if not assigned(ALoader) then
|
|
begin
|
|
debugln(DBG_VERBOSE, 'Read debug-info from separate object files.');
|
|
TDbgMachoDataSource.LoadSubFiles(PLoader.SubFiles, LList);
|
|
end;
|
|
end;
|
|
|
|
constructor TDbgMachoDataSource.Create(ASource: TDbgFileLoader; ADebugMap: TObject; ALoadedTargetImageAddr: TDbgPtr; OwnSource: Boolean);
|
|
const
|
|
SymbolsSectionName : array [Boolean] of AnsiString = (_symbol, _symbolstrings);
|
|
var
|
|
p: PDbgImageSectionEx;
|
|
fs: TMachOsection;
|
|
i: Integer;
|
|
Name: String;
|
|
soffset: int64;
|
|
ssize: int64;
|
|
begin
|
|
inherited Create(ASource, ADebugMap, ALoadedTargetImageAddr, OwnSource);
|
|
|
|
fSource := ASource;
|
|
fOwnSource := OwnSource;
|
|
|
|
ReadFile;
|
|
|
|
FSections := TStringListUTF8Fast.Create;
|
|
FSections.Sorted := True;
|
|
FSections.Duplicates := dupAccept;
|
|
FSections.CaseSensitive := False;
|
|
|
|
for i := 0 to fFile.sections.Count - 1 do begin
|
|
fs := TMachoSection(fFile.sections[i]);
|
|
New(p);
|
|
|
|
if fs.is32 then begin
|
|
Name := FixMachoName(fs.sec32.sectname);
|
|
P^.Offs := fs.sec32.offset;
|
|
p^.Sect.Size := fs.sec32.size;
|
|
end
|
|
else begin
|
|
Name := FixMachoName(fs.sec64.sectname);
|
|
P^.Offs := fs.sec64.offset;
|
|
p^.Sect.Size := fs.sec64.size;
|
|
end;
|
|
|
|
p^.Sect.VirtualAddress := 0; // Todo?
|
|
p^.Sect.IsLoadable := False; // Todo - needed for remote uploads via RSP only
|
|
p^.Loaded := False;
|
|
FSections.AddObject(Name, TObject(p));
|
|
end;
|
|
|
|
if GetSymTableSectionInfo(false, soffset, ssize) then begin
|
|
new(p);
|
|
p^.Offs:=soffset;
|
|
p^.Sect.Size:=ssize;
|
|
p^.Sect.VirtualAddress:=0;
|
|
p^.Loaded:=false;
|
|
FSections.AddObject(SymbolsSectionName[false], TObject(p));
|
|
end;
|
|
|
|
if GetSymTableSectionInfo(true, soffset, ssize) then begin
|
|
new(p);
|
|
p^.Offs:=soffset;
|
|
p^.Sect.Size:=ssize;
|
|
p^.Sect.VirtualAddress:=0;
|
|
p^.Loaded:=false;
|
|
FSections.AddObject(SymbolsSectionName[true], TObject(p));
|
|
end;
|
|
|
|
if assigned(ADebugMap) then
|
|
ParseSubAppleDwarfDataMap(ADebugMap)
|
|
else
|
|
ParseMainAppleDwarfDataMap;
|
|
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
|
|
Freemem(FSections.Objects[0]);
|
|
FSections.Delete(0);
|
|
end;
|
|
FreeAndNil(FSections);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
{function TDbgMachoDataSource.SectionsCount: Integer;
|
|
begin
|
|
if not Assigned(fFile) then ReadFile;
|
|
Result := fFile.Sections.Count;
|
|
if isStabs then inc(Result, 2);
|
|
end;
|
|
|
|
function TDbgMachoDataSource.GetSection(Index: Integer; var Name: AnsiString; var Size: Int64): Boolean;
|
|
var
|
|
cnt : Integer;
|
|
sstr : Boolean;
|
|
const
|
|
StabSectionName : array [Boolean] of AnsiString = (_stab, _stabstr);
|
|
begin
|
|
if not Assigned(fFile) then ReadFile;
|
|
cnt := fFile.Sections.Count;
|
|
if isStabs then inc(cnt, 2);
|
|
Result := (Index >= 0) and (Index < cnt);
|
|
if not Result then Exit;
|
|
|
|
if Index < fFile.Sections.Count then begin
|
|
with TMachoSection(fFile.sections[index]) do
|
|
if is32 then begin
|
|
Name := FixMachoName(sec32.sectname);
|
|
Size := sec32.size;
|
|
end else begin
|
|
Name := FixMachoName(sec64.sectname);
|
|
Size := sec64.size;
|
|
end;
|
|
end else begin
|
|
sstr := Index = cnt - 1;
|
|
Name := StabSectionName[sstr];
|
|
Result := GetStabSectionInfo(sstr, Size);
|
|
end;
|
|
end;
|
|
|
|
function TDbgMachoDataSource.GetSectionData(index: Integer; outStream: TStream): Boolean;
|
|
var
|
|
ofs : Int64;
|
|
sz : Int64;
|
|
begin
|
|
//todo: method will be removed
|
|
if not Assigned(outStream) then begin
|
|
Result := false;
|
|
Exit;
|
|
end;
|
|
if not Assigned(fFile) then ReadFile;
|
|
Result := (Index >= 0) and (Index < fFile.Sections.Count);
|
|
if not Result then Exit;
|
|
|
|
with TMachOsection(fFile.sections[index]) do begin
|
|
if is32 then begin
|
|
ofs := sec32.offset;
|
|
sz := sec32.size;
|
|
end else begin
|
|
ofs := sec64.offset;
|
|
sz := sec64.size;
|
|
end;
|
|
end;
|
|
if ofs > 0 then begin
|
|
fSource.Position:=ofs;
|
|
outStream.CopyFrom(fSource, sz);
|
|
end;
|
|
end;}
|
|
|
|
function TDbgMachoDataSource.GetSectionInfo(const SectionName: AnsiString; var Size: int64): Boolean;
|
|
var
|
|
idx : integer;
|
|
symtablestr : Boolean;
|
|
dummy : int64;
|
|
begin
|
|
if not fileRead then ReadFile;
|
|
|
|
symtablestr := (SectionName = _symbolstrings);
|
|
if symtablestr or (SectionName = _symbol) then
|
|
Result := GetSymTableSectionInfo(symtablestr, dummy, Size)
|
|
else begin
|
|
idx := GetSectionIndex(SectionName);
|
|
Result := idx >= 0;
|
|
if not Result then Exit;
|
|
|
|
with TMachOsection(fFile.sections[idx]) do
|
|
if is32
|
|
then Size := sec32.size
|
|
else Size := sec64.size;
|
|
end;
|
|
end;
|
|
|
|
function TDbgMachoDataSource.GetSectionData(const SectionName: AnsiString; Offset, Size: Int64; var Buf: array of byte): Int64;
|
|
var
|
|
idx : Integer;
|
|
sofs : int64;
|
|
ssize : int64;
|
|
symstr : Boolean;
|
|
sz : Int64;
|
|
s : TMachOsection;
|
|
begin
|
|
if not fileRead then ReadFile;
|
|
|
|
Result := 0;
|
|
symstr := SectionName = _symbolstrings;
|
|
if symstr or (SectionName = _symbol) then begin
|
|
if not GetSymTableSectionInfo(symstr, sofs, ssize) then
|
|
Exit;
|
|
end else begin
|
|
idx := GetSectionIndex(SectionName);
|
|
s := TMachOsection(fFile.sections[idx]);
|
|
if s.is32 then begin
|
|
ssize := s.sec32.size;
|
|
sofs := s.sec32.offset;
|
|
end else begin
|
|
sofs := s.sec64.offset;
|
|
ssize := s.sec64.size;
|
|
end;
|
|
end;
|
|
|
|
sz := ssize - Offset;
|
|
if sz < 0 then Exit;
|
|
|
|
//fSource.Position := sofs + Offset;
|
|
//Result := fSource.Read(Buf[0], sz);
|
|
Result := fSource.Read(sofs + Offset, sz, @Buf[0]);
|
|
end;
|
|
|
|
procedure TDbgMachoDataSource.ParseMainAppleDwarfDataMap;
|
|
var
|
|
p: PDbgImageSection;
|
|
ps: PDbgImageSection;
|
|
SymbolArr: PnlistArray;
|
|
SymbolArr64: PnlistArray64;
|
|
SymbolStr: pointer;
|
|
i: integer;
|
|
SymbolCount: integer;
|
|
SymbolType, SymbolSect: uint8_t;
|
|
StringOffset: uint32_t;
|
|
SymbolValue: QWord;
|
|
State: TDebugTableState;
|
|
AddressMap, AddressMapTmp: TDbgAddressMap;
|
|
ProcName: string;
|
|
DwarfDebugMap: TAppleDwarfDebugMap;
|
|
FullDwarfDebugMap: TDbgAddressMapPointerHashList;
|
|
TmpAddressMapList: TDbgAddressMapHashList;
|
|
ind: THTCustomNode;
|
|
begin
|
|
DwarfDebugMap:=nil;
|
|
FullDwarfDebugMap := TDbgAddressMapPointerHashList.Create;
|
|
TmpAddressMapList := TDbgAddressMapHashList.Create;
|
|
p := Section[_symbol];
|
|
ps := Section[_symbolstrings];
|
|
if assigned(p) and assigned(ps) then
|
|
begin
|
|
SymbolStr:=PDbgImageSectionEx(ps)^.Sect.RawData;
|
|
if TargetInfo.Bitness = b64 then
|
|
begin
|
|
SymbolArr64:=PDbgImageSectionEx(p)^.Sect.RawData;
|
|
SymbolCount := PDbgImageSectionEx(p)^.Sect.Size div sizeof(nlist_64);
|
|
end
|
|
else
|
|
begin
|
|
SymbolArr:=PDbgImageSectionEx(p)^.Sect.RawData;
|
|
SymbolCount := PDbgImageSectionEx(p)^.Sect.Size div sizeof(nlist);
|
|
end;
|
|
state := dtsEnd;
|
|
for i := 0 to SymbolCount-1 do
|
|
begin
|
|
if TargetInfo.Bitness = b64 then
|
|
begin
|
|
SymbolType := SymbolArr64[i].n_type;
|
|
StringOffset := SymbolArr64[i].n_un.n_strx;
|
|
SymbolValue := SymbolArr64[i].n_value;
|
|
SymbolSect := SymbolArr64[i].n_sect;
|
|
end
|
|
else
|
|
begin
|
|
SymbolType := SymbolArr[i].n_type;
|
|
StringOffset := SymbolArr[i].n_un.n_strx;
|
|
SymbolValue := SymbolArr[i].n_value;
|
|
SymbolSect := SymbolArr[i].n_sect;
|
|
end;
|
|
case state of
|
|
dtsEnd:
|
|
begin
|
|
if SymbolType = N_SO then
|
|
begin
|
|
if not assigned(DwarfDebugMap) then
|
|
DwarfDebugMap := TAppleDwarfDebugMap.Create
|
|
else
|
|
DwarfDebugMap.clear;
|
|
DwarfDebugMap.Dir := pchar(SymbolStr+StringOffset);
|
|
state := dtsDir;
|
|
end
|
|
else if (SymbolType and not uint8_t(N_EXT or N_PEXT) = N_SECT) and (SymbolValue <> 0) then begin
|
|
ind := FullDwarfDebugMap.Find(pchar(SymbolStr+StringOffset));
|
|
if (ind = nil) then begin
|
|
ind := TmpAddressMapList.Find(pchar(SymbolStr+StringOffset));
|
|
if (ind = nil) then begin
|
|
AddressMapTmp.NewAddr:=SymbolValue;
|
|
AddressMapTmp.OrgAddr:=0;
|
|
AddressMapTmp.Length:=0;
|
|
TmpAddressMapList.Add(pchar(SymbolStr+StringOffset), AddressMapTmp);
|
|
end
|
|
else
|
|
TmpAddressMapList.ItemPointerFromNode(ind)^.NewAddr := SymbolValue;
|
|
end
|
|
else
|
|
if (FullDwarfDebugMap.ItemPointerFromNode(ind)^.NewAddr = 0) then begin
|
|
FullDwarfDebugMap.ItemPointerFromNode(ind)^.NewAddr := SymbolValue;
|
|
end;
|
|
end;
|
|
end;
|
|
dtsDir:
|
|
begin
|
|
if SymbolType = N_SO then
|
|
begin
|
|
DwarfDebugMap.SourceFile:=pchar(SymbolStr+StringOffset);
|
|
inc(state);
|
|
end
|
|
else
|
|
state := dtsEnd;
|
|
end;
|
|
dtsSource:
|
|
begin
|
|
if SymbolType = N_OSO then
|
|
begin
|
|
DwarfDebugMap.ObjectFile:=pchar(SymbolStr+StringOffset);
|
|
DwarfDebugMap.ObjFileAge:=SymbolValue;
|
|
inc(state);
|
|
end;
|
|
end;
|
|
dtsObjectFile:
|
|
begin
|
|
if (SymbolType = N_BNSYM) then
|
|
begin
|
|
inc(state);
|
|
end
|
|
else if (SymbolType = N_STSYM) or (SymbolType = N_GSYM) then
|
|
begin
|
|
AddressMap.NewAddr:=SymbolValue;
|
|
AddressMap.OrgAddr:=0;
|
|
AddressMap.Length:=0;
|
|
if (SymbolType = N_GSYM) and (SymbolValue = 0) then begin
|
|
ind := TmpAddressMapList.Find(pchar(SymbolStr+StringOffset));
|
|
if (ind <> nil) then begin
|
|
AddressMap.NewAddr:=TmpAddressMapList.ItemPointerFromNode(ind)^.NewAddr ;
|
|
end;
|
|
end;
|
|
DwarfDebugMap.GlobalList.Add(pchar(SymbolStr+StringOffset), AddressMap);
|
|
if (SymbolType = N_GSYM) and (AddressMap.NewAddr = 0) then begin
|
|
ind := DwarfDebugMap.GlobalList.Find(pchar(SymbolStr+StringOffset));
|
|
FullDwarfDebugMap.Add(pchar(SymbolStr+StringOffset),
|
|
DwarfDebugMap.GlobalList.ItemPointerFromNode(ind));
|
|
end;
|
|
end
|
|
else if (SymbolType = N_SO) and (SymbolSect=1) then
|
|
begin
|
|
state := dtsEnd;
|
|
SubFiles.AddObject(DwarfDebugMap.ObjectFile, DwarfDebugMap);
|
|
DwarfDebugMap:=nil;
|
|
end;
|
|
end;
|
|
dtsProc:
|
|
begin
|
|
if (SymbolType = N_FUN) and (SymbolSect=1) then
|
|
begin
|
|
AddressMap.NewAddr:=SymbolValue;
|
|
ProcName:=pchar(SymbolStr+StringOffset);
|
|
inc(state);
|
|
end;
|
|
end;
|
|
dtsProcLen:
|
|
begin
|
|
if (SymbolType = N_FUN) and (SymbolSect=0) then
|
|
begin
|
|
AddressMap.Length:=SymbolValue;
|
|
inc(state);
|
|
end;
|
|
end;
|
|
dtsProcEnd:
|
|
begin
|
|
if (SymbolType = N_ENSYM) and (SymbolSect=1) then
|
|
begin
|
|
DwarfDebugMap.GlobalList.Add(ProcName, AddressMap);
|
|
state := dtsObjectFile;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
FullDwarfDebugMap.Free;
|
|
TmpAddressMapList.Free
|
|
end;
|
|
|
|
procedure TDbgMachoDataSource.ParseSubAppleDwarfDataMap(ADebugMap: TObject);
|
|
var
|
|
p: PDbgImageSection;
|
|
ps: PDbgImageSection;
|
|
SymbolArr: PnlistArray;
|
|
SymbolArr64: PnlistArray64;
|
|
SymbolStr: pointer;
|
|
SymbolType: uint8_t;
|
|
StringOffset: uint32_t;
|
|
SymbolValue: QWord;
|
|
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
|
|
SymbolStr:=PDbgImageSectionEx(ps)^.Sect.RawData;
|
|
if TargetInfo.Bitness = b64 then
|
|
begin
|
|
SymbolArr64:=PDbgImageSectionEx(p)^.Sect.RawData;
|
|
SymbolCount := PDbgImageSectionEx(p)^.Sect.Size div sizeof(nlist_64);
|
|
end
|
|
else
|
|
begin
|
|
SymbolArr:=PDbgImageSectionEx(p)^.Sect.RawData;
|
|
SymbolCount := PDbgImageSectionEx(p)^.Sect.Size div sizeof(nlist);
|
|
end;
|
|
for i := 0 to SymbolCount-1 do
|
|
begin
|
|
if TargetInfo.Bitness = b64 then
|
|
begin
|
|
SymbolType := SymbolArr64[i].n_type;
|
|
StringOffset := SymbolArr64[i].n_un.n_strx;
|
|
SymbolValue := SymbolArr64[i].n_value;
|
|
end
|
|
else
|
|
begin
|
|
SymbolType := SymbolArr[i].n_type;
|
|
StringOffset := SymbolArr[i].n_un.n_strx;
|
|
SymbolValue := SymbolArr[i].n_value;
|
|
end;
|
|
if SymbolType and not uint8_t(N_EXT or N_PEXT) = N_SECT then
|
|
begin
|
|
s := pchar(SymbolStr+StringOffset);
|
|
ind := MainDwarfDebugMap.GlobalList.Find(s);
|
|
if assigned(ind) then
|
|
begin
|
|
AddressMap:=MainDwarfDebugMap.GlobalList.ItemFromNode(ind);
|
|
AddressMap.OrgAddr:=SymbolValue;
|
|
AddressMapList.Add(AddressMap);
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
initialization
|
|
RegisterImageReaderClass( TDbgMachoDataSource );
|
|
|
|
DBG_VERBOSE := DebugLogger.FindOrRegisterLogGroup('DBG_VERBOSE' {$IFDEF DBG_VERBOSE} , True {$ENDIF} );
|
|
DBG_WARNINGS := DebugLogger.FindOrRegisterLogGroup('DBG_WARNINGS' {$IFDEF DBG_WARNINGS} , True {$ENDIF} );
|
|
end.
|
|
|