lineinfo.pp 17 KB

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