lineinfo.pp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646
  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 linux}
  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 linux}
  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+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 linux}
  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 linux}
  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 then the first one
  525. contained the directory PM }
  526. if stabs[i].nvalue=filestab.nvalue then
  527. dirstab:=filestab
  528. else
  529. fillchar(dirstab,sizeof(tstab),0);
  530. filestab:=stabs[i];
  531. fillchar(linestab,sizeof(tstab),0);
  532. { if new file then func is not valid anymore PM }
  533. if stabs[i].ntype=N_SourceFile then
  534. begin
  535. fillchar(funcstab,sizeof(tstab),0);
  536. fillchar(lastfunc,sizeof(tstab),0);
  537. end;
  538. end;
  539. end;
  540. end;
  541. end;
  542. dec(stabsleft,stabscnt);
  543. until found or (stabsleft=0);
  544. { get the line,source,function info }
  545. line:=linestab.ndesc;
  546. if dirstab.ntype<>0 then
  547. begin
  548. seek(f,stabstrofs+dirstab.strpos);
  549. blockread(f,source[1],high(source)-1,res);
  550. dirlength:=strlen(@source[1]);
  551. source[0]:=chr(dirlength);
  552. end
  553. else
  554. dirlength:=0;
  555. if filestab.ntype<>0 then
  556. begin
  557. seek(f,stabstrofs+filestab.strpos);
  558. blockread(f,source[dirlength+1],high(source)-(dirlength+1),res);
  559. source[0]:=chr(strlen(@source[1]));
  560. end;
  561. if funcstab.ntype<>0 then
  562. begin
  563. seek(f,stabstrofs+funcstab.strpos);
  564. blockread(f,func[1],high(func)-1,res);
  565. func[0]:=chr(strlen(@func[1]));
  566. i:=pos(':',func);
  567. if i>0 then
  568. Delete(func,i,255);
  569. end;
  570. end;
  571. function StabBackTraceStr(addr:longint):string;
  572. var
  573. func,
  574. source : string;
  575. hs : string[32];
  576. line : longint;
  577. begin
  578. GetLineInfo(addr,func,source,line);
  579. { if there was an error with opening reset the hook to the system default }
  580. if not Opened then
  581. BackTraceStrFunc:=@SysBackTraceStr;
  582. { create string }
  583. StabBackTraceStr:=' 0x'+HexStr(addr,8);
  584. if func<>'' then
  585. StabBackTraceStr:=StabBackTraceStr+' '+func;
  586. if source<>'' then
  587. begin
  588. if func<>'' then
  589. StabBackTraceStr:=StabBackTraceStr+', ';
  590. if line<>0 then
  591. begin
  592. str(line,hs);
  593. StabBackTraceStr:=StabBackTraceStr+' line '+hs;
  594. end;
  595. StabBackTraceStr:=StabBackTraceStr+' of '+source;
  596. end;
  597. end;
  598. initialization
  599. BackTraceStrFunc:=@StabBackTraceStr;
  600. finalization
  601. if opened then
  602. CloseStabs;
  603. end.
  604. {
  605. $Log$
  606. Revision 1.2 2000-07-13 11:33:44 michael
  607. + removed logs
  608. }