exeinfo.pp 46 KB

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