exeinfo.pp 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239
  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. 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) or defined(dragonfly)}
  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. {$ifdef GO32V2}
  331. seclen:=sechdr.datalen;
  332. {$else GO32V2}
  333. { In PECOFF, datalen includes file padding up to the next section.
  334. vsize is the actual payload size if it does not exceed datalen,
  335. otherwise it is .bss (or alike) section that we should ignore. }
  336. if sechdr.vsize<=sechdr.datalen then
  337. seclen:=sechdr.vsize
  338. else
  339. exit;
  340. {$endif GO32V2}
  341. FindSectionCoff:=true;
  342. exit;
  343. end;
  344. end;
  345. end;
  346. {$endif PE32 or PE32PLUS or GO32V2}
  347. {$ifdef go32v2}
  348. function OpenGo32Coff(var e:TExeFile):boolean;
  349. type
  350. tgo32coffheader=packed record
  351. mach : word;
  352. nsects : word;
  353. time : longint;
  354. sympos : longint;
  355. syms : longint;
  356. opthdr : word;
  357. flag : word;
  358. other : array[0..27] of byte;
  359. end;
  360. const
  361. ParagraphSize = 512;
  362. var
  363. coffheader : tgo32coffheader;
  364. DosHeader: TDosHeader;
  365. BRead: cardinal;
  366. begin
  367. OpenGo32Coff:=false;
  368. { read and check header }
  369. if E.Size < SizeOf (DosHeader) then
  370. Exit;
  371. BlockRead (E.F, DosHeader, SizeOf (DosHeader), BRead);
  372. if BRead <> SizeOf (DosHeader) then
  373. Exit;
  374. if DosHeader.E_Magic = $5A4D then
  375. begin
  376. E.ImgOffset := DosHeader.e_cp * ParagraphSize;
  377. if DosHeader.e_cblp > 0 then
  378. E.ImgOffset := E.ImgOffset + DosHeader.e_cblp - ParagraphSize;
  379. end;
  380. if e.size < E.ImgOffset + sizeof(coffheader) then
  381. exit;
  382. seek(e.f,E.ImgOffset);
  383. blockread(e.f,coffheader,sizeof(coffheader));
  384. if coffheader.mach<>$14c then
  385. exit;
  386. e.sechdrofs:=filepos(e.f);
  387. e.nsects:=coffheader.nsects;
  388. e.secstrofs:=coffheader.sympos+coffheader.syms*sizeof(coffsymbol)+4;
  389. if e.secstrofs>e.size then
  390. exit;
  391. OpenGo32Coff:=true;
  392. end;
  393. {$endif Go32v2}
  394. {$ifdef PE32}
  395. function OpenPeCoff(var e:TExeFile):boolean;
  396. type
  397. tpeheader = packed record
  398. PEMagic : longint;
  399. Machine : word;
  400. NumberOfSections : word;
  401. TimeDateStamp : longint;
  402. PointerToSymbolTable : longint;
  403. NumberOfSymbols : longint;
  404. SizeOfOptionalHeader : word;
  405. Characteristics : word;
  406. Magic : word;
  407. MajorLinkerVersion : byte;
  408. MinorLinkerVersion : byte;
  409. SizeOfCode : longint;
  410. SizeOfInitializedData : longint;
  411. SizeOfUninitializedData : longint;
  412. AddressOfEntryPoint : longint;
  413. BaseOfCode : longint;
  414. BaseOfData : longint;
  415. ImageBase : longint;
  416. SectionAlignment : longint;
  417. FileAlignment : longint;
  418. MajorOperatingSystemVersion : word;
  419. MinorOperatingSystemVersion : word;
  420. MajorImageVersion : word;
  421. MinorImageVersion : word;
  422. MajorSubsystemVersion : word;
  423. MinorSubsystemVersion : word;
  424. Reserved1 : longint;
  425. SizeOfImage : longint;
  426. SizeOfHeaders : longint;
  427. CheckSum : longint;
  428. Subsystem : word;
  429. DllCharacteristics : word;
  430. SizeOfStackReserve : longint;
  431. SizeOfStackCommit : longint;
  432. SizeOfHeapReserve : longint;
  433. SizeOfHeapCommit : longint;
  434. LoaderFlags : longint;
  435. NumberOfRvaAndSizes : longint;
  436. DataDirectory : array[1..$80] of byte;
  437. end;
  438. var
  439. dosheader : tdosheader;
  440. peheader : tpeheader;
  441. begin
  442. OpenPeCoff:=false;
  443. { read and check header }
  444. if e.size<sizeof(dosheader) then
  445. exit;
  446. blockread(e.f,dosheader,sizeof(tdosheader));
  447. seek(e.f,dosheader.e_lfanew);
  448. blockread(e.f,peheader,sizeof(tpeheader));
  449. if peheader.pemagic<>$4550 then
  450. exit;
  451. e.sechdrofs:=filepos(e.f);
  452. e.nsects:=peheader.NumberOfSections;
  453. e.secstrofs:=peheader.PointerToSymbolTable+peheader.NumberOfSymbols*sizeof(coffsymbol);
  454. if e.secstrofs>e.size then
  455. exit;
  456. e.processaddress:=peheader.ImageBase;
  457. OpenPeCoff:=true;
  458. end;
  459. {$endif PE32}
  460. {$ifdef PE32PLUS}
  461. function OpenPePlusCoff(var e:TExeFile):boolean;
  462. type
  463. tpeheader = packed record
  464. PEMagic : longint;
  465. Machine : word;
  466. NumberOfSections : word;
  467. TimeDateStamp : longint;
  468. PointerToSymbolTable : longint;
  469. NumberOfSymbols : longint;
  470. SizeOfOptionalHeader : word;
  471. Characteristics : word;
  472. Magic : word;
  473. MajorLinkerVersion : byte;
  474. MinorLinkerVersion : byte;
  475. SizeOfCode : longint;
  476. SizeOfInitializedData : longint;
  477. SizeOfUninitializedData : longint;
  478. AddressOfEntryPoint : longint;
  479. BaseOfCode : longint;
  480. ImageBase : qword;
  481. SectionAlignment : longint;
  482. FileAlignment : longint;
  483. MajorOperatingSystemVersion : word;
  484. MinorOperatingSystemVersion : word;
  485. MajorImageVersion : word;
  486. MinorImageVersion : word;
  487. MajorSubsystemVersion : word;
  488. MinorSubsystemVersion : word;
  489. Reserved1 : longint;
  490. SizeOfImage : longint;
  491. SizeOfHeaders : longint;
  492. CheckSum : longint;
  493. Subsystem : word;
  494. DllCharacteristics : word;
  495. SizeOfStackReserve : qword;
  496. SizeOfStackCommit : qword;
  497. SizeOfHeapReserve : qword;
  498. SizeOfHeapCommit : qword;
  499. LoaderFlags : longint;
  500. NumberOfRvaAndSizes : longint;
  501. DataDirectory : array[1..$80] of byte;
  502. end;
  503. var
  504. dosheader : tdosheader;
  505. peheader : tpeheader;
  506. begin
  507. OpenPePlusCoff:=false;
  508. { read and check header }
  509. if E.Size<sizeof(dosheader) then
  510. exit;
  511. blockread(E.F,dosheader,sizeof(tdosheader));
  512. seek(E.F,dosheader.e_lfanew);
  513. blockread(E.F,peheader,sizeof(tpeheader));
  514. if peheader.pemagic<>$4550 then
  515. exit;
  516. e.sechdrofs:=filepos(e.f);
  517. e.nsects:=peheader.NumberOfSections;
  518. e.secstrofs:=peheader.PointerToSymbolTable+peheader.NumberOfSymbols*sizeof(coffsymbol);
  519. if e.secstrofs>e.size then
  520. exit;
  521. e.processaddress:=peheader.ImageBase;
  522. OpenPePlusCoff:=true;
  523. end;
  524. {$endif PE32PLUS}
  525. {****************************************************************************
  526. AOUT
  527. ****************************************************************************}
  528. {$IFDEF EMX}
  529. type
  530. TEmxHeader = packed record
  531. Version: array [1..16] of char;
  532. Bound: word;
  533. AoutOfs: longint;
  534. Options: array [1..42] of char;
  535. end;
  536. TAoutHeader = packed record
  537. Magic: word;
  538. Machine: byte;
  539. Flags: byte;
  540. TextSize: longint;
  541. DataSize: longint;
  542. BssSize: longint;
  543. SymbSize: longint;
  544. EntryPoint: longint;
  545. TextRelocSize: longint;
  546. DataRelocSize: longint;
  547. end;
  548. const
  549. PageSizeFill = $FFF;
  550. var
  551. DosHeader: TDosHeader;
  552. EmxHeader: TEmxHeader;
  553. AoutHeader: TAoutHeader;
  554. StabOfs: PtrUInt;
  555. S4: string [4];
  556. function OpenEMXaout (var E: TExeFile): boolean;
  557. begin
  558. OpenEMXaout := false;
  559. { GDB after 4.18 uses offset to function begin
  560. in text section but OS/2 version still uses 4.16 PM }
  561. E.FunctionRelative := false;
  562. { read and check header }
  563. if E.Size > SizeOf (DosHeader) then
  564. begin
  565. BlockRead (E.F, DosHeader, SizeOf (TDosHeader));
  566. {$IFDEF DEBUG_LINEINFO}
  567. WriteLn (StdErr, 'DosHeader.E_CParHdr = ', DosHeader.E_cParHdr);
  568. {$ENDIF DEBUG_LINEINFO}
  569. if E.Size > DosHeader.e_cparhdr shl 4 + SizeOf (TEmxHeader) then
  570. begin
  571. Seek (E.F, DosHeader.e_cparhdr shl 4);
  572. BlockRead (E.F, EmxHeader, SizeOf (TEmxHeader));
  573. S4 [0] := #4;
  574. Move (EmxHeader.Version, S4 [1], 4);
  575. if (S4 = 'emx ') and
  576. (E.Size > EmxHeader.AoutOfs + SizeOf (TAoutHeader)) then
  577. begin
  578. {$IFDEF DEBUG_LINEINFO}
  579. WriteLn (StdErr, 'EmxHeader.AoutOfs = ', EmxHeader.AoutOfs, '/', HexStr (pointer (EmxHeader.AoutOfs)));
  580. {$ENDIF DEBUG_LINEINFO}
  581. Seek (E.F, EmxHeader.AoutOfs);
  582. BlockRead (E.F, AoutHeader, SizeOf (TAoutHeader));
  583. {$IFDEF DEBUG_LINEINFO}
  584. WriteLn (StdErr, 'AoutHeader.Magic = ', AoutHeader.Magic);
  585. {$ENDIF DEBUG_LINEINFO}
  586. { if AOutHeader.Magic = $10B then}
  587. StabOfs := (EmxHeader.AoutOfs or PageSizeFill) + 1
  588. + AoutHeader.TextSize
  589. + AoutHeader.DataSize
  590. + AoutHeader.TextRelocSize
  591. + AoutHeader.DataRelocSize;
  592. {$IFDEF DEBUG_LINEINFO}
  593. WriteLn (StdErr, 'AoutHeader.TextSize = ', AoutHeader.TextSize, '/', HexStr (pointer (AoutHeader.TextSize)));
  594. WriteLn (StdErr, 'AoutHeader.DataSize = ', AoutHeader.DataSize, '/', HexStr (pointer (AoutHeader.DataSize)));
  595. WriteLn (StdErr, 'AoutHeader.TextRelocSize = ', AoutHeader.TextRelocSize, '/', HexStr (pointer (AoutHeader.TextRelocSize)));
  596. WriteLn (StdErr, 'AoutHeader.DataRelocSize = ', AoutHeader.DataRelocSize, '/', HexStr (pointer (AoutHeader.DataRelocSize)));
  597. WriteLn (StdErr, 'AoutHeader.SymbSize = ', AoutHeader.SymbSize, '/', HexStr (pointer (AoutHeader.SymbSize)));
  598. WriteLn (StdErr, 'StabOfs = ', StabOfs, '/', HexStr (pointer (StabOfs)));
  599. {$ENDIF DEBUG_LINEINFO}
  600. if E.Size > StabOfs + AoutHeader.SymbSize then
  601. OpenEMXaout := true;
  602. end;
  603. end;
  604. end;
  605. end;
  606. function FindSectionEMXaout (var E: TExeFile; const ASecName: string;
  607. var SecOfs, SecLen: longint): boolean;
  608. begin
  609. FindSectionEMXaout := false;
  610. if ASecName = '.stab' then
  611. begin
  612. SecOfs := StabOfs;
  613. SecLen := AoutHeader.SymbSize;
  614. FindSectionEMXaout := true;
  615. end else
  616. if ASecName = '.stabstr' then
  617. begin
  618. SecOfs := StabOfs + AoutHeader.SymbSize;
  619. SecLen := E.Size - Pred (SecOfs);
  620. FindSectionEMXaout := true;
  621. end;
  622. end;
  623. {$ENDIF EMX}
  624. {****************************************************************************
  625. ELF
  626. ****************************************************************************}
  627. {$if defined(ELF32) or defined(BEOS)}
  628. type
  629. telfheader=packed record
  630. magic0123 : longint;
  631. file_class : byte;
  632. data_encoding : byte;
  633. file_version : byte;
  634. padding : array[$07..$0f] of byte;
  635. e_type : word;
  636. e_machine : word;
  637. e_version : longword;
  638. e_entry : longword; // entrypoint
  639. e_phoff : longword; // program header offset
  640. e_shoff : longword; // sections header offset
  641. e_flags : longword;
  642. e_ehsize : word; // elf header size in bytes
  643. e_phentsize : word; // size of an entry in the program header array
  644. e_phnum : word; // 0..e_phnum-1 of entrys
  645. e_shentsize : word; // size of an entry in sections header array
  646. e_shnum : word; // 0..e_shnum-1 of entrys
  647. e_shstrndx : word; // index of string section header
  648. end;
  649. telfsechdr=packed record
  650. sh_name : longword;
  651. sh_type : longword;
  652. sh_flags : longword;
  653. sh_addr : longword;
  654. sh_offset : longword;
  655. sh_size : longword;
  656. sh_link : longword;
  657. sh_info : longword;
  658. sh_addralign : longword;
  659. sh_entsize : longword;
  660. end;
  661. telfproghdr=packed record
  662. p_type : longword;
  663. p_offset : longword;
  664. p_vaddr : longword;
  665. p_paddr : longword;
  666. p_filesz : longword;
  667. p_memsz : longword;
  668. p_flags : longword;
  669. p_align : longword;
  670. end;
  671. {$endif ELF32 or BEOS}
  672. {$ifdef ELF64}
  673. type
  674. telfheader=packed record
  675. magic0123 : longint;
  676. file_class : byte;
  677. data_encoding : byte;
  678. file_version : byte;
  679. padding : array[$07..$0f] of byte;
  680. e_type : word;
  681. e_machine : word;
  682. e_version : longword;
  683. e_entry : int64; // entrypoint
  684. e_phoff : int64; // program header offset
  685. e_shoff : int64; // sections header offset
  686. e_flags : longword;
  687. e_ehsize : word; // elf header size in bytes
  688. e_phentsize : word; // size of an entry in the program header array
  689. e_phnum : word; // 0..e_phnum-1 of entrys
  690. e_shentsize : word; // size of an entry in sections header array
  691. e_shnum : word; // 0..e_shnum-1 of entrys
  692. e_shstrndx : word; // index of string section header
  693. end;
  694. type
  695. telfsechdr=packed record
  696. sh_name : longword;
  697. sh_type : longword;
  698. sh_flags : int64;
  699. sh_addr : int64;
  700. sh_offset : int64;
  701. sh_size : int64;
  702. sh_link : longword;
  703. sh_info : longword;
  704. sh_addralign : int64;
  705. sh_entsize : int64;
  706. end;
  707. telfproghdr=packed record
  708. p_type : longword;
  709. p_flags : longword;
  710. p_offset : qword;
  711. p_vaddr : qword;
  712. p_paddr : qword;
  713. p_filesz : qword;
  714. p_memsz : qword;
  715. p_align : qword;
  716. end;
  717. {$endif ELF64}
  718. {$if defined(ELF32) or defined(ELF64) or defined(BEOS)}
  719. function OpenElf(var e:TExeFile):boolean;
  720. var
  721. elfheader : telfheader;
  722. elfsec : telfsechdr;
  723. phdr : telfproghdr;
  724. i : longint;
  725. begin
  726. OpenElf:=false;
  727. { read and check header }
  728. if e.size<sizeof(telfheader) then
  729. exit;
  730. blockread(e.f,elfheader,sizeof(telfheader));
  731. if elfheader.magic0123<>{$ifdef ENDIAN_LITTLE}$464c457f{$else}$7f454c46{$endif} then
  732. exit;
  733. if elfheader.e_shentsize<>sizeof(telfsechdr) then
  734. exit;
  735. { read section names }
  736. seek(e.f,elfheader.e_shoff+elfheader.e_shstrndx*cardinal(sizeof(telfsechdr)));
  737. blockread(e.f,elfsec,sizeof(telfsechdr));
  738. e.secstrofs:=elfsec.sh_offset;
  739. e.sechdrofs:=elfheader.e_shoff;
  740. e.nsects:=elfheader.e_shnum;
  741. { scan program headers to find the image base address }
  742. e.processaddress:=High(e.processaddress);
  743. seek(e.f,elfheader.e_phoff);
  744. for i:=1 to elfheader.e_phnum do
  745. begin
  746. blockread(e.f,phdr,sizeof(phdr));
  747. if (phdr.p_type = 1 {PT_LOAD}) and (ptruint(phdr.p_vaddr) < e.processaddress) then
  748. e.processaddress:=phdr.p_vaddr;
  749. end;
  750. if e.processaddress = High(e.processaddress) then
  751. e.processaddress:=0;
  752. OpenElf:=true;
  753. end;
  754. function FindSectionElf(var e:TExeFile;const asecname:string;var secofs,seclen:longint):boolean;
  755. var
  756. elfsec : telfsechdr;
  757. secname : string;
  758. secnamebuf : array[0..255] of char;
  759. oldofs,
  760. bufsize,i : longint;
  761. begin
  762. FindSectionElf:=false;
  763. seek(e.f,e.sechdrofs);
  764. for i:=1 to e.nsects do
  765. begin
  766. blockread(e.f,elfsec,sizeof(telfsechdr));
  767. fillchar(secnamebuf,sizeof(secnamebuf),0);
  768. oldofs:=filepos(e.f);
  769. seek(e.f,e.secstrofs+elfsec.sh_name);
  770. blockread(e.f,secnamebuf,sizeof(secnamebuf)-1,bufsize);
  771. seek(e.f,oldofs);
  772. secname:=strpas(secnamebuf);
  773. if asecname=secname then
  774. begin
  775. secofs:=elfsec.sh_offset;
  776. seclen:=elfsec.sh_size;
  777. FindSectionElf:=true;
  778. exit;
  779. end;
  780. end;
  781. end;
  782. {$endif ELF32 or ELF64 or BEOS}
  783. {$ifdef beos}
  784. {$i ptypes.inc}
  785. type
  786. // Descriptive formats
  787. status_t = Longint;
  788. team_id = Longint;
  789. image_id = Longint;
  790. { image types }
  791. const
  792. B_APP_IMAGE = 1;
  793. B_LIBRARY_IMAGE = 2;
  794. B_ADD_ON_IMAGE = 3;
  795. B_SYSTEM_IMAGE = 4;
  796. B_OK = 0;
  797. type
  798. image_info = packed record
  799. id : image_id;
  800. _type : longint;
  801. sequence: longint;
  802. init_order: longint;
  803. init_routine: pointer;
  804. term_routine: pointer;
  805. device: dev_t;
  806. node: ino_t;
  807. name: array[0..MAXPATHLEN-1] of char;
  808. { name: string[255];
  809. name2: string[255];
  810. name3: string[255];
  811. name4: string[255];
  812. name5: string[5];
  813. }
  814. text: pointer;
  815. data: pointer;
  816. text_size: longint;
  817. data_size: longint;
  818. end;
  819. 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';
  820. function OpenElf32Beos(var e:TExeFile):boolean;
  821. var
  822. cookie : longint;
  823. info : image_info;
  824. begin
  825. // The only BeOS specific part is setting the processaddress
  826. cookie := 0;
  827. OpenElf32Beos:=false;
  828. fillchar(info, sizeof(image_info), 0);
  829. while get_next_image_info(0,cookie,info,sizeof(info))=B_OK do
  830. begin
  831. if e.filename=String(pchar(@info.name)) then
  832. begin
  833. if (info._type = B_APP_IMAGE) then
  834. e.processaddress := cardinal(info.text)
  835. else
  836. e.processaddress := 0;
  837. OpenElf32Beos := OpenElf(e);
  838. exit;
  839. end;
  840. end;
  841. end;
  842. {$endif beos}
  843. {****************************************************************************
  844. MACHO
  845. ****************************************************************************}
  846. {$ifdef darwin}
  847. type
  848. MachoFatHeader= packed record
  849. magic: longint;
  850. nfatarch: longint;
  851. end;
  852. MachoHeader=packed record
  853. magic: longword;
  854. cpu_type_t: longint;
  855. cpu_subtype_t: longint;
  856. filetype: longint;
  857. ncmds: longint;
  858. sizeofcmds: longint;
  859. flags: longint;
  860. end;
  861. cmdblock=packed record
  862. cmd: longint;
  863. cmdsize: longint;
  864. end;
  865. symbSeg=packed record
  866. symoff : longint;
  867. nsyms : longint;
  868. stroff : longint;
  869. strsize: longint;
  870. end;
  871. tstab=packed record
  872. strpos : longint;
  873. ntype : byte;
  874. nother : byte;
  875. ndesc : word;
  876. nvalue : dword;
  877. end;
  878. function OpenMachO32PPC(var e:TExeFile):boolean;
  879. var
  880. mh:MachoHeader;
  881. begin
  882. OpenMachO32PPC:= false;
  883. E.FunctionRelative:=false;
  884. if e.size<sizeof(mh) then
  885. exit;
  886. blockread (e.f, mh, sizeof(mh));
  887. e.sechdrofs:=filepos(e.f);
  888. e.nsects:=mh.ncmds;
  889. OpenMachO32PPC:=true;
  890. end;
  891. function FindSectionMachO32PPC(var e:TExeFile;const asecname:string;var secofs,seclen:longint):boolean;
  892. var
  893. i: longint;
  894. block:cmdblock;
  895. symbolsSeg: symbSeg;
  896. begin
  897. FindSectionMachO32PPC:=false;
  898. seek(e.f,e.sechdrofs);
  899. for i:= 1 to e.nsects do
  900. begin
  901. {$I-}
  902. blockread (e.f, block, sizeof(block));
  903. {$I+}
  904. if IOResult <> 0 then
  905. Exit;
  906. if block.cmd = $2 then
  907. begin
  908. blockread (e.f, symbolsSeg, sizeof(symbolsSeg));
  909. if asecname='.stab' then
  910. begin
  911. secofs:=symbolsSeg.symoff;
  912. { the caller will divide again by sizeof(tstab) }
  913. seclen:=symbolsSeg.nsyms*sizeof(tstab);
  914. FindSectionMachO32PPC:=true;
  915. end
  916. else if asecname='.stabstr' then
  917. begin
  918. secofs:=symbolsSeg.stroff;
  919. seclen:=symbolsSeg.strsize;
  920. FindSectionMachO32PPC:=true;
  921. end;
  922. exit;
  923. end;
  924. Seek(e.f, FilePos (e.f) + block.cmdsize - sizeof(block));
  925. end;
  926. end;
  927. {$endif darwin}
  928. {****************************************************************************
  929. CRC
  930. ****************************************************************************}
  931. var
  932. Crc32Tbl : array[0..255] of cardinal;
  933. procedure MakeCRC32Tbl;
  934. var
  935. crc : cardinal;
  936. i,n : integer;
  937. begin
  938. for i:=0 to 255 do
  939. begin
  940. crc:=i;
  941. for n:=1 to 8 do
  942. if (crc and 1)<>0 then
  943. crc:=(crc shr 1) xor cardinal($edb88320)
  944. else
  945. crc:=crc shr 1;
  946. Crc32Tbl[i]:=crc;
  947. end;
  948. end;
  949. Function UpdateCrc32(InitCrc:cardinal;const InBuf;InLen:LongInt):cardinal;
  950. var
  951. i : LongInt;
  952. p : pchar;
  953. begin
  954. if Crc32Tbl[1]=0 then
  955. MakeCrc32Tbl;
  956. p:=@InBuf;
  957. UpdateCrc32:=not InitCrc;
  958. for i:=1 to InLen do
  959. begin
  960. UpdateCrc32:=Crc32Tbl[byte(UpdateCrc32) xor byte(p^)] xor (UpdateCrc32 shr 8);
  961. inc(p);
  962. end;
  963. UpdateCrc32:=not UpdateCrc32;
  964. end;
  965. {****************************************************************************
  966. Generic Executable Open/Close
  967. ****************************************************************************}
  968. type
  969. TOpenProc=function(var e:TExeFile):boolean;
  970. TFindSectionProc=function(var e:TExeFile;const asecname:string;var secofs,seclen:longint):boolean;
  971. TExeProcRec=record
  972. openproc : TOpenProc;
  973. findproc : TFindSectionProc;
  974. end;
  975. const
  976. ExeProcs : TExeProcRec = (
  977. {$ifdef go32v2}
  978. openproc : @OpenGo32Coff;
  979. findproc : @FindSectionCoff;
  980. {$endif}
  981. {$ifdef PE32}
  982. openproc : @OpenPeCoff;
  983. findproc : @FindSectionCoff;
  984. {$endif}
  985. {$ifdef PE32PLUS}
  986. openproc : @OpenPePlusCoff;
  987. findproc : @FindSectionCoff;
  988. {$endif PE32PLUS}
  989. {$if defined(ELF32) or defined(ELF64)}
  990. openproc : @OpenElf;
  991. findproc : @FindSectionElf;
  992. {$endif ELF32 or ELF64}
  993. {$ifdef BEOS}
  994. openproc : @OpenElf32Beos;
  995. findproc : @FindSectionElf;
  996. {$endif BEOS}
  997. {$ifdef darwin}
  998. openproc : @OpenMachO32PPC;
  999. findproc : @FindSectionMachO32PPC;
  1000. {$endif darwin}
  1001. {$IFDEF EMX}
  1002. openproc : @OpenEMXaout;
  1003. findproc : @FindSectionEMXaout;
  1004. {$ENDIF EMX}
  1005. {$ifdef netware}
  1006. openproc : @OpenNetwareNLM;
  1007. findproc : @FindSectionNetwareNLM;
  1008. {$endif}
  1009. );
  1010. function OpenExeFile(var e:TExeFile;const fn:string):boolean;
  1011. var
  1012. ofm : word;
  1013. begin
  1014. OpenExeFile:=false;
  1015. fillchar(e,sizeof(e),0);
  1016. e.bufsize:=sizeof(e.buf);
  1017. e.filename:=fn;
  1018. if fn='' then // we don't want to read stdin
  1019. exit;
  1020. assign(e.f,fn);
  1021. {$I-}
  1022. ofm:=filemode;
  1023. filemode:=$40;
  1024. reset(e.f,1);
  1025. filemode:=ofm;
  1026. {$I+}
  1027. if ioresult<>0 then
  1028. exit;
  1029. e.isopen:=true;
  1030. // cache filesize
  1031. e.size:=filesize(e.f);
  1032. E.FunctionRelative := true;
  1033. E.ImgOffset := 0;
  1034. if ExeProcs.OpenProc<>nil then
  1035. OpenExeFile:=ExeProcs.OpenProc(e);
  1036. end;
  1037. function CloseExeFile(var e:TExeFile):boolean;
  1038. begin
  1039. CloseExeFile:=false;
  1040. if not e.isopen then
  1041. exit;
  1042. e.isopen:=false;
  1043. close(e.f);
  1044. CloseExeFile:=true;
  1045. end;
  1046. function FindExeSection(var e:TExeFile;const secname:string;var secofs,seclen:longint):boolean;
  1047. begin
  1048. FindExeSection:=false;
  1049. if not e.isopen then
  1050. exit;
  1051. if ExeProcs.FindProc<>nil then
  1052. FindExeSection:=ExeProcs.FindProc(e,secname,secofs,seclen);
  1053. end;
  1054. function CheckDbgFile(var e:TExeFile;const fn:string;dbgcrc:cardinal):boolean;
  1055. var
  1056. c : cardinal;
  1057. ofm : word;
  1058. g : file;
  1059. begin
  1060. CheckDbgFile:=false;
  1061. assign(g,fn);
  1062. {$I-}
  1063. ofm:=filemode;
  1064. filemode:=$40;
  1065. reset(g,1);
  1066. filemode:=ofm;
  1067. {$I+}
  1068. if ioresult<>0 then
  1069. exit;
  1070. { We reuse the buffer from e here to prevent too much stack allocation }
  1071. c:=0;
  1072. repeat
  1073. blockread(g,e.buf,e.bufsize,e.bufcnt);
  1074. c:=UpdateCrc32(c,e.buf,e.bufcnt);
  1075. until e.bufcnt<e.bufsize;
  1076. close(g);
  1077. CheckDbgFile:=(dbgcrc=c);
  1078. end;
  1079. function ReadDebugLink(var e:TExeFile;var dbgfn:string):boolean;
  1080. var
  1081. dbglink : array[0..255] of char;
  1082. i,
  1083. dbglinklen,
  1084. dbglinkofs : longint;
  1085. dbgcrc : cardinal;
  1086. begin
  1087. ReadDebugLink:=false;
  1088. if not FindExeSection(e,'.gnu_debuglink',dbglinkofs,dbglinklen) then
  1089. exit;
  1090. if dbglinklen>sizeof(dbglink)-1 then
  1091. exit;
  1092. fillchar(dbglink,sizeof(dbglink),0);
  1093. seek(e.f,dbglinkofs);
  1094. blockread(e.f,dbglink,dbglinklen);
  1095. dbgfn:=strpas(dbglink);
  1096. if length(dbgfn)=0 then
  1097. exit;
  1098. i:=align(length(dbgfn)+1,4);
  1099. if (i+4)>dbglinklen then
  1100. exit;
  1101. move(dbglink[i],dbgcrc,4);
  1102. { current dir }
  1103. if CheckDbgFile(e,dbgfn,dbgcrc) then
  1104. begin
  1105. ReadDebugLink:=true;
  1106. exit;
  1107. end;
  1108. { executable dir }
  1109. i:=length(e.filename);
  1110. while (i>0) and not(e.filename[i] in AllowDirectorySeparators) do
  1111. dec(i);
  1112. if i>0 then
  1113. begin
  1114. dbgfn:=copy(e.filename,1,i)+dbgfn;
  1115. if CheckDbgFile(e,dbgfn,dbgcrc) then
  1116. begin
  1117. ReadDebugLink:=true;
  1118. exit;
  1119. end;
  1120. end;
  1121. end;
  1122. end.