lineinfo.pp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672
  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:dword;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 : dword;
  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. {$ifdef ENDIAN_LITTLE}
  374. if elfheader.magic0123<>$464c457f then
  375. exit;
  376. {$endif ENDIAN_LITTLE}
  377. {$ifdef ENDIAN_BIG}
  378. if elfheader.magic0123<>$7f454c46 then
  379. exit;
  380. { this seems to be at least the case for m68k cpu PM }
  381. {$ifdef m68k}
  382. {StabsFunctionRelative:=false;}
  383. {$endif m68k}
  384. {$endif ENDIAN_BIG}
  385. if elfheader.e_shentsize<>sizeof(telf32sechdr) then
  386. exit;
  387. { read section names }
  388. seek(f,elfheader.e_shoff+cardinal(elfheader.e_shstrndx)*sizeof(telf32sechdr));
  389. blockread(f,elfsec,sizeof(telf32sechdr));
  390. seek(f,elfsec.sh_offset);
  391. blockread(f,secnames,sizeof(secnames));
  392. { read section info }
  393. seek(f,elfheader.e_shoff);
  394. for i:=1to elfheader.e_shnum do
  395. begin
  396. blockread(f,elfsec,sizeof(telf32sechdr));
  397. pname:=@secnames[elfsec.sh_name];
  398. if (pname[4]='b') and
  399. (pname[1]='s') and
  400. (pname[2]='t') then
  401. begin
  402. if (pname[5]='s') and
  403. (pname[6]='t') then
  404. stabstrofs:=elfsec.sh_offset
  405. else
  406. begin
  407. stabofs:=elfsec.sh_offset;
  408. stabcnt:=elfsec.sh_size div sizeof(tstab);
  409. end;
  410. end;
  411. end;
  412. LoadElf32:=(stabofs<>-1) and (stabstrofs<>-1);
  413. end;
  414. {$endif unix}
  415. {****************************************************************************
  416. Executable Open/Close
  417. ****************************************************************************}
  418. procedure CloseStabs;
  419. begin
  420. close(f);
  421. opened:=false;
  422. end;
  423. function OpenStabs:boolean;
  424. var
  425. ofm : word;
  426. begin
  427. OpenStabs:=false;
  428. assign(f,paramstr(0));
  429. {$I-}
  430. ofm:=filemode;
  431. filemode:=$40;
  432. reset(f,1);
  433. filemode:=ofm;
  434. {$I+}
  435. if ioresult<>0 then
  436. exit;
  437. opened:=true;
  438. {$ifdef go32v2}
  439. if LoadGo32Coff then
  440. begin
  441. OpenStabs:=true;
  442. exit;
  443. end;
  444. {$endif}
  445. {$IFDEF EMX}
  446. if LoadEMXaout then
  447. begin
  448. OpenStabs:=true;
  449. exit;
  450. end;
  451. {$ENDIF EMX}
  452. {$ifdef win32}
  453. if LoadPECoff then
  454. begin
  455. OpenStabs:=true;
  456. exit;
  457. end;
  458. {$endif}
  459. {$ifdef unix}
  460. if LoadElf32 then
  461. begin
  462. OpenStabs:=true;
  463. exit;
  464. end;
  465. {$endif}
  466. CloseStabs;
  467. end;
  468. procedure GetLineInfo(addr:dword;var func,source:string;var line:longint);
  469. var
  470. res : {$ifdef tp}integer{$else}longint{$endif};
  471. stabsleft,
  472. stabscnt,i : longint;
  473. found : boolean;
  474. lastfunc : tstab;
  475. begin
  476. fillchar(func,high(func)+1,0);
  477. fillchar(source,high(source)+1,0);
  478. line:=0;
  479. if not opened then
  480. begin
  481. if not OpenStabs then
  482. exit;
  483. end;
  484. fillchar(funcstab,sizeof(tstab),0);
  485. fillchar(filestab,sizeof(tstab),0);
  486. fillchar(dirstab,sizeof(tstab),0);
  487. fillchar(linestab,sizeof(tstab),0);
  488. fillchar(lastfunc,sizeof(tstab),0);
  489. found:=false;
  490. seek(f,stabofs);
  491. stabsleft:=stabcnt;
  492. repeat
  493. if stabsleft>maxstabs then
  494. stabscnt:=maxstabs
  495. else
  496. stabscnt:=stabsleft;
  497. blockread(f,stabs,stabscnt*sizeof(tstab),res);
  498. stabscnt:=res div sizeof(tstab);
  499. for i:=0 to stabscnt-1 do
  500. begin
  501. case stabs[i].ntype of
  502. N_BssLine,
  503. N_DataLine,
  504. N_TextLine :
  505. begin
  506. if (stabs[i].ntype=N_TextLine) and StabsFunctionRelative then
  507. inc(stabs[i].nvalue,lastfunc.nvalue);
  508. if (stabs[i].nvalue<=addr) and
  509. (stabs[i].nvalue>linestab.nvalue) then
  510. begin
  511. { if it's equal we can stop and take the last info }
  512. if stabs[i].nvalue=addr then
  513. found:=true
  514. else
  515. linestab:=stabs[i];
  516. end;
  517. end;
  518. N_Function :
  519. begin
  520. lastfunc:=stabs[i];
  521. if (stabs[i].nvalue<=addr) and
  522. (stabs[i].nvalue>funcstab.nvalue) then
  523. begin
  524. funcstab:=stabs[i];
  525. fillchar(linestab,sizeof(tstab),0);
  526. end;
  527. end;
  528. N_SourceFile,
  529. N_IncludeFile :
  530. begin
  531. if (stabs[i].nvalue<=addr) and
  532. (stabs[i].nvalue>=filestab.nvalue) then
  533. begin
  534. { if same value and type then the first one
  535. contained the directory PM }
  536. if (stabs[i].nvalue=filestab.nvalue) and
  537. (stabs[i].ntype=filestab.ntype) then
  538. dirstab:=filestab
  539. else
  540. fillchar(dirstab,sizeof(tstab),0);
  541. filestab:=stabs[i];
  542. fillchar(linestab,sizeof(tstab),0);
  543. { if new file then func is not valid anymore PM }
  544. if stabs[i].ntype=N_SourceFile then
  545. begin
  546. fillchar(funcstab,sizeof(tstab),0);
  547. fillchar(lastfunc,sizeof(tstab),0);
  548. end;
  549. end;
  550. end;
  551. end;
  552. end;
  553. dec(stabsleft,stabscnt);
  554. until found or (stabsleft=0);
  555. { get the line,source,function info }
  556. line:=linestab.ndesc;
  557. if dirstab.ntype<>0 then
  558. begin
  559. seek(f,stabstrofs+dirstab.strpos);
  560. blockread(f,source[1],high(source)-1,res);
  561. dirlength:=strlen(@source[1]);
  562. source[0]:=chr(dirlength);
  563. end
  564. else
  565. dirlength:=0;
  566. if filestab.ntype<>0 then
  567. begin
  568. seek(f,stabstrofs+filestab.strpos);
  569. blockread(f,source[dirlength+1],high(source)-(dirlength+1),res);
  570. source[0]:=chr(strlen(@source[1]));
  571. end;
  572. if funcstab.ntype<>0 then
  573. begin
  574. seek(f,stabstrofs+funcstab.strpos);
  575. blockread(f,func[1],high(func)-1,res);
  576. func[0]:=chr(strlen(@func[1]));
  577. i:=pos(':',func);
  578. if i>0 then
  579. Delete(func,i,255);
  580. end;
  581. end;
  582. function StabBackTraceStr(addr:longint):string;
  583. var
  584. func,
  585. source : string;
  586. hs : string[32];
  587. line : longint;
  588. Store : function (addr : longint) : string;
  589. begin
  590. { reset to prevent infinite recursion if problems inside the code PM }
  591. Store:=BackTraceStrFunc;
  592. BackTraceStrFunc:=@SysBackTraceStr;
  593. GetLineInfo(dword(addr),func,source,line);
  594. { create string }
  595. StabBackTraceStr:=' 0x'+HexStr(addr,8);
  596. if func<>'' then
  597. StabBackTraceStr:=StabBackTraceStr+' '+func;
  598. if source<>'' then
  599. begin
  600. if func<>'' then
  601. StabBackTraceStr:=StabBackTraceStr+', ';
  602. if line<>0 then
  603. begin
  604. str(line,hs);
  605. StabBackTraceStr:=StabBackTraceStr+' line '+hs;
  606. end;
  607. StabBackTraceStr:=StabBackTraceStr+' of '+source;
  608. end;
  609. if Opened then
  610. BackTraceStrFunc:=Store;
  611. end;
  612. initialization
  613. BackTraceStrFunc:=@StabBackTraceStr;
  614. finalization
  615. if opened then
  616. CloseStabs;
  617. end.
  618. {
  619. $Log$
  620. Revision 1.6 2001-07-29 13:43:57 peter
  621. * m68k updates merged
  622. Revision 1.5 2000/12/18 14:01:11 jonas
  623. * added cardinal typecast to avoid signed evaluation
  624. Revision 1.4 2000/11/13 13:40:04 marco
  625. * Renamefest
  626. Revision 1.3 2000/10/14 21:55:07 peter
  627. * fixed concatting of source and include filenames (merged)
  628. Revision 1.2 2000/07/13 11:33:44 michael
  629. + removed logs
  630. }