lineinfo.pp 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 2000 by Peter Vreman
  4. Stabs Line Info Retriever
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. unit lineinfo;
  12. interface
  13. {$mode objfpc}
  14. {$S-}
  15. {$Q-}
  16. procedure GetLineInfo(addr:ptruint;var func,source:string;var line:longint);
  17. implementation
  18. uses
  19. exeinfo,strings;
  20. const
  21. N_Function = $24;
  22. N_TextLine = $44;
  23. N_DataLine = $46;
  24. N_BssLine = $48;
  25. N_SourceFile = $64;
  26. N_IncludeFile = $84;
  27. maxstabs = 40; { size of the stabs buffer }
  28. var
  29. { GDB after 4.18 uses offset to function begin
  30. in text section but OS/2 version still uses 4.16 PM }
  31. StabsFunctionRelative: boolean;
  32. type
  33. pstab=^tstab;
  34. tstab=packed record
  35. strpos : longint;
  36. ntype : byte;
  37. nother : byte;
  38. ndesc : word;
  39. nvalue : dword;
  40. end;
  41. { We use static variable so almost no stack is required, and is thus
  42. more safe when an error has occured in the program }
  43. var
  44. e : TExeFile;
  45. staberr : boolean;
  46. stabcnt, { amount of stabs }
  47. stablen,
  48. stabofs, { absolute stab section offset in executable }
  49. stabstrlen,
  50. stabstrofs : longint; { absolute stabstr section offset in executable }
  51. dirlength : longint; { length of the dirctory part of the source file }
  52. stabs : array[0..maxstabs-1] of tstab; { buffer }
  53. funcstab, { stab with current function info }
  54. linestab, { stab with current line info }
  55. dirstab, { stab with current directory info }
  56. filestab : tstab; { stab with current file info }
  57. function OpenStabs:boolean;
  58. begin
  59. result:=false;
  60. if staberr then
  61. exit;
  62. if not OpenExeFile(e,paramstr(0)) then
  63. exit;
  64. StabsFunctionRelative := E.FunctionRelative;
  65. if FindExeSection(e,'.stab',stabofs,stablen) and
  66. FindExeSection(e,'.stabstr',stabstrofs,stabstrlen) then
  67. begin
  68. stabcnt:=stablen div sizeof(tstab);
  69. result:=true;
  70. end
  71. else
  72. begin
  73. staberr:=true;
  74. exit;
  75. end;
  76. end;
  77. procedure CloseStabs;
  78. begin
  79. CloseExeFile(e);
  80. end;
  81. procedure GetLineInfo(addr:ptruint;var func,source:string;var line:longint);
  82. var
  83. res,
  84. stabsleft,
  85. stabscnt,i : longint;
  86. found : boolean;
  87. lastfunc : tstab;
  88. begin
  89. fillchar(func,high(func)+1,0);
  90. fillchar(source,high(source)+1,0);
  91. line:=0;
  92. if staberr then
  93. exit;
  94. if not e.isopen then
  95. begin
  96. if not OpenStabs then
  97. exit;
  98. end;
  99. { correct the value to the correct address in the file }
  100. { processaddress is set in OpenStabs }
  101. addr := addr - e.processaddress;
  102. fillchar(funcstab,sizeof(tstab),0);
  103. fillchar(filestab,sizeof(tstab),0);
  104. fillchar(dirstab,sizeof(tstab),0);
  105. fillchar(linestab,sizeof(tstab),0);
  106. fillchar(lastfunc,sizeof(tstab),0);
  107. found:=false;
  108. seek(e.f,stabofs);
  109. stabsleft:=stabcnt;
  110. repeat
  111. if stabsleft>maxstabs then
  112. stabscnt:=maxstabs
  113. else
  114. stabscnt:=stabsleft;
  115. blockread(e.f,stabs,stabscnt*sizeof(tstab),res);
  116. stabscnt:=res div sizeof(tstab);
  117. for i:=0 to stabscnt-1 do
  118. begin
  119. case stabs[i].ntype of
  120. N_BssLine,
  121. N_DataLine,
  122. N_TextLine :
  123. begin
  124. if (stabs[i].ntype=N_TextLine) and StabsFunctionRelative then
  125. inc(stabs[i].nvalue,lastfunc.nvalue);
  126. if (stabs[i].nvalue<=addr) and
  127. (stabs[i].nvalue>linestab.nvalue) then
  128. begin
  129. { if it's equal we can stop and take the last info }
  130. if stabs[i].nvalue=addr then
  131. found:=true
  132. else
  133. linestab:=stabs[i];
  134. end;
  135. end;
  136. N_Function :
  137. begin
  138. lastfunc:=stabs[i];
  139. if (stabs[i].nvalue<=addr) and
  140. (stabs[i].nvalue>funcstab.nvalue) then
  141. begin
  142. funcstab:=stabs[i];
  143. fillchar(linestab,sizeof(tstab),0);
  144. end;
  145. end;
  146. N_SourceFile,
  147. N_IncludeFile :
  148. begin
  149. if (stabs[i].nvalue<=addr) and
  150. (stabs[i].nvalue>=filestab.nvalue) then
  151. begin
  152. { if same value and type then the first one
  153. contained the directory PM }
  154. if (stabs[i].nvalue=filestab.nvalue) and
  155. (stabs[i].ntype=filestab.ntype) then
  156. dirstab:=filestab
  157. else
  158. fillchar(dirstab,sizeof(tstab),0);
  159. filestab:=stabs[i];
  160. fillchar(linestab,sizeof(tstab),0);
  161. { if new file then func is not valid anymore PM }
  162. if stabs[i].ntype=N_SourceFile then
  163. begin
  164. fillchar(funcstab,sizeof(tstab),0);
  165. fillchar(lastfunc,sizeof(tstab),0);
  166. end;
  167. end;
  168. end;
  169. end;
  170. end;
  171. dec(stabsleft,stabscnt);
  172. until found or (stabsleft=0);
  173. { get the line,source,function info }
  174. line:=linestab.ndesc;
  175. if dirstab.ntype<>0 then
  176. begin
  177. seek(e.f,stabstrofs+dirstab.strpos);
  178. blockread(e.f,source[1],high(source)-1,res);
  179. dirlength:=strlen(@source[1]);
  180. source[0]:=chr(dirlength);
  181. end
  182. else
  183. dirlength:=0;
  184. if filestab.ntype<>0 then
  185. begin
  186. seek(e.f,stabstrofs+filestab.strpos);
  187. blockread(e.f,source[dirlength+1],high(source)-(dirlength+1),res);
  188. source[0]:=chr(strlen(@source[1]));
  189. end;
  190. if funcstab.ntype<>0 then
  191. begin
  192. seek(e.f,stabstrofs+funcstab.strpos);
  193. blockread(e.f,func[1],high(func)-1,res);
  194. func[0]:=chr(strlen(@func[1]));
  195. i:=pos(':',func);
  196. if i>0 then
  197. Delete(func,i,255);
  198. end;
  199. end;
  200. function StabBackTraceStr(addr:Pointer):shortstring;
  201. var
  202. func,
  203. source : string;
  204. hs : string[32];
  205. line : longint;
  206. Store : TBackTraceStrFunc;
  207. begin
  208. { reset to prevent infinite recursion if problems inside the code PM }
  209. Store:=BackTraceStrFunc;
  210. BackTraceStrFunc:=@SysBackTraceStr;
  211. GetLineInfo(ptruint(addr),func,source,line);
  212. { create string }
  213. {$ifdef netware}
  214. { we need addr relative to code start on netware }
  215. dec(addr,ptruint(system.NWGetCodeStart));
  216. StabBackTraceStr:=' CodeStart + $'+HexStr(ptruint(addr),sizeof(ptruint)*2);
  217. {$else}
  218. StabBackTraceStr:=' $'+HexStr(ptruint(addr),sizeof(ptruint)*2);
  219. {$endif}
  220. if func<>'' then
  221. StabBackTraceStr:=StabBackTraceStr+' '+func;
  222. if source<>'' then
  223. begin
  224. if func<>'' then
  225. StabBackTraceStr:=StabBackTraceStr+', ';
  226. if line<>0 then
  227. begin
  228. str(line,hs);
  229. StabBackTraceStr:=StabBackTraceStr+' line '+hs;
  230. end;
  231. StabBackTraceStr:=StabBackTraceStr+' of '+source;
  232. end;
  233. if e.IsOpen then
  234. BackTraceStrFunc:=Store;
  235. end;
  236. initialization
  237. BackTraceStrFunc:=@StabBackTraceStr;
  238. finalization
  239. if e.isopen then
  240. CloseStabs;
  241. end.