fpc/rtl/inc/exeinfo.pp
2021-09-04 19:12:04 +02:00

1709 lines
46 KiB
ObjectPascal

{
This file is part of the Free Pascal run time library.
Copyright (c) 2008 by Peter Vreman
Executable file reading functions
See the file COPYING.FPC, included in this distribution,
for details about the copyright.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
**********************************************************************}
{
This unit should not be compiled in objfpc mode, since this would make it
dependent on objpas unit.
}
{ Disable checks of pointers explictly,
as we are dealing here with special pointer that
might be seen as invalid by heaptrc unit CheckPointer function }
{$checkpointer off}
{$modeswitch out}
unit exeinfo;
interface
{$S-}
type
TExeProcessAddress = {$ifdef cpui8086}word{$else}ptruint{$endif};
TExeOffset = {$ifdef cpui8086}longword{$else}ptruint{$endif};
TExeFile=record
f : file;
// cached filesize
size : int64;
isopen : boolean;
nsects : longint;
sechdrofs,
secstrofs : TExeOffset;
processaddress : TExeProcessAddress;
{$ifdef cpui8086}
processsegment : word;
{$endif cpui8086}
{$ifdef darwin}
{ total size of all headers }
loadcommandssize: ptruint;
{$endif}
FunctionRelative: boolean;
// Offset of the binary image forming permanent offset to all retrieved values
ImgOffset: TExeOffset;
filename : string;
// Allocate static buffer for reading data
buf : array[0..4095] of byte;
bufsize,
bufcnt : longint;
end;
function OpenExeFile(var e:TExeFile;const fn:string):boolean;
function FindExeSection(var e:TExeFile;const secname:string;var secofs,seclen:longint):boolean;
function CloseExeFile(var e:TExeFile):boolean;
function ReadDebugLink(var e:TExeFile;var dbgfn:string):boolean;
{$ifdef CPUI8086}
procedure GetModuleByAddr(addr: farpointer; var baseaddr: farpointer; var filename: string);
{$else CPUI8086}
procedure GetModuleByAddr(addr: pointer; var baseaddr: pointer; var filename: string);
{$endif CPUI8086}
implementation
uses
{$ifdef darwin}
ctypes, baseunix, dl,
{$endif}
strings{$ifdef windows},windows{$endif windows};
{$if defined(unix) and not defined(beos) and not defined(haiku)}
procedure GetModuleByAddr(addr: pointer; var baseaddr: pointer; var filename: string);
begin
if assigned(UnixGetModuleByAddrHook) then
UnixGetModuleByAddrHook(addr,baseaddr,filename)
else
begin
baseaddr:=nil;
filename:=ParamStr(0);
end;
end;
{$elseif defined(windows)}
var
Tmm: TMemoryBasicInformation;
{$ifdef FPC_OS_UNICODE}
TST: array[0..Max_Path] of WideChar;
{$else}
TST: array[0..Max_Path] of Char;
{$endif FPC_OS_UNICODE}
procedure GetModuleByAddr(addr: pointer; var baseaddr: pointer; var filename: string);
begin
baseaddr:=nil;
if VirtualQuery(addr, @Tmm, SizeOf(Tmm))<>sizeof(Tmm) then
filename:=ParamStr(0)
else
begin
baseaddr:=Tmm.AllocationBase;
TST[0]:= #0;
if baseaddr <> nil then
begin
GetModuleFileName(THandle(Tmm.AllocationBase), TST, Length(TST));
{$ifdef FPC_OS_UNICODE}
filename:= String(PWideChar(@TST));
{$else}
filename:= String(PChar(@TST));
{$endif FPC_OS_UNICODE}
end;
end;
end;
{$elseif defined(morphos) or defined(aros) or defined(amigaos4)}
procedure startsymbol; external name '_start';
procedure GetModuleByAddr(addr: pointer; var baseaddr: pointer; var filename: string);
begin
baseaddr:= @startsymbol;
{$ifdef FPC_HAS_FEATURE_COMMANDARGS}
filename:=ParamStr(0);
{$else FPC_HAS_FEATURE_COMMANDARGS}
filename:='';
{$endif FPC_HAS_FEATURE_COMMANDARGS}
end;
{$elseif defined(msdos)}
procedure GetModuleByAddr(addr: farpointer; var baseaddr: farpointer; var filename: string);
begin
baseaddr:=Ptr(PrefixSeg+16,0);
filename:=ParamStr(0);
end;
{$elseif defined(beos) or defined(haiku)}
{$i ptypes.inc}
{$i ostypes.inc}
function get_next_image_info(team: team_id; var cookie:longint; var info:image_info; size: size_t) : status_t;cdecl; external 'root' name '_get_next_image_info';
procedure GetModuleByAddr(addr: pointer; var baseaddr: pointer; var filename: string);
const
B_OK = 0;
var
cookie : longint;
info : image_info;
begin
filename:='';
baseaddr:=nil;
cookie:=0;
fillchar(info, sizeof(image_info), 0);
while get_next_image_info(0,cookie,info,sizeof(info))=B_OK do
begin
if (info._type = B_APP_IMAGE) and
(addr >= info.text) and (addr <= (info.text + info.text_size)) then
begin
baseaddr:=info.text;
filename:=PChar(@info.name);
end;
end;
end;
{$else}
{$ifdef CPUI8086}
procedure GetModuleByAddr(addr: farpointer; var baseaddr: farpointer; var filename: string);
{$else CPUI8086}
procedure GetModuleByAddr(addr: pointer; var baseaddr: pointer; var filename: string);
{$endif CPUI8086}
begin
baseaddr:= nil;
{$ifdef FPC_HAS_FEATURE_COMMANDARGS}
filename:=ParamStr(0);
{$else FPC_HAS_FEATURE_COMMANDARGS}
filename:='';
{$endif FPC_HAS_FEATURE_COMMANDARGS}
end;
{$endif}
{****************************************************************************
Executable Loaders
****************************************************************************}
{$if defined(freebsd) or defined(netbsd) or defined (openbsd) or defined(linux) or defined(sunos) or defined(android) or defined(dragonfly)}
{$ifdef cpu64}
{$define ELF64}
{$define FIND_BASEADDR_ELF}
{$else}
{$define ELF32}
{$define FIND_BASEADDR_ELF}
{$endif}
{$endif}
{$if defined(beos) or defined(haiku)}
{$ifdef cpu64}
{$define ELF64}
{$else}
{$define ELF32}
{$endif}
{$endif}
{$if defined(morphos) or defined(aros) or defined(amigaos4)}
{$ifdef cpu64}
{$define ELF64}
{$else}
{$define ELF32}
{$endif}
{$endif}
{$if defined(msdos)}
{$define ELF32}
{$endif}
{$if defined(win32) or defined(wince)}
{$define PE32}
{$endif}
{$if defined(win64)}
{$define PE32PLUS}
{$endif}
{$ifdef netwlibc}
{$define netware}
{$endif}
{$IFDEF OS2}
{$DEFINE EMX}
{$ENDIF OS2}
{****************************************************************************
DOS Stub
****************************************************************************}
{$if defined(EMX) or defined(PE32) or defined(PE32PLUS) or defined(GO32V2) or defined(MSDOS)}
type
tdosheader = packed record
e_magic : word;
e_cblp : word;
e_cp : word;
e_crlc : word;
e_cparhdr : word;
e_minalloc : word;
e_maxalloc : word;
e_ss : word;
e_sp : word;
e_csum : word;
e_ip : word;
e_cs : word;
e_lfarlc : word;
e_ovno : word;
e_res : array[0..3] of word;
e_oemid : word;
e_oeminfo : word;
e_res2 : array[0..9] of word;
e_lfanew : longint;
end;
{$endif EMX or PE32 or PE32PLUS or GO32v2}
{****************************************************************************
NLM
****************************************************************************}
{$ifdef netware}
function getByte(var f:file):byte;
begin
BlockRead (f,getByte,1);
end;
procedure Skip (var f:file; bytes : longint);
var i : longint;
begin
for i := 1 to bytes do getbyte(f);
end;
function get0String (var f:file) : string;
var c : char;
begin
get0String := '';
c := char (getbyte(f));
while (c <> #0) do
begin
get0String := get0String + c;
c := char (getbyte(f));
end;
end;
function getint32 (var f:file): longint;
begin
blockread (F, getint32, 4);
end;
const SIZE_OF_NLM_INTERNAL_FIXED_HEADER = 130;
SIZE_OF_NLM_INTERNAL_VERSION_HEADER = 32;
SIZE_OF_NLM_INTERNAL_EXTENDED_HEADER = 124;
function openNetwareNLM(var e:TExeFile):boolean;
var valid : boolean;
name : string;
hdrLength,
dataOffset,
dataLength : longint;
function getLString : String;
var Res:string;
begin
blockread (e.F, res, 1);
if length (res) > 0 THEN
blockread (e.F, res[1], length (res));
getbyte(e.f);
getLString := res;
end;
function getFixString (Len : byte) : string;
var i : byte;
begin
getFixString := '';
for I := 1 to Len do
getFixString := getFixString + char (getbyte(e.f));
end;
function getword : word;
begin
blockread (e.F, getword, 2);
end;
begin
e.sechdrofs := 0;
openNetwareNLM:=false;
// read and check header
Skip (e.f,SIZE_OF_NLM_INTERNAL_FIXED_HEADER);
getLString; // NLM Description
getInt32(e.f); // Stacksize
getInt32(e.f); // Reserved
skip(e.f,5); // old Thread Name
getLString; // Screen Name
getLString; // Thread Name
hdrLength := -1;
dataOffset := -1;
dataLength := -1;
valid := true;
repeat
name := getFixString (8);
if (name = 'VeRsIoN#') then
begin
Skip (e.f,SIZE_OF_NLM_INTERNAL_VERSION_HEADER-8);
end else
if (name = 'CoPyRiGh') then
begin
getword; // T=
getLString; // Copyright String
end else
if (name = 'MeSsAgEs') then
begin
skip (e.f,SIZE_OF_NLM_INTERNAL_EXTENDED_HEADER - 8);
end else
if (name = 'CuStHeAd') then
begin
hdrLength := getInt32(e.f);
dataOffset := getInt32(e.f);
dataLength := getInt32(e.f);
Skip (e.f,8); // dateStamp
Valid := false;
end else
Valid := false;
until not valid;
if (hdrLength = -1) or (dataOffset = -1) or (dataLength = -1) then
exit;
Seek (e.F, dataOffset);
e.sechdrofs := dataOffset;
openNetwareNLM := (e.sechdrofs > 0);
end;
function FindSectionNetwareNLM(var e:TExeFile;const asecname:string;var secofs,seclen:longint):boolean;
var name : string;
alignAmount : longint;
begin
seek(e.f,e.sechdrofs);
(* The format of the section information is:
null terminated section name
zeroes to adjust to 4 byte boundary
4 byte section data file pointer
4 byte section size *)
Repeat
Name := Get0String(e.f);
alignAmount := 4 - ((length (Name) + 1) MOD 4);
Skip (e.f,AlignAmount);
if (Name = asecname) then
begin
secOfs := getInt32(e.f);
secLen := getInt32(e.f);
end else
Skip(e.f,8);
until (Name = '') or (Name = asecname);
FindSectionNetwareNLM := (Name=asecname);
end;
{$endif}
{****************************************************************************
COFF
****************************************************************************}
{$if defined(PE32) or defined(PE32PLUS) or defined(GO32V2)}
type
tcoffsechdr=packed record
name : array[0..7] of char;
vsize : longint;
rvaofs : longint;
datalen : longint;
datapos : longint;
relocpos : longint;
lineno1 : longint;
nrelocs : word;
lineno2 : word;
flags : longint;
end;
coffsymbol=packed record
name : array[0..3] of char; { real is [0..7], which overlaps the strofs ! }
strofs : longint;
value : longint;
section : smallint;
empty : word;
typ : byte;
aux : byte;
end;
function FindSectionCoff(var e:TExeFile;const asecname:string;var secofs,seclen:longint):boolean;
var
i : longint;
sechdr : tcoffsechdr;
secname : string;
secnamebuf : array[0..255] of char;
code,
oldofs,
bufsize : longint;
strofs : cardinal;
begin
FindSectionCoff:=false;
{ read section info }
seek(e.f,e.sechdrofs);
for i:=1 to e.nsects do
begin
blockread(e.f,sechdr,sizeof(sechdr),bufsize);
move(sechdr.name,secnamebuf,8);
secnamebuf[8]:=#0;
secname:=strpas(secnamebuf);
if secname[1]='/' then
begin
Val(Copy(secname,2,8),strofs,code);
if code=0 then
begin
fillchar(secnamebuf,sizeof(secnamebuf),0);
oldofs:=filepos(e.f);
seek(e.f,e.secstrofs+strofs);
blockread(e.f,secnamebuf,sizeof(secnamebuf),bufsize);
seek(e.f,oldofs);
secname:=strpas(secnamebuf);
end
else
secname:='';
end;
if asecname=secname then
begin
secofs:=cardinal(sechdr.datapos) + E.ImgOffset;
{$ifdef GO32V2}
seclen:=sechdr.datalen;
{$else GO32V2}
{ In PECOFF, datalen includes file padding up to the next section.
vsize is the actual payload size if it does not exceed datalen,
otherwise it is .bss (or alike) section that we should ignore. }
if sechdr.vsize<=sechdr.datalen then
seclen:=sechdr.vsize
else
exit;
{$endif GO32V2}
FindSectionCoff:=true;
exit;
end;
end;
end;
{$endif PE32 or PE32PLUS or GO32V2}
{$ifdef go32v2}
function OpenGo32Coff(var e:TExeFile):boolean;
type
tgo32coffheader=packed record
mach : word;
nsects : word;
time : longint;
sympos : longint;
syms : longint;
opthdr : word;
flag : word;
other : array[0..27] of byte;
end;
const
ParagraphSize = 512;
var
coffheader : tgo32coffheader;
DosHeader: TDosHeader;
BRead: cardinal;
begin
OpenGo32Coff:=false;
{ read and check header }
if E.Size < SizeOf (DosHeader) then
Exit;
BlockRead (E.F, DosHeader, SizeOf (DosHeader), BRead);
if BRead <> SizeOf (DosHeader) then
Exit;
if DosHeader.E_Magic = $5A4D then
begin
E.ImgOffset := DosHeader.e_cp * ParagraphSize;
if DosHeader.e_cblp > 0 then
E.ImgOffset := E.ImgOffset + DosHeader.e_cblp - ParagraphSize;
end;
if e.size < E.ImgOffset + sizeof(coffheader) then
exit;
seek(e.f,E.ImgOffset);
blockread(e.f,coffheader,sizeof(coffheader));
if coffheader.mach<>$14c then
exit;
e.sechdrofs:=filepos(e.f);
e.nsects:=coffheader.nsects;
e.secstrofs:=coffheader.sympos+coffheader.syms*sizeof(coffsymbol)+4;
if e.secstrofs>e.size then
exit;
OpenGo32Coff:=true;
end;
{$endif Go32v2}
{$ifdef PE32}
function OpenPeCoff(var e:TExeFile):boolean;
type
tpeheader = packed record
PEMagic : longint;
Machine : word;
NumberOfSections : word;
TimeDateStamp : longint;
PointerToSymbolTable : longint;
NumberOfSymbols : longint;
SizeOfOptionalHeader : word;
Characteristics : word;
Magic : word;
MajorLinkerVersion : byte;
MinorLinkerVersion : byte;
SizeOfCode : longint;
SizeOfInitializedData : longint;
SizeOfUninitializedData : longint;
AddressOfEntryPoint : longint;
BaseOfCode : longint;
BaseOfData : longint;
ImageBase : longint;
SectionAlignment : longint;
FileAlignment : longint;
MajorOperatingSystemVersion : word;
MinorOperatingSystemVersion : word;
MajorImageVersion : word;
MinorImageVersion : word;
MajorSubsystemVersion : word;
MinorSubsystemVersion : word;
Reserved1 : longint;
SizeOfImage : longint;
SizeOfHeaders : longint;
CheckSum : longint;
Subsystem : word;
DllCharacteristics : word;
SizeOfStackReserve : longint;
SizeOfStackCommit : longint;
SizeOfHeapReserve : longint;
SizeOfHeapCommit : longint;
LoaderFlags : longint;
NumberOfRvaAndSizes : longint;
DataDirectory : array[1..$80] of byte;
end;
var
dosheader : tdosheader;
peheader : tpeheader;
begin
OpenPeCoff:=false;
{ read and check header }
if e.size<sizeof(dosheader) then
exit;
blockread(e.f,dosheader,sizeof(tdosheader));
seek(e.f,dosheader.e_lfanew);
blockread(e.f,peheader,sizeof(tpeheader));
if peheader.pemagic<>$4550 then
exit;
e.sechdrofs:=filepos(e.f);
e.nsects:=peheader.NumberOfSections;
e.secstrofs:=peheader.PointerToSymbolTable+peheader.NumberOfSymbols*sizeof(coffsymbol);
if e.secstrofs>e.size then
exit;
e.processaddress:=peheader.ImageBase;
OpenPeCoff:=true;
end;
{$endif PE32}
{$ifdef PE32PLUS}
function OpenPePlusCoff(var e:TExeFile):boolean;
type
tpeheader = packed record
PEMagic : longint;
Machine : word;
NumberOfSections : word;
TimeDateStamp : longint;
PointerToSymbolTable : longint;
NumberOfSymbols : longint;
SizeOfOptionalHeader : word;
Characteristics : word;
Magic : word;
MajorLinkerVersion : byte;
MinorLinkerVersion : byte;
SizeOfCode : longint;
SizeOfInitializedData : longint;
SizeOfUninitializedData : longint;
AddressOfEntryPoint : longint;
BaseOfCode : longint;
ImageBase : qword;
SectionAlignment : longint;
FileAlignment : longint;
MajorOperatingSystemVersion : word;
MinorOperatingSystemVersion : word;
MajorImageVersion : word;
MinorImageVersion : word;
MajorSubsystemVersion : word;
MinorSubsystemVersion : word;
Reserved1 : longint;
SizeOfImage : longint;
SizeOfHeaders : longint;
CheckSum : longint;
Subsystem : word;
DllCharacteristics : word;
SizeOfStackReserve : qword;
SizeOfStackCommit : qword;
SizeOfHeapReserve : qword;
SizeOfHeapCommit : qword;
LoaderFlags : longint;
NumberOfRvaAndSizes : longint;
DataDirectory : array[1..$80] of byte;
end;
var
dosheader : tdosheader;
peheader : tpeheader;
begin
OpenPePlusCoff:=false;
{ read and check header }
if E.Size<sizeof(dosheader) then
exit;
blockread(E.F,dosheader,sizeof(tdosheader));
seek(E.F,dosheader.e_lfanew);
blockread(E.F,peheader,sizeof(tpeheader));
if peheader.pemagic<>$4550 then
exit;
e.sechdrofs:=filepos(e.f);
e.nsects:=peheader.NumberOfSections;
e.secstrofs:=peheader.PointerToSymbolTable+peheader.NumberOfSymbols*sizeof(coffsymbol);
if e.secstrofs>e.size then
exit;
e.processaddress:=peheader.ImageBase;
OpenPePlusCoff:=true;
end;
{$endif PE32PLUS}
{****************************************************************************
AOUT
****************************************************************************}
{$IFDEF EMX}
type
TEmxHeader = packed record
Version: array [1..16] of char;
Bound: word;
AoutOfs: longint;
Options: array [1..42] of char;
end;
TAoutHeader = packed record
Magic: word;
Machine: byte;
Flags: byte;
TextSize: longint;
DataSize: longint;
BssSize: longint;
SymbSize: longint;
EntryPoint: longint;
TextRelocSize: longint;
DataRelocSize: longint;
end;
const
PageSizeFill = $FFF;
var
DosHeader: TDosHeader;
EmxHeader: TEmxHeader;
AoutHeader: TAoutHeader;
StabOfs: PtrUInt;
S4: string [4];
function OpenEMXaout (var E: TExeFile): boolean;
begin
OpenEMXaout := false;
{ GDB after 4.18 uses offset to function begin
in text section but OS/2 version still uses 4.16 PM }
E.FunctionRelative := false;
{ read and check header }
if E.Size > SizeOf (DosHeader) then
begin
BlockRead (E.F, DosHeader, SizeOf (TDosHeader));
{$IFDEF DEBUG_LINEINFO}
WriteLn (StdErr, 'DosHeader.E_CParHdr = ', DosHeader.E_cParHdr);
{$ENDIF DEBUG_LINEINFO}
if E.Size > DosHeader.e_cparhdr shl 4 + SizeOf (TEmxHeader) then
begin
Seek (E.F, DosHeader.e_cparhdr shl 4);
BlockRead (E.F, EmxHeader, SizeOf (TEmxHeader));
S4 [0] := #4;
Move (EmxHeader.Version, S4 [1], 4);
if (S4 = 'emx ') and
(E.Size > EmxHeader.AoutOfs + SizeOf (TAoutHeader)) then
begin
{$IFDEF DEBUG_LINEINFO}
WriteLn (StdErr, 'EmxHeader.AoutOfs = ', EmxHeader.AoutOfs, '/', HexStr (pointer (EmxHeader.AoutOfs)));
{$ENDIF DEBUG_LINEINFO}
Seek (E.F, EmxHeader.AoutOfs);
BlockRead (E.F, AoutHeader, SizeOf (TAoutHeader));
{$IFDEF DEBUG_LINEINFO}
WriteLn (StdErr, 'AoutHeader.Magic = ', AoutHeader.Magic);
{$ENDIF DEBUG_LINEINFO}
{ if AOutHeader.Magic = $10B then}
StabOfs := (EmxHeader.AoutOfs or PageSizeFill) + 1
+ AoutHeader.TextSize
+ AoutHeader.DataSize
+ AoutHeader.TextRelocSize
+ AoutHeader.DataRelocSize;
{$IFDEF DEBUG_LINEINFO}
WriteLn (StdErr, 'AoutHeader.TextSize = ', AoutHeader.TextSize, '/', HexStr (pointer (AoutHeader.TextSize)));
WriteLn (StdErr, 'AoutHeader.DataSize = ', AoutHeader.DataSize, '/', HexStr (pointer (AoutHeader.DataSize)));
WriteLn (StdErr, 'AoutHeader.TextRelocSize = ', AoutHeader.TextRelocSize, '/', HexStr (pointer (AoutHeader.TextRelocSize)));
WriteLn (StdErr, 'AoutHeader.DataRelocSize = ', AoutHeader.DataRelocSize, '/', HexStr (pointer (AoutHeader.DataRelocSize)));
WriteLn (StdErr, 'AoutHeader.SymbSize = ', AoutHeader.SymbSize, '/', HexStr (pointer (AoutHeader.SymbSize)));
WriteLn (StdErr, 'StabOfs = ', StabOfs, '/', HexStr (pointer (StabOfs)));
{$ENDIF DEBUG_LINEINFO}
if E.Size > StabOfs + AoutHeader.SymbSize then
OpenEMXaout := true;
end;
end;
end;
end;
function FindSectionEMXaout (var E: TExeFile; const ASecName: string;
var SecOfs, SecLen: longint): boolean;
begin
FindSectionEMXaout := false;
if ASecName = '.stab' then
begin
SecOfs := StabOfs;
SecLen := AoutHeader.SymbSize;
FindSectionEMXaout := true;
end else
if ASecName = '.stabstr' then
begin
SecOfs := StabOfs + AoutHeader.SymbSize;
SecLen := E.Size - Pred (SecOfs);
FindSectionEMXaout := true;
end;
end;
{$ENDIF EMX}
{****************************************************************************
ELF
****************************************************************************}
{$if defined(ELF32)}
type
telfheader=packed record
magic0123 : longint;
file_class : byte;
data_encoding : byte;
file_version : byte;
padding : array[$07..$0f] of byte;
e_type : word;
e_machine : word;
e_version : longword;
e_entry : longword; // entrypoint
e_phoff : longword; // program header offset
e_shoff : longword; // sections header offset
e_flags : longword;
e_ehsize : word; // elf header size in bytes
e_phentsize : word; // size of an entry in the program header array
e_phnum : word; // 0..e_phnum-1 of entrys
e_shentsize : word; // size of an entry in sections header array
e_shnum : word; // 0..e_shnum-1 of entrys
e_shstrndx : word; // index of string section header
end;
telfsechdr=packed record
sh_name : longword;
sh_type : longword;
sh_flags : longword;
sh_addr : longword;
sh_offset : longword;
sh_size : longword;
sh_link : longword;
sh_info : longword;
sh_addralign : longword;
sh_entsize : longword;
end;
telfproghdr=packed record
p_type : longword;
p_offset : longword;
p_vaddr : longword;
p_paddr : longword;
p_filesz : longword;
p_memsz : longword;
p_flags : longword;
p_align : longword;
end;
{$endif ELF32}
{$ifdef ELF64}
type
telfheader=packed record
magic0123 : longint;
file_class : byte;
data_encoding : byte;
file_version : byte;
padding : array[$07..$0f] of byte;
e_type : word;
e_machine : word;
e_version : longword;
e_entry : int64; // entrypoint
e_phoff : int64; // program header offset
e_shoff : int64; // sections header offset
e_flags : longword;
e_ehsize : word; // elf header size in bytes
e_phentsize : word; // size of an entry in the program header array
e_phnum : word; // 0..e_phnum-1 of entrys
e_shentsize : word; // size of an entry in sections header array
e_shnum : word; // 0..e_shnum-1 of entrys
e_shstrndx : word; // index of string section header
end;
type
telfsechdr=packed record
sh_name : longword;
sh_type : longword;
sh_flags : int64;
sh_addr : int64;
sh_offset : int64;
sh_size : int64;
sh_link : longword;
sh_info : longword;
sh_addralign : int64;
sh_entsize : int64;
end;
telfproghdr=packed record
p_type : longword;
p_flags : longword;
p_offset : qword;
p_vaddr : qword;
p_paddr : qword;
p_filesz : qword;
p_memsz : qword;
p_align : qword;
end;
{$endif ELF64}
{$if defined(ELF32) or defined(ELF64)}
{$ifdef FIND_BASEADDR_ELF}
var
LocalJmpBuf : Jmp_Buf;
procedure LocalError;
begin
Longjmp(LocalJmpBuf,1);
end;
procedure GetExeInMemoryBaseAddr(addr : pointer; var BaseAddr : pointer;
var filename : openstring);
type
AT_HDR = record
typ : ptruint;
value : ptruint;
end;
P_AT_HDR = ^AT_HDR;
{ Values taken from /usr/include/linux/auxvec.h }
const
AT_HDR_COUNT = 5;{ AT_PHNUM }
AT_HDR_SIZE = 4; { AT_PHENT }
AT_HDR_Addr = 3; { AT_PHDR }
AT_EXE_FN = 31; {AT_EXECFN }
var
pc : ppchar;
pat_hdr : P_AT_HDR;
i, phdr_count : ptrint;
phdr_size : ptruint;
phdr : ^telfproghdr;
found_addr : ptruint;
SavedExitProc : pointer;
begin
filename:=ParamStr(0);
SavedExitProc:=ExitProc;
ExitProc:=@LocalError;
if SetJmp(LocalJmpBuf)=0 then
begin
{ Try, avoided in order to remove exception installation }
pc:=envp;
phdr_count:=-1;
phdr_size:=0;
phdr:=nil;
found_addr:=ptruint(-1);
while (assigned(pc^)) do
inc (pointer(pc), sizeof(ptruint));
inc(pointer(pc), sizeof(ptruint));
pat_hdr:=P_AT_HDR(pc);
while assigned(pat_hdr) do
begin
if (pat_hdr^.typ=0) and (pat_hdr^.value=0) then
break;
if pat_hdr^.typ = AT_HDR_COUNT then
phdr_count:=pat_hdr^.value;
if pat_hdr^.typ = AT_HDR_SIZE then
phdr_size:=pat_hdr^.value;
if pat_hdr^.typ = AT_HDR_Addr then
phdr := pointer(pat_hdr^.value);
if pat_hdr^.typ = AT_EXE_FN then
filename:=strpas(pchar(pat_hdr^.value));
inc (pointer(pat_hdr),sizeof(AT_HDR));
end;
if (phdr_count>0) and (phdr_size = sizeof (telfproghdr))
and assigned(phdr) then
begin
for i:=0 to phdr_count -1 do
begin
if (phdr^.p_type = 1 {PT_LOAD}) and (ptruint(phdr^.p_vaddr) < found_addr) then
found_addr:=phdr^.p_vaddr;
inc(pointer(phdr), phdr_size);
end;
{$ifdef DEBUG_LINEINFO}
end
else
begin
if (phdr_count=-1) then
writeln(stderr,'AUX entry AT_PHNUM not found');
if (phdr_size=0) then
writeln(stderr,'AUX entry AT_PHENT not found');
if (phdr=nil) then
writeln(stderr,'AUX entry AT_PHDR not found');
{$endif DEBUG_LINEINFO}
end;
if found_addr<>ptruint(-1) then
begin
{$ifdef DEBUG_LINEINFO}
Writeln(stderr,'Found addr = $',hexstr(found_addr,2 * sizeof(ptruint)));
{$endif}
BaseAddr:=pointer(found_addr);
end
{$ifdef DEBUG_LINEINFO}
else
writeln(stderr,'Error parsing stack');
{$endif DEBUG_LINEINFO}
end
else
begin
{$ifdef DEBUG_LINEINFO}
writeln(stderr,'Exception parsing stack');
{$endif DEBUG_LINEINFO}
end;
ExitProc:=SavedExitProc;
end;
{$endif FIND_BASEADDR_ELF}
function OpenElf(var e:TExeFile):boolean;
{$ifdef MSDOS}
const
ParagraphSize = 512;
{$endif MSDOS}
var
elfheader : telfheader;
elfsec : telfsechdr;
phdr : telfproghdr;
i : longint;
{$ifdef MSDOS}
DosHeader : tdosheader;
BRead : cardinal;
{$endif MSDOS}
begin
OpenElf:=false;
{$ifdef MSDOS}
{ read and check header }
if E.Size < SizeOf (DosHeader) then
Exit;
BlockRead (E.F, DosHeader, SizeOf (DosHeader), BRead);
if BRead <> SizeOf (DosHeader) then
Exit;
if DosHeader.E_Magic = $5A4D then
begin
E.ImgOffset := LongWord(DosHeader.e_cp) * ParagraphSize;
if DosHeader.e_cblp > 0 then
E.ImgOffset := E.ImgOffset + DosHeader.e_cblp - ParagraphSize;
end;
{$endif MSDOS}
{ read and check header }
if e.size<(sizeof(telfheader)+e.ImgOffset) then
exit;
seek(e.f,e.ImgOffset);
blockread(e.f,elfheader,sizeof(telfheader));
if elfheader.magic0123<>{$ifdef ENDIAN_LITTLE}$464c457f{$else}$7f454c46{$endif} then
exit;
if elfheader.e_shentsize<>sizeof(telfsechdr) then
exit;
{ read section names }
seek(e.f,e.ImgOffset+elfheader.e_shoff+elfheader.e_shstrndx*cardinal(sizeof(telfsechdr)));
blockread(e.f,elfsec,sizeof(telfsechdr));
e.secstrofs:=elfsec.sh_offset;
e.sechdrofs:=elfheader.e_shoff;
e.nsects:=elfheader.e_shnum;
{$ifdef MSDOS}
{ e.processaddress is already initialized to 0 }
e.processsegment:=PrefixSeg+16;
{$else MSDOS}
{ scan program headers to find the image base address }
e.processaddress:=High(e.processaddress);
seek(e.f,e.ImgOffset+elfheader.e_phoff);
for i:=1 to elfheader.e_phnum do
begin
blockread(e.f,phdr,sizeof(phdr));
if (phdr.p_type = 1 {PT_LOAD}) and (ptruint(phdr.p_vaddr) < e.processaddress) then
e.processaddress:=phdr.p_vaddr;
end;
if e.processaddress = High(e.processaddress) then
e.processaddress:=0;
{$endif MSDOS}
OpenElf:=true;
end;
function FindSectionElf(var e:TExeFile;const asecname:string;var secofs,seclen:longint):boolean;
var
elfsec : telfsechdr;
secname : string;
secnamebuf : array[0..255] of char;
oldofs,
bufsize,i : longint;
begin
FindSectionElf:=false;
seek(e.f,e.ImgOffset+e.sechdrofs);
for i:=1 to e.nsects do
begin
blockread(e.f,elfsec,sizeof(telfsechdr));
fillchar(secnamebuf,sizeof(secnamebuf),0);
oldofs:=filepos(e.f);
seek(e.f,e.ImgOffset+e.secstrofs+elfsec.sh_name);
blockread(e.f,secnamebuf,sizeof(secnamebuf)-1,bufsize);
seek(e.f,oldofs);
secname:=strpas(secnamebuf);
if asecname=secname then
begin
secofs:=e.ImgOffset+elfsec.sh_offset;
seclen:=elfsec.sh_size;
FindSectionElf:=true;
exit;
end;
end;
end;
{$endif ELF32 or ELF64}
{****************************************************************************
MACHO
****************************************************************************}
{$ifdef darwin}
{$push}
{$packrecords c}
type
tmach_integer = cint;
tmach_cpu_type = tmach_integer;
tmach_cpu_subtype = tmach_integer;
tmach_cpu_threadtype = tmach_integer;
tmach_fat_header=record
magic: cuint32;
nfatarch: cuint32;
end;
tmach_fat_arch=record
cputype: tmach_cpu_type;
cpusubtype: tmach_cpu_subtype;
offset: cuint32;
size: cuint32;
align: cuint32;
end;
pmach_fat_arch = ^tmach_fat_arch;
(* not yet supported (only needed for slices or combined slice size > 4GB; unrelated to 64 bit processes)
tmach_fat_arch_64=record
cputype: tmach_cpu_type;
cpusubtype: tmach_cpu_subtype;
offset: cuint64;
size: cuint64;
align: cuint32;
reserved: cuint32;
end;
*)
{ note: always big endian }
tmach_header=record
magic: cuint32;
cputype: tmach_cpu_type;
cpusubtype: tmach_cpu_subtype;
filetype: cuint32;
ncmds: cuint32;
sizeofcmds: cuint32;
flags: cuint32;
{$IFDEF CPU64}
reserved: cuint32;
{$ENDIF}
end;
pmach_header = ^tmach_header;
tmach_load_command=record
cmd: cuint32;
cmdsize: cuint32;
end;
pmach_load_command=^tmach_load_command;
tmach_symtab_command=record
cmd : cuint32;
cmdsize: cuint32;
symoff : cuint32;
nsyms : cuint32;
stroff : cuint32;
strsize: cuint32;
end;
pmach_symtab_command = ^tmach_symtab_command;
tstab=record
strpos : longword;
ntype : byte;
nother : byte;
ndesc : word;
nvalue : longword;
end;
pstab = ^tstab;
tmach_vm_prot = cint;
tmach_segment_command = record
cmd : cuint32;
cmdsize : cuint32;
segname : array [0..15] of Char;
vmaddr : {$IFDEF CPU64}cuint64{$ELSE}cuint32{$ENDIF};
vmsize : {$IFDEF CPU64}cuint64{$ELSE}cuint32{$ENDIF};
fileoff : {$IFDEF CPU64}cuint64{$ELSE}cuint32{$ENDIF};
filesize: {$IFDEF CPU64}cuint64{$ELSE}cuint32{$ENDIF};
maxprot : tmach_vm_prot;
initptot: tmach_vm_prot;
nsects : cuint32;
flags : cuint32;
end;
pmach_segment_command = ^tmach_segment_command;
tmach_uuid_command = record
cmd : cuint32;
cmdsize : cuint32;
uuid : array[0..15] of cuint8;
end;
pmach_uuid_command = ^tmach_uuid_command;
tmach_section = record
sectname : array [0..15] of Char;
segname : array [0..15] of Char;
addr : {$IFDEF CPU64}cuint64{$ELSE}cuint32{$ENDIF};
size : {$IFDEF CPU64}cuint64{$ELSE}cuint32{$ENDIF};
offset : cuint32;
align : cuint32;
reloff : cuint32;
nreloc : cuint32;
flags : cuint32;
reserved1: cuint32;
reserved2: cuint32;
{$IFDEF CPU64}
reserved3: cuint32;
{$ENDIF}
end;
pmach_section = ^tmach_section;
tmach_fat_archs = array[1..high(longint) div sizeof(tmach_header)] of tmach_fat_arch;
tmach_fat_header_archs = record
header: tmach_fat_header;
archs: tmach_fat_archs;
end;
pmach_fat_header_archs = ^tmach_fat_header_archs;
{$pop}
const
MACH_MH_EXECUTE = $02;
MACH_FAT_MAGIC = $cafebabe;
// not yet supported: only for binaries with slices > 4GB, or total size > 4GB
// MACH_FAT_MAGIC_64 = $cafebabf;
{$ifdef cpu32}
MACH_MAGIC = $feedface;
{$else}
MACH_MAGIC = $feedfacf;
{$endif}
MACH_CPU_ARCH_MASK = cuint32($ff000000);
{$ifdef cpu32}
MACH_LC_SEGMENT = $01;
{$else}
MACH_LC_SEGMENT = $19;
{$endif}
MACH_LC_SYMTAB = $02;
MACH_LC_UUID = $1b;
{ the in-memory mapping of the mach header of the main binary }
function _NSGetMachExecuteHeader: pmach_header; cdecl; external 'c';
function getpagesize: cint; cdecl; external 'c';
function MapMachO(const h: THandle; offset, len: SizeUInt; out addr: pointer; out memoffset, mappedsize: SizeUInt): boolean;
var
pagesize: cint;
begin
pagesize:=getpagesize;
addr:=fpmmap(nil, len+(offset and (pagesize-1)), PROT_READ, MAP_PRIVATE, h, offset and not(pagesize-1));
if addr=MAP_FAILED then
begin
addr:=nil;
memoffset:=0;
mappedsize:=0;
end
else
begin
memoffset:=offset and (pagesize - 1);
mappedsize:=len+(offset and (pagesize-1));
end;
end;
procedure UnmapMachO(p: pointer; size: SizeUInt);
begin
fpmunmap(p,size);
end;
function OpenMachO(var e:TExeFile):boolean;
var
mh : tmach_header;
processmh : pmach_header;
cmd: pmach_load_command;
segmentcmd: pmach_segment_command;
mappedexe: pointer;
mappedoffset, mappedsize: SizeUInt;
i: cuint32;
foundpagezero: boolean;
begin
OpenMachO:=false;
E.FunctionRelative:=false;
if e.size<sizeof(mh) then
exit;
blockread (e.f, mh, sizeof(mh));
case mh.magic of
MACH_FAT_MAGIC:
begin
{ todo }
exit
end;
MACH_MAGIC:
begin
// check that at least the architecture matches (we should also check the subarch,
// but that's harder because of architecture-specific backward compatibility rules)
processmh:=_NSGetMachExecuteHeader;
if (mh.cputype and not(MACH_CPU_ARCH_MASK)) <> (processmh^.cputype and not(MACH_CPU_ARCH_MASK)) then
exit;
end;
else
exit;
end;
e.sechdrofs:=filepos(e.f);
e.nsects:=mh.ncmds;
e.loadcommandssize:=mh.sizeofcmds;
if mh.filetype = MACH_MH_EXECUTE then
begin
foundpagezero:= false;
{ make sure to unmap again on all exit paths }
if not MapMachO(filerec(e.f).handle, e.sechdrofs, e.loadcommandssize, mappedexe, mappedoffset, mappedsize) then
exit;
cmd:=pmach_load_command(mappedexe+mappedoffset);
for i:= 1 to e.nsects do
begin
case cmd^.cmd of
MACH_LC_SEGMENT:
begin
segmentcmd:=pmach_segment_command(cmd);
if segmentcmd^.segname='__PAGEZERO' then
begin
e.processaddress:=segmentcmd^.vmaddr+segmentcmd^.vmsize;
OpenMachO:=true;
break;
end;
end;
end;
cmd:=pmach_load_command(pointer(cmd)+cmd^.cmdsize);
end;
UnmapMachO(mappedexe, mappedsize);
end
else
OpenMachO:=true;
end;
function FindSectionMachO(var e:TExeFile;const asecname:string;var secofs,seclen:longint):boolean;
var
i, j: cuint32;
cmd: pmach_load_command;
symtabcmd: pmach_symtab_command;
segmentcmd: pmach_segment_command;
section: pmach_section;
mappedexe: pointer;
mappedoffset, mappedsize: SizeUInt;
dwarfsecname: string;
begin
FindSectionMachO:=false;
{ make sure to unmap again on all exit paths }
if not MapMachO(filerec(e.f).handle, e.sechdrofs, e.loadcommandssize, mappedexe, mappedoffset, mappedsize) then
exit;
cmd:=pmach_load_command(mappedexe+mappedoffset);
for i:= 1 to e.nsects do
begin
case cmd^.cmd of
MACH_LC_SEGMENT:
begin
segmentcmd:=pmach_segment_command(cmd);
if segmentcmd^.segname='__DWARF' then
begin
if asecname[1]='.' then
dwarfsecname:='__'+copy(asecname,2,length(asecname))
else
dwarfsecname:=asecname;
section:=pmach_section(pointer(segmentcmd)+sizeof(segmentcmd^));
for j:=1 to segmentcmd^.nsects do
begin
if section^.sectname = dwarfsecname then
begin
secofs:=section^.offset;
seclen:=section^.size;
FindSectionMachO:=true;
UnmapMachO(mappedexe, mappedsize);
exit;
end;
inc(section);
end;
end;
end;
MACH_LC_SYMTAB:
begin
symtabcmd:=pmach_symtab_command(cmd);
if asecname='.stab' then
begin
secofs:=symtabcmd^.symoff;
{ the caller will divide again by sizeof(tstab) }
seclen:=symtabcmd^.nsyms*sizeof(tstab);
FindSectionMachO:=true;
end
else if asecname='.stabstr' then
begin
secofs:=symtabcmd^.stroff;
seclen:=symtabcmd^.strsize;
FindSectionMachO:=true;
end;
if FindSectionMachO then
begin
UnmapMachO(mappedexe, mappedsize);
exit;
end;
end;
end;
cmd:=pmach_load_command(pointer(cmd)+cmd^.cmdsize);
end;
UnmapMachO(mappedexe, mappedsize);
end;
{$endif darwin}
{****************************************************************************
CRC
****************************************************************************}
var
Crc32Tbl : array[0..255] of cardinal;
procedure MakeCRC32Tbl;
var
crc : cardinal;
i,n : integer;
begin
for i:=0 to 255 do
begin
crc:=i;
for n:=1 to 8 do
if (crc and 1)<>0 then
crc:=(crc shr 1) xor cardinal($edb88320)
else
crc:=crc shr 1;
Crc32Tbl[i]:=crc;
end;
end;
Function UpdateCrc32(InitCrc:cardinal;const InBuf;InLen:LongInt):cardinal;
var
i : LongInt;
p : pchar;
begin
if Crc32Tbl[1]=0 then
MakeCrc32Tbl;
p:=@InBuf;
UpdateCrc32:=not InitCrc;
for i:=1 to InLen do
begin
UpdateCrc32:=Crc32Tbl[byte(UpdateCrc32) xor byte(p^)] xor (UpdateCrc32 shr 8);
inc(p);
end;
UpdateCrc32:=not UpdateCrc32;
end;
{****************************************************************************
Generic Executable Open/Close
****************************************************************************}
type
TOpenProc=function(var e:TExeFile):boolean;
TFindSectionProc=function(var e:TExeFile;const asecname:string;var secofs,seclen:longint):boolean;
TExeProcRec=record
openproc : TOpenProc;
findproc : TFindSectionProc;
end;
const
ExeProcs : TExeProcRec = (
{$ifdef go32v2}
openproc : @OpenGo32Coff;
findproc : @FindSectionCoff;
{$endif}
{$ifdef PE32}
openproc : @OpenPeCoff;
findproc : @FindSectionCoff;
{$endif}
{$ifdef PE32PLUS}
openproc : @OpenPePlusCoff;
findproc : @FindSectionCoff;
{$endif PE32PLUS}
{$if defined(ELF32) or defined(ELF64)}
openproc : @OpenElf;
findproc : @FindSectionElf;
{$endif ELF32 or ELF64}
{$ifdef darwin}
openproc : @OpenMachO;
findproc : @FindSectionMachO;
{$endif darwin}
{$IFDEF EMX}
openproc : @OpenEMXaout;
findproc : @FindSectionEMXaout;
{$ENDIF EMX}
{$ifdef netware}
openproc : @OpenNetwareNLM;
findproc : @FindSectionNetwareNLM;
{$endif}
);
function OpenExeFile(var e:TExeFile;const fn:string):boolean;
var
ofm : word;
begin
OpenExeFile:=false;
fillchar(e,sizeof(e),0);
e.bufsize:=sizeof(e.buf);
e.filename:=fn;
if fn='' then // we don't want to read stdin
exit;
assign(e.f,fn);
{$I-}
ofm:=filemode;
filemode:=$40;
reset(e.f,1);
filemode:=ofm;
{$I+}
if ioresult<>0 then
exit;
e.isopen:=true;
// cache filesize
e.size:=filesize(e.f);
E.FunctionRelative := true;
E.ImgOffset := 0;
if ExeProcs.OpenProc<>nil then
OpenExeFile:=ExeProcs.OpenProc(e);
end;
function CloseExeFile(var e:TExeFile):boolean;
begin
CloseExeFile:=false;
if not e.isopen then
exit;
e.isopen:=false;
close(e.f);
CloseExeFile:=true;
end;
function FindExeSection(var e:TExeFile;const secname:string;var secofs,seclen:longint):boolean;
begin
FindExeSection:=false;
if not e.isopen then
exit;
if ExeProcs.FindProc<>nil then
FindExeSection:=ExeProcs.FindProc(e,secname,secofs,seclen);
end;
function CheckDbgFile(var e:TExeFile;const fn:string;dbgcrc:cardinal):boolean;
var
c : cardinal;
ofm : word;
g : file;
begin
CheckDbgFile:=false;
assign(g,fn);
{$I-}
ofm:=filemode;
filemode:=$40;
reset(g,1);
filemode:=ofm;
{$I+}
if ioresult<>0 then
exit;
{ We reuse the buffer from e here to prevent too much stack allocation }
c:=0;
repeat
blockread(g,e.buf,e.bufsize,e.bufcnt);
c:=UpdateCrc32(c,e.buf,e.bufcnt);
until e.bufcnt<e.bufsize;
close(g);
CheckDbgFile:=(dbgcrc=c);
end;
{$ifndef darwin}
function ReadDebugLink(var e:TExeFile;var dbgfn:string):boolean;
var
dbglink : array[0..255] of char;
i,
dbglinklen,
dbglinkofs : longint;
dbgcrc : cardinal;
begin
ReadDebugLink:=false;
if not FindExeSection(e,'.gnu_debuglink',dbglinkofs,dbglinklen) then
exit;
if dbglinklen>sizeof(dbglink)-1 then
exit;
fillchar(dbglink,sizeof(dbglink),0);
seek(e.f,dbglinkofs);
blockread(e.f,dbglink,dbglinklen);
dbgfn:=strpas(dbglink);
if length(dbgfn)=0 then
exit;
i:=align(length(dbgfn)+1,4);
if (i+4)>dbglinklen then
exit;
move(dbglink[i],dbgcrc,4);
{ current dir }
if CheckDbgFile(e,dbgfn,dbgcrc) then
begin
ReadDebugLink:=true;
exit;
end;
{ executable dir }
i:=length(e.filename);
while (i>0) and not(e.filename[i] in AllowDirectorySeparators) do
dec(i);
if i>0 then
begin
dbgfn:=copy(e.filename,1,i)+dbgfn;
if CheckDbgFile(e,dbgfn,dbgcrc) then
begin
ReadDebugLink:=true;
exit;
end;
end;
end;
{$else}
function ReadDebugLink(var e:TExeFile;var dbgfn:string):boolean;
var
dsymexefile: TExeFile;
execmd, dsymcmd: pmach_load_command;
exeuuidcmd, dsymuuidcmd: pmach_uuid_command;
mappedexe, mappeddsym: pointer;
mappedexeoffset, mappedexesize, mappeddsymoffset, mappeddsymsize: SizeUInt;
i, j: cuint32;
filenamestartpos, b: byte;
begin
ReadDebugLink:=false;
if not MapMachO(filerec(e.f).handle, e.sechdrofs, e.loadcommandssize, mappedexe, mappedexeoffset, mappedexesize) then
exit;
execmd:=pmach_load_command(mappedexe+mappedexeoffset);
for i:=1 to e.nsects do
begin
case execmd^.cmd of
MACH_LC_UUID:
begin
exeuuidcmd:=pmach_uuid_command(execmd);
filenamestartpos:=1;
for b:=1 to length(e.filename) do
begin
if e.filename[b] = '/' then
filenamestartpos:=b+1;
end;
if not OpenExeFile(dsymexefile,e.filename+'.dSYM/Contents/Resources/DWARF/'+copy(e.filename,filenamestartpos,length(e.filename))) then
begin
{$IFDEF DEBUG_LINEINFO}
writeln(stderr,'OpenExeFile for ',e.filename+'.dSYM/Contents/Resources/DWARF/'+copy(e.filename,filenamestartpos,length(e.filename)),' did not succeed.');
{$endif DEBUG_LINEINFO}
UnmapMachO(mappedexe, mappedexesize);
exit;
end;
if not MapMachO(filerec(dsymexefile.f).handle, dsymexefile.sechdrofs, dsymexefile.loadcommandssize, mappeddsym, mappeddsymoffset, mappeddsymsize) then
begin
CloseExeFile(dsymexefile);
UnmapMachO(mappedexe, mappedexesize);
exit;
end;
dsymcmd:=pmach_load_command(mappeddsym+mappeddsymoffset);
for j:=1 to dsymexefile.nsects do
begin
case dsymcmd^.cmd of
MACH_LC_UUID:
begin
dsymuuidcmd:=pmach_uuid_command(dsymcmd);
if comparebyte(exeuuidcmd^.uuid, dsymuuidcmd^.uuid, sizeof(exeuuidcmd^.uuid)) = 0 then
begin
dbgfn:=dsymexefile.filename;
ReadDebugLink:=true;
end;
break;
end;
end;
end;
UnmapMachO(mappeddsym, mappeddsymsize);
CloseExeFile(dsymexefile);
UnmapMachO(mappedexe, mappedexesize);
exit;
end;
end;
execmd:=pmach_load_command(pointer(execmd)+execmd^.cmdsize);
end;
UnmapMachO(mappedexe, mappedexesize);
end;
{$endif}
begin
{$ifdef FIND_BASEADDR_ELF}
UnixGetModuleByAddrHook:=@GetExeInMemoryBaseAddr;
{$endif FIND_BASEADDR_ELF}
end.