exeinfo.pp 32 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243
  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. unit exeinfo;
  16. interface
  17. {$S-}
  18. type
  19. TExeFile=record
  20. f : file;
  21. // cached filesize
  22. size : int64;
  23. isopen : boolean;
  24. nsects : longint;
  25. sechdrofs,
  26. secstrofs : ptruint;
  27. processaddress : ptruint;
  28. FunctionRelative: boolean;
  29. // Offset of the binary image forming permanent offset to all retrieved values
  30. ImgOffset: ptruint;
  31. filename : string;
  32. // Allocate static buffer for reading data
  33. buf : array[0..4095] of byte;
  34. bufsize,
  35. bufcnt : longint;
  36. end;
  37. function OpenExeFile(var e:TExeFile;const fn:string):boolean;
  38. function FindExeSection(var e:TExeFile;const secname:string;var secofs,seclen:longint):boolean;
  39. function CloseExeFile(var e:TExeFile):boolean;
  40. function ReadDebugLink(var e:TExeFile;var dbgfn:string):boolean;
  41. procedure GetModuleByAddr(addr: pointer; var baseaddr: pointer; var filename: string);
  42. implementation
  43. uses
  44. strings{$ifdef windows},windows{$endif windows};
  45. {$ifdef unix}
  46. procedure GetModuleByAddr(addr: pointer; var baseaddr: pointer; var filename: string);
  47. begin
  48. if assigned(UnixGetModuleByAddrHook) then
  49. UnixGetModuleByAddrHook(addr,baseaddr,filename)
  50. else
  51. begin
  52. baseaddr:=nil;
  53. filename:=ParamStr(0);
  54. end;
  55. end;
  56. {$else unix}
  57. {$ifdef windows}
  58. var
  59. Tmm: TMemoryBasicInformation;
  60. {$ifdef FPC_OS_UNICODE}
  61. TST: array[0..Max_Path] of WideChar;
  62. {$else}
  63. TST: array[0..Max_Path] of Char;
  64. {$endif FPC_OS_UNICODE}
  65. procedure GetModuleByAddr(addr: pointer; var baseaddr: pointer; var filename: string);
  66. begin
  67. baseaddr:=nil;
  68. if VirtualQuery(addr, @Tmm, SizeOf(Tmm))<>sizeof(Tmm) then
  69. filename:=ParamStr(0)
  70. else
  71. begin
  72. baseaddr:=Tmm.AllocationBase;
  73. TST[0]:= #0;
  74. GetModuleFileName(THandle(Tmm.AllocationBase), TST, Length(TST));
  75. {$ifdef FPC_OS_UNICODE}
  76. filename:= String(PWideChar(@TST));
  77. {$else}
  78. filename:= String(PChar(@TST));
  79. {$endif FPC_OS_UNICODE}
  80. end;
  81. end;
  82. {$else windows}
  83. procedure GetModuleByAddr(addr: pointer; var baseaddr: pointer; var filename: string);
  84. begin
  85. baseaddr:= nil;
  86. {$ifdef FPC_HAS_FEATURE_COMMANDARGS}
  87. filename:=ParamStr(0);
  88. {$else FPC_HAS_FEATURE_COMMANDARGS}
  89. filename:='';
  90. {$endif FPC_HAS_FEATURE_COMMANDARGS}
  91. end;
  92. {$endif windows}
  93. {$endif unix}
  94. {****************************************************************************
  95. Executable Loaders
  96. ****************************************************************************}
  97. {$if defined(freebsd) or defined(netbsd) or defined (openbsd) or defined(linux) or defined(sunos) or defined(android) or defined(dragonfly)}
  98. {$ifdef cpu64}
  99. {$define ELF64}
  100. {$else}
  101. {$define ELF32}
  102. {$endif}
  103. {$endif}
  104. {$if defined(win32) or defined(wince)}
  105. {$define PE32}
  106. {$endif}
  107. {$if defined(win64)}
  108. {$define PE32PLUS}
  109. {$endif}
  110. {$ifdef netwlibc}
  111. {$define netware}
  112. {$endif}
  113. {$IFDEF OS2}
  114. {$DEFINE EMX}
  115. {$ENDIF OS2}
  116. {****************************************************************************
  117. DOS Stub
  118. ****************************************************************************}
  119. {$if defined(EMX) or defined(PE32) or defined(PE32PLUS) or defined(GO32V2)}
  120. type
  121. tdosheader = packed record
  122. e_magic : word;
  123. e_cblp : word;
  124. e_cp : word;
  125. e_crlc : word;
  126. e_cparhdr : word;
  127. e_minalloc : word;
  128. e_maxalloc : word;
  129. e_ss : word;
  130. e_sp : word;
  131. e_csum : word;
  132. e_ip : word;
  133. e_cs : word;
  134. e_lfarlc : word;
  135. e_ovno : word;
  136. e_res : array[0..3] of word;
  137. e_oemid : word;
  138. e_oeminfo : word;
  139. e_res2 : array[0..9] of word;
  140. e_lfanew : longint;
  141. end;
  142. {$endif EMX or PE32 or PE32PLUS or GO32v2}
  143. {****************************************************************************
  144. NLM
  145. ****************************************************************************}
  146. {$ifdef netware}
  147. function getByte(var f:file):byte;
  148. begin
  149. BlockRead (f,getByte,1);
  150. end;
  151. procedure Skip (var f:file; bytes : longint);
  152. var i : longint;
  153. begin
  154. for i := 1 to bytes do getbyte(f);
  155. end;
  156. function get0String (var f:file) : string;
  157. var c : char;
  158. begin
  159. get0String := '';
  160. c := char (getbyte(f));
  161. while (c <> #0) do
  162. begin
  163. get0String := get0String + c;
  164. c := char (getbyte(f));
  165. end;
  166. end;
  167. function getint32 (var f:file): longint;
  168. begin
  169. blockread (F, getint32, 4);
  170. end;
  171. const SIZE_OF_NLM_INTERNAL_FIXED_HEADER = 130;
  172. SIZE_OF_NLM_INTERNAL_VERSION_HEADER = 32;
  173. SIZE_OF_NLM_INTERNAL_EXTENDED_HEADER = 124;
  174. function openNetwareNLM(var e:TExeFile):boolean;
  175. var valid : boolean;
  176. name : string;
  177. hdrLength,
  178. dataOffset,
  179. dataLength : longint;
  180. function getLString : String;
  181. var Res:string;
  182. begin
  183. blockread (e.F, res, 1);
  184. if length (res) > 0 THEN
  185. blockread (e.F, res[1], length (res));
  186. getbyte(e.f);
  187. getLString := res;
  188. end;
  189. function getFixString (Len : byte) : string;
  190. var i : byte;
  191. begin
  192. getFixString := '';
  193. for I := 1 to Len do
  194. getFixString := getFixString + char (getbyte(e.f));
  195. end;
  196. function getword : word;
  197. begin
  198. blockread (e.F, getword, 2);
  199. end;
  200. begin
  201. e.sechdrofs := 0;
  202. openNetwareNLM:=false;
  203. // read and check header
  204. Skip (e.f,SIZE_OF_NLM_INTERNAL_FIXED_HEADER);
  205. getLString; // NLM Description
  206. getInt32(e.f); // Stacksize
  207. getInt32(e.f); // Reserved
  208. skip(e.f,5); // old Thread Name
  209. getLString; // Screen Name
  210. getLString; // Thread Name
  211. hdrLength := -1;
  212. dataOffset := -1;
  213. dataLength := -1;
  214. valid := true;
  215. repeat
  216. name := getFixString (8);
  217. if (name = 'VeRsIoN#') then
  218. begin
  219. Skip (e.f,SIZE_OF_NLM_INTERNAL_VERSION_HEADER-8);
  220. end else
  221. if (name = 'CoPyRiGh') then
  222. begin
  223. getword; // T=
  224. getLString; // Copyright String
  225. end else
  226. if (name = 'MeSsAgEs') then
  227. begin
  228. skip (e.f,SIZE_OF_NLM_INTERNAL_EXTENDED_HEADER - 8);
  229. end else
  230. if (name = 'CuStHeAd') then
  231. begin
  232. hdrLength := getInt32(e.f);
  233. dataOffset := getInt32(e.f);
  234. dataLength := getInt32(e.f);
  235. Skip (e.f,8); // dateStamp
  236. Valid := false;
  237. end else
  238. Valid := false;
  239. until not valid;
  240. if (hdrLength = -1) or (dataOffset = -1) or (dataLength = -1) then
  241. exit;
  242. Seek (e.F, dataOffset);
  243. e.sechdrofs := dataOffset;
  244. openNetwareNLM := (e.sechdrofs > 0);
  245. end;
  246. function FindSectionNetwareNLM(var e:TExeFile;const asecname:string;var secofs,seclen:longint):boolean;
  247. var name : string;
  248. alignAmount : longint;
  249. begin
  250. seek(e.f,e.sechdrofs);
  251. (* The format of the section information is:
  252. null terminated section name
  253. zeroes to adjust to 4 byte boundary
  254. 4 byte section data file pointer
  255. 4 byte section size *)
  256. Repeat
  257. Name := Get0String(e.f);
  258. alignAmount := 4 - ((length (Name) + 1) MOD 4);
  259. Skip (e.f,AlignAmount);
  260. if (Name = asecname) then
  261. begin
  262. secOfs := getInt32(e.f);
  263. secLen := getInt32(e.f);
  264. end else
  265. Skip(e.f,8);
  266. until (Name = '') or (Name = asecname);
  267. FindSectionNetwareNLM := (Name=asecname);
  268. end;
  269. {$endif}
  270. {****************************************************************************
  271. COFF
  272. ****************************************************************************}
  273. {$if defined(PE32) or defined(PE32PLUS) or defined(GO32V2)}
  274. type
  275. tcoffsechdr=packed record
  276. name : array[0..7] of char;
  277. vsize : longint;
  278. rvaofs : longint;
  279. datalen : longint;
  280. datapos : longint;
  281. relocpos : longint;
  282. lineno1 : longint;
  283. nrelocs : word;
  284. lineno2 : word;
  285. flags : longint;
  286. end;
  287. coffsymbol=packed record
  288. name : array[0..3] of char; { real is [0..7], which overlaps the strofs ! }
  289. strofs : longint;
  290. value : longint;
  291. section : smallint;
  292. empty : word;
  293. typ : byte;
  294. aux : byte;
  295. end;
  296. function FindSectionCoff(var e:TExeFile;const asecname:string;var secofs,seclen:longint):boolean;
  297. var
  298. i : longint;
  299. sechdr : tcoffsechdr;
  300. secname : string;
  301. secnamebuf : array[0..255] of char;
  302. code,
  303. oldofs,
  304. bufsize : longint;
  305. strofs : cardinal;
  306. begin
  307. FindSectionCoff:=false;
  308. { read section info }
  309. seek(e.f,e.sechdrofs);
  310. for i:=1 to e.nsects do
  311. begin
  312. blockread(e.f,sechdr,sizeof(sechdr),bufsize);
  313. move(sechdr.name,secnamebuf,8);
  314. secnamebuf[8]:=#0;
  315. secname:=strpas(secnamebuf);
  316. if secname[1]='/' then
  317. begin
  318. Val(Copy(secname,2,8),strofs,code);
  319. if code=0 then
  320. begin
  321. fillchar(secnamebuf,sizeof(secnamebuf),0);
  322. oldofs:=filepos(e.f);
  323. seek(e.f,e.secstrofs+strofs);
  324. blockread(e.f,secnamebuf,sizeof(secnamebuf),bufsize);
  325. seek(e.f,oldofs);
  326. secname:=strpas(secnamebuf);
  327. end
  328. else
  329. secname:='';
  330. end;
  331. if asecname=secname then
  332. begin
  333. secofs:=cardinal(sechdr.datapos) + E.ImgOffset;
  334. {$ifdef GO32V2}
  335. seclen:=sechdr.datalen;
  336. {$else GO32V2}
  337. { In PECOFF, datalen includes file padding up to the next section.
  338. vsize is the actual payload size if it does not exceed datalen,
  339. otherwise it is .bss (or alike) section that we should ignore. }
  340. if sechdr.vsize<=sechdr.datalen then
  341. seclen:=sechdr.vsize
  342. else
  343. exit;
  344. {$endif GO32V2}
  345. FindSectionCoff:=true;
  346. exit;
  347. end;
  348. end;
  349. end;
  350. {$endif PE32 or PE32PLUS or GO32V2}
  351. {$ifdef go32v2}
  352. function OpenGo32Coff(var e:TExeFile):boolean;
  353. type
  354. tgo32coffheader=packed record
  355. mach : word;
  356. nsects : word;
  357. time : longint;
  358. sympos : longint;
  359. syms : longint;
  360. opthdr : word;
  361. flag : word;
  362. other : array[0..27] of byte;
  363. end;
  364. const
  365. ParagraphSize = 512;
  366. var
  367. coffheader : tgo32coffheader;
  368. DosHeader: TDosHeader;
  369. BRead: cardinal;
  370. begin
  371. OpenGo32Coff:=false;
  372. { read and check header }
  373. if E.Size < SizeOf (DosHeader) then
  374. Exit;
  375. BlockRead (E.F, DosHeader, SizeOf (DosHeader), BRead);
  376. if BRead <> SizeOf (DosHeader) then
  377. Exit;
  378. if DosHeader.E_Magic = $5A4D then
  379. begin
  380. E.ImgOffset := DosHeader.e_cp * ParagraphSize;
  381. if DosHeader.e_cblp > 0 then
  382. E.ImgOffset := E.ImgOffset + DosHeader.e_cblp - ParagraphSize;
  383. end;
  384. if e.size < E.ImgOffset + sizeof(coffheader) then
  385. exit;
  386. seek(e.f,E.ImgOffset);
  387. blockread(e.f,coffheader,sizeof(coffheader));
  388. if coffheader.mach<>$14c then
  389. exit;
  390. e.sechdrofs:=filepos(e.f);
  391. e.nsects:=coffheader.nsects;
  392. e.secstrofs:=coffheader.sympos+coffheader.syms*sizeof(coffsymbol)+4;
  393. if e.secstrofs>e.size then
  394. exit;
  395. OpenGo32Coff:=true;
  396. end;
  397. {$endif Go32v2}
  398. {$ifdef PE32}
  399. function OpenPeCoff(var e:TExeFile):boolean;
  400. type
  401. tpeheader = packed record
  402. PEMagic : longint;
  403. Machine : word;
  404. NumberOfSections : word;
  405. TimeDateStamp : longint;
  406. PointerToSymbolTable : longint;
  407. NumberOfSymbols : longint;
  408. SizeOfOptionalHeader : word;
  409. Characteristics : word;
  410. Magic : word;
  411. MajorLinkerVersion : byte;
  412. MinorLinkerVersion : byte;
  413. SizeOfCode : longint;
  414. SizeOfInitializedData : longint;
  415. SizeOfUninitializedData : longint;
  416. AddressOfEntryPoint : longint;
  417. BaseOfCode : longint;
  418. BaseOfData : longint;
  419. ImageBase : longint;
  420. SectionAlignment : longint;
  421. FileAlignment : longint;
  422. MajorOperatingSystemVersion : word;
  423. MinorOperatingSystemVersion : word;
  424. MajorImageVersion : word;
  425. MinorImageVersion : word;
  426. MajorSubsystemVersion : word;
  427. MinorSubsystemVersion : word;
  428. Reserved1 : longint;
  429. SizeOfImage : longint;
  430. SizeOfHeaders : longint;
  431. CheckSum : longint;
  432. Subsystem : word;
  433. DllCharacteristics : word;
  434. SizeOfStackReserve : longint;
  435. SizeOfStackCommit : longint;
  436. SizeOfHeapReserve : longint;
  437. SizeOfHeapCommit : longint;
  438. LoaderFlags : longint;
  439. NumberOfRvaAndSizes : longint;
  440. DataDirectory : array[1..$80] of byte;
  441. end;
  442. var
  443. dosheader : tdosheader;
  444. peheader : tpeheader;
  445. begin
  446. OpenPeCoff:=false;
  447. { read and check header }
  448. if e.size<sizeof(dosheader) then
  449. exit;
  450. blockread(e.f,dosheader,sizeof(tdosheader));
  451. seek(e.f,dosheader.e_lfanew);
  452. blockread(e.f,peheader,sizeof(tpeheader));
  453. if peheader.pemagic<>$4550 then
  454. exit;
  455. e.sechdrofs:=filepos(e.f);
  456. e.nsects:=peheader.NumberOfSections;
  457. e.secstrofs:=peheader.PointerToSymbolTable+peheader.NumberOfSymbols*sizeof(coffsymbol);
  458. if e.secstrofs>e.size then
  459. exit;
  460. e.processaddress:=peheader.ImageBase;
  461. OpenPeCoff:=true;
  462. end;
  463. {$endif PE32}
  464. {$ifdef PE32PLUS}
  465. function OpenPePlusCoff(var e:TExeFile):boolean;
  466. type
  467. tpeheader = packed record
  468. PEMagic : longint;
  469. Machine : word;
  470. NumberOfSections : word;
  471. TimeDateStamp : longint;
  472. PointerToSymbolTable : longint;
  473. NumberOfSymbols : longint;
  474. SizeOfOptionalHeader : word;
  475. Characteristics : word;
  476. Magic : word;
  477. MajorLinkerVersion : byte;
  478. MinorLinkerVersion : byte;
  479. SizeOfCode : longint;
  480. SizeOfInitializedData : longint;
  481. SizeOfUninitializedData : longint;
  482. AddressOfEntryPoint : longint;
  483. BaseOfCode : longint;
  484. ImageBase : qword;
  485. SectionAlignment : longint;
  486. FileAlignment : longint;
  487. MajorOperatingSystemVersion : word;
  488. MinorOperatingSystemVersion : word;
  489. MajorImageVersion : word;
  490. MinorImageVersion : word;
  491. MajorSubsystemVersion : word;
  492. MinorSubsystemVersion : word;
  493. Reserved1 : longint;
  494. SizeOfImage : longint;
  495. SizeOfHeaders : longint;
  496. CheckSum : longint;
  497. Subsystem : word;
  498. DllCharacteristics : word;
  499. SizeOfStackReserve : qword;
  500. SizeOfStackCommit : qword;
  501. SizeOfHeapReserve : qword;
  502. SizeOfHeapCommit : qword;
  503. LoaderFlags : longint;
  504. NumberOfRvaAndSizes : longint;
  505. DataDirectory : array[1..$80] of byte;
  506. end;
  507. var
  508. dosheader : tdosheader;
  509. peheader : tpeheader;
  510. begin
  511. OpenPePlusCoff:=false;
  512. { read and check header }
  513. if E.Size<sizeof(dosheader) then
  514. exit;
  515. blockread(E.F,dosheader,sizeof(tdosheader));
  516. seek(E.F,dosheader.e_lfanew);
  517. blockread(E.F,peheader,sizeof(tpeheader));
  518. if peheader.pemagic<>$4550 then
  519. exit;
  520. e.sechdrofs:=filepos(e.f);
  521. e.nsects:=peheader.NumberOfSections;
  522. e.secstrofs:=peheader.PointerToSymbolTable+peheader.NumberOfSymbols*sizeof(coffsymbol);
  523. if e.secstrofs>e.size then
  524. exit;
  525. e.processaddress:=peheader.ImageBase;
  526. OpenPePlusCoff:=true;
  527. end;
  528. {$endif PE32PLUS}
  529. {****************************************************************************
  530. AOUT
  531. ****************************************************************************}
  532. {$IFDEF EMX}
  533. type
  534. TEmxHeader = packed record
  535. Version: array [1..16] of char;
  536. Bound: word;
  537. AoutOfs: longint;
  538. Options: array [1..42] of char;
  539. end;
  540. TAoutHeader = packed record
  541. Magic: word;
  542. Machine: byte;
  543. Flags: byte;
  544. TextSize: longint;
  545. DataSize: longint;
  546. BssSize: longint;
  547. SymbSize: longint;
  548. EntryPoint: longint;
  549. TextRelocSize: longint;
  550. DataRelocSize: longint;
  551. end;
  552. const
  553. PageSizeFill = $FFF;
  554. var
  555. DosHeader: TDosHeader;
  556. EmxHeader: TEmxHeader;
  557. AoutHeader: TAoutHeader;
  558. StabOfs: PtrUInt;
  559. S4: string [4];
  560. function OpenEMXaout (var E: TExeFile): boolean;
  561. begin
  562. OpenEMXaout := false;
  563. { GDB after 4.18 uses offset to function begin
  564. in text section but OS/2 version still uses 4.16 PM }
  565. E.FunctionRelative := false;
  566. { read and check header }
  567. if E.Size > SizeOf (DosHeader) then
  568. begin
  569. BlockRead (E.F, DosHeader, SizeOf (TDosHeader));
  570. {$IFDEF DEBUG_LINEINFO}
  571. WriteLn (StdErr, 'DosHeader.E_CParHdr = ', DosHeader.E_cParHdr);
  572. {$ENDIF DEBUG_LINEINFO}
  573. if E.Size > DosHeader.e_cparhdr shl 4 + SizeOf (TEmxHeader) then
  574. begin
  575. Seek (E.F, DosHeader.e_cparhdr shl 4);
  576. BlockRead (E.F, EmxHeader, SizeOf (TEmxHeader));
  577. S4 [0] := #4;
  578. Move (EmxHeader.Version, S4 [1], 4);
  579. if (S4 = 'emx ') and
  580. (E.Size > EmxHeader.AoutOfs + SizeOf (TAoutHeader)) then
  581. begin
  582. {$IFDEF DEBUG_LINEINFO}
  583. WriteLn (StdErr, 'EmxHeader.AoutOfs = ', EmxHeader.AoutOfs, '/', HexStr (pointer (EmxHeader.AoutOfs)));
  584. {$ENDIF DEBUG_LINEINFO}
  585. Seek (E.F, EmxHeader.AoutOfs);
  586. BlockRead (E.F, AoutHeader, SizeOf (TAoutHeader));
  587. {$IFDEF DEBUG_LINEINFO}
  588. WriteLn (StdErr, 'AoutHeader.Magic = ', AoutHeader.Magic);
  589. {$ENDIF DEBUG_LINEINFO}
  590. { if AOutHeader.Magic = $10B then}
  591. StabOfs := (EmxHeader.AoutOfs or PageSizeFill) + 1
  592. + AoutHeader.TextSize
  593. + AoutHeader.DataSize
  594. + AoutHeader.TextRelocSize
  595. + AoutHeader.DataRelocSize;
  596. {$IFDEF DEBUG_LINEINFO}
  597. WriteLn (StdErr, 'AoutHeader.TextSize = ', AoutHeader.TextSize, '/', HexStr (pointer (AoutHeader.TextSize)));
  598. WriteLn (StdErr, 'AoutHeader.DataSize = ', AoutHeader.DataSize, '/', HexStr (pointer (AoutHeader.DataSize)));
  599. WriteLn (StdErr, 'AoutHeader.TextRelocSize = ', AoutHeader.TextRelocSize, '/', HexStr (pointer (AoutHeader.TextRelocSize)));
  600. WriteLn (StdErr, 'AoutHeader.DataRelocSize = ', AoutHeader.DataRelocSize, '/', HexStr (pointer (AoutHeader.DataRelocSize)));
  601. WriteLn (StdErr, 'AoutHeader.SymbSize = ', AoutHeader.SymbSize, '/', HexStr (pointer (AoutHeader.SymbSize)));
  602. WriteLn (StdErr, 'StabOfs = ', StabOfs, '/', HexStr (pointer (StabOfs)));
  603. {$ENDIF DEBUG_LINEINFO}
  604. if E.Size > StabOfs + AoutHeader.SymbSize then
  605. OpenEMXaout := true;
  606. end;
  607. end;
  608. end;
  609. end;
  610. function FindSectionEMXaout (var E: TExeFile; const ASecName: string;
  611. var SecOfs, SecLen: longint): boolean;
  612. begin
  613. FindSectionEMXaout := false;
  614. if ASecName = '.stab' then
  615. begin
  616. SecOfs := StabOfs;
  617. SecLen := AoutHeader.SymbSize;
  618. FindSectionEMXaout := true;
  619. end else
  620. if ASecName = '.stabstr' then
  621. begin
  622. SecOfs := StabOfs + AoutHeader.SymbSize;
  623. SecLen := E.Size - Pred (SecOfs);
  624. FindSectionEMXaout := true;
  625. end;
  626. end;
  627. {$ENDIF EMX}
  628. {****************************************************************************
  629. ELF
  630. ****************************************************************************}
  631. {$if defined(ELF32) or defined(BEOS)}
  632. type
  633. telfheader=packed record
  634. magic0123 : longint;
  635. file_class : byte;
  636. data_encoding : byte;
  637. file_version : byte;
  638. padding : array[$07..$0f] of byte;
  639. e_type : word;
  640. e_machine : word;
  641. e_version : longword;
  642. e_entry : longword; // entrypoint
  643. e_phoff : longword; // program header offset
  644. e_shoff : longword; // sections header offset
  645. e_flags : longword;
  646. e_ehsize : word; // elf header size in bytes
  647. e_phentsize : word; // size of an entry in the program header array
  648. e_phnum : word; // 0..e_phnum-1 of entrys
  649. e_shentsize : word; // size of an entry in sections header array
  650. e_shnum : word; // 0..e_shnum-1 of entrys
  651. e_shstrndx : word; // index of string section header
  652. end;
  653. telfsechdr=packed record
  654. sh_name : longword;
  655. sh_type : longword;
  656. sh_flags : longword;
  657. sh_addr : longword;
  658. sh_offset : longword;
  659. sh_size : longword;
  660. sh_link : longword;
  661. sh_info : longword;
  662. sh_addralign : longword;
  663. sh_entsize : longword;
  664. end;
  665. telfproghdr=packed record
  666. p_type : longword;
  667. p_offset : longword;
  668. p_vaddr : longword;
  669. p_paddr : longword;
  670. p_filesz : longword;
  671. p_memsz : longword;
  672. p_flags : longword;
  673. p_align : longword;
  674. end;
  675. {$endif ELF32 or BEOS}
  676. {$ifdef ELF64}
  677. type
  678. telfheader=packed record
  679. magic0123 : longint;
  680. file_class : byte;
  681. data_encoding : byte;
  682. file_version : byte;
  683. padding : array[$07..$0f] of byte;
  684. e_type : word;
  685. e_machine : word;
  686. e_version : longword;
  687. e_entry : int64; // entrypoint
  688. e_phoff : int64; // program header offset
  689. e_shoff : int64; // sections header offset
  690. e_flags : longword;
  691. e_ehsize : word; // elf header size in bytes
  692. e_phentsize : word; // size of an entry in the program header array
  693. e_phnum : word; // 0..e_phnum-1 of entrys
  694. e_shentsize : word; // size of an entry in sections header array
  695. e_shnum : word; // 0..e_shnum-1 of entrys
  696. e_shstrndx : word; // index of string section header
  697. end;
  698. type
  699. telfsechdr=packed record
  700. sh_name : longword;
  701. sh_type : longword;
  702. sh_flags : int64;
  703. sh_addr : int64;
  704. sh_offset : int64;
  705. sh_size : int64;
  706. sh_link : longword;
  707. sh_info : longword;
  708. sh_addralign : int64;
  709. sh_entsize : int64;
  710. end;
  711. telfproghdr=packed record
  712. p_type : longword;
  713. p_flags : longword;
  714. p_offset : qword;
  715. p_vaddr : qword;
  716. p_paddr : qword;
  717. p_filesz : qword;
  718. p_memsz : qword;
  719. p_align : qword;
  720. end;
  721. {$endif ELF64}
  722. {$if defined(ELF32) or defined(ELF64) or defined(BEOS)}
  723. function OpenElf(var e:TExeFile):boolean;
  724. var
  725. elfheader : telfheader;
  726. elfsec : telfsechdr;
  727. phdr : telfproghdr;
  728. i : longint;
  729. begin
  730. OpenElf:=false;
  731. { read and check header }
  732. if e.size<sizeof(telfheader) then
  733. exit;
  734. blockread(e.f,elfheader,sizeof(telfheader));
  735. if elfheader.magic0123<>{$ifdef ENDIAN_LITTLE}$464c457f{$else}$7f454c46{$endif} then
  736. exit;
  737. if elfheader.e_shentsize<>sizeof(telfsechdr) then
  738. exit;
  739. { read section names }
  740. seek(e.f,elfheader.e_shoff+elfheader.e_shstrndx*cardinal(sizeof(telfsechdr)));
  741. blockread(e.f,elfsec,sizeof(telfsechdr));
  742. e.secstrofs:=elfsec.sh_offset;
  743. e.sechdrofs:=elfheader.e_shoff;
  744. e.nsects:=elfheader.e_shnum;
  745. { scan program headers to find the image base address }
  746. e.processaddress:=High(e.processaddress);
  747. seek(e.f,elfheader.e_phoff);
  748. for i:=1 to elfheader.e_phnum do
  749. begin
  750. blockread(e.f,phdr,sizeof(phdr));
  751. if (phdr.p_type = 1 {PT_LOAD}) and (ptruint(phdr.p_vaddr) < e.processaddress) then
  752. e.processaddress:=phdr.p_vaddr;
  753. end;
  754. if e.processaddress = High(e.processaddress) then
  755. e.processaddress:=0;
  756. OpenElf:=true;
  757. end;
  758. function FindSectionElf(var e:TExeFile;const asecname:string;var secofs,seclen:longint):boolean;
  759. var
  760. elfsec : telfsechdr;
  761. secname : string;
  762. secnamebuf : array[0..255] of char;
  763. oldofs,
  764. bufsize,i : longint;
  765. begin
  766. FindSectionElf:=false;
  767. seek(e.f,e.sechdrofs);
  768. for i:=1 to e.nsects do
  769. begin
  770. blockread(e.f,elfsec,sizeof(telfsechdr));
  771. fillchar(secnamebuf,sizeof(secnamebuf),0);
  772. oldofs:=filepos(e.f);
  773. seek(e.f,e.secstrofs+elfsec.sh_name);
  774. blockread(e.f,secnamebuf,sizeof(secnamebuf)-1,bufsize);
  775. seek(e.f,oldofs);
  776. secname:=strpas(secnamebuf);
  777. if asecname=secname then
  778. begin
  779. secofs:=elfsec.sh_offset;
  780. seclen:=elfsec.sh_size;
  781. FindSectionElf:=true;
  782. exit;
  783. end;
  784. end;
  785. end;
  786. {$endif ELF32 or ELF64 or BEOS}
  787. {$ifdef beos}
  788. {$i ptypes.inc}
  789. type
  790. // Descriptive formats
  791. status_t = Longint;
  792. team_id = Longint;
  793. image_id = Longint;
  794. { image types }
  795. const
  796. B_APP_IMAGE = 1;
  797. B_LIBRARY_IMAGE = 2;
  798. B_ADD_ON_IMAGE = 3;
  799. B_SYSTEM_IMAGE = 4;
  800. B_OK = 0;
  801. type
  802. image_info = packed record
  803. id : image_id;
  804. _type : longint;
  805. sequence: longint;
  806. init_order: longint;
  807. init_routine: pointer;
  808. term_routine: pointer;
  809. device: dev_t;
  810. node: ino_t;
  811. name: array[0..MAXPATHLEN-1] of char;
  812. { name: string[255];
  813. name2: string[255];
  814. name3: string[255];
  815. name4: string[255];
  816. name5: string[5];
  817. }
  818. text: pointer;
  819. data: pointer;
  820. text_size: longint;
  821. data_size: longint;
  822. end;
  823. 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';
  824. function OpenElf32Beos(var e:TExeFile):boolean;
  825. var
  826. cookie : longint;
  827. info : image_info;
  828. begin
  829. // The only BeOS specific part is setting the processaddress
  830. cookie := 0;
  831. OpenElf32Beos:=false;
  832. fillchar(info, sizeof(image_info), 0);
  833. while get_next_image_info(0,cookie,info,sizeof(info))=B_OK do
  834. begin
  835. if e.filename=String(pchar(@info.name)) then
  836. begin
  837. if (info._type = B_APP_IMAGE) then
  838. e.processaddress := cardinal(info.text)
  839. else
  840. e.processaddress := 0;
  841. OpenElf32Beos := OpenElf(e);
  842. exit;
  843. end;
  844. end;
  845. end;
  846. {$endif beos}
  847. {****************************************************************************
  848. MACHO
  849. ****************************************************************************}
  850. {$ifdef darwin}
  851. type
  852. MachoFatHeader= packed record
  853. magic: longint;
  854. nfatarch: longint;
  855. end;
  856. MachoHeader=packed record
  857. magic: longword;
  858. cpu_type_t: longint;
  859. cpu_subtype_t: longint;
  860. filetype: longint;
  861. ncmds: longint;
  862. sizeofcmds: longint;
  863. flags: longint;
  864. end;
  865. cmdblock=packed record
  866. cmd: longint;
  867. cmdsize: longint;
  868. end;
  869. symbSeg=packed record
  870. symoff : longint;
  871. nsyms : longint;
  872. stroff : longint;
  873. strsize: longint;
  874. end;
  875. tstab=packed record
  876. strpos : longint;
  877. ntype : byte;
  878. nother : byte;
  879. ndesc : word;
  880. nvalue : dword;
  881. end;
  882. function OpenMachO32PPC(var e:TExeFile):boolean;
  883. var
  884. mh:MachoHeader;
  885. begin
  886. OpenMachO32PPC:= false;
  887. E.FunctionRelative:=false;
  888. if e.size<sizeof(mh) then
  889. exit;
  890. blockread (e.f, mh, sizeof(mh));
  891. e.sechdrofs:=filepos(e.f);
  892. e.nsects:=mh.ncmds;
  893. OpenMachO32PPC:=true;
  894. end;
  895. function FindSectionMachO32PPC(var e:TExeFile;const asecname:string;var secofs,seclen:longint):boolean;
  896. var
  897. i: longint;
  898. block:cmdblock;
  899. symbolsSeg: symbSeg;
  900. begin
  901. FindSectionMachO32PPC:=false;
  902. seek(e.f,e.sechdrofs);
  903. for i:= 1 to e.nsects do
  904. begin
  905. {$I-}
  906. blockread (e.f, block, sizeof(block));
  907. {$I+}
  908. if IOResult <> 0 then
  909. Exit;
  910. if block.cmd = $2 then
  911. begin
  912. blockread (e.f, symbolsSeg, sizeof(symbolsSeg));
  913. if asecname='.stab' then
  914. begin
  915. secofs:=symbolsSeg.symoff;
  916. { the caller will divide again by sizeof(tstab) }
  917. seclen:=symbolsSeg.nsyms*sizeof(tstab);
  918. FindSectionMachO32PPC:=true;
  919. end
  920. else if asecname='.stabstr' then
  921. begin
  922. secofs:=symbolsSeg.stroff;
  923. seclen:=symbolsSeg.strsize;
  924. FindSectionMachO32PPC:=true;
  925. end;
  926. exit;
  927. end;
  928. Seek(e.f, FilePos (e.f) + block.cmdsize - sizeof(block));
  929. end;
  930. end;
  931. {$endif darwin}
  932. {****************************************************************************
  933. CRC
  934. ****************************************************************************}
  935. var
  936. Crc32Tbl : array[0..255] of cardinal;
  937. procedure MakeCRC32Tbl;
  938. var
  939. crc : cardinal;
  940. i,n : integer;
  941. begin
  942. for i:=0 to 255 do
  943. begin
  944. crc:=i;
  945. for n:=1 to 8 do
  946. if (crc and 1)<>0 then
  947. crc:=(crc shr 1) xor cardinal($edb88320)
  948. else
  949. crc:=crc shr 1;
  950. Crc32Tbl[i]:=crc;
  951. end;
  952. end;
  953. Function UpdateCrc32(InitCrc:cardinal;const InBuf;InLen:LongInt):cardinal;
  954. var
  955. i : LongInt;
  956. p : pchar;
  957. begin
  958. if Crc32Tbl[1]=0 then
  959. MakeCrc32Tbl;
  960. p:=@InBuf;
  961. UpdateCrc32:=not InitCrc;
  962. for i:=1 to InLen do
  963. begin
  964. UpdateCrc32:=Crc32Tbl[byte(UpdateCrc32) xor byte(p^)] xor (UpdateCrc32 shr 8);
  965. inc(p);
  966. end;
  967. UpdateCrc32:=not UpdateCrc32;
  968. end;
  969. {****************************************************************************
  970. Generic Executable Open/Close
  971. ****************************************************************************}
  972. type
  973. TOpenProc=function(var e:TExeFile):boolean;
  974. TFindSectionProc=function(var e:TExeFile;const asecname:string;var secofs,seclen:longint):boolean;
  975. TExeProcRec=record
  976. openproc : TOpenProc;
  977. findproc : TFindSectionProc;
  978. end;
  979. const
  980. ExeProcs : TExeProcRec = (
  981. {$ifdef go32v2}
  982. openproc : @OpenGo32Coff;
  983. findproc : @FindSectionCoff;
  984. {$endif}
  985. {$ifdef PE32}
  986. openproc : @OpenPeCoff;
  987. findproc : @FindSectionCoff;
  988. {$endif}
  989. {$ifdef PE32PLUS}
  990. openproc : @OpenPePlusCoff;
  991. findproc : @FindSectionCoff;
  992. {$endif PE32PLUS}
  993. {$if defined(ELF32) or defined(ELF64)}
  994. openproc : @OpenElf;
  995. findproc : @FindSectionElf;
  996. {$endif ELF32 or ELF64}
  997. {$ifdef BEOS}
  998. openproc : @OpenElf32Beos;
  999. findproc : @FindSectionElf;
  1000. {$endif BEOS}
  1001. {$ifdef darwin}
  1002. openproc : @OpenMachO32PPC;
  1003. findproc : @FindSectionMachO32PPC;
  1004. {$endif darwin}
  1005. {$IFDEF EMX}
  1006. openproc : @OpenEMXaout;
  1007. findproc : @FindSectionEMXaout;
  1008. {$ENDIF EMX}
  1009. {$ifdef netware}
  1010. openproc : @OpenNetwareNLM;
  1011. findproc : @FindSectionNetwareNLM;
  1012. {$endif}
  1013. );
  1014. function OpenExeFile(var e:TExeFile;const fn:string):boolean;
  1015. var
  1016. ofm : word;
  1017. begin
  1018. OpenExeFile:=false;
  1019. fillchar(e,sizeof(e),0);
  1020. e.bufsize:=sizeof(e.buf);
  1021. e.filename:=fn;
  1022. if fn='' then // we don't want to read stdin
  1023. exit;
  1024. assign(e.f,fn);
  1025. {$I-}
  1026. ofm:=filemode;
  1027. filemode:=$40;
  1028. reset(e.f,1);
  1029. filemode:=ofm;
  1030. {$I+}
  1031. if ioresult<>0 then
  1032. exit;
  1033. e.isopen:=true;
  1034. // cache filesize
  1035. e.size:=filesize(e.f);
  1036. E.FunctionRelative := true;
  1037. E.ImgOffset := 0;
  1038. if ExeProcs.OpenProc<>nil then
  1039. OpenExeFile:=ExeProcs.OpenProc(e);
  1040. end;
  1041. function CloseExeFile(var e:TExeFile):boolean;
  1042. begin
  1043. CloseExeFile:=false;
  1044. if not e.isopen then
  1045. exit;
  1046. e.isopen:=false;
  1047. close(e.f);
  1048. CloseExeFile:=true;
  1049. end;
  1050. function FindExeSection(var e:TExeFile;const secname:string;var secofs,seclen:longint):boolean;
  1051. begin
  1052. FindExeSection:=false;
  1053. if not e.isopen then
  1054. exit;
  1055. if ExeProcs.FindProc<>nil then
  1056. FindExeSection:=ExeProcs.FindProc(e,secname,secofs,seclen);
  1057. end;
  1058. function CheckDbgFile(var e:TExeFile;const fn:string;dbgcrc:cardinal):boolean;
  1059. var
  1060. c : cardinal;
  1061. ofm : word;
  1062. g : file;
  1063. begin
  1064. CheckDbgFile:=false;
  1065. assign(g,fn);
  1066. {$I-}
  1067. ofm:=filemode;
  1068. filemode:=$40;
  1069. reset(g,1);
  1070. filemode:=ofm;
  1071. {$I+}
  1072. if ioresult<>0 then
  1073. exit;
  1074. { We reuse the buffer from e here to prevent too much stack allocation }
  1075. c:=0;
  1076. repeat
  1077. blockread(g,e.buf,e.bufsize,e.bufcnt);
  1078. c:=UpdateCrc32(c,e.buf,e.bufcnt);
  1079. until e.bufcnt<e.bufsize;
  1080. close(g);
  1081. CheckDbgFile:=(dbgcrc=c);
  1082. end;
  1083. function ReadDebugLink(var e:TExeFile;var dbgfn:string):boolean;
  1084. var
  1085. dbglink : array[0..255] of char;
  1086. i,
  1087. dbglinklen,
  1088. dbglinkofs : longint;
  1089. dbgcrc : cardinal;
  1090. begin
  1091. ReadDebugLink:=false;
  1092. if not FindExeSection(e,'.gnu_debuglink',dbglinkofs,dbglinklen) then
  1093. exit;
  1094. if dbglinklen>sizeof(dbglink)-1 then
  1095. exit;
  1096. fillchar(dbglink,sizeof(dbglink),0);
  1097. seek(e.f,dbglinkofs);
  1098. blockread(e.f,dbglink,dbglinklen);
  1099. dbgfn:=strpas(dbglink);
  1100. if length(dbgfn)=0 then
  1101. exit;
  1102. i:=align(length(dbgfn)+1,4);
  1103. if (i+4)>dbglinklen then
  1104. exit;
  1105. move(dbglink[i],dbgcrc,4);
  1106. { current dir }
  1107. if CheckDbgFile(e,dbgfn,dbgcrc) then
  1108. begin
  1109. ReadDebugLink:=true;
  1110. exit;
  1111. end;
  1112. { executable dir }
  1113. i:=length(e.filename);
  1114. while (i>0) and not(e.filename[i] in AllowDirectorySeparators) do
  1115. dec(i);
  1116. if i>0 then
  1117. begin
  1118. dbgfn:=copy(e.filename,1,i)+dbgfn;
  1119. if CheckDbgFile(e,dbgfn,dbgcrc) then
  1120. begin
  1121. ReadDebugLink:=true;
  1122. exit;
  1123. end;
  1124. end;
  1125. end;
  1126. end.