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