lineinfo.pp 8.2 KB

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