lineinfo.pp 17 KB

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