lineinfo.pp 17 KB

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