exeinfo.pp 46 KB

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