fpc/rtl/inc/lineinfo.pp
peter 3befb1f832 * refactor executable loading into exeinfo unit so
it can be reused also for lnfodwrf

git-svn-id: trunk@9780 -
2008-01-17 23:47:42 +00:00

268 lines
7.0 KiB
ObjectPascal

{
This file is part of the Free Pascal run time library.
Copyright (c) 2000 by Peter Vreman
Stabs Line Info Retriever
See the file COPYING.FPC, included in this distribution,
for details about the copyright.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
**********************************************************************}
unit lineinfo;
interface
{$mode objfpc}
{$S-}
{$Q-}
procedure GetLineInfo(addr:ptruint;var func,source:string;var line:longint);
implementation
uses
exeinfo,strings;
const
N_Function = $24;
N_TextLine = $44;
N_DataLine = $46;
N_BssLine = $48;
N_SourceFile = $64;
N_IncludeFile = $84;
maxstabs = 40; { size of the stabs buffer }
{ GDB after 4.18 uses offset to function begin
in text section but OS/2 version still uses 4.16 PM }
StabsFunctionRelative : boolean = true;
type
pstab=^tstab;
tstab=packed record
strpos : longint;
ntype : byte;
nother : byte;
ndesc : word;
nvalue : dword;
end;
{ We use static variable so almost no stack is required, and is thus
more safe when an error has occured in the program }
var
e : TExeFile;
staberr : boolean;
stabcnt, { amount of stabs }
stablen,
stabofs, { absolute stab section offset in executable }
stabstrlen,
stabstrofs : longint; { absolute stabstr section offset in executable }
dirlength : longint; { length of the dirctory part of the source file }
stabs : array[0..maxstabs-1] of tstab; { buffer }
funcstab, { stab with current function info }
linestab, { stab with current line info }
dirstab, { stab with current directory info }
filestab : tstab; { stab with current file info }
function OpenStabs:boolean;
begin
result:=false;
if staberr then
exit;
if not OpenExeFile(e,paramstr(0)) then
exit;
if FindExeSection(e,'.stab',stabofs,stablen) and
FindExeSection(e,'.stabstr',stabstrofs,stabstrlen) then
begin
stabcnt:=stablen div sizeof(tstab);
result:=true;
end
else
begin
staberr:=true;
exit;
end;
end;
procedure CloseStabs;
begin
CloseExeFile(e);
end;
procedure GetLineInfo(addr:ptruint;var func,source:string;var line:longint);
var
res,
stabsleft,
stabscnt,i : longint;
found : boolean;
lastfunc : tstab;
begin
fillchar(func,high(func)+1,0);
fillchar(source,high(source)+1,0);
line:=0;
if staberr then
exit;
if not e.isopen then
begin
if not OpenStabs then
exit;
end;
{ correct the value to the correct address in the file }
{ processaddress is set in OpenStabs }
addr := addr - e.processaddress;
fillchar(funcstab,sizeof(tstab),0);
fillchar(filestab,sizeof(tstab),0);
fillchar(dirstab,sizeof(tstab),0);
fillchar(linestab,sizeof(tstab),0);
fillchar(lastfunc,sizeof(tstab),0);
found:=false;
seek(e.f,stabofs);
stabsleft:=stabcnt;
repeat
if stabsleft>maxstabs then
stabscnt:=maxstabs
else
stabscnt:=stabsleft;
blockread(e.f,stabs,stabscnt*sizeof(tstab),res);
stabscnt:=res div sizeof(tstab);
for i:=0 to stabscnt-1 do
begin
case stabs[i].ntype of
N_BssLine,
N_DataLine,
N_TextLine :
begin
if (stabs[i].ntype=N_TextLine) and StabsFunctionRelative then
inc(stabs[i].nvalue,lastfunc.nvalue);
if (stabs[i].nvalue<=addr) and
(stabs[i].nvalue>linestab.nvalue) then
begin
{ if it's equal we can stop and take the last info }
if stabs[i].nvalue=addr then
found:=true
else
linestab:=stabs[i];
end;
end;
N_Function :
begin
lastfunc:=stabs[i];
if (stabs[i].nvalue<=addr) and
(stabs[i].nvalue>funcstab.nvalue) then
begin
funcstab:=stabs[i];
fillchar(linestab,sizeof(tstab),0);
end;
end;
N_SourceFile,
N_IncludeFile :
begin
if (stabs[i].nvalue<=addr) and
(stabs[i].nvalue>=filestab.nvalue) then
begin
{ if same value and type then the first one
contained the directory PM }
if (stabs[i].nvalue=filestab.nvalue) and
(stabs[i].ntype=filestab.ntype) then
dirstab:=filestab
else
fillchar(dirstab,sizeof(tstab),0);
filestab:=stabs[i];
fillchar(linestab,sizeof(tstab),0);
{ if new file then func is not valid anymore PM }
if stabs[i].ntype=N_SourceFile then
begin
fillchar(funcstab,sizeof(tstab),0);
fillchar(lastfunc,sizeof(tstab),0);
end;
end;
end;
end;
end;
dec(stabsleft,stabscnt);
until found or (stabsleft=0);
{ get the line,source,function info }
line:=linestab.ndesc;
if dirstab.ntype<>0 then
begin
seek(e.f,stabstrofs+dirstab.strpos);
blockread(e.f,source[1],high(source)-1,res);
dirlength:=strlen(@source[1]);
source[0]:=chr(dirlength);
end
else
dirlength:=0;
if filestab.ntype<>0 then
begin
seek(e.f,stabstrofs+filestab.strpos);
blockread(e.f,source[dirlength+1],high(source)-(dirlength+1),res);
source[0]:=chr(strlen(@source[1]));
end;
if funcstab.ntype<>0 then
begin
seek(e.f,stabstrofs+funcstab.strpos);
blockread(e.f,func[1],high(func)-1,res);
func[0]:=chr(strlen(@func[1]));
i:=pos(':',func);
if i>0 then
Delete(func,i,255);
end;
end;
function StabBackTraceStr(addr:Pointer):shortstring;
var
func,
source : string;
hs : string[32];
line : longint;
Store : TBackTraceStrFunc;
begin
{ reset to prevent infinite recursion if problems inside the code PM }
Store:=BackTraceStrFunc;
BackTraceStrFunc:=@SysBackTraceStr;
GetLineInfo(ptruint(addr),func,source,line);
{ create string }
{$ifdef netware}
{ we need addr relative to code start on netware }
dec(addr,ptruint(system.NWGetCodeStart));
StabBackTraceStr:=' CodeStart + $'+HexStr(ptruint(addr),sizeof(ptruint)*2);
{$else}
StabBackTraceStr:=' $'+HexStr(ptruint(addr),sizeof(ptruint)*2);
{$endif}
if func<>'' then
StabBackTraceStr:=StabBackTraceStr+' '+func;
if source<>'' then
begin
if func<>'' then
StabBackTraceStr:=StabBackTraceStr+', ';
if line<>0 then
begin
str(line,hs);
StabBackTraceStr:=StabBackTraceStr+' line '+hs;
end;
StabBackTraceStr:=StabBackTraceStr+' of '+source;
end;
if e.IsOpen then
BackTraceStrFunc:=Store;
end;
initialization
BackTraceStrFunc:=@StabBackTraceStr;
finalization
if e.isopen then
CloseStabs;
end.