lineinfo.pp 16 KB

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