lineinfo.pp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649
  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. { if new file then func is not valid anymore PM }
  519. if stabs[i].ntype=N_SourceFile then
  520. begin
  521. fillchar(funcstab,sizeof(tstab),0);
  522. fillchar(lastfunc,sizeof(tstab),0);
  523. end;
  524. end;
  525. end;
  526. end;
  527. end;
  528. dec(stabsleft,stabscnt);
  529. until found or (stabsleft=0);
  530. { get the line,source,function info }
  531. line:=linestab.ndesc;
  532. if dirstab.ntype<>0 then
  533. begin
  534. seek(f,stabstrofs+dirstab.strpos);
  535. blockread(f,source[1],high(source)-1,res);
  536. dirlength:=strlen(@source[1]);
  537. source[0]:=chr(dirlength);
  538. end
  539. else
  540. dirlength:=0;
  541. if filestab.ntype<>0 then
  542. begin
  543. seek(f,stabstrofs+filestab.strpos);
  544. blockread(f,source[dirlength+1],high(source)-(dirlength+1),res);
  545. source[0]:=chr(strlen(@source[1]));
  546. end;
  547. if funcstab.ntype<>0 then
  548. begin
  549. seek(f,stabstrofs+funcstab.strpos);
  550. blockread(f,func[1],high(func)-1,res);
  551. func[0]:=chr(strlen(@func[1]));
  552. i:=pos(':',func);
  553. if i>0 then
  554. Delete(func,i,255);
  555. end;
  556. end;
  557. function StabBackTraceStr(addr:longint):string;
  558. var
  559. func,
  560. source : string;
  561. hs : string[32];
  562. line : longint;
  563. begin
  564. GetLineInfo(addr,func,source,line);
  565. { if there was an error with opening reset the hook to the system default }
  566. if not Opened then
  567. BackTraceStrFunc:=@SysBackTraceStr;
  568. { create string }
  569. StabBackTraceStr:=' 0x'+HexStr(addr,8);
  570. if func<>'' then
  571. StabBackTraceStr:=StabBackTraceStr+' '+func;
  572. if source<>'' then
  573. begin
  574. if func<>'' then
  575. StabBackTraceStr:=StabBackTraceStr+', ';
  576. if line<>0 then
  577. begin
  578. str(line,hs);
  579. StabBackTraceStr:=StabBackTraceStr+' line '+hs;
  580. end;
  581. StabBackTraceStr:=StabBackTraceStr+' of '+source;
  582. end;
  583. end;
  584. initialization
  585. BackTraceStrFunc:=@StabBackTraceStr;
  586. finalization
  587. if opened then
  588. CloseStabs;
  589. end.
  590. {
  591. $Log$
  592. Revision 1.8 2000-04-12 11:15:06 pierre
  593. * reset funcstab when changing object
  594. Revision 1.7 2000/03/23 22:00:08 pierre
  595. * fix for OS/2 hopefully
  596. Revision 1.6 2000/03/19 18:10:41 hajny
  597. + added support for EMX
  598. Revision 1.5 2000/02/09 16:59:30 peter
  599. * truncated log
  600. Revision 1.4 2000/02/08 15:23:02 pierre
  601. * fix for directories included in stabsinfo
  602. Revision 1.3 2000/02/06 22:13:42 florian
  603. * small typo for go32 fixed
  604. Revision 1.2 2000/02/06 19:14:22 peter
  605. * linux elf support
  606. Revision 1.1 2000/02/06 17:19:22 peter
  607. * lineinfo unit added which uses stabs to get lineinfo for backtraces
  608. }