exeinfo.pp 47 KB

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