2
0

exeinfo.pp 47 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737
  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_EXE_FN = 31; {AT_EXECFN }
  856. var
  857. pc : PPAnsiChar;
  858. pat_hdr : P_AT_HDR;
  859. i, phdr_count : ptrint;
  860. phdr_size : ptruint;
  861. phdr : ^telfproghdr;
  862. found_addr : ptruint;
  863. SavedExitProc : pointer;
  864. begin
  865. filename:=ParamStr(0);
  866. SavedExitProc:=ExitProc;
  867. ExitProc:=@LocalError;
  868. if SetJmp(LocalJmpBuf)=0 then
  869. begin
  870. { Try, avoided in order to remove exception installation }
  871. pc:=envp;
  872. phdr_count:=-1;
  873. phdr_size:=0;
  874. phdr:=nil;
  875. found_addr:=ptruint(-1);
  876. while (assigned(pc^)) do
  877. inc (pointer(pc), sizeof(ptruint));
  878. inc(pointer(pc), sizeof(ptruint));
  879. pat_hdr:=P_AT_HDR(pc);
  880. while assigned(pat_hdr) do
  881. begin
  882. if (pat_hdr^.typ=0) and (pat_hdr^.value=0) then
  883. break;
  884. if pat_hdr^.typ = AT_HDR_COUNT then
  885. phdr_count:=pat_hdr^.value;
  886. if pat_hdr^.typ = AT_HDR_SIZE then
  887. phdr_size:=pat_hdr^.value;
  888. if pat_hdr^.typ = AT_HDR_Addr then
  889. phdr := pointer(pat_hdr^.value);
  890. if pat_hdr^.typ = AT_EXE_FN then
  891. filename:=strpas(pansichar(pat_hdr^.value));
  892. inc (pointer(pat_hdr),sizeof(AT_HDR));
  893. end;
  894. if (phdr_count>0) and (phdr_size = sizeof (telfproghdr))
  895. and assigned(phdr) then
  896. begin
  897. for i:=0 to phdr_count -1 do
  898. begin
  899. if (phdr^.p_type = 1 {PT_LOAD}) and (ptruint(phdr^.p_vaddr) < found_addr) then
  900. found_addr:=phdr^.p_vaddr;
  901. inc(pointer(phdr), phdr_size);
  902. end;
  903. {$ifdef DEBUG_LINEINFO}
  904. end
  905. else
  906. begin
  907. if (phdr_count=-1) then
  908. writeln(stderr,'AUX entry AT_PHNUM not found');
  909. if (phdr_size=0) then
  910. writeln(stderr,'AUX entry AT_PHENT not found');
  911. if (phdr=nil) then
  912. writeln(stderr,'AUX entry AT_PHDR not found');
  913. {$endif DEBUG_LINEINFO}
  914. end;
  915. if found_addr<>ptruint(-1) then
  916. begin
  917. {$ifdef DEBUG_LINEINFO}
  918. Writeln(stderr,'Found addr = $',hexstr(found_addr,2 * sizeof(ptruint)));
  919. {$endif}
  920. BaseAddr:=pointer(found_addr);
  921. end
  922. {$ifdef DEBUG_LINEINFO}
  923. else
  924. writeln(stderr,'Error parsing stack');
  925. {$endif DEBUG_LINEINFO}
  926. end
  927. else
  928. begin
  929. {$ifdef DEBUG_LINEINFO}
  930. writeln(stderr,'Exception parsing stack');
  931. {$endif DEBUG_LINEINFO}
  932. end;
  933. ExitProc:=SavedExitProc;
  934. end;
  935. {$endif FIND_BASEADDR_ELF}
  936. function OpenElf(var e:TExeFile):boolean;
  937. {$ifdef MSDOS}
  938. const
  939. ParagraphSize = 512;
  940. {$endif MSDOS}
  941. var
  942. elfheader : telfheader;
  943. elfsec : telfsechdr;
  944. phdr : telfproghdr;
  945. i : longint;
  946. {$ifdef MSDOS}
  947. DosHeader : tdosheader;
  948. BRead : cardinal;
  949. {$endif MSDOS}
  950. begin
  951. OpenElf:=false;
  952. {$ifdef MSDOS}
  953. { read and check header }
  954. if E.Size < SizeOf (DosHeader) then
  955. Exit;
  956. BlockRead (E.F, DosHeader, SizeOf (DosHeader), BRead);
  957. if BRead <> SizeOf (DosHeader) then
  958. Exit;
  959. if DosHeader.E_Magic = $5A4D then
  960. begin
  961. E.ImgOffset := LongWord(DosHeader.e_cp) * ParagraphSize;
  962. if DosHeader.e_cblp > 0 then
  963. E.ImgOffset := E.ImgOffset + DosHeader.e_cblp - ParagraphSize;
  964. end;
  965. {$endif MSDOS}
  966. { read and check header }
  967. if e.size<(sizeof(telfheader)+e.ImgOffset) then
  968. exit;
  969. seek(e.f,e.ImgOffset);
  970. blockread(e.f,elfheader,sizeof(telfheader));
  971. if elfheader.magic0123<>{$ifdef ENDIAN_LITTLE}$464c457f{$else}$7f454c46{$endif} then
  972. exit;
  973. if elfheader.e_shentsize<>sizeof(telfsechdr) then
  974. exit;
  975. { read section names }
  976. seek(e.f,e.ImgOffset+elfheader.e_shoff+elfheader.e_shstrndx*cardinal(sizeof(telfsechdr)));
  977. blockread(e.f,elfsec,sizeof(telfsechdr));
  978. e.secstrofs:=elfsec.sh_offset;
  979. e.sechdrofs:=elfheader.e_shoff;
  980. e.nsects:=elfheader.e_shnum;
  981. {$ifdef MSDOS}
  982. { e.processaddress is already initialized to 0 }
  983. e.processsegment:=PrefixSeg+16;
  984. {$else MSDOS}
  985. { scan program headers to find the image base address }
  986. e.processaddress:=High(e.processaddress);
  987. seek(e.f,e.ImgOffset+elfheader.e_phoff);
  988. for i:=1 to elfheader.e_phnum do
  989. begin
  990. blockread(e.f,phdr,sizeof(phdr));
  991. if (phdr.p_type = 1 {PT_LOAD}) and (ptruint(phdr.p_vaddr) < e.processaddress) then
  992. e.processaddress:=phdr.p_vaddr;
  993. end;
  994. if e.processaddress = High(e.processaddress) then
  995. e.processaddress:=0;
  996. {$endif MSDOS}
  997. OpenElf:=true;
  998. end;
  999. function FindSectionElf(var e:TExeFile;const asecname:shortstring;var secofs,seclen:longint):boolean;
  1000. var
  1001. elfsec : telfsechdr;
  1002. secname : string;
  1003. secnamebuf : array[0..255] of ansichar;
  1004. oldofs,
  1005. bufsize,i : longint;
  1006. begin
  1007. FindSectionElf:=false;
  1008. seek(e.f,e.ImgOffset+e.sechdrofs);
  1009. for i:=1 to e.nsects do
  1010. begin
  1011. blockread(e.f,elfsec,sizeof(telfsechdr));
  1012. fillchar(secnamebuf,sizeof(secnamebuf),0);
  1013. oldofs:=filepos(e.f);
  1014. seek(e.f,e.ImgOffset+e.secstrofs+elfsec.sh_name);
  1015. blockread(e.f,secnamebuf,sizeof(secnamebuf)-1,bufsize);
  1016. seek(e.f,oldofs);
  1017. secname:=strpas(secnamebuf);
  1018. if asecname=secname then
  1019. begin
  1020. secofs:=e.ImgOffset+elfsec.sh_offset;
  1021. seclen:=elfsec.sh_size;
  1022. FindSectionElf:=true;
  1023. exit;
  1024. end;
  1025. end;
  1026. end;
  1027. {$endif ELF32 or ELF64}
  1028. {****************************************************************************
  1029. MACHO
  1030. ****************************************************************************}
  1031. {$ifdef darwin}
  1032. {$push}
  1033. {$packrecords c}
  1034. type
  1035. tmach_integer = cint;
  1036. tmach_cpu_type = tmach_integer;
  1037. tmach_cpu_subtype = tmach_integer;
  1038. tmach_cpu_threadtype = tmach_integer;
  1039. tmach_fat_header=record
  1040. magic: cuint32;
  1041. nfatarch: cuint32;
  1042. end;
  1043. tmach_fat_arch=record
  1044. cputype: tmach_cpu_type;
  1045. cpusubtype: tmach_cpu_subtype;
  1046. offset: cuint32;
  1047. size: cuint32;
  1048. align: cuint32;
  1049. end;
  1050. pmach_fat_arch = ^tmach_fat_arch;
  1051. (* not yet supported (only needed for slices or combined slice size > 4GB; unrelated to 64 bit processes)
  1052. tmach_fat_arch_64=record
  1053. cputype: tmach_cpu_type;
  1054. cpusubtype: tmach_cpu_subtype;
  1055. offset: cuint64;
  1056. size: cuint64;
  1057. align: cuint32;
  1058. reserved: cuint32;
  1059. end;
  1060. *)
  1061. { note: always big endian }
  1062. tmach_header=record
  1063. magic: cuint32;
  1064. cputype: tmach_cpu_type;
  1065. cpusubtype: tmach_cpu_subtype;
  1066. filetype: cuint32;
  1067. ncmds: cuint32;
  1068. sizeofcmds: cuint32;
  1069. flags: cuint32;
  1070. {$IFDEF CPU64}
  1071. reserved: cuint32;
  1072. {$ENDIF}
  1073. end;
  1074. pmach_header = ^tmach_header;
  1075. tmach_load_command=record
  1076. cmd: cuint32;
  1077. cmdsize: cuint32;
  1078. end;
  1079. pmach_load_command=^tmach_load_command;
  1080. tmach_symtab_command=record
  1081. cmd : cuint32;
  1082. cmdsize: cuint32;
  1083. symoff : cuint32;
  1084. nsyms : cuint32;
  1085. stroff : cuint32;
  1086. strsize: cuint32;
  1087. end;
  1088. pmach_symtab_command = ^tmach_symtab_command;
  1089. tstab=record
  1090. strpos : longword;
  1091. ntype : byte;
  1092. nother : byte;
  1093. ndesc : word;
  1094. nvalue : longword;
  1095. end;
  1096. pstab = ^tstab;
  1097. tmach_vm_prot = cint;
  1098. tmach_segment_command = record
  1099. cmd : cuint32;
  1100. cmdsize : cuint32;
  1101. segname : array [0..15] of AnsiChar;
  1102. vmaddr : {$IFDEF CPU64}cuint64{$ELSE}cuint32{$ENDIF};
  1103. vmsize : {$IFDEF CPU64}cuint64{$ELSE}cuint32{$ENDIF};
  1104. fileoff : {$IFDEF CPU64}cuint64{$ELSE}cuint32{$ENDIF};
  1105. filesize: {$IFDEF CPU64}cuint64{$ELSE}cuint32{$ENDIF};
  1106. maxprot : tmach_vm_prot;
  1107. initptot: tmach_vm_prot;
  1108. nsects : cuint32;
  1109. flags : cuint32;
  1110. end;
  1111. pmach_segment_command = ^tmach_segment_command;
  1112. tmach_uuid_command = record
  1113. cmd : cuint32;
  1114. cmdsize : cuint32;
  1115. uuid : array[0..15] of cuint8;
  1116. end;
  1117. pmach_uuid_command = ^tmach_uuid_command;
  1118. tmach_section = record
  1119. sectname : array [0..15] of AnsiChar;
  1120. segname : array [0..15] of AnsiChar;
  1121. addr : {$IFDEF CPU64}cuint64{$ELSE}cuint32{$ENDIF};
  1122. size : {$IFDEF CPU64}cuint64{$ELSE}cuint32{$ENDIF};
  1123. offset : cuint32;
  1124. align : cuint32;
  1125. reloff : cuint32;
  1126. nreloc : cuint32;
  1127. flags : cuint32;
  1128. reserved1: cuint32;
  1129. reserved2: cuint32;
  1130. {$IFDEF CPU64}
  1131. reserved3: cuint32;
  1132. {$ENDIF}
  1133. end;
  1134. pmach_section = ^tmach_section;
  1135. tmach_fat_archs = array[1..high(longint) div sizeof(tmach_header)] of tmach_fat_arch;
  1136. tmach_fat_header_archs = record
  1137. header: tmach_fat_header;
  1138. archs: tmach_fat_archs;
  1139. end;
  1140. pmach_fat_header_archs = ^tmach_fat_header_archs;
  1141. {$pop}
  1142. const
  1143. MACH_MH_EXECUTE = $02;
  1144. MACH_FAT_MAGIC = $cafebabe;
  1145. // not yet supported: only for binaries with slices > 4GB, or total size > 4GB
  1146. // MACH_FAT_MAGIC_64 = $cafebabf;
  1147. {$ifdef cpu32}
  1148. MACH_MAGIC = $feedface;
  1149. {$else}
  1150. MACH_MAGIC = $feedfacf;
  1151. {$endif}
  1152. MACH_CPU_ARCH_MASK = cuint32($ff000000);
  1153. {$ifdef cpu32}
  1154. MACH_LC_SEGMENT = $01;
  1155. {$else}
  1156. MACH_LC_SEGMENT = $19;
  1157. {$endif}
  1158. MACH_LC_SYMTAB = $02;
  1159. MACH_LC_UUID = $1b;
  1160. { the in-memory mapping of the mach header of the main binary }
  1161. function _NSGetMachExecuteHeader: pmach_header; cdecl; external 'c';
  1162. function getpagesize: cint; cdecl; external 'c';
  1163. function MapMachO(const h: THandle; offset, len: SizeUInt; out addr: pointer; out memoffset, mappedsize: SizeUInt): boolean;
  1164. var
  1165. pagesize: cint;
  1166. begin
  1167. pagesize:=getpagesize;
  1168. addr:=fpmmap(nil, len+(offset and (pagesize-1)), PROT_READ, MAP_PRIVATE, h, offset and not(pagesize-1));
  1169. if addr=MAP_FAILED then
  1170. begin
  1171. addr:=nil;
  1172. memoffset:=0;
  1173. mappedsize:=0;
  1174. end
  1175. else
  1176. begin
  1177. memoffset:=offset and (pagesize - 1);
  1178. mappedsize:=len+(offset and (pagesize-1));
  1179. end;
  1180. end;
  1181. procedure UnmapMachO(p: pointer; size: SizeUInt);
  1182. begin
  1183. fpmunmap(p,size);
  1184. end;
  1185. function OpenMachO(var e:TExeFile):boolean;
  1186. var
  1187. mh : tmach_header;
  1188. processmh : pmach_header;
  1189. cmd: pmach_load_command;
  1190. segmentcmd: pmach_segment_command;
  1191. mappedexe: pointer;
  1192. mappedoffset, mappedsize: SizeUInt;
  1193. i: cuint32;
  1194. foundpagezero: boolean;
  1195. begin
  1196. OpenMachO:=false;
  1197. E.FunctionRelative:=false;
  1198. if e.size<sizeof(mh) then
  1199. exit;
  1200. blockread (e.f, mh, sizeof(mh));
  1201. case mh.magic of
  1202. MACH_FAT_MAGIC:
  1203. begin
  1204. { todo }
  1205. exit
  1206. end;
  1207. MACH_MAGIC:
  1208. begin
  1209. // check that at least the architecture matches (we should also check the subarch,
  1210. // but that's harder because of architecture-specific backward compatibility rules)
  1211. processmh:=_NSGetMachExecuteHeader;
  1212. if (mh.cputype and not(MACH_CPU_ARCH_MASK)) <> (processmh^.cputype and not(MACH_CPU_ARCH_MASK)) then
  1213. exit;
  1214. end;
  1215. else
  1216. exit;
  1217. end;
  1218. e.sechdrofs:=filepos(e.f);
  1219. e.nsects:=mh.ncmds;
  1220. e.loadcommandssize:=mh.sizeofcmds;
  1221. if mh.filetype = MACH_MH_EXECUTE then
  1222. begin
  1223. foundpagezero:= false;
  1224. { make sure to unmap again on all exit paths }
  1225. if not MapMachO(filerec(e.f).handle, e.sechdrofs, e.loadcommandssize, mappedexe, mappedoffset, mappedsize) then
  1226. exit;
  1227. cmd:=pmach_load_command(mappedexe+mappedoffset);
  1228. for i:= 1 to e.nsects do
  1229. begin
  1230. case cmd^.cmd of
  1231. MACH_LC_SEGMENT:
  1232. begin
  1233. segmentcmd:=pmach_segment_command(cmd);
  1234. if segmentcmd^.segname='__PAGEZERO' then
  1235. begin
  1236. e.processaddress:=segmentcmd^.vmaddr+segmentcmd^.vmsize;
  1237. OpenMachO:=true;
  1238. break;
  1239. end;
  1240. end;
  1241. end;
  1242. cmd:=pmach_load_command(pointer(cmd)+cmd^.cmdsize);
  1243. end;
  1244. UnmapMachO(mappedexe, mappedsize);
  1245. end
  1246. else
  1247. OpenMachO:=true;
  1248. end;
  1249. function FindSectionMachO(var e:TExeFile;const asecname:shortstring;var secofs,seclen:longint):boolean;
  1250. var
  1251. i, j: cuint32;
  1252. cmd: pmach_load_command;
  1253. symtabcmd: pmach_symtab_command;
  1254. segmentcmd: pmach_segment_command;
  1255. section: pmach_section;
  1256. mappedexe: pointer;
  1257. mappedoffset, mappedsize: SizeUInt;
  1258. dwarfsecname: shortstring;
  1259. begin
  1260. FindSectionMachO:=false;
  1261. { make sure to unmap again on all exit paths }
  1262. if not MapMachO(filerec(e.f).handle, e.sechdrofs, e.loadcommandssize, mappedexe, mappedoffset, mappedsize) then
  1263. exit;
  1264. cmd:=pmach_load_command(mappedexe+mappedoffset);
  1265. for i:= 1 to e.nsects do
  1266. begin
  1267. case cmd^.cmd of
  1268. MACH_LC_SEGMENT:
  1269. begin
  1270. segmentcmd:=pmach_segment_command(cmd);
  1271. if segmentcmd^.segname='__DWARF' then
  1272. begin
  1273. if asecname[1]='.' then
  1274. dwarfsecname:='__'+copy(asecname,2,length(asecname))
  1275. else
  1276. dwarfsecname:=asecname;
  1277. section:=pmach_section(pointer(segmentcmd)+sizeof(segmentcmd^));
  1278. for j:=1 to segmentcmd^.nsects do
  1279. begin
  1280. if section^.sectname = dwarfsecname then
  1281. begin
  1282. secofs:=section^.offset;
  1283. seclen:=section^.size;
  1284. FindSectionMachO:=true;
  1285. UnmapMachO(mappedexe, mappedsize);
  1286. exit;
  1287. end;
  1288. inc(section);
  1289. end;
  1290. end;
  1291. end;
  1292. MACH_LC_SYMTAB:
  1293. begin
  1294. symtabcmd:=pmach_symtab_command(cmd);
  1295. if asecname='.stab' then
  1296. begin
  1297. secofs:=symtabcmd^.symoff;
  1298. { the caller will divide again by sizeof(tstab) }
  1299. seclen:=symtabcmd^.nsyms*sizeof(tstab);
  1300. FindSectionMachO:=true;
  1301. end
  1302. else if asecname='.stabstr' then
  1303. begin
  1304. secofs:=symtabcmd^.stroff;
  1305. seclen:=symtabcmd^.strsize;
  1306. FindSectionMachO:=true;
  1307. end;
  1308. if FindSectionMachO then
  1309. begin
  1310. UnmapMachO(mappedexe, mappedsize);
  1311. exit;
  1312. end;
  1313. end;
  1314. end;
  1315. cmd:=pmach_load_command(pointer(cmd)+cmd^.cmdsize);
  1316. end;
  1317. UnmapMachO(mappedexe, mappedsize);
  1318. end;
  1319. {$endif darwin}
  1320. {****************************************************************************
  1321. CRC
  1322. ****************************************************************************}
  1323. var
  1324. Crc32Tbl : array[0..255] of cardinal;
  1325. procedure MakeCRC32Tbl;
  1326. var
  1327. crc : cardinal;
  1328. i,n : integer;
  1329. begin
  1330. for i:=0 to 255 do
  1331. begin
  1332. crc:=i;
  1333. for n:=1 to 8 do
  1334. if (crc and 1)<>0 then
  1335. crc:=(crc shr 1) xor cardinal($edb88320)
  1336. else
  1337. crc:=crc shr 1;
  1338. Crc32Tbl[i]:=crc;
  1339. end;
  1340. end;
  1341. Function UpdateCrc32(InitCrc:cardinal;const InBuf;InLen:LongInt):cardinal;
  1342. var
  1343. i : LongInt;
  1344. p : pansichar;
  1345. begin
  1346. if Crc32Tbl[1]=0 then
  1347. MakeCrc32Tbl;
  1348. p:=@InBuf;
  1349. UpdateCrc32:=not InitCrc;
  1350. for i:=1 to InLen do
  1351. begin
  1352. UpdateCrc32:=Crc32Tbl[byte(UpdateCrc32) xor byte(p^)] xor (UpdateCrc32 shr 8);
  1353. inc(p);
  1354. end;
  1355. UpdateCrc32:=not UpdateCrc32;
  1356. end;
  1357. {****************************************************************************
  1358. Generic Executable Open/Close
  1359. ****************************************************************************}
  1360. type
  1361. TOpenProc=function(var e:TExeFile):boolean;
  1362. TFindSectionProc=function(var e:TExeFile;const asecname:shortstring;var secofs,seclen:longint):boolean;
  1363. TExeProcRec=record
  1364. openproc : TOpenProc;
  1365. findproc : TFindSectionProc;
  1366. end;
  1367. const
  1368. ExeProcs : TExeProcRec = (
  1369. {$ifdef go32v2}
  1370. openproc : @OpenGo32Coff;
  1371. findproc : @FindSectionCoff;
  1372. {$endif}
  1373. {$ifdef PE32}
  1374. openproc : @OpenPeCoff;
  1375. findproc : @FindSectionCoff;
  1376. {$endif}
  1377. {$ifdef PE32PLUS}
  1378. openproc : @OpenPePlusCoff;
  1379. findproc : @FindSectionCoff;
  1380. {$endif PE32PLUS}
  1381. {$if defined(ELF32) or defined(ELF64)}
  1382. openproc : @OpenElf;
  1383. findproc : @FindSectionElf;
  1384. {$endif ELF32 or ELF64}
  1385. {$ifdef darwin}
  1386. openproc : @OpenMachO;
  1387. findproc : @FindSectionMachO;
  1388. {$endif darwin}
  1389. {$IFDEF EMX}
  1390. openproc : @OpenEMXaout;
  1391. findproc : @FindSectionEMXaout;
  1392. {$ENDIF EMX}
  1393. {$ifdef netware}
  1394. openproc : @OpenNetwareNLM;
  1395. findproc : @FindSectionNetwareNLM;
  1396. {$endif}
  1397. );
  1398. function OpenExeFile(var e:TExeFile;const fn:shortstring):boolean;
  1399. var
  1400. ofm : word;
  1401. begin
  1402. OpenExeFile:=false;
  1403. fillchar(e,sizeof(e),0);
  1404. e.bufsize:=sizeof(e.buf);
  1405. e.filename:=fn;
  1406. if fn='' then // we don't want to read stdin
  1407. exit;
  1408. assign(e.f,fn);
  1409. {$I-}
  1410. ofm:=filemode;
  1411. filemode:=$40;
  1412. reset(e.f,1);
  1413. filemode:=ofm;
  1414. {$I+}
  1415. if ioresult<>0 then
  1416. exit;
  1417. e.isopen:=true;
  1418. // cache filesize
  1419. e.size:=filesize(e.f);
  1420. E.FunctionRelative := true;
  1421. E.ImgOffset := 0;
  1422. if ExeProcs.OpenProc<>nil then
  1423. OpenExeFile:=ExeProcs.OpenProc(e);
  1424. end;
  1425. function CloseExeFile(var e:TExeFile):boolean;
  1426. begin
  1427. CloseExeFile:=false;
  1428. if not e.isopen then
  1429. exit;
  1430. e.isopen:=false;
  1431. close(e.f);
  1432. CloseExeFile:=true;
  1433. end;
  1434. function FindExeSection(var e:TExeFile;const secname:shortstring;var secofs,seclen:longint):boolean;
  1435. begin
  1436. FindExeSection:=false;
  1437. if not e.isopen then
  1438. exit;
  1439. if ExeProcs.FindProc<>nil then
  1440. FindExeSection:=ExeProcs.FindProc(e,secname,secofs,seclen);
  1441. end;
  1442. function CheckDbgFile(var e:TExeFile;const fn:shortstring;dbgcrc:cardinal):boolean;
  1443. var
  1444. c : cardinal;
  1445. ofm : word;
  1446. g : file;
  1447. begin
  1448. CheckDbgFile:=false;
  1449. assign(g,fn);
  1450. {$I-}
  1451. ofm:=filemode;
  1452. filemode:=$40;
  1453. reset(g,1);
  1454. filemode:=ofm;
  1455. {$I+}
  1456. if ioresult<>0 then
  1457. exit;
  1458. { We reuse the buffer from e here to prevent too much stack allocation }
  1459. c:=0;
  1460. repeat
  1461. blockread(g,e.buf,e.bufsize,e.bufcnt);
  1462. c:=UpdateCrc32(c,e.buf,e.bufcnt);
  1463. until e.bufcnt<e.bufsize;
  1464. close(g);
  1465. CheckDbgFile:=(dbgcrc=c);
  1466. end;
  1467. {$ifndef darwin}
  1468. function ReadDebugLink(var e:TExeFile;var dbgfn:ansistring):boolean;
  1469. var
  1470. dbglink : array[0..255] of AnsiChar;
  1471. i,
  1472. dbglinklen,
  1473. dbglinkofs : longint;
  1474. dbgcrc : cardinal;
  1475. begin
  1476. ReadDebugLink:=false;
  1477. if not FindExeSection(e,'.gnu_debuglink',dbglinkofs,dbglinklen) then
  1478. exit;
  1479. if dbglinklen>sizeof(dbglink)-1 then
  1480. exit;
  1481. fillchar(dbglink,sizeof(dbglink),0);
  1482. seek(e.f,dbglinkofs);
  1483. blockread(e.f,dbglink,dbglinklen);
  1484. dbgfn:=strpas(dbglink);
  1485. if length(dbgfn)=0 then
  1486. exit;
  1487. i:=align(length(dbgfn)+1,4);
  1488. if (i+4)>dbglinklen then
  1489. exit;
  1490. move(dbglink[i],dbgcrc,4);
  1491. { current dir }
  1492. if CheckDbgFile(e,dbgfn,dbgcrc) then
  1493. begin
  1494. ReadDebugLink:=true;
  1495. exit;
  1496. end;
  1497. { executable dir }
  1498. i:=length(e.filename);
  1499. while (i>0) and not(e.filename[i] in AllowDirectorySeparators) do
  1500. dec(i);
  1501. if i>0 then
  1502. begin
  1503. dbgfn:=copy(e.filename,1,i)+dbgfn;
  1504. if CheckDbgFile(e,dbgfn,dbgcrc) then
  1505. begin
  1506. ReadDebugLink:=true;
  1507. exit;
  1508. end;
  1509. end;
  1510. end;
  1511. {$else}
  1512. function ReadDebugLink(var e:TExeFile;var dbgfn:ansistring):boolean;
  1513. var
  1514. dsymexefile: TExeFile;
  1515. execmd, dsymcmd: pmach_load_command;
  1516. exeuuidcmd, dsymuuidcmd: pmach_uuid_command;
  1517. mappedexe, mappeddsym: pointer;
  1518. mappedexeoffset, mappedexesize, mappeddsymoffset, mappeddsymsize: SizeUInt;
  1519. i, j: cuint32;
  1520. filenamestartpos, b: byte;
  1521. begin
  1522. ReadDebugLink:=false;
  1523. if not MapMachO(filerec(e.f).handle, e.sechdrofs, e.loadcommandssize, mappedexe, mappedexeoffset, mappedexesize) then
  1524. exit;
  1525. execmd:=pmach_load_command(mappedexe+mappedexeoffset);
  1526. for i:=1 to e.nsects do
  1527. begin
  1528. case execmd^.cmd of
  1529. MACH_LC_UUID:
  1530. begin
  1531. exeuuidcmd:=pmach_uuid_command(execmd);
  1532. filenamestartpos:=1;
  1533. for b:=1 to length(e.filename) do
  1534. begin
  1535. if e.filename[b] = '/' then
  1536. filenamestartpos:=b+1;
  1537. end;
  1538. if not OpenExeFile(dsymexefile,e.filename+'.dSYM/Contents/Resources/DWARF/'+copy(e.filename,filenamestartpos,length(e.filename))) then
  1539. begin
  1540. {$IFDEF DEBUG_LINEINFO}
  1541. writeln(stderr,'OpenExeFile for ',e.filename+'.dSYM/Contents/Resources/DWARF/'+copy(e.filename,filenamestartpos,length(e.filename)),' did not succeed.');
  1542. {$endif DEBUG_LINEINFO}
  1543. UnmapMachO(mappedexe, mappedexesize);
  1544. exit;
  1545. end;
  1546. if not MapMachO(filerec(dsymexefile.f).handle, dsymexefile.sechdrofs, dsymexefile.loadcommandssize, mappeddsym, mappeddsymoffset, mappeddsymsize) then
  1547. begin
  1548. CloseExeFile(dsymexefile);
  1549. UnmapMachO(mappedexe, mappedexesize);
  1550. exit;
  1551. end;
  1552. dsymcmd:=pmach_load_command(mappeddsym+mappeddsymoffset);
  1553. for j:=1 to dsymexefile.nsects do
  1554. begin
  1555. case dsymcmd^.cmd of
  1556. MACH_LC_UUID:
  1557. begin
  1558. dsymuuidcmd:=pmach_uuid_command(dsymcmd);
  1559. if comparebyte(exeuuidcmd^.uuid, dsymuuidcmd^.uuid, sizeof(exeuuidcmd^.uuid)) = 0 then
  1560. begin
  1561. dbgfn:=dsymexefile.filename;
  1562. ReadDebugLink:=true;
  1563. end;
  1564. break;
  1565. end;
  1566. end;
  1567. end;
  1568. UnmapMachO(mappeddsym, mappeddsymsize);
  1569. CloseExeFile(dsymexefile);
  1570. UnmapMachO(mappedexe, mappedexesize);
  1571. exit;
  1572. end;
  1573. end;
  1574. execmd:=pmach_load_command(pointer(execmd)+execmd^.cmdsize);
  1575. end;
  1576. UnmapMachO(mappedexe, mappedexesize);
  1577. end;
  1578. {$endif}
  1579. begin
  1580. {$ifdef FIND_BASEADDR_ELF}
  1581. UnixGetModuleByAddrHook:=@GetExeInMemoryBaseAddr;
  1582. {$endif FIND_BASEADDR_ELF}
  1583. end.