lineinfo.pp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632
  1. {
  2. $Id$
  3. This file is part of the Free Pascal run time library.
  4. Copyright (c) 2000 by Peter Vreman
  5. Stabs Line Info Retriever
  6. See the file COPYING.FPC, included in this distribution,
  7. for details about the copyright.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  11. **********************************************************************}
  12. unit lineinfo;
  13. interface
  14. {$IFDEF OS2}
  15. {$DEFINE EMX} (* EMX is the only possibility under OS/2 at the moment *)
  16. {$ENDIF OS2}
  17. implementation
  18. uses
  19. strings;
  20. const
  21. N_Function = $24;
  22. N_TextLine = $44;
  23. N_DataLine = $46;
  24. N_BssLine = $48;
  25. N_SourceFile = $64;
  26. N_IncludeFile = $84;
  27. maxstabs = 40; { size of the stabs buffer }
  28. type
  29. pstab=^tstab;
  30. tstab=packed record
  31. strpos : longint;
  32. ntype : byte;
  33. nother : byte;
  34. ndesc : word;
  35. nvalue : longint;
  36. end;
  37. { We use static variable so almost no stack is required, and is thus
  38. more safe when an error has occured in the program }
  39. var
  40. opened : boolean; { set if the file is already open }
  41. f : file; { current file }
  42. stabcnt, { amount of stabs }
  43. stabofs, { absolute stab section offset in executable }
  44. stabstrofs : longint; { absolute stabstr section offset in executable }
  45. dirlength : longint; { length of the dirctory part of the source file }
  46. stabs : array[0..maxstabs-1] of tstab; { buffer }
  47. funcstab, { stab with current function info }
  48. linestab, { stab with current line info }
  49. dirstab, { stab with current directory info }
  50. filestab : tstab; { stab with current file info }
  51. {****************************************************************************
  52. Executable Loaders
  53. ****************************************************************************}
  54. {$ifdef go32v2}
  55. function LoadGo32Coff:boolean;
  56. type
  57. tcoffheader=packed record
  58. mach : word;
  59. nsects : word;
  60. time : longint;
  61. sympos : longint;
  62. syms : longint;
  63. opthdr : word;
  64. flag : word;
  65. other : array[0..27] of byte;
  66. end;
  67. tcoffsechdr=packed record
  68. name : array[0..7] of char;
  69. vsize : longint;
  70. rvaofs : longint;
  71. datalen : longint;
  72. datapos : longint;
  73. relocpos : longint;
  74. lineno1 : longint;
  75. nrelocs : word;
  76. lineno2 : word;
  77. flags : longint;
  78. end;
  79. var
  80. coffheader : tcoffheader;
  81. coffsec : tcoffsechdr;
  82. i : longint;
  83. begin
  84. LoadGo32Coff:=false;
  85. stabofs:=-1;
  86. stabstrofs:=-1;
  87. { read and check header }
  88. if filesize(f)<2048+sizeof(tcoffheader) then
  89. exit;
  90. seek(f,2048);
  91. blockread(f,coffheader,sizeof(tcoffheader));
  92. if coffheader.mach<>$14c then
  93. exit;
  94. { read section info }
  95. for i:=1to coffheader.nSects do
  96. begin
  97. blockread(f,coffsec,sizeof(tcoffsechdr));
  98. if (coffsec.name[4]='b') and
  99. (coffsec.name[1]='s') and
  100. (coffsec.name[2]='t') then
  101. begin
  102. if (coffsec.name[5]='s') and
  103. (coffsec.name[6]='t') then
  104. stabstrofs:=coffsec.datapos+2048
  105. else
  106. begin
  107. stabofs:=coffsec.datapos+2048;
  108. stabcnt:=coffsec.datalen div sizeof(tstab);
  109. end;
  110. end;
  111. end;
  112. LoadGo32Coff:=(stabofs<>-1) and (stabstrofs<>-1);
  113. end;
  114. {$endif Go32v2}
  115. {$ifdef win32}
  116. function LoadPeCoff:boolean;
  117. type
  118. tdosheader = packed record
  119. e_magic : word;
  120. e_cblp : word;
  121. e_cp : word;
  122. e_crlc : word;
  123. e_cparhdr : word;
  124. e_minalloc : word;
  125. e_maxalloc : word;
  126. e_ss : word;
  127. e_sp : word;
  128. e_csum : word;
  129. e_ip : word;
  130. e_cs : word;
  131. e_lfarlc : word;
  132. e_ovno : word;
  133. e_res : array[0..3] of word;
  134. e_oemid : word;
  135. e_oeminfo : word;
  136. e_res2 : array[0..9] of word;
  137. e_lfanew : longint;
  138. end;
  139. tpeheader = packed record
  140. PEMagic : longint;
  141. Machine : word;
  142. NumberOfSections : word;
  143. TimeDateStamp : longint;
  144. PointerToSymbolTable : longint;
  145. NumberOfSymbols : longint;
  146. SizeOfOptionalHeader : word;
  147. Characteristics : word;
  148. Magic : word;
  149. MajorLinkerVersion : byte;
  150. MinorLinkerVersion : byte;
  151. SizeOfCode : longint;
  152. SizeOfInitializedData : longint;
  153. SizeOfUninitializedData : longint;
  154. AddressOfEntryPoint : longint;
  155. BaseOfCode : longint;
  156. BaseOfData : longint;
  157. ImageBase : longint;
  158. SectionAlignment : longint;
  159. FileAlignment : longint;
  160. MajorOperatingSystemVersion : word;
  161. MinorOperatingSystemVersion : word;
  162. MajorImageVersion : word;
  163. MinorImageVersion : word;
  164. MajorSubsystemVersion : word;
  165. MinorSubsystemVersion : word;
  166. Reserved1 : longint;
  167. SizeOfImage : longint;
  168. SizeOfHeaders : longint;
  169. CheckSum : longint;
  170. Subsystem : word;
  171. DllCharacteristics : word;
  172. SizeOfStackReserve : longint;
  173. SizeOfStackCommit : longint;
  174. SizeOfHeapReserve : longint;
  175. SizeOfHeapCommit : longint;
  176. LoaderFlags : longint;
  177. NumberOfRvaAndSizes : longint;
  178. DataDirectory : array[1..$80] of byte;
  179. end;
  180. tcoffsechdr=packed record
  181. name : array[0..7] of char;
  182. vsize : longint;
  183. rvaofs : longint;
  184. datalen : longint;
  185. datapos : longint;
  186. relocpos : longint;
  187. lineno1 : longint;
  188. nrelocs : word;
  189. lineno2 : word;
  190. flags : longint;
  191. end;
  192. var
  193. dosheader : tdosheader;
  194. peheader : tpeheader;
  195. coffsec : tcoffsechdr;
  196. i : longint;
  197. begin
  198. LoadPeCoff:=false;
  199. stabofs:=-1;
  200. stabstrofs:=-1;
  201. { read and check header }
  202. if filesize(f)<sizeof(dosheader) then
  203. exit;
  204. blockread(f,dosheader,sizeof(tdosheader));
  205. seek(f,dosheader.e_lfanew);
  206. blockread(f,peheader,sizeof(tpeheader));
  207. if peheader.pemagic<>$4550 then
  208. exit;
  209. { read section info }
  210. for i:=1to peheader.NumberOfSections do
  211. begin
  212. blockread(f,coffsec,sizeof(tcoffsechdr));
  213. if (coffsec.name[4]='b') and
  214. (coffsec.name[1]='s') and
  215. (coffsec.name[2]='t') then
  216. begin
  217. if (coffsec.name[5]='s') and
  218. (coffsec.name[6]='t') then
  219. stabstrofs:=coffsec.datapos
  220. else
  221. begin
  222. stabofs:=coffsec.datapos;
  223. stabcnt:=coffsec.datalen div sizeof(tstab);
  224. end;
  225. end;
  226. end;
  227. LoadPeCoff:=(stabofs<>-1) and (stabstrofs<>-1);
  228. end;
  229. {$endif Win32}
  230. {$IFDEF EMX}
  231. function LoadEMXaout: boolean;
  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. TEmxHeader = packed record
  255. Version: array [1..16] of char;
  256. Bound: word;
  257. AoutOfs: longint;
  258. Options: array [1..42] of char;
  259. end;
  260. TAoutHeader = packed record
  261. Magic: word;
  262. Machine: byte;
  263. Flags: byte;
  264. TextSize: longint;
  265. DataSize: longint;
  266. BssSize: longint;
  267. SymbSize: longint;
  268. EntryPoint: longint;
  269. TextRelocSize: longint;
  270. DataRelocSize: longint;
  271. end;
  272. const
  273. StartPageSize = $1000;
  274. var
  275. DosHeader: TDosHeader;
  276. EmxHeader: TEmxHeader;
  277. AoutHeader: TAoutHeader;
  278. S4: string [4];
  279. begin
  280. LoadEMXaout := false;
  281. StabOfs := -1;
  282. StabStrOfs := -1;
  283. { read and check header }
  284. if FileSize (F) > SizeOf (DosHeader) then
  285. begin
  286. BlockRead (F, DosHeader, SizeOf (TDosHeader));
  287. Seek (F, DosHeader.e_cparhdr shl 4);
  288. BlockRead (F, EmxHeader, SizeOf (TEmxHeader));
  289. S4 [0] := #4;
  290. Move (EmxHeader.Version, S4 [1], 4);
  291. if S4 = 'emx ' then
  292. begin
  293. Seek (F, EmxHeader.AoutOfs);
  294. BlockRead (F, AoutHeader, SizeOf (TAoutHeader));
  295. StabOfs := (Succ (EmxHeader.AoutOfs div StartPageSize)) * StartPageSize
  296. + AoutHeader.TextSize + AoutHeader.DataSize + 4;
  297. (* I don't really know, where this "+ 4" comes from, *)
  298. (* but it seems to be correct. :-) - TH *)
  299. StabCnt := AoutHeader.SymbSize div SizeOf (TStab);
  300. StabStrOfs := StabOfs + AoutHeader.SymbSize;
  301. LoadEMXaout := (StabOfs <> -1) and (StabStrOfs <> -1);
  302. end;
  303. end;
  304. end;
  305. {$ENDIF EMX}
  306. {$ifdef linux}
  307. function LoadElf32:boolean;
  308. type
  309. telf32header=packed record
  310. magic0123 : longint;
  311. file_class : byte;
  312. data_encoding : byte;
  313. file_version : byte;
  314. padding : array[$07..$0f] of byte;
  315. e_type : word;
  316. e_machine : word;
  317. e_version : longword;
  318. e_entry : longword; // entrypoint
  319. e_phoff : longword; // program header offset
  320. e_shoff : longword; // sections header offset
  321. e_flags : longword;
  322. e_ehsize : word; // elf header size in bytes
  323. e_phentsize : word; // size of an entry in the program header array
  324. e_phnum : word; // 0..e_phnum-1 of entrys
  325. e_shentsize : word; // size of an entry in sections header array
  326. e_shnum : word; // 0..e_shnum-1 of entrys
  327. e_shstrndx : word; // index of string section header
  328. end;
  329. telf32sechdr=packed record
  330. sh_name : longword;
  331. sh_type : longword;
  332. sh_flags : longword;
  333. sh_addr : longword;
  334. sh_offset : longword;
  335. sh_size : longword;
  336. sh_link : longword;
  337. sh_info : longword;
  338. sh_addralign : longword;
  339. sh_entsize : longword;
  340. end;
  341. var
  342. elfheader : telf32header;
  343. elfsec : telf32sechdr;
  344. secnames : array[0..255] of char;
  345. pname : pchar;
  346. i : longint;
  347. begin
  348. LoadElf32:=false;
  349. stabofs:=-1;
  350. stabstrofs:=-1;
  351. { read and check header }
  352. if filesize(f)<sizeof(telf32header) then
  353. exit;
  354. blockread(f,elfheader,sizeof(telf32header));
  355. if elfheader.magic0123<>$464c457f then
  356. exit;
  357. if elfheader.e_shentsize<>sizeof(telf32sechdr) then
  358. exit;
  359. { read section names }
  360. seek(f,elfheader.e_shoff+elfheader.e_shstrndx*sizeof(telf32sechdr));
  361. blockread(f,elfsec,sizeof(telf32sechdr));
  362. seek(f,elfsec.sh_offset);
  363. blockread(f,secnames,sizeof(secnames));
  364. { read section info }
  365. seek(f,elfheader.e_shoff);
  366. for i:=1to elfheader.e_shnum do
  367. begin
  368. blockread(f,elfsec,sizeof(telf32sechdr));
  369. pname:=@secnames[elfsec.sh_name];
  370. if (pname[4]='b') and
  371. (pname[1]='s') and
  372. (pname[2]='t') then
  373. begin
  374. if (pname[5]='s') and
  375. (pname[6]='t') then
  376. stabstrofs:=elfsec.sh_offset
  377. else
  378. begin
  379. stabofs:=elfsec.sh_offset;
  380. stabcnt:=elfsec.sh_size div sizeof(tstab);
  381. end;
  382. end;
  383. end;
  384. LoadElf32:=(stabofs<>-1) and (stabstrofs<>-1);
  385. end;
  386. {$endif linux}
  387. {****************************************************************************
  388. Executable Open/Close
  389. ****************************************************************************}
  390. procedure CloseStabs;
  391. begin
  392. close(f);
  393. opened:=false;
  394. end;
  395. function OpenStabs:boolean;
  396. var
  397. ofm : word;
  398. begin
  399. OpenStabs:=false;
  400. assign(f,paramstr(0));
  401. {$I-}
  402. ofm:=filemode;
  403. filemode:=$40;
  404. reset(f,1);
  405. filemode:=ofm;
  406. {$I+}
  407. if ioresult<>0 then
  408. exit;
  409. opened:=true;
  410. {$ifdef go32v2}
  411. if LoadGo32Coff then
  412. begin
  413. OpenStabs:=true;
  414. exit;
  415. end;
  416. {$endif}
  417. {$IFDEF EMX}
  418. if LoadEMXaout then
  419. begin
  420. OpenStabs:=true;
  421. exit;
  422. end;
  423. {$ENDIF EMX}
  424. {$ifdef win32}
  425. if LoadPECoff then
  426. begin
  427. OpenStabs:=true;
  428. exit;
  429. end;
  430. {$endif}
  431. {$ifdef linux}
  432. if LoadElf32 then
  433. begin
  434. OpenStabs:=true;
  435. exit;
  436. end;
  437. {$endif}
  438. CloseStabs;
  439. end;
  440. procedure GetLineInfo(addr:longint;var func,source:string;var line:longint);
  441. var
  442. res : {$ifdef tp}integer{$else}longint{$endif};
  443. stabsleft,
  444. stabscnt,i : longint;
  445. found : boolean;
  446. lastfunc : tstab;
  447. begin
  448. fillchar(func,high(func)+1,0);
  449. fillchar(source,high(source)+1,0);
  450. line:=0;
  451. if not opened then
  452. begin
  453. if not OpenStabs then
  454. exit;
  455. end;
  456. fillchar(funcstab,sizeof(tstab),0);
  457. fillchar(filestab,sizeof(tstab),0);
  458. fillchar(dirstab,sizeof(tstab),0);
  459. fillchar(linestab,sizeof(tstab),0);
  460. fillchar(lastfunc,sizeof(tstab),0);
  461. found:=false;
  462. seek(f,stabofs);
  463. stabsleft:=stabcnt;
  464. repeat
  465. if stabsleft>maxstabs then
  466. stabscnt:=maxstabs
  467. else
  468. stabscnt:=stabsleft;
  469. blockread(f,stabs,stabscnt*sizeof(tstab),res);
  470. stabscnt:=res div sizeof(tstab);
  471. for i:=0 to stabscnt-1 do
  472. begin
  473. case stabs[i].ntype of
  474. N_BssLine,
  475. N_DataLine,
  476. N_TextLine :
  477. begin
  478. inc(stabs[i].nvalue,lastfunc.nvalue);
  479. if (stabs[i].nvalue<=addr) and
  480. (stabs[i].nvalue>linestab.nvalue) then
  481. begin
  482. { if it's equal we can stop and take the last info }
  483. if stabs[i].nvalue=addr then
  484. found:=true
  485. else
  486. linestab:=stabs[i];
  487. end;
  488. end;
  489. N_Function :
  490. begin
  491. lastfunc:=stabs[i];
  492. if (stabs[i].nvalue<=addr) and
  493. (stabs[i].nvalue>funcstab.nvalue) then
  494. begin
  495. funcstab:=stabs[i];
  496. fillchar(linestab,sizeof(tstab),0);
  497. end;
  498. end;
  499. N_SourceFile,
  500. N_IncludeFile :
  501. begin
  502. if (stabs[i].nvalue<=addr) and
  503. (stabs[i].nvalue>=filestab.nvalue) then
  504. begin
  505. { if same value then the first one
  506. contained the directory PM }
  507. if stabs[i].nvalue=filestab.nvalue then
  508. dirstab:=filestab
  509. else
  510. fillchar(dirstab,sizeof(tstab),0);
  511. filestab:=stabs[i];
  512. fillchar(linestab,sizeof(tstab),0);
  513. end;
  514. end;
  515. end;
  516. end;
  517. dec(stabsleft,stabscnt);
  518. until found or (stabsleft=0);
  519. { get the line,source,function info }
  520. line:=linestab.ndesc;
  521. if dirstab.ntype<>0 then
  522. begin
  523. seek(f,stabstrofs+dirstab.strpos);
  524. blockread(f,source[1],high(source)-1,res);
  525. dirlength:=strlen(@source[1]);
  526. source[0]:=chr(dirlength);
  527. end
  528. else
  529. dirlength:=0;
  530. if filestab.ntype<>0 then
  531. begin
  532. seek(f,stabstrofs+filestab.strpos);
  533. blockread(f,source[dirlength+1],high(source)-(dirlength+1),res);
  534. source[0]:=chr(strlen(@source[1]));
  535. end;
  536. if funcstab.ntype<>0 then
  537. begin
  538. seek(f,stabstrofs+funcstab.strpos);
  539. blockread(f,func[1],high(func)-1,res);
  540. func[0]:=chr(strlen(@func[1]));
  541. i:=pos(':',func);
  542. if i>0 then
  543. Delete(func,i,255);
  544. end;
  545. end;
  546. function StabBackTraceStr(addr:longint):string;
  547. var
  548. func,
  549. source : string;
  550. hs : string[32];
  551. line : longint;
  552. begin
  553. GetLineInfo(addr,func,source,line);
  554. { if there was an error with opening reset the hook to the system default }
  555. if not Opened then
  556. BackTraceStrFunc:=@SysBackTraceStr;
  557. { create string }
  558. StabBackTraceStr:=' 0x'+HexStr(addr,8);
  559. if func<>'' then
  560. StabBackTraceStr:=StabBackTraceStr+' '+func;
  561. if source<>'' then
  562. begin
  563. if func<>'' then
  564. StabBackTraceStr:=StabBackTraceStr+', ';
  565. if line<>0 then
  566. begin
  567. str(line,hs);
  568. StabBackTraceStr:=StabBackTraceStr+' line '+hs;
  569. end;
  570. StabBackTraceStr:=StabBackTraceStr+' of '+source;
  571. end;
  572. end;
  573. initialization
  574. BackTraceStrFunc:=@StabBackTraceStr;
  575. finalization
  576. if opened then
  577. CloseStabs;
  578. end.
  579. {
  580. $Log$
  581. Revision 1.6 2000-03-19 18:10:41 hajny
  582. + added support for EMX
  583. Revision 1.5 2000/02/09 16:59:30 peter
  584. * truncated log
  585. Revision 1.4 2000/02/08 15:23:02 pierre
  586. * fix for directories included in stabsinfo
  587. Revision 1.3 2000/02/06 22:13:42 florian
  588. * small typo for go32 fixed
  589. Revision 1.2 2000/02/06 19:14:22 peter
  590. * linux elf support
  591. Revision 1.1 2000/02/06 17:19:22 peter
  592. * lineinfo unit added which uses stabs to get lineinfo for backtraces
  593. }