exeinfo.pp 47 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 2008 by Peter Vreman
  4. Executable file reading functions
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. {
  12. This unit should not be compiled in objfpc mode, since this would make it
  13. dependent on objpas unit.
  14. }
  15. { Disable checks of pointers explictly,
  16. as we are dealing here with special pointer that
  17. might be seen as invalid by heaptrc unit CheckPointer function }
  18. {$checkpointer off}
  19. {$modeswitch out}
  20. {$IFNDEF FPC_DOTTEDUNITS}
  21. unit exeinfo;
  22. {$ENDIF FPC_DOTTEDUNITS}
  23. interface
  24. {$S-}
  25. type
  26. TExeProcessAddress = {$ifdef cpui8086}word{$else}ptruint{$endif};
  27. TExeOffset = {$ifdef cpui8086}longword{$else}ptruint{$endif};
  28. TExeFile=record
  29. f : file;
  30. // cached filesize
  31. size : int64;
  32. isopen : boolean;
  33. nsects : longint;
  34. sechdrofs,
  35. secstrofs : TExeOffset;
  36. processaddress : TExeProcessAddress;
  37. {$ifdef cpui8086}
  38. processsegment : word;
  39. {$endif cpui8086}
  40. {$ifdef darwin}
  41. { total size of all headers }
  42. loadcommandssize: ptruint;
  43. {$endif}
  44. FunctionRelative: boolean;
  45. // Offset of the binary image forming permanent offset to all retrieved values
  46. ImgOffset: TExeOffset;
  47. filename : shortstring;
  48. // Allocate static buffer for reading data
  49. buf : array[0..4095] of byte;
  50. bufsize,
  51. bufcnt : longint;
  52. end;
  53. function OpenExeFile(var e:TExeFile;const fn:shortstring):boolean;
  54. function FindExeSection(var e:TExeFile;const secname:shortstring;var secofs,seclen:longint):boolean;
  55. function CloseExeFile(var e:TExeFile):boolean;
  56. function ReadDebugLink(var e:TExeFile;var dbgfn:ansistring):boolean;
  57. {$ifdef CPUI8086}
  58. procedure GetModuleByAddr(addr: farpointer; var baseaddr: farpointer; var filename: ansistring);
  59. {$else CPUI8086}
  60. procedure GetModuleByAddr(addr: pointer; var baseaddr: pointer; var filename: ansistring);
  61. {$endif CPUI8086}
  62. implementation
  63. {$IFDEF FPC_DOTTEDUNITS}
  64. uses
  65. {$ifdef darwin}
  66. System.CTypes, UnixApi.Base, UnixApi.Dl,
  67. {$endif}
  68. {$ifdef Windows}
  69. WinApi.Windows,
  70. {$endif Windows}
  71. System.Strings;
  72. {$ELSE FPC_DOTTEDUNITS}
  73. uses
  74. {$ifdef darwin}
  75. ctypes, baseunix, dl,
  76. {$endif}
  77. strings{$ifdef windows},windows{$endif windows};
  78. {$ENDIF FPC_DOTTEDUNITS}
  79. {$if defined(unix) and not defined(beos) and not defined(haiku)}
  80. procedure GetModuleByAddr(addr: pointer; var baseaddr: pointer; var filename: ansistring);
  81. begin
  82. if assigned(UnixGetModuleByAddrHook) then
  83. UnixGetModuleByAddrHook(addr,baseaddr,filename)
  84. else
  85. begin
  86. baseaddr:=nil;
  87. filename:=ParamStr(0);
  88. end;
  89. end;
  90. {$elseif defined(windows)}
  91. var
  92. Tmm: TMemoryBasicInformation;
  93. {$ifdef FPC_OS_UNICODE}
  94. TST: array[0..Max_Path] of WideChar;
  95. {$else}
  96. TST: array[0..Max_Path] of AnsiChar;
  97. {$endif FPC_OS_UNICODE}
  98. procedure GetModuleByAddr(addr: pointer; var baseaddr: pointer; var filename: ansistring);
  99. begin
  100. baseaddr:=nil;
  101. if VirtualQuery(addr, @Tmm, SizeOf(Tmm))<>sizeof(Tmm) then
  102. filename:=ParamStr(0)
  103. else
  104. begin
  105. baseaddr:=Tmm.AllocationBase;
  106. TST[0]:= #0;
  107. if baseaddr <> nil then
  108. begin
  109. GetModuleFileName(THandle(Tmm.AllocationBase), TST, Length(TST));
  110. {$ifdef FPC_OS_UNICODE}
  111. filename:= String(PWideChar(@TST));
  112. {$else}
  113. filename:= String(PAnsiChar(@TST));
  114. {$endif FPC_OS_UNICODE}
  115. end;
  116. end;
  117. end;
  118. {$elseif defined(morphos) or defined(aros) or defined(amigaos4)}
  119. procedure startsymbol; external name '_start';
  120. procedure GetModuleByAddr(addr: pointer; var baseaddr: pointer; var filename: ansistring);
  121. begin
  122. baseaddr:= @startsymbol;
  123. {$ifdef FPC_HAS_FEATURE_COMMANDARGS}
  124. filename:=ParamStr(0);
  125. {$else FPC_HAS_FEATURE_COMMANDARGS}
  126. filename:='';
  127. {$endif FPC_HAS_FEATURE_COMMANDARGS}
  128. end;
  129. {$elseif defined(msdos)}
  130. procedure GetModuleByAddr(addr: farpointer; var baseaddr: farpointer; var filename: ansistring);
  131. begin
  132. baseaddr:=Ptr(PrefixSeg+16,0);
  133. filename:=ParamStr(0);
  134. end;
  135. {$elseif defined(beos) or defined(haiku)}
  136. {$i ptypes.inc}
  137. {$i ostypes.inc}
  138. 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';
  139. procedure GetModuleByAddr(addr: pointer; var baseaddr: pointer; var filename: ansistring);
  140. const
  141. B_OK = 0;
  142. var
  143. cookie : longint;
  144. info : image_info;
  145. begin
  146. filename:='';
  147. baseaddr:=nil;
  148. cookie:=0;
  149. fillchar(info, sizeof(image_info), 0);
  150. while get_next_image_info(0,cookie,info,sizeof(info))=B_OK do
  151. begin
  152. if (info._type = B_APP_IMAGE) and
  153. (addr >= info.text) and (addr <= (info.text + info.text_size)) then
  154. begin
  155. baseaddr:=info.text;
  156. filename:=PAnsiChar(@info.name);
  157. end;
  158. end;
  159. end;
  160. {$else}
  161. {$ifdef CPUI8086}
  162. procedure GetModuleByAddr(addr: farpointer; var baseaddr: farpointer; var filename: Ansistring);
  163. {$else CPUI8086}
  164. procedure GetModuleByAddr(addr: pointer; var baseaddr: pointer; var filename: Ansistring);
  165. {$endif CPUI8086}
  166. begin
  167. baseaddr:= nil;
  168. {$ifdef FPC_HAS_FEATURE_COMMANDARGS}
  169. filename:=ParamStr(0);
  170. {$else FPC_HAS_FEATURE_COMMANDARGS}
  171. filename:='';
  172. {$endif FPC_HAS_FEATURE_COMMANDARGS}
  173. end;
  174. {$endif}
  175. {****************************************************************************
  176. Executable Loaders
  177. ****************************************************************************}
  178. {$if defined(freebsd) or defined(netbsd) or defined (openbsd) or defined(linux) or defined(sunos) or defined(android) or defined(dragonfly)}
  179. {$ifdef cpu64}
  180. {$define ELF64}
  181. {$define FIND_BASEADDR_ELF}
  182. {$else}
  183. {$define ELF32}
  184. {$define FIND_BASEADDR_ELF}
  185. {$endif}
  186. {$endif}
  187. {$if defined(beos) or defined(haiku)}
  188. {$ifdef cpu64}
  189. {$define ELF64}
  190. {$else}
  191. {$define ELF32}
  192. {$endif}
  193. {$endif}
  194. {$if defined(morphos) or defined(aros) or defined(amigaos4)}
  195. {$ifdef cpu64}
  196. {$define ELF64}
  197. {$else}
  198. {$define ELF32}
  199. {$endif}
  200. {$endif}
  201. {$if defined(msdos)}
  202. {$define ELF32}
  203. {$endif}
  204. {$if defined(win32) or defined(wince)}
  205. {$define PE32}
  206. {$endif}
  207. {$if defined(win64)}
  208. {$define PE32PLUS}
  209. {$endif}
  210. {$ifdef netwlibc}
  211. {$define netware}
  212. {$endif}
  213. {$IFDEF OS2}
  214. {$DEFINE EMX}
  215. {$ENDIF OS2}
  216. {****************************************************************************
  217. DOS Stub
  218. ****************************************************************************}
  219. {$if defined(EMX) or defined(PE32) or defined(PE32PLUS) or defined(GO32V2) or defined(MSDOS)}
  220. type
  221. tdosheader = packed record
  222. e_magic : word;
  223. e_cblp : word;
  224. e_cp : word;
  225. e_crlc : word;
  226. e_cparhdr : word;
  227. e_minalloc : word;
  228. e_maxalloc : word;
  229. e_ss : word;
  230. e_sp : word;
  231. e_csum : word;
  232. e_ip : word;
  233. e_cs : word;
  234. e_lfarlc : word;
  235. e_ovno : word;
  236. e_res : array[0..3] of word;
  237. e_oemid : word;
  238. e_oeminfo : word;
  239. e_res2 : array[0..9] of word;
  240. e_lfanew : longint;
  241. end;
  242. {$endif EMX or PE32 or PE32PLUS or GO32v2}
  243. {****************************************************************************
  244. NLM
  245. ****************************************************************************}
  246. {$ifdef netware}
  247. function getByte(var f:file):byte;
  248. begin
  249. BlockRead (f,getByte,1);
  250. end;
  251. procedure Skip (var f:file; bytes : longint);
  252. var i : longint;
  253. begin
  254. for i := 1 to bytes do getbyte(f);
  255. end;
  256. function get0String (var f:file) : shortstring;
  257. var c : AnsiChar;
  258. begin
  259. get0String := '';
  260. c := AnsiChar (getbyte(f));
  261. while (c <> #0) do
  262. begin
  263. get0String := get0String + c;
  264. c := AnsiChar (getbyte(f));
  265. end;
  266. end;
  267. function getint32 (var f:file): longint;
  268. begin
  269. blockread (F, getint32, 4);
  270. end;
  271. const SIZE_OF_NLM_INTERNAL_FIXED_HEADER = 130;
  272. SIZE_OF_NLM_INTERNAL_VERSION_HEADER = 32;
  273. SIZE_OF_NLM_INTERNAL_EXTENDED_HEADER = 124;
  274. function openNetwareNLM(var e:TExeFile):boolean;
  275. var valid : boolean;
  276. name : shortstring;
  277. hdrLength,
  278. dataOffset,
  279. dataLength : longint;
  280. function getLString : ShortString;
  281. var Res:Shortstring;
  282. begin
  283. blockread (e.F, res, 1);
  284. if length (res) > 0 THEN
  285. blockread (e.F, res[1], length (res));
  286. getbyte(e.f);
  287. getLString := res;
  288. end;
  289. function getFixString (Len : byte) : shortstring;
  290. var i : byte;
  291. begin
  292. getFixString := '';
  293. for I := 1 to Len do
  294. getFixString := getFixString + AnsiChar (getbyte(e.f));
  295. end;
  296. function getword : word;
  297. begin
  298. blockread (e.F, getword, 2);
  299. end;
  300. begin
  301. e.sechdrofs := 0;
  302. openNetwareNLM:=false;
  303. // read and check header
  304. Skip (e.f,SIZE_OF_NLM_INTERNAL_FIXED_HEADER);
  305. getLString; // NLM Description
  306. getInt32(e.f); // Stacksize
  307. getInt32(e.f); // Reserved
  308. skip(e.f,5); // old Thread Name
  309. getLString; // Screen Name
  310. getLString; // Thread Name
  311. hdrLength := -1;
  312. dataOffset := -1;
  313. dataLength := -1;
  314. valid := true;
  315. repeat
  316. name := getFixString (8);
  317. if (name = 'VeRsIoN#') then
  318. begin
  319. Skip (e.f,SIZE_OF_NLM_INTERNAL_VERSION_HEADER-8);
  320. end else
  321. if (name = 'CoPyRiGh') then
  322. begin
  323. getword; // T=
  324. getLString; // Copyright String
  325. end else
  326. if (name = 'MeSsAgEs') then
  327. begin
  328. skip (e.f,SIZE_OF_NLM_INTERNAL_EXTENDED_HEADER - 8);
  329. end else
  330. if (name = 'CuStHeAd') then
  331. begin
  332. hdrLength := getInt32(e.f);
  333. dataOffset := getInt32(e.f);
  334. dataLength := getInt32(e.f);
  335. Skip (e.f,8); // dateStamp
  336. Valid := false;
  337. end else
  338. Valid := false;
  339. until not valid;
  340. if (hdrLength = -1) or (dataOffset = -1) or (dataLength = -1) then
  341. exit;
  342. Seek (e.F, dataOffset);
  343. e.sechdrofs := dataOffset;
  344. openNetwareNLM := (e.sechdrofs > 0);
  345. end;
  346. function FindSectionNetwareNLM(var e:TExeFile;const asecname:shortstring;var secofs,seclen:longint):boolean;
  347. var name : shortstring;
  348. alignAmount : longint;
  349. begin
  350. seek(e.f,e.sechdrofs);
  351. (* The format of the section information is:
  352. null terminated section name
  353. zeroes to adjust to 4 byte boundary
  354. 4 byte section data file pointer
  355. 4 byte section size *)
  356. Repeat
  357. Name := Get0String(e.f);
  358. alignAmount := 4 - ((length (Name) + 1) MOD 4);
  359. Skip (e.f,AlignAmount);
  360. if (Name = asecname) then
  361. begin
  362. secOfs := getInt32(e.f);
  363. secLen := getInt32(e.f);
  364. end else
  365. Skip(e.f,8);
  366. until (Name = '') or (Name = asecname);
  367. FindSectionNetwareNLM := (Name=asecname);
  368. end;
  369. {$endif}
  370. {****************************************************************************
  371. COFF
  372. ****************************************************************************}
  373. {$if defined(PE32) or defined(PE32PLUS) or defined(GO32V2)}
  374. type
  375. tcoffsechdr=packed record
  376. name : array[0..7] of ansichar;
  377. vsize : longint;
  378. rvaofs : longint;
  379. datalen : longint;
  380. datapos : longint;
  381. relocpos : longint;
  382. lineno1 : longint;
  383. nrelocs : word;
  384. lineno2 : word;
  385. flags : longint;
  386. end;
  387. coffsymbol=packed record
  388. name : array[0..3] of ansichar; { real is [0..7], which overlaps the strofs ! }
  389. strofs : longint;
  390. value : longint;
  391. section : smallint;
  392. empty : word;
  393. typ : byte;
  394. aux : byte;
  395. end;
  396. function FindSectionCoff(var e:TExeFile;const asecname:shortstring;var secofs,seclen:longint):boolean;
  397. var
  398. i : longint;
  399. sechdr : tcoffsechdr;
  400. secname : shortstring;
  401. secnamebuf : array[0..255] of ansichar;
  402. code,
  403. oldofs,
  404. bufsize : longint;
  405. strofs : cardinal;
  406. begin
  407. FindSectionCoff:=false;
  408. { read section info }
  409. seek(e.f,e.sechdrofs);
  410. for i:=1 to e.nsects do
  411. begin
  412. blockread(e.f,sechdr,sizeof(sechdr),bufsize);
  413. move(sechdr.name,secnamebuf,8);
  414. secnamebuf[8]:=#0;
  415. secname:=strpas(secnamebuf);
  416. if secname[1]='/' then
  417. begin
  418. Val(Copy(secname,2,8),strofs,code);
  419. if code=0 then
  420. begin
  421. fillchar(secnamebuf,sizeof(secnamebuf),0);
  422. oldofs:=filepos(e.f);
  423. seek(e.f,e.secstrofs+strofs);
  424. blockread(e.f,secnamebuf,sizeof(secnamebuf),bufsize);
  425. seek(e.f,oldofs);
  426. secname:=strpas(secnamebuf);
  427. end
  428. else
  429. secname:='';
  430. end;
  431. if asecname=secname then
  432. begin
  433. secofs:=cardinal(sechdr.datapos) + E.ImgOffset;
  434. {$ifdef GO32V2}
  435. seclen:=sechdr.datalen;
  436. {$else GO32V2}
  437. { In PECOFF, datalen includes file padding up to the next section.
  438. vsize is the actual payload size if it does not exceed datalen,
  439. otherwise it is .bss (or alike) section that we should ignore. }
  440. if sechdr.vsize<=sechdr.datalen then
  441. seclen:=sechdr.vsize
  442. else
  443. exit;
  444. {$endif GO32V2}
  445. FindSectionCoff:=true;
  446. exit;
  447. end;
  448. end;
  449. end;
  450. {$endif PE32 or PE32PLUS or GO32V2}
  451. {$ifdef go32v2}
  452. function OpenGo32Coff(var e:TExeFile):boolean;
  453. type
  454. tgo32coffheader=packed record
  455. mach : word;
  456. nsects : word;
  457. time : longint;
  458. sympos : longint;
  459. syms : longint;
  460. opthdr : word;
  461. flag : word;
  462. other : array[0..27] of byte;
  463. end;
  464. const
  465. ParagraphSize = 512;
  466. var
  467. coffheader : tgo32coffheader;
  468. DosHeader: TDosHeader;
  469. BRead: cardinal;
  470. begin
  471. OpenGo32Coff:=false;
  472. { read and check header }
  473. if E.Size < SizeOf (DosHeader) then
  474. Exit;
  475. BlockRead (E.F, DosHeader, SizeOf (DosHeader), BRead);
  476. if BRead <> SizeOf (DosHeader) then
  477. Exit;
  478. if DosHeader.E_Magic = $5A4D then
  479. begin
  480. E.ImgOffset := DosHeader.e_cp * ParagraphSize;
  481. if DosHeader.e_cblp > 0 then
  482. E.ImgOffset := E.ImgOffset + DosHeader.e_cblp - ParagraphSize;
  483. end;
  484. if e.size < E.ImgOffset + sizeof(coffheader) then
  485. exit;
  486. seek(e.f,E.ImgOffset);
  487. blockread(e.f,coffheader,sizeof(coffheader));
  488. if coffheader.mach<>$14c then
  489. exit;
  490. e.sechdrofs:=filepos(e.f);
  491. e.nsects:=coffheader.nsects;
  492. e.secstrofs:=coffheader.sympos+coffheader.syms*sizeof(coffsymbol)+4;
  493. if e.secstrofs>e.size then
  494. exit;
  495. OpenGo32Coff:=true;
  496. end;
  497. {$endif Go32v2}
  498. {$ifdef PE32}
  499. function OpenPeCoff(var e:TExeFile):boolean;
  500. type
  501. tpeheader = packed record
  502. PEMagic : longint;
  503. Machine : word;
  504. NumberOfSections : word;
  505. TimeDateStamp : longint;
  506. PointerToSymbolTable : longint;
  507. NumberOfSymbols : longint;
  508. SizeOfOptionalHeader : word;
  509. Characteristics : word;
  510. Magic : word;
  511. MajorLinkerVersion : byte;
  512. MinorLinkerVersion : byte;
  513. SizeOfCode : longint;
  514. SizeOfInitializedData : longint;
  515. SizeOfUninitializedData : longint;
  516. AddressOfEntryPoint : longint;
  517. BaseOfCode : longint;
  518. BaseOfData : longint;
  519. ImageBase : longint;
  520. SectionAlignment : longint;
  521. FileAlignment : longint;
  522. MajorOperatingSystemVersion : word;
  523. MinorOperatingSystemVersion : word;
  524. MajorImageVersion : word;
  525. MinorImageVersion : word;
  526. MajorSubsystemVersion : word;
  527. MinorSubsystemVersion : word;
  528. Reserved1 : longint;
  529. SizeOfImage : longint;
  530. SizeOfHeaders : longint;
  531. CheckSum : longint;
  532. Subsystem : word;
  533. DllCharacteristics : word;
  534. SizeOfStackReserve : longint;
  535. SizeOfStackCommit : longint;
  536. SizeOfHeapReserve : longint;
  537. SizeOfHeapCommit : longint;
  538. LoaderFlags : longint;
  539. NumberOfRvaAndSizes : longint;
  540. DataDirectory : array[1..$80] of byte;
  541. end;
  542. var
  543. dosheader : tdosheader;
  544. peheader : tpeheader;
  545. begin
  546. OpenPeCoff:=false;
  547. { read and check header }
  548. if e.size<sizeof(dosheader) then
  549. exit;
  550. blockread(e.f,dosheader,sizeof(tdosheader));
  551. seek(e.f,dosheader.e_lfanew);
  552. blockread(e.f,peheader,sizeof(tpeheader));
  553. if peheader.pemagic<>$4550 then
  554. exit;
  555. e.sechdrofs:=filepos(e.f);
  556. e.nsects:=peheader.NumberOfSections;
  557. e.secstrofs:=peheader.PointerToSymbolTable+peheader.NumberOfSymbols*sizeof(coffsymbol);
  558. if e.secstrofs>e.size then
  559. exit;
  560. e.processaddress:=peheader.ImageBase;
  561. OpenPeCoff:=true;
  562. end;
  563. {$endif PE32}
  564. {$ifdef PE32PLUS}
  565. function OpenPePlusCoff(var e:TExeFile):boolean;
  566. type
  567. tpeheader = packed record
  568. PEMagic : longint;
  569. Machine : word;
  570. NumberOfSections : word;
  571. TimeDateStamp : longint;
  572. PointerToSymbolTable : longint;
  573. NumberOfSymbols : longint;
  574. SizeOfOptionalHeader : word;
  575. Characteristics : word;
  576. Magic : word;
  577. MajorLinkerVersion : byte;
  578. MinorLinkerVersion : byte;
  579. SizeOfCode : longint;
  580. SizeOfInitializedData : longint;
  581. SizeOfUninitializedData : longint;
  582. AddressOfEntryPoint : longint;
  583. BaseOfCode : longint;
  584. ImageBase : qword;
  585. SectionAlignment : longint;
  586. FileAlignment : longint;
  587. MajorOperatingSystemVersion : word;
  588. MinorOperatingSystemVersion : word;
  589. MajorImageVersion : word;
  590. MinorImageVersion : word;
  591. MajorSubsystemVersion : word;
  592. MinorSubsystemVersion : word;
  593. Reserved1 : longint;
  594. SizeOfImage : longint;
  595. SizeOfHeaders : longint;
  596. CheckSum : longint;
  597. Subsystem : word;
  598. DllCharacteristics : word;
  599. SizeOfStackReserve : qword;
  600. SizeOfStackCommit : qword;
  601. SizeOfHeapReserve : qword;
  602. SizeOfHeapCommit : qword;
  603. LoaderFlags : longint;
  604. NumberOfRvaAndSizes : longint;
  605. DataDirectory : array[1..$80] of byte;
  606. end;
  607. var
  608. dosheader : tdosheader;
  609. peheader : tpeheader;
  610. begin
  611. OpenPePlusCoff:=false;
  612. { read and check header }
  613. if E.Size<sizeof(dosheader) then
  614. exit;
  615. blockread(E.F,dosheader,sizeof(tdosheader));
  616. seek(E.F,dosheader.e_lfanew);
  617. blockread(E.F,peheader,sizeof(tpeheader));
  618. if peheader.pemagic<>$4550 then
  619. exit;
  620. e.sechdrofs:=filepos(e.f);
  621. e.nsects:=peheader.NumberOfSections;
  622. e.secstrofs:=peheader.PointerToSymbolTable+peheader.NumberOfSymbols*sizeof(coffsymbol);
  623. if e.secstrofs>e.size then
  624. exit;
  625. e.processaddress:=peheader.ImageBase;
  626. OpenPePlusCoff:=true;
  627. end;
  628. {$endif PE32PLUS}
  629. {****************************************************************************
  630. AOUT
  631. ****************************************************************************}
  632. {$IFDEF EMX}
  633. type
  634. TEmxHeader = packed record
  635. Version: array [1..16] of AnsiChar;
  636. Bound: word;
  637. AoutOfs: longint;
  638. Options: array [1..42] of AnsiChar;
  639. end;
  640. TAoutHeader = packed record
  641. Magic: word;
  642. Machine: byte;
  643. Flags: byte;
  644. TextSize: longint;
  645. DataSize: longint;
  646. BssSize: longint;
  647. SymbSize: longint;
  648. EntryPoint: longint;
  649. TextRelocSize: longint;
  650. DataRelocSize: longint;
  651. end;
  652. const
  653. PageSizeFill = $FFF;
  654. var
  655. DosHeader: TDosHeader;
  656. EmxHeader: TEmxHeader;
  657. AoutHeader: TAoutHeader;
  658. StabOfs: PtrUInt;
  659. S4: string [4];
  660. function OpenEMXaout (var E: TExeFile): boolean;
  661. begin
  662. OpenEMXaout := false;
  663. { GDB after 4.18 uses offset to function begin
  664. in text section but OS/2 version still uses 4.16 PM }
  665. E.FunctionRelative := false;
  666. { read and check header }
  667. if E.Size > SizeOf (DosHeader) then
  668. begin
  669. BlockRead (E.F, DosHeader, SizeOf (TDosHeader));
  670. {$IFDEF DEBUG_LINEINFO}
  671. WriteLn (StdErr, 'DosHeader.E_CParHdr = ', DosHeader.E_cParHdr);
  672. {$ENDIF DEBUG_LINEINFO}
  673. if E.Size > DosHeader.e_cparhdr shl 4 + SizeOf (TEmxHeader) then
  674. begin
  675. Seek (E.F, DosHeader.e_cparhdr shl 4);
  676. BlockRead (E.F, EmxHeader, SizeOf (TEmxHeader));
  677. S4 [0] := #4;
  678. Move (EmxHeader.Version, S4 [1], 4);
  679. if (S4 = 'emx ') and
  680. (E.Size > EmxHeader.AoutOfs + SizeOf (TAoutHeader)) then
  681. begin
  682. {$IFDEF DEBUG_LINEINFO}
  683. WriteLn (StdErr, 'EmxHeader.AoutOfs = ', EmxHeader.AoutOfs, '/', HexStr (pointer (EmxHeader.AoutOfs)));
  684. {$ENDIF DEBUG_LINEINFO}
  685. Seek (E.F, EmxHeader.AoutOfs);
  686. BlockRead (E.F, AoutHeader, SizeOf (TAoutHeader));
  687. {$IFDEF DEBUG_LINEINFO}
  688. WriteLn (StdErr, 'AoutHeader.Magic = ', AoutHeader.Magic);
  689. {$ENDIF DEBUG_LINEINFO}
  690. { if AOutHeader.Magic = $10B then}
  691. StabOfs := (EmxHeader.AoutOfs or PageSizeFill) + 1
  692. + AoutHeader.TextSize
  693. + AoutHeader.DataSize
  694. + AoutHeader.TextRelocSize
  695. + AoutHeader.DataRelocSize;
  696. {$IFDEF DEBUG_LINEINFO}
  697. WriteLn (StdErr, 'AoutHeader.TextSize = ', AoutHeader.TextSize, '/', HexStr (pointer (AoutHeader.TextSize)));
  698. WriteLn (StdErr, 'AoutHeader.DataSize = ', AoutHeader.DataSize, '/', HexStr (pointer (AoutHeader.DataSize)));
  699. WriteLn (StdErr, 'AoutHeader.TextRelocSize = ', AoutHeader.TextRelocSize, '/', HexStr (pointer (AoutHeader.TextRelocSize)));
  700. WriteLn (StdErr, 'AoutHeader.DataRelocSize = ', AoutHeader.DataRelocSize, '/', HexStr (pointer (AoutHeader.DataRelocSize)));
  701. WriteLn (StdErr, 'AoutHeader.SymbSize = ', AoutHeader.SymbSize, '/', HexStr (pointer (AoutHeader.SymbSize)));
  702. WriteLn (StdErr, 'StabOfs = ', StabOfs, '/', HexStr (pointer (StabOfs)));
  703. {$ENDIF DEBUG_LINEINFO}
  704. if E.Size > StabOfs + AoutHeader.SymbSize then
  705. OpenEMXaout := true;
  706. end;
  707. end;
  708. end;
  709. end;
  710. function FindSectionEMXaout (var E: TExeFile; const ASecName: shortstring;
  711. var SecOfs, SecLen: longint): boolean;
  712. begin
  713. FindSectionEMXaout := false;
  714. if ASecName = '.stab' then
  715. begin
  716. SecOfs := StabOfs;
  717. SecLen := AoutHeader.SymbSize;
  718. FindSectionEMXaout := true;
  719. end else
  720. if ASecName = '.stabstr' then
  721. begin
  722. SecOfs := StabOfs + AoutHeader.SymbSize;
  723. SecLen := E.Size - Pred (SecOfs);
  724. FindSectionEMXaout := true;
  725. end;
  726. end;
  727. {$ENDIF EMX}
  728. {****************************************************************************
  729. ELF
  730. ****************************************************************************}
  731. {$if defined(ELF32)}
  732. type
  733. telfheader=packed record
  734. magic0123 : longint;
  735. file_class : byte;
  736. data_encoding : byte;
  737. file_version : byte;
  738. padding : array[$07..$0f] of byte;
  739. e_type : word;
  740. e_machine : word;
  741. e_version : longword;
  742. e_entry : longword; // entrypoint
  743. e_phoff : longword; // program header offset
  744. e_shoff : longword; // sections header offset
  745. e_flags : longword;
  746. e_ehsize : word; // elf header size in bytes
  747. e_phentsize : word; // size of an entry in the program header array
  748. e_phnum : word; // 0..e_phnum-1 of entrys
  749. e_shentsize : word; // size of an entry in sections header array
  750. e_shnum : word; // 0..e_shnum-1 of entrys
  751. e_shstrndx : word; // index of string section header
  752. end;
  753. telfsechdr=packed record
  754. sh_name : longword;
  755. sh_type : longword;
  756. sh_flags : longword;
  757. sh_addr : longword;
  758. sh_offset : longword;
  759. sh_size : longword;
  760. sh_link : longword;
  761. sh_info : longword;
  762. sh_addralign : longword;
  763. sh_entsize : longword;
  764. end;
  765. telfproghdr=packed record
  766. p_type : longword;
  767. p_offset : longword;
  768. p_vaddr : longword;
  769. p_paddr : longword;
  770. p_filesz : longword;
  771. p_memsz : longword;
  772. p_flags : longword;
  773. p_align : longword;
  774. end;
  775. {$endif ELF32}
  776. {$ifdef ELF64}
  777. type
  778. telfheader=packed record
  779. magic0123 : longint;
  780. file_class : byte;
  781. data_encoding : byte;
  782. file_version : byte;
  783. padding : array[$07..$0f] of byte;
  784. e_type : word;
  785. e_machine : word;
  786. e_version : longword;
  787. e_entry : int64; // entrypoint
  788. e_phoff : int64; // program header offset
  789. e_shoff : int64; // sections header offset
  790. e_flags : longword;
  791. e_ehsize : word; // elf header size in bytes
  792. e_phentsize : word; // size of an entry in the program header array
  793. e_phnum : word; // 0..e_phnum-1 of entrys
  794. e_shentsize : word; // size of an entry in sections header array
  795. e_shnum : word; // 0..e_shnum-1 of entrys
  796. e_shstrndx : word; // index of string section header
  797. end;
  798. type
  799. telfsechdr=packed record
  800. sh_name : longword;
  801. sh_type : longword;
  802. sh_flags : int64;
  803. sh_addr : int64;
  804. sh_offset : int64;
  805. sh_size : int64;
  806. sh_link : longword;
  807. sh_info : longword;
  808. sh_addralign : int64;
  809. sh_entsize : int64;
  810. end;
  811. telfproghdr=packed record
  812. p_type : longword;
  813. p_flags : longword;
  814. p_offset : qword;
  815. p_vaddr : qword;
  816. p_paddr : qword;
  817. p_filesz : qword;
  818. p_memsz : qword;
  819. p_align : qword;
  820. end;
  821. {$endif ELF64}
  822. {$if defined(ELF32) or defined(ELF64)}
  823. {$ifdef FIND_BASEADDR_ELF}
  824. var
  825. LocalJmpBuf : Jmp_Buf;
  826. procedure LocalError;
  827. begin
  828. Longjmp(LocalJmpBuf,1);
  829. end;
  830. procedure GetExeInMemoryBaseAddr(addr : pointer; var BaseAddr : pointer;
  831. var filename : ansistring);
  832. type
  833. AT_HDR = record
  834. typ : ptruint;
  835. value : ptruint;
  836. end;
  837. P_AT_HDR = ^AT_HDR;
  838. { Values taken from /usr/include/linux/auxvec.h }
  839. const
  840. AT_HDR_COUNT = 5;{ AT_PHNUM }
  841. AT_HDR_SIZE = 4; { AT_PHENT }
  842. AT_HDR_Addr = 3; { AT_PHDR }
  843. AT_EXE_FN = 31; {AT_EXECFN }
  844. var
  845. pc : PPAnsiChar;
  846. pat_hdr : P_AT_HDR;
  847. i, phdr_count : ptrint;
  848. phdr_size : ptruint;
  849. phdr : ^telfproghdr;
  850. found_addr : ptruint;
  851. SavedExitProc : pointer;
  852. begin
  853. filename:=ParamStr(0);
  854. SavedExitProc:=ExitProc;
  855. ExitProc:=@LocalError;
  856. if SetJmp(LocalJmpBuf)=0 then
  857. begin
  858. { Try, avoided in order to remove exception installation }
  859. pc:=envp;
  860. phdr_count:=-1;
  861. phdr_size:=0;
  862. phdr:=nil;
  863. found_addr:=ptruint(-1);
  864. while (assigned(pc^)) do
  865. inc (pointer(pc), sizeof(ptruint));
  866. inc(pointer(pc), sizeof(ptruint));
  867. pat_hdr:=P_AT_HDR(pc);
  868. while assigned(pat_hdr) do
  869. begin
  870. if (pat_hdr^.typ=0) and (pat_hdr^.value=0) then
  871. break;
  872. if pat_hdr^.typ = AT_HDR_COUNT then
  873. phdr_count:=pat_hdr^.value;
  874. if pat_hdr^.typ = AT_HDR_SIZE then
  875. phdr_size:=pat_hdr^.value;
  876. if pat_hdr^.typ = AT_HDR_Addr then
  877. phdr := pointer(pat_hdr^.value);
  878. if pat_hdr^.typ = AT_EXE_FN then
  879. filename:=strpas(pansichar(pat_hdr^.value));
  880. inc (pointer(pat_hdr),sizeof(AT_HDR));
  881. end;
  882. if (phdr_count>0) and (phdr_size = sizeof (telfproghdr))
  883. and assigned(phdr) then
  884. begin
  885. for i:=0 to phdr_count -1 do
  886. begin
  887. if (phdr^.p_type = 1 {PT_LOAD}) and (ptruint(phdr^.p_vaddr) < found_addr) then
  888. found_addr:=phdr^.p_vaddr;
  889. inc(pointer(phdr), phdr_size);
  890. end;
  891. {$ifdef DEBUG_LINEINFO}
  892. end
  893. else
  894. begin
  895. if (phdr_count=-1) then
  896. writeln(stderr,'AUX entry AT_PHNUM not found');
  897. if (phdr_size=0) then
  898. writeln(stderr,'AUX entry AT_PHENT not found');
  899. if (phdr=nil) then
  900. writeln(stderr,'AUX entry AT_PHDR not found');
  901. {$endif DEBUG_LINEINFO}
  902. end;
  903. if found_addr<>ptruint(-1) then
  904. begin
  905. {$ifdef DEBUG_LINEINFO}
  906. Writeln(stderr,'Found addr = $',hexstr(found_addr,2 * sizeof(ptruint)));
  907. {$endif}
  908. BaseAddr:=pointer(found_addr);
  909. end
  910. {$ifdef DEBUG_LINEINFO}
  911. else
  912. writeln(stderr,'Error parsing stack');
  913. {$endif DEBUG_LINEINFO}
  914. end
  915. else
  916. begin
  917. {$ifdef DEBUG_LINEINFO}
  918. writeln(stderr,'Exception parsing stack');
  919. {$endif DEBUG_LINEINFO}
  920. end;
  921. ExitProc:=SavedExitProc;
  922. end;
  923. {$endif FIND_BASEADDR_ELF}
  924. function OpenElf(var e:TExeFile):boolean;
  925. {$ifdef MSDOS}
  926. const
  927. ParagraphSize = 512;
  928. {$endif MSDOS}
  929. var
  930. elfheader : telfheader;
  931. elfsec : telfsechdr;
  932. phdr : telfproghdr;
  933. i : longint;
  934. {$ifdef MSDOS}
  935. DosHeader : tdosheader;
  936. BRead : cardinal;
  937. {$endif MSDOS}
  938. begin
  939. OpenElf:=false;
  940. {$ifdef MSDOS}
  941. { read and check header }
  942. if E.Size < SizeOf (DosHeader) then
  943. Exit;
  944. BlockRead (E.F, DosHeader, SizeOf (DosHeader), BRead);
  945. if BRead <> SizeOf (DosHeader) then
  946. Exit;
  947. if DosHeader.E_Magic = $5A4D then
  948. begin
  949. E.ImgOffset := LongWord(DosHeader.e_cp) * ParagraphSize;
  950. if DosHeader.e_cblp > 0 then
  951. E.ImgOffset := E.ImgOffset + DosHeader.e_cblp - ParagraphSize;
  952. end;
  953. {$endif MSDOS}
  954. { read and check header }
  955. if e.size<(sizeof(telfheader)+e.ImgOffset) then
  956. exit;
  957. seek(e.f,e.ImgOffset);
  958. blockread(e.f,elfheader,sizeof(telfheader));
  959. if elfheader.magic0123<>{$ifdef ENDIAN_LITTLE}$464c457f{$else}$7f454c46{$endif} then
  960. exit;
  961. if elfheader.e_shentsize<>sizeof(telfsechdr) then
  962. exit;
  963. { read section names }
  964. seek(e.f,e.ImgOffset+elfheader.e_shoff+elfheader.e_shstrndx*cardinal(sizeof(telfsechdr)));
  965. blockread(e.f,elfsec,sizeof(telfsechdr));
  966. e.secstrofs:=elfsec.sh_offset;
  967. e.sechdrofs:=elfheader.e_shoff;
  968. e.nsects:=elfheader.e_shnum;
  969. {$ifdef MSDOS}
  970. { e.processaddress is already initialized to 0 }
  971. e.processsegment:=PrefixSeg+16;
  972. {$else MSDOS}
  973. { scan program headers to find the image base address }
  974. e.processaddress:=High(e.processaddress);
  975. seek(e.f,e.ImgOffset+elfheader.e_phoff);
  976. for i:=1 to elfheader.e_phnum do
  977. begin
  978. blockread(e.f,phdr,sizeof(phdr));
  979. if (phdr.p_type = 1 {PT_LOAD}) and (ptruint(phdr.p_vaddr) < e.processaddress) then
  980. e.processaddress:=phdr.p_vaddr;
  981. end;
  982. if e.processaddress = High(e.processaddress) then
  983. e.processaddress:=0;
  984. {$endif MSDOS}
  985. OpenElf:=true;
  986. end;
  987. function FindSectionElf(var e:TExeFile;const asecname:shortstring;var secofs,seclen:longint):boolean;
  988. var
  989. elfsec : telfsechdr;
  990. secname : string;
  991. secnamebuf : array[0..255] of ansichar;
  992. oldofs,
  993. bufsize,i : longint;
  994. begin
  995. FindSectionElf:=false;
  996. seek(e.f,e.ImgOffset+e.sechdrofs);
  997. for i:=1 to e.nsects do
  998. begin
  999. blockread(e.f,elfsec,sizeof(telfsechdr));
  1000. fillchar(secnamebuf,sizeof(secnamebuf),0);
  1001. oldofs:=filepos(e.f);
  1002. seek(e.f,e.ImgOffset+e.secstrofs+elfsec.sh_name);
  1003. blockread(e.f,secnamebuf,sizeof(secnamebuf)-1,bufsize);
  1004. seek(e.f,oldofs);
  1005. secname:=strpas(secnamebuf);
  1006. if asecname=secname then
  1007. begin
  1008. secofs:=e.ImgOffset+elfsec.sh_offset;
  1009. seclen:=elfsec.sh_size;
  1010. FindSectionElf:=true;
  1011. exit;
  1012. end;
  1013. end;
  1014. end;
  1015. {$endif ELF32 or ELF64}
  1016. {****************************************************************************
  1017. MACHO
  1018. ****************************************************************************}
  1019. {$ifdef darwin}
  1020. {$push}
  1021. {$packrecords c}
  1022. type
  1023. tmach_integer = cint;
  1024. tmach_cpu_type = tmach_integer;
  1025. tmach_cpu_subtype = tmach_integer;
  1026. tmach_cpu_threadtype = tmach_integer;
  1027. tmach_fat_header=record
  1028. magic: cuint32;
  1029. nfatarch: cuint32;
  1030. end;
  1031. tmach_fat_arch=record
  1032. cputype: tmach_cpu_type;
  1033. cpusubtype: tmach_cpu_subtype;
  1034. offset: cuint32;
  1035. size: cuint32;
  1036. align: cuint32;
  1037. end;
  1038. pmach_fat_arch = ^tmach_fat_arch;
  1039. (* not yet supported (only needed for slices or combined slice size > 4GB; unrelated to 64 bit processes)
  1040. tmach_fat_arch_64=record
  1041. cputype: tmach_cpu_type;
  1042. cpusubtype: tmach_cpu_subtype;
  1043. offset: cuint64;
  1044. size: cuint64;
  1045. align: cuint32;
  1046. reserved: cuint32;
  1047. end;
  1048. *)
  1049. { note: always big endian }
  1050. tmach_header=record
  1051. magic: cuint32;
  1052. cputype: tmach_cpu_type;
  1053. cpusubtype: tmach_cpu_subtype;
  1054. filetype: cuint32;
  1055. ncmds: cuint32;
  1056. sizeofcmds: cuint32;
  1057. flags: cuint32;
  1058. {$IFDEF CPU64}
  1059. reserved: cuint32;
  1060. {$ENDIF}
  1061. end;
  1062. pmach_header = ^tmach_header;
  1063. tmach_load_command=record
  1064. cmd: cuint32;
  1065. cmdsize: cuint32;
  1066. end;
  1067. pmach_load_command=^tmach_load_command;
  1068. tmach_symtab_command=record
  1069. cmd : cuint32;
  1070. cmdsize: cuint32;
  1071. symoff : cuint32;
  1072. nsyms : cuint32;
  1073. stroff : cuint32;
  1074. strsize: cuint32;
  1075. end;
  1076. pmach_symtab_command = ^tmach_symtab_command;
  1077. tstab=record
  1078. strpos : longword;
  1079. ntype : byte;
  1080. nother : byte;
  1081. ndesc : word;
  1082. nvalue : longword;
  1083. end;
  1084. pstab = ^tstab;
  1085. tmach_vm_prot = cint;
  1086. tmach_segment_command = record
  1087. cmd : cuint32;
  1088. cmdsize : cuint32;
  1089. segname : array [0..15] of AnsiChar;
  1090. vmaddr : {$IFDEF CPU64}cuint64{$ELSE}cuint32{$ENDIF};
  1091. vmsize : {$IFDEF CPU64}cuint64{$ELSE}cuint32{$ENDIF};
  1092. fileoff : {$IFDEF CPU64}cuint64{$ELSE}cuint32{$ENDIF};
  1093. filesize: {$IFDEF CPU64}cuint64{$ELSE}cuint32{$ENDIF};
  1094. maxprot : tmach_vm_prot;
  1095. initptot: tmach_vm_prot;
  1096. nsects : cuint32;
  1097. flags : cuint32;
  1098. end;
  1099. pmach_segment_command = ^tmach_segment_command;
  1100. tmach_uuid_command = record
  1101. cmd : cuint32;
  1102. cmdsize : cuint32;
  1103. uuid : array[0..15] of cuint8;
  1104. end;
  1105. pmach_uuid_command = ^tmach_uuid_command;
  1106. tmach_section = record
  1107. sectname : array [0..15] of AnsiChar;
  1108. segname : array [0..15] of AnsiChar;
  1109. addr : {$IFDEF CPU64}cuint64{$ELSE}cuint32{$ENDIF};
  1110. size : {$IFDEF CPU64}cuint64{$ELSE}cuint32{$ENDIF};
  1111. offset : cuint32;
  1112. align : cuint32;
  1113. reloff : cuint32;
  1114. nreloc : cuint32;
  1115. flags : cuint32;
  1116. reserved1: cuint32;
  1117. reserved2: cuint32;
  1118. {$IFDEF CPU64}
  1119. reserved3: cuint32;
  1120. {$ENDIF}
  1121. end;
  1122. pmach_section = ^tmach_section;
  1123. tmach_fat_archs = array[1..high(longint) div sizeof(tmach_header)] of tmach_fat_arch;
  1124. tmach_fat_header_archs = record
  1125. header: tmach_fat_header;
  1126. archs: tmach_fat_archs;
  1127. end;
  1128. pmach_fat_header_archs = ^tmach_fat_header_archs;
  1129. {$pop}
  1130. const
  1131. MACH_MH_EXECUTE = $02;
  1132. MACH_FAT_MAGIC = $cafebabe;
  1133. // not yet supported: only for binaries with slices > 4GB, or total size > 4GB
  1134. // MACH_FAT_MAGIC_64 = $cafebabf;
  1135. {$ifdef cpu32}
  1136. MACH_MAGIC = $feedface;
  1137. {$else}
  1138. MACH_MAGIC = $feedfacf;
  1139. {$endif}
  1140. MACH_CPU_ARCH_MASK = cuint32($ff000000);
  1141. {$ifdef cpu32}
  1142. MACH_LC_SEGMENT = $01;
  1143. {$else}
  1144. MACH_LC_SEGMENT = $19;
  1145. {$endif}
  1146. MACH_LC_SYMTAB = $02;
  1147. MACH_LC_UUID = $1b;
  1148. { the in-memory mapping of the mach header of the main binary }
  1149. function _NSGetMachExecuteHeader: pmach_header; cdecl; external 'c';
  1150. function getpagesize: cint; cdecl; external 'c';
  1151. function MapMachO(const h: THandle; offset, len: SizeUInt; out addr: pointer; out memoffset, mappedsize: SizeUInt): boolean;
  1152. var
  1153. pagesize: cint;
  1154. begin
  1155. pagesize:=getpagesize;
  1156. addr:=fpmmap(nil, len+(offset and (pagesize-1)), PROT_READ, MAP_PRIVATE, h, offset and not(pagesize-1));
  1157. if addr=MAP_FAILED then
  1158. begin
  1159. addr:=nil;
  1160. memoffset:=0;
  1161. mappedsize:=0;
  1162. end
  1163. else
  1164. begin
  1165. memoffset:=offset and (pagesize - 1);
  1166. mappedsize:=len+(offset and (pagesize-1));
  1167. end;
  1168. end;
  1169. procedure UnmapMachO(p: pointer; size: SizeUInt);
  1170. begin
  1171. fpmunmap(p,size);
  1172. end;
  1173. function OpenMachO(var e:TExeFile):boolean;
  1174. var
  1175. mh : tmach_header;
  1176. processmh : pmach_header;
  1177. cmd: pmach_load_command;
  1178. segmentcmd: pmach_segment_command;
  1179. mappedexe: pointer;
  1180. mappedoffset, mappedsize: SizeUInt;
  1181. i: cuint32;
  1182. foundpagezero: boolean;
  1183. begin
  1184. OpenMachO:=false;
  1185. E.FunctionRelative:=false;
  1186. if e.size<sizeof(mh) then
  1187. exit;
  1188. blockread (e.f, mh, sizeof(mh));
  1189. case mh.magic of
  1190. MACH_FAT_MAGIC:
  1191. begin
  1192. { todo }
  1193. exit
  1194. end;
  1195. MACH_MAGIC:
  1196. begin
  1197. // check that at least the architecture matches (we should also check the subarch,
  1198. // but that's harder because of architecture-specific backward compatibility rules)
  1199. processmh:=_NSGetMachExecuteHeader;
  1200. if (mh.cputype and not(MACH_CPU_ARCH_MASK)) <> (processmh^.cputype and not(MACH_CPU_ARCH_MASK)) then
  1201. exit;
  1202. end;
  1203. else
  1204. exit;
  1205. end;
  1206. e.sechdrofs:=filepos(e.f);
  1207. e.nsects:=mh.ncmds;
  1208. e.loadcommandssize:=mh.sizeofcmds;
  1209. if mh.filetype = MACH_MH_EXECUTE then
  1210. begin
  1211. foundpagezero:= false;
  1212. { make sure to unmap again on all exit paths }
  1213. if not MapMachO(filerec(e.f).handle, e.sechdrofs, e.loadcommandssize, mappedexe, mappedoffset, mappedsize) then
  1214. exit;
  1215. cmd:=pmach_load_command(mappedexe+mappedoffset);
  1216. for i:= 1 to e.nsects do
  1217. begin
  1218. case cmd^.cmd of
  1219. MACH_LC_SEGMENT:
  1220. begin
  1221. segmentcmd:=pmach_segment_command(cmd);
  1222. if segmentcmd^.segname='__PAGEZERO' then
  1223. begin
  1224. e.processaddress:=segmentcmd^.vmaddr+segmentcmd^.vmsize;
  1225. OpenMachO:=true;
  1226. break;
  1227. end;
  1228. end;
  1229. end;
  1230. cmd:=pmach_load_command(pointer(cmd)+cmd^.cmdsize);
  1231. end;
  1232. UnmapMachO(mappedexe, mappedsize);
  1233. end
  1234. else
  1235. OpenMachO:=true;
  1236. end;
  1237. function FindSectionMachO(var e:TExeFile;const asecname:shortstring;var secofs,seclen:longint):boolean;
  1238. var
  1239. i, j: cuint32;
  1240. cmd: pmach_load_command;
  1241. symtabcmd: pmach_symtab_command;
  1242. segmentcmd: pmach_segment_command;
  1243. section: pmach_section;
  1244. mappedexe: pointer;
  1245. mappedoffset, mappedsize: SizeUInt;
  1246. dwarfsecname: shortstring;
  1247. begin
  1248. FindSectionMachO:=false;
  1249. { make sure to unmap again on all exit paths }
  1250. if not MapMachO(filerec(e.f).handle, e.sechdrofs, e.loadcommandssize, mappedexe, mappedoffset, mappedsize) then
  1251. exit;
  1252. cmd:=pmach_load_command(mappedexe+mappedoffset);
  1253. for i:= 1 to e.nsects do
  1254. begin
  1255. case cmd^.cmd of
  1256. MACH_LC_SEGMENT:
  1257. begin
  1258. segmentcmd:=pmach_segment_command(cmd);
  1259. if segmentcmd^.segname='__DWARF' then
  1260. begin
  1261. if asecname[1]='.' then
  1262. dwarfsecname:='__'+copy(asecname,2,length(asecname))
  1263. else
  1264. dwarfsecname:=asecname;
  1265. section:=pmach_section(pointer(segmentcmd)+sizeof(segmentcmd^));
  1266. for j:=1 to segmentcmd^.nsects do
  1267. begin
  1268. if section^.sectname = dwarfsecname then
  1269. begin
  1270. secofs:=section^.offset;
  1271. seclen:=section^.size;
  1272. FindSectionMachO:=true;
  1273. UnmapMachO(mappedexe, mappedsize);
  1274. exit;
  1275. end;
  1276. inc(section);
  1277. end;
  1278. end;
  1279. end;
  1280. MACH_LC_SYMTAB:
  1281. begin
  1282. symtabcmd:=pmach_symtab_command(cmd);
  1283. if asecname='.stab' then
  1284. begin
  1285. secofs:=symtabcmd^.symoff;
  1286. { the caller will divide again by sizeof(tstab) }
  1287. seclen:=symtabcmd^.nsyms*sizeof(tstab);
  1288. FindSectionMachO:=true;
  1289. end
  1290. else if asecname='.stabstr' then
  1291. begin
  1292. secofs:=symtabcmd^.stroff;
  1293. seclen:=symtabcmd^.strsize;
  1294. FindSectionMachO:=true;
  1295. end;
  1296. if FindSectionMachO then
  1297. begin
  1298. UnmapMachO(mappedexe, mappedsize);
  1299. exit;
  1300. end;
  1301. end;
  1302. end;
  1303. cmd:=pmach_load_command(pointer(cmd)+cmd^.cmdsize);
  1304. end;
  1305. UnmapMachO(mappedexe, mappedsize);
  1306. end;
  1307. {$endif darwin}
  1308. {****************************************************************************
  1309. CRC
  1310. ****************************************************************************}
  1311. var
  1312. Crc32Tbl : array[0..255] of cardinal;
  1313. procedure MakeCRC32Tbl;
  1314. var
  1315. crc : cardinal;
  1316. i,n : integer;
  1317. begin
  1318. for i:=0 to 255 do
  1319. begin
  1320. crc:=i;
  1321. for n:=1 to 8 do
  1322. if (crc and 1)<>0 then
  1323. crc:=(crc shr 1) xor cardinal($edb88320)
  1324. else
  1325. crc:=crc shr 1;
  1326. Crc32Tbl[i]:=crc;
  1327. end;
  1328. end;
  1329. Function UpdateCrc32(InitCrc:cardinal;const InBuf;InLen:LongInt):cardinal;
  1330. var
  1331. i : LongInt;
  1332. p : pansichar;
  1333. begin
  1334. if Crc32Tbl[1]=0 then
  1335. MakeCrc32Tbl;
  1336. p:=@InBuf;
  1337. UpdateCrc32:=not InitCrc;
  1338. for i:=1 to InLen do
  1339. begin
  1340. UpdateCrc32:=Crc32Tbl[byte(UpdateCrc32) xor byte(p^)] xor (UpdateCrc32 shr 8);
  1341. inc(p);
  1342. end;
  1343. UpdateCrc32:=not UpdateCrc32;
  1344. end;
  1345. {****************************************************************************
  1346. Generic Executable Open/Close
  1347. ****************************************************************************}
  1348. type
  1349. TOpenProc=function(var e:TExeFile):boolean;
  1350. TFindSectionProc=function(var e:TExeFile;const asecname:shortstring;var secofs,seclen:longint):boolean;
  1351. TExeProcRec=record
  1352. openproc : TOpenProc;
  1353. findproc : TFindSectionProc;
  1354. end;
  1355. const
  1356. ExeProcs : TExeProcRec = (
  1357. {$ifdef go32v2}
  1358. openproc : @OpenGo32Coff;
  1359. findproc : @FindSectionCoff;
  1360. {$endif}
  1361. {$ifdef PE32}
  1362. openproc : @OpenPeCoff;
  1363. findproc : @FindSectionCoff;
  1364. {$endif}
  1365. {$ifdef PE32PLUS}
  1366. openproc : @OpenPePlusCoff;
  1367. findproc : @FindSectionCoff;
  1368. {$endif PE32PLUS}
  1369. {$if defined(ELF32) or defined(ELF64)}
  1370. openproc : @OpenElf;
  1371. findproc : @FindSectionElf;
  1372. {$endif ELF32 or ELF64}
  1373. {$ifdef darwin}
  1374. openproc : @OpenMachO;
  1375. findproc : @FindSectionMachO;
  1376. {$endif darwin}
  1377. {$IFDEF EMX}
  1378. openproc : @OpenEMXaout;
  1379. findproc : @FindSectionEMXaout;
  1380. {$ENDIF EMX}
  1381. {$ifdef netware}
  1382. openproc : @OpenNetwareNLM;
  1383. findproc : @FindSectionNetwareNLM;
  1384. {$endif}
  1385. );
  1386. function OpenExeFile(var e:TExeFile;const fn:shortstring):boolean;
  1387. var
  1388. ofm : word;
  1389. begin
  1390. OpenExeFile:=false;
  1391. fillchar(e,sizeof(e),0);
  1392. e.bufsize:=sizeof(e.buf);
  1393. e.filename:=fn;
  1394. if fn='' then // we don't want to read stdin
  1395. exit;
  1396. assign(e.f,fn);
  1397. {$I-}
  1398. ofm:=filemode;
  1399. filemode:=$40;
  1400. reset(e.f,1);
  1401. filemode:=ofm;
  1402. {$I+}
  1403. if ioresult<>0 then
  1404. exit;
  1405. e.isopen:=true;
  1406. // cache filesize
  1407. e.size:=filesize(e.f);
  1408. E.FunctionRelative := true;
  1409. E.ImgOffset := 0;
  1410. if ExeProcs.OpenProc<>nil then
  1411. OpenExeFile:=ExeProcs.OpenProc(e);
  1412. end;
  1413. function CloseExeFile(var e:TExeFile):boolean;
  1414. begin
  1415. CloseExeFile:=false;
  1416. if not e.isopen then
  1417. exit;
  1418. e.isopen:=false;
  1419. close(e.f);
  1420. CloseExeFile:=true;
  1421. end;
  1422. function FindExeSection(var e:TExeFile;const secname:shortstring;var secofs,seclen:longint):boolean;
  1423. begin
  1424. FindExeSection:=false;
  1425. if not e.isopen then
  1426. exit;
  1427. if ExeProcs.FindProc<>nil then
  1428. FindExeSection:=ExeProcs.FindProc(e,secname,secofs,seclen);
  1429. end;
  1430. function CheckDbgFile(var e:TExeFile;const fn:shortstring;dbgcrc:cardinal):boolean;
  1431. var
  1432. c : cardinal;
  1433. ofm : word;
  1434. g : file;
  1435. begin
  1436. CheckDbgFile:=false;
  1437. assign(g,fn);
  1438. {$I-}
  1439. ofm:=filemode;
  1440. filemode:=$40;
  1441. reset(g,1);
  1442. filemode:=ofm;
  1443. {$I+}
  1444. if ioresult<>0 then
  1445. exit;
  1446. { We reuse the buffer from e here to prevent too much stack allocation }
  1447. c:=0;
  1448. repeat
  1449. blockread(g,e.buf,e.bufsize,e.bufcnt);
  1450. c:=UpdateCrc32(c,e.buf,e.bufcnt);
  1451. until e.bufcnt<e.bufsize;
  1452. close(g);
  1453. CheckDbgFile:=(dbgcrc=c);
  1454. end;
  1455. {$ifndef darwin}
  1456. function ReadDebugLink(var e:TExeFile;var dbgfn:ansistring):boolean;
  1457. var
  1458. dbglink : array[0..255] of AnsiChar;
  1459. i,
  1460. dbglinklen,
  1461. dbglinkofs : longint;
  1462. dbgcrc : cardinal;
  1463. begin
  1464. ReadDebugLink:=false;
  1465. if not FindExeSection(e,'.gnu_debuglink',dbglinkofs,dbglinklen) then
  1466. exit;
  1467. if dbglinklen>sizeof(dbglink)-1 then
  1468. exit;
  1469. fillchar(dbglink,sizeof(dbglink),0);
  1470. seek(e.f,dbglinkofs);
  1471. blockread(e.f,dbglink,dbglinklen);
  1472. dbgfn:=strpas(dbglink);
  1473. if length(dbgfn)=0 then
  1474. exit;
  1475. i:=align(length(dbgfn)+1,4);
  1476. if (i+4)>dbglinklen then
  1477. exit;
  1478. move(dbglink[i],dbgcrc,4);
  1479. { current dir }
  1480. if CheckDbgFile(e,dbgfn,dbgcrc) then
  1481. begin
  1482. ReadDebugLink:=true;
  1483. exit;
  1484. end;
  1485. { executable dir }
  1486. i:=length(e.filename);
  1487. while (i>0) and not(e.filename[i] in AllowDirectorySeparators) do
  1488. dec(i);
  1489. if i>0 then
  1490. begin
  1491. dbgfn:=copy(e.filename,1,i)+dbgfn;
  1492. if CheckDbgFile(e,dbgfn,dbgcrc) then
  1493. begin
  1494. ReadDebugLink:=true;
  1495. exit;
  1496. end;
  1497. end;
  1498. end;
  1499. {$else}
  1500. function ReadDebugLink(var e:TExeFile;var dbgfn:ansistring):boolean;
  1501. var
  1502. dsymexefile: TExeFile;
  1503. execmd, dsymcmd: pmach_load_command;
  1504. exeuuidcmd, dsymuuidcmd: pmach_uuid_command;
  1505. mappedexe, mappeddsym: pointer;
  1506. mappedexeoffset, mappedexesize, mappeddsymoffset, mappeddsymsize: SizeUInt;
  1507. i, j: cuint32;
  1508. filenamestartpos, b: byte;
  1509. begin
  1510. ReadDebugLink:=false;
  1511. if not MapMachO(filerec(e.f).handle, e.sechdrofs, e.loadcommandssize, mappedexe, mappedexeoffset, mappedexesize) then
  1512. exit;
  1513. execmd:=pmach_load_command(mappedexe+mappedexeoffset);
  1514. for i:=1 to e.nsects do
  1515. begin
  1516. case execmd^.cmd of
  1517. MACH_LC_UUID:
  1518. begin
  1519. exeuuidcmd:=pmach_uuid_command(execmd);
  1520. filenamestartpos:=1;
  1521. for b:=1 to length(e.filename) do
  1522. begin
  1523. if e.filename[b] = '/' then
  1524. filenamestartpos:=b+1;
  1525. end;
  1526. if not OpenExeFile(dsymexefile,e.filename+'.dSYM/Contents/Resources/DWARF/'+copy(e.filename,filenamestartpos,length(e.filename))) then
  1527. begin
  1528. {$IFDEF DEBUG_LINEINFO}
  1529. writeln(stderr,'OpenExeFile for ',e.filename+'.dSYM/Contents/Resources/DWARF/'+copy(e.filename,filenamestartpos,length(e.filename)),' did not succeed.');
  1530. {$endif DEBUG_LINEINFO}
  1531. UnmapMachO(mappedexe, mappedexesize);
  1532. exit;
  1533. end;
  1534. if not MapMachO(filerec(dsymexefile.f).handle, dsymexefile.sechdrofs, dsymexefile.loadcommandssize, mappeddsym, mappeddsymoffset, mappeddsymsize) then
  1535. begin
  1536. CloseExeFile(dsymexefile);
  1537. UnmapMachO(mappedexe, mappedexesize);
  1538. exit;
  1539. end;
  1540. dsymcmd:=pmach_load_command(mappeddsym+mappeddsymoffset);
  1541. for j:=1 to dsymexefile.nsects do
  1542. begin
  1543. case dsymcmd^.cmd of
  1544. MACH_LC_UUID:
  1545. begin
  1546. dsymuuidcmd:=pmach_uuid_command(dsymcmd);
  1547. if comparebyte(exeuuidcmd^.uuid, dsymuuidcmd^.uuid, sizeof(exeuuidcmd^.uuid)) = 0 then
  1548. begin
  1549. dbgfn:=dsymexefile.filename;
  1550. ReadDebugLink:=true;
  1551. end;
  1552. break;
  1553. end;
  1554. end;
  1555. end;
  1556. UnmapMachO(mappeddsym, mappeddsymsize);
  1557. CloseExeFile(dsymexefile);
  1558. UnmapMachO(mappedexe, mappedexesize);
  1559. exit;
  1560. end;
  1561. end;
  1562. execmd:=pmach_load_command(pointer(execmd)+execmd^.cmdsize);
  1563. end;
  1564. UnmapMachO(mappedexe, mappedexesize);
  1565. end;
  1566. {$endif}
  1567. begin
  1568. {$ifdef FIND_BASEADDR_ELF}
  1569. UnixGetModuleByAddrHook:=@GetExeInMemoryBaseAddr;
  1570. {$endif FIND_BASEADDR_ELF}
  1571. end.