exeinfo.pp 31 KB

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