exeinfo.pp 49 KB

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