lineinfo.pp 14 KB

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