lineinfo.pp 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  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. {
  12. This unit should not be compiled in objfpc mode, since this would make it
  13. dependent on objpas unit.
  14. }
  15. unit lineinfo;
  16. interface
  17. {$S-}
  18. {$Q-}
  19. function GetLineInfo(addr:ptruint;var func,source:string;var line:longint) : boolean;
  20. implementation
  21. uses
  22. exeinfo,strings;
  23. const
  24. N_Function = $24;
  25. N_TextLine = $44;
  26. N_DataLine = $46;
  27. N_BssLine = $48;
  28. N_SourceFile = $64;
  29. N_IncludeFile = $84;
  30. maxstabs = 40; { size of the stabs buffer }
  31. var
  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;
  35. type
  36. pstab=^tstab;
  37. tstab=packed record
  38. strpos : longint;
  39. ntype : byte;
  40. nother : byte;
  41. ndesc : word;
  42. nvalue : dword;
  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. e : TExeFile;
  48. staberr : boolean;
  49. stabcnt, { amount of stabs }
  50. stablen,
  51. stabofs, { absolute stab section offset in executable }
  52. stabstrlen,
  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. filename,
  61. dbgfn : string;
  62. function OpenStabs(addr : pointer) : boolean;
  63. var
  64. baseaddr : pointer;
  65. begin
  66. OpenStabs:=false;
  67. if staberr then
  68. exit;
  69. GetModuleByAddr(addr,baseaddr,filename);
  70. {$ifdef DEBUG_LINEINFO}
  71. writeln(stderr,filename,' Baseaddr: ',hexstr(ptruint(baseaddr),sizeof(baseaddr)*2));
  72. {$endif DEBUG_LINEINFO}
  73. if not OpenExeFile(e,filename) then
  74. exit;
  75. if ReadDebugLink(e,dbgfn) then
  76. begin
  77. CloseExeFile(e);
  78. if not OpenExeFile(e,dbgfn) then
  79. exit;
  80. end;
  81. e.processaddress:=ptruint(baseaddr)-e.processaddress;
  82. StabsFunctionRelative := E.FunctionRelative;
  83. if FindExeSection(e,'.stab',stabofs,stablen) and
  84. FindExeSection(e,'.stabstr',stabstrofs,stabstrlen) then
  85. begin
  86. stabcnt:=stablen div sizeof(tstab);
  87. OpenStabs:=true;
  88. end
  89. else
  90. begin
  91. staberr:=true;
  92. exit;
  93. end;
  94. end;
  95. procedure CloseStabs;
  96. begin
  97. CloseExeFile(e);
  98. end;
  99. function GetLineInfo(addr:ptruint;var func,source:string;var line:longint) : boolean;
  100. var
  101. res,
  102. stabsleft,
  103. stabscnt,i : longint;
  104. found : boolean;
  105. lastfunc : tstab;
  106. begin
  107. GetLineInfo:=false;
  108. {$ifdef DEBUG_LINEINFO}
  109. writeln(stderr,'GetLineInfo called');
  110. {$endif DEBUG_LINEINFO}
  111. fillchar(func,high(func)+1,0);
  112. fillchar(source,high(source)+1,0);
  113. line:=0;
  114. if staberr then
  115. exit;
  116. if not e.isopen then
  117. begin
  118. if not OpenStabs(pointer(addr)) then
  119. exit;
  120. end;
  121. { correct the value to the correct address in the file }
  122. { processaddress is set in OpenStabs }
  123. addr := dword(addr - e.processaddress);
  124. {$ifdef DEBUG_LINEINFO}
  125. writeln(stderr,'Addr: ',hexstr(addr,sizeof(addr)*2));
  126. {$endif DEBUG_LINEINFO}
  127. fillchar(funcstab,sizeof(tstab),0);
  128. fillchar(filestab,sizeof(tstab),0);
  129. fillchar(dirstab,sizeof(tstab),0);
  130. fillchar(linestab,sizeof(tstab),0);
  131. fillchar(lastfunc,sizeof(tstab),0);
  132. found:=false;
  133. seek(e.f,stabofs);
  134. stabsleft:=stabcnt;
  135. repeat
  136. if stabsleft>maxstabs then
  137. stabscnt:=maxstabs
  138. else
  139. stabscnt:=stabsleft;
  140. blockread(e.f,stabs,stabscnt*sizeof(tstab),res);
  141. stabscnt:=res div sizeof(tstab);
  142. for i:=0 to stabscnt-1 do
  143. begin
  144. case stabs[i].ntype of
  145. N_BssLine,
  146. N_DataLine,
  147. N_TextLine :
  148. begin
  149. if (stabs[i].ntype=N_TextLine) and StabsFunctionRelative then
  150. inc(stabs[i].nvalue,lastfunc.nvalue);
  151. if (stabs[i].nvalue<=addr) and
  152. (stabs[i].nvalue>linestab.nvalue) then
  153. begin
  154. { if it's equal we can stop and take the last info }
  155. if stabs[i].nvalue=addr then
  156. found:=true
  157. else
  158. linestab:=stabs[i];
  159. end;
  160. end;
  161. N_Function :
  162. begin
  163. lastfunc:=stabs[i];
  164. if (stabs[i].nvalue<=addr) and
  165. (stabs[i].nvalue>funcstab.nvalue) then
  166. begin
  167. funcstab:=stabs[i];
  168. fillchar(linestab,sizeof(tstab),0);
  169. end;
  170. end;
  171. N_SourceFile,
  172. N_IncludeFile :
  173. begin
  174. if (stabs[i].nvalue<=addr) and
  175. (stabs[i].nvalue>=filestab.nvalue) then
  176. begin
  177. { if same value and type then the first one
  178. contained the directory PM }
  179. if (stabs[i].nvalue=filestab.nvalue) and
  180. (stabs[i].ntype=filestab.ntype) then
  181. dirstab:=filestab
  182. else
  183. fillchar(dirstab,sizeof(tstab),0);
  184. filestab:=stabs[i];
  185. fillchar(linestab,sizeof(tstab),0);
  186. { if new file then func is not valid anymore PM }
  187. if stabs[i].ntype=N_SourceFile then
  188. begin
  189. fillchar(funcstab,sizeof(tstab),0);
  190. fillchar(lastfunc,sizeof(tstab),0);
  191. end;
  192. end;
  193. end;
  194. end;
  195. end;
  196. dec(stabsleft,stabscnt);
  197. until found or (stabsleft=0);
  198. { get the line,source,function info }
  199. line:=linestab.ndesc;
  200. if dirstab.ntype<>0 then
  201. begin
  202. seek(e.f,stabstrofs+dirstab.strpos);
  203. blockread(e.f,source[1],high(source)-1,res);
  204. dirlength:=strlen(@source[1]);
  205. source[0]:=chr(dirlength);
  206. end
  207. else
  208. dirlength:=0;
  209. if filestab.ntype<>0 then
  210. begin
  211. seek(e.f,stabstrofs+filestab.strpos);
  212. blockread(e.f,source[dirlength+1],high(source)-(dirlength+1),res);
  213. source[0]:=chr(strlen(@source[1]));
  214. end;
  215. if funcstab.ntype<>0 then
  216. begin
  217. seek(e.f,stabstrofs+funcstab.strpos);
  218. blockread(e.f,func[1],high(func)-1,res);
  219. func[0]:=chr(strlen(@func[1]));
  220. i:=pos(':',func);
  221. if i>0 then
  222. Delete(func,i,255);
  223. end;
  224. if e.isopen then
  225. CloseStabs;
  226. GetLineInfo:=true;
  227. end;
  228. function StabBackTraceStr(addr:Pointer):shortstring;
  229. var
  230. func,
  231. source : string;
  232. hs : string[32];
  233. line : longint;
  234. Store : TBackTraceStrFunc;
  235. Success : boolean;
  236. begin
  237. {$ifdef DEBUG_LINEINFO}
  238. writeln(stderr,'StabBackTraceStr called');
  239. {$endif DEBUG_LINEINFO}
  240. { reset to prevent infinite recursion if problems inside the code PM }
  241. Success:=false;
  242. Store:=BackTraceStrFunc;
  243. BackTraceStrFunc:=@SysBackTraceStr;
  244. Success:=GetLineInfo(ptruint(addr),func,source,line);
  245. { create string }
  246. {$ifdef netware}
  247. { we need addr relative to code start on netware }
  248. dec(addr,ptruint(system.NWGetCodeStart));
  249. StabBackTraceStr:=' CodeStart + $'+HexStr(ptruint(addr),sizeof(ptruint)*2);
  250. {$else}
  251. StabBackTraceStr:=' $'+HexStr(ptruint(addr),sizeof(ptruint)*2);
  252. {$endif}
  253. if func<>'' then
  254. StabBackTraceStr:=StabBackTraceStr+' '+func;
  255. if source<>'' then
  256. begin
  257. if func<>'' then
  258. StabBackTraceStr:=StabBackTraceStr+', ';
  259. if line<>0 then
  260. begin
  261. str(line,hs);
  262. StabBackTraceStr:=StabBackTraceStr+' line '+hs;
  263. end;
  264. StabBackTraceStr:=StabBackTraceStr+' of '+source;
  265. end;
  266. if Success then
  267. BackTraceStrFunc:=Store;
  268. end;
  269. initialization
  270. BackTraceStrFunc:=@StabBackTraceStr;
  271. finalization
  272. if e.isopen then
  273. CloseStabs;
  274. end.