lazarus/components/leakview/dbginforeader.pas
2021-06-10 22:11:03 +00:00

923 lines
26 KiB
ObjectPascal

{
This file is a modified copy of the FreePascal units lnfodwrf and lineinfo
}
unit DbgInfoReader;
{$mode objfpc}{$H-}{$S-}
interface
function OpenSymbolFile(AFileName: string): boolean;
procedure CloseSymbolFile;
function GetLineInfo(addr:ptruint; out func,source:string; out line:longint) : boolean;
implementation
uses
exeinfo, strings;
var
{ the input file to read DWARF/STABS debug info from, i.e. paramstr(0) }
e : TExeFile;
filename, dbgfn : string;
//baseaddr : pointer;
HasStabs, HasDwarf: Boolean;
{%region ********************* lnfodwrf ************************************}
{ Current issues:
- ignores DW_LNS_SET_FILE
}
{$MACRO ON}
//{$DEFINE DEBUG_DWARF_PARSER}
{$ifdef DEBUG_DWARF_PARSER}
{$define DEBUG_WRITELN := WriteLn}
{$define DEBUG_COMMENT := }
{$else}
{$define DEBUG_WRITELN := //}
{$define DEBUG_COMMENT := //}
{$endif}
{ some type definitions }
type
Bool8 = ByteBool;
const
EBUF_SIZE = 100;
{$WARNING This code is not thread-safe, and needs improvement}
var
EBuf: Array [0..EBUF_SIZE-1] of Byte;
EBufCnt, EBufPos: Integer;
{ the offset and size of the DWARF debug_line section in the file }
DwarfOffset : longint;
DwarfSize : longint;
{ DWARF 2 default opcodes}
const
{ Extended opcodes }
DW_LNE_END_SEQUENCE = 1;
DW_LNE_SET_ADDRESS = 2;
DW_LNE_DEFINE_FILE = 3;
{ Standard opcodes }
DW_LNS_COPY = 1;
DW_LNS_ADVANCE_PC = 2;
DW_LNS_ADVANCE_LINE = 3;
DW_LNS_SET_FILE = 4;
DW_LNS_SET_COLUMN = 5;
DW_LNS_NEGATE_STMT = 6;
DW_LNS_SET_BASIC_BLOCK = 7;
DW_LNS_CONST_ADD_PC = 8;
DW_LNS_FIXED_ADVANCE_PC = 9;
DW_LNS_SET_PROLOGUE_END = 10;
DW_LNS_SET_EPILOGUE_BEGIN = 11;
DW_LNS_SET_ISA = 12;
type
{ state record for the line info state machine }
TMachineState = record
address : QWord;
file_id : DWord;
line : QWord;
column : DWord;
is_stmt : Boolean;
basic_block : Boolean;
end_sequence : Boolean;
prolouge_end : Boolean;
epilouge_begin : Boolean;
isa : DWord;
append_row : Boolean;
end;
{ DWARF line number program header preceding the line number program, 64 bit version }
TLineNumberProgramHeader64 = packed record
magic : DWord;
unit_length : QWord;
version : Word;
length : QWord;
minimum_instruction_length : Byte;
default_is_stmt : Bool8;
line_base : ShortInt;
line_range : Byte;
opcode_base : Byte;
end;
{ DWARF line number program header preceding the line number program, 32 bit version }
TLineNumberProgramHeader32 = packed record
unit_length : DWord;
version : Word;
length : DWord;
minimum_instruction_length : Byte;
default_is_stmt : Bool8;
line_base : ShortInt;
line_range : Byte;
opcode_base : Byte;
end;
{---------------------------------------------------------------------------
I/O utility functions
---------------------------------------------------------------------------}
var
base, limit : int64;
index : int64;
function Init(aBase, aLimit : Int64) : Boolean;
begin
base := aBase;
limit := aLimit;
Init := (aBase + limit) <= e.size;
seek(e.f, base);
EBufCnt := 0;
EBufPos := 0;
index := 0;
end;
function Init(aBase : Int64) : Boolean;
begin
Init := Init(aBase, limit - (aBase - base));
end;
function Pos() : Int64;
begin
Pos := index;
end;
procedure Seek(const newIndex : Int64);
begin
index := newIndex;
system.seek(e.f, base + index);
EBufCnt := 0;
EBufPos := 0;
end;
{ Returns the next Byte from the input stream, or -1 if there has been
an error }
function ReadNext() : Longint; inline;
var
bytesread : integer;
begin
ReadNext := -1;
if EBufPos >= EBufCnt then begin
EBufPos := 0;
EBufCnt := EBUF_SIZE;
if EBufCnt > limit - index then
EBufCnt := limit - index;
blockread(e.f, EBuf, EBufCnt, bytesread{%H-});
EBufCnt := bytesread;
end;
if EBufPos < EBufCnt then begin
ReadNext := EBuf[EBufPos];
inc(EBufPos);
inc(index);
end
else
ReadNext := -1;
end;
{ Reads the next size bytes into dest. Returns true if successful,
false otherwise. Note that dest may be partially overwritten after
returning false. }
function ReadNext(var dest; size : SizeInt) : Boolean; //inline;
var
bytesread, totalread : SizeInt;
r: Boolean;
d: PByte;
begin
d := @dest;
totalread := 0;
r := True;
while (totalread < size) and r do begin;
if EBufPos >= EBufCnt then begin
EBufPos := 0;
EBufCnt := EBUF_SIZE;
if EBufCnt > limit - index then
EBufCnt := limit - index;
blockread(e.f, EBuf, EBufCnt, bytesread{%H-});
EBufCnt := bytesread;
if bytesread <= 0 then
r := False;
end;
if EBufPos < EBufCnt then begin
bytesread := EBufCnt - EBufPos;
if bytesread > size - totalread then bytesread := size - totalread;
System.Move(EBuf[EBufPos], d[totalread], bytesread);
inc(EBufPos, bytesread);
inc(index, bytesread);
inc(totalread, bytesread);
end;
end;
ReadNext := r;
end;
{ Reads an unsigned LEB encoded number from the input stream }
function ReadULEB128() : QWord;
var
shift : Byte;
data : PtrInt;
val : QWord;
begin
shift := 0;
ReadULEB128 := 0;
data := ReadNext();
while (data <> -1) do begin
val := data and $7f;
ReadULEB128 := ReadULEB128 or (val shl shift);
inc(shift, 7);
if ((data and $80) = 0) then
break;
data := ReadNext();
end;
end;
{ Reads a signed LEB encoded number from the input stream }
function ReadLEB128() : Int64;
var
shift : Byte;
data : PtrInt;
val : Int64;
begin
shift := 0;
ReadLEB128 := 0;
data := ReadNext();
while (data <> -1) do begin
val := data and $7f;
ReadLEB128 := ReadLEB128 or (val shl shift);
inc(shift, 7);
if ((data and $80) = 0) then
break;
data := ReadNext();
end;
{ extend sign. Note that we can not use shl/shr since the latter does not
translate to arithmetic shifting for signed types }
ReadLEB128 := (not ((ReadLEB128 and (1 shl (shift-1)))-1)) or ReadLEB128;
end;
{ Reads an address from the current input stream }
function ReadAddress() : PtrUInt;
begin
ReadNext(ReadAddress{%H-}, sizeof(ReadAddress));
end;
{ Reads a zero-terminated string from the current input stream. If the
string is larger than 255 chars (maximum allowed number of elements in
a ShortString, excess characters will be chopped off. }
function ReadString() : ShortString;
var
temp : PtrInt;
i : PtrUInt;
begin
i := 1;
temp := ReadNext();
while (temp > 0) do begin
ReadString[i] := char(temp);
if (i = 255) then begin
{ skip remaining characters }
repeat
temp := ReadNext();
until (temp <= 0);
break;
end;
inc(i);
temp := ReadNext();
end;
{ unexpected end of file occurred? }
if (temp = -1) then
ReadString := ''
else
Byte(ReadString[0]) := i-1;
end;
{ Reads an unsigned Half from the current input stream }
function ReadUHalf() : Word;
begin
ReadNext(ReadUHalf{%H-}, sizeof(ReadUHalf));
end;
{---------------------------------------------------------------------------
Generic Dwarf lineinfo reader
The line info reader is based on the information contained in
DWARF Debugging Information Format Version 3
Chapter 6.2 "Line Number Information"
from the
DWARF Debugging Information Format Workgroup.
For more information on this document see also
http://dwarf.freestandards.org/
---------------------------------------------------------------------------}
{ initializes the line info state to the default values }
procedure InitStateRegisters(var state : TMachineState; const aIs_Stmt : Bool8);
begin
with state do begin
address := 0;
file_id := 1;
line := 1;
column := 0;
is_stmt := aIs_Stmt;
basic_block := false;
end_sequence := false;
prolouge_end := false;
epilouge_begin := false;
isa := 0;
append_row := false;
end;
end;
{ Skips all line info directory entries }
procedure SkipDirectories();
var s : ShortString;
begin
while (true) do begin
s := ReadString();
if (s = '') then break;
DEBUG_WRITELN('Skipping directory : ', s);
end;
end;
{ Skips an LEB128 }
procedure SkipLEB128();
{$ifdef DEBUG_DWARF_PARSER}
var temp : QWord;
{$endif}
begin
{$ifdef DEBUG_DWARF_PARSER}temp := {$endif}ReadLEB128();
DEBUG_WRITELN('Skipping LEB128 : ', temp);
end;
{ Skips the filename section from the current file stream }
procedure SkipFilenames();
var s : ShortString;
begin
while (true) do begin
s := ReadString();
if (s = '') then break;
DEBUG_WRITELN('Skipping filename : ', s);
SkipLEB128(); { skip the directory index for the file }
SkipLEB128(); { skip last modification time for file }
SkipLEB128(); { skip length of file }
end;
end;
function CalculateAddressIncrement(opcode : Byte; const header : TLineNumberProgramHeader64) : Int64;
begin
CalculateAddressIncrement := (Int64(opcode) - header.opcode_base) div header.line_range * header.minimum_instruction_length;
end;
function GetFullFilename(const filenameStart, directoryStart : Int64; const file_id : DWord) : ShortString;
var
i : DWord;
filename, directory : ShortString;
dirindex : Int64;
begin
filename := '';
directory := '';
i := 1;
Seek(filenameStart);
while (i <= file_id) do begin
filename := ReadString();
DEBUG_WRITELN('Found "', filename, '"');
if (filename = '') then break;
dirindex := ReadLEB128(); { read the directory index for the file }
SkipLEB128(); { skip last modification time for file }
SkipLEB128(); { skip length of file }
inc(i);
end;
{ if we could not find the file index, exit }
if (filename = '') then begin
GetFullFilename := '(Unknown file)';
exit;
end;
Seek(directoryStart);
i := 1;
while (i <= dirindex) do begin
directory := ReadString();
if (directory = '') then break;
inc(i);
end;
if (directory<>'') and (directory[length(directory)]<>'/') then
directory:=directory+'/';
GetFullFilename := directory + filename;
end;
function ParseCompilationUnit(const addr : PtrUInt; const file_offset : QWord;
var source : String; var line : longint; var found : Boolean) : QWord;
var
state : TMachineState;
{ we need both headers on the stack, although we only use the 64 bit one internally }
header64 : TLineNumberProgramHeader64;
header32 : TLineNumberProgramHeader32;
adjusted_opcode : Int64;
opcode : PtrInt;
extended_opcode : Byte;
extended_opcode_length : PtrInt;
i, addrIncrement, lineIncrement : PtrInt;
{$ifdef DEBUG_DWARF_PARSER}
s : ShortString;
{$endif}
numoptable : array[1..255] of Byte;
{ the offset into the file where the include directories are stored for this compilation unit }
include_directories : QWord;
{ the offset into the file where the file names are stored for this compilation unit }
file_names : Int64;
temp_length : DWord;
unit_length : QWord;
header_length : SizeInt;
first_row : Boolean;
prev_line : QWord;
prev_file : DWord;
begin
prev_line := 0;
prev_file := 0;
first_row := true;
found := false;
ReadNext(temp_length{%H-}, sizeof(temp_length));
if (temp_length <> $ffffffff) then begin
unit_length := temp_length + sizeof(temp_length)
end else begin
ReadNext(unit_length, sizeof(unit_length));
inc(unit_length, 12);
end;
ParseCompilationUnit := file_offset + unit_length;
Init(file_offset, unit_length);
DEBUG_WRITELN('Unit length: ', unit_length);
if (temp_length <> $ffffffff) then begin
DEBUG_WRITELN('32 bit DWARF detected');
ReadNext(header32{%H-}, sizeof(header32));
header64.magic := $ffffffff;
header64.unit_length := header32.unit_length;
header64.version := header32.version;
header64.length := header32.length;
header64.minimum_instruction_length := header32.minimum_instruction_length;
header64.default_is_stmt := header32.default_is_stmt;
header64.line_base := header32.line_base;
header64.line_range := header32.line_range;
header64.opcode_base := header32.opcode_base;
header_length :=
sizeof(header32.length) + sizeof(header32.version) +
sizeof(header32.unit_length);
end else begin
DEBUG_WRITELN('64 bit DWARF detected');
ReadNext(header64, sizeof(header64));
header_length :=
sizeof(header64.magic) + sizeof(header64.version) +
sizeof(header64.length) + sizeof(header64.unit_length);
end;
inc(header_length, header64.length);
fillchar(numoptable{%H-}, sizeof(numoptable), #0);
ReadNext(numoptable, header64.opcode_base-1);
DEBUG_WRITELN('Opcode parameter count table');
for i := 1 to header64.opcode_base-1 do begin
DEBUG_WRITELN('Opcode[', i, '] - ', numoptable[i], ' parameters');
end;
DEBUG_WRITELN('Reading directories...');
include_directories := Pos();
SkipDirectories();
DEBUG_WRITELN('Reading filenames...');
file_names := Pos();
SkipFilenames();
Seek(header_length);
with header64 do begin
InitStateRegisters(state{%H-}, default_is_stmt);
end;
opcode := ReadNext();
while (opcode <> -1) and (not found) do begin
DEBUG_WRITELN('Next opcode: ');
case (opcode) of
{ extended opcode }
0 : begin
extended_opcode_length := ReadULEB128();
extended_opcode := ReadNext();
case (extended_opcode) of
DW_LNE_END_SEQUENCE : begin
state.end_sequence := true;
state.append_row := true;
DEBUG_WRITELN('DW_LNE_END_SEQUENCE');
end;
DW_LNE_SET_ADDRESS : begin
state.address := ReadAddress();
DEBUG_WRITELN('DW_LNE_SET_ADDRESS (', hexstr(state.address, sizeof(state.address)*2), ')');
end;
DW_LNE_DEFINE_FILE : begin
{$ifdef DEBUG_DWARF_PARSER}s := {$endif}ReadString();
SkipLEB128();
SkipLEB128();
SkipLEB128();
DEBUG_WRITELN('DW_LNE_DEFINE_FILE (', s, ')');
end;
else begin
DEBUG_WRITELN('Unknown extended opcode (opcode ', extended_opcode, ' length ', extended_opcode_length, ')');
for i := 0 to extended_opcode_length-2 do
ReadNext();
end;
end;
end;
DW_LNS_COPY : begin
state.basic_block := false;
state.prolouge_end := false;
state.epilouge_begin := false;
state.append_row := true;
DEBUG_WRITELN('DW_LNS_COPY');
end;
DW_LNS_ADVANCE_PC : begin
inc(state.address, ReadULEB128() * header64.minimum_instruction_length);
DEBUG_WRITELN('DW_LNS_ADVANCE_PC (', hexstr(state.address, sizeof(state.address)*2), ')');
end;
DW_LNS_ADVANCE_LINE : begin
// inc(state.line, ReadLEB128()); negative values are allowed
// but those may generate a range check error
state.line := state.line + ReadLEB128();
DEBUG_WRITELN('DW_LNS_ADVANCE_LINE (', state.line, ')');
end;
DW_LNS_SET_FILE : begin
state.file_id := ReadULEB128();
DEBUG_WRITELN('DW_LNS_SET_FILE (', state.file_id, ')');
end;
DW_LNS_SET_COLUMN : begin
state.column := ReadULEB128();
DEBUG_WRITELN('DW_LNS_SET_COLUMN (', state.column, ')');
end;
DW_LNS_NEGATE_STMT : begin
state.is_stmt := not state.is_stmt;
DEBUG_WRITELN('DW_LNS_NEGATE_STMT (', state.is_stmt, ')');
end;
DW_LNS_SET_BASIC_BLOCK : begin
state.basic_block := true;
DEBUG_WRITELN('DW_LNS_SET_BASIC_BLOCK');
end;
DW_LNS_CONST_ADD_PC : begin
inc(state.address, CalculateAddressIncrement(255, header64));
DEBUG_WRITELN('DW_LNS_CONST_ADD_PC (', hexstr(state.address, sizeof(state.address)*2), ')');
end;
DW_LNS_FIXED_ADVANCE_PC : begin
inc(state.address, ReadUHalf());
DEBUG_WRITELN('DW_LNS_FIXED_ADVANCE_PC (', hexstr(state.address, sizeof(state.address)*2), ')');
end;
DW_LNS_SET_PROLOGUE_END : begin
state.prolouge_end := true;
DEBUG_WRITELN('DW_LNS_SET_PROLOGUE_END');
end;
DW_LNS_SET_EPILOGUE_BEGIN : begin
state.epilouge_begin := true;
DEBUG_WRITELN('DW_LNS_SET_EPILOGUE_BEGIN');
end;
DW_LNS_SET_ISA : begin
state.isa := ReadULEB128();
DEBUG_WRITELN('DW_LNS_SET_ISA (', state.isa, ')');
end;
else begin { special opcode }
if (opcode < header64.opcode_base) then begin
DEBUG_WRITELN('Unknown standard opcode $', hexstr(opcode, 2), '; skipping');
for i := 1 to numoptable[opcode] do
SkipLEB128();
end else begin
adjusted_opcode := opcode - header64.opcode_base;
addrIncrement := CalculateAddressIncrement(opcode, header64);
inc(state.address, addrIncrement);
lineIncrement := header64.line_base + (adjusted_opcode mod header64.line_range);
inc(state.line, lineIncrement);
DEBUG_WRITELN('Special opcode $', hexstr(opcode, 2), ' address increment: ', addrIncrement, ' new line: ', lineIncrement);
state.basic_block := false;
state.prolouge_end := false;
state.epilouge_begin := false;
state.append_row := true;
end;
end;
end;
if (state.append_row) then begin
DEBUG_WRITELN('Current state : address = ', hexstr(state.address, sizeof(state.address) * 2),
DEBUG_COMMENT ' file_id = ', state.file_id, ' line = ', state.line, ' column = ', state.column,
DEBUG_COMMENT ' is_stmt = ', state.is_stmt, ' basic_block = ', state.basic_block,
DEBUG_COMMENT ' end_sequence = ', state.end_sequence, ' prolouge_end = ', state.prolouge_end,
DEBUG_COMMENT ' epilouge_begin = ', state.epilouge_begin, ' isa = ', state.isa);
if (first_row) then begin
if (state.address > addr) then
break;
first_row := false;
end;
{ when we have found the address we need to return the previous
line because that contains the call instruction }
if (state.address >= addr) then
found:=true
else
begin
{ save line information }
prev_file := state.file_id;
prev_line := state.line;
end;
state.append_row := false;
if (state.end_sequence) then begin
InitStateRegisters(state, header64.default_is_stmt);
first_row := true;
end;
end;
opcode := ReadNext();
end;
if (found) then begin
line := prev_line;
source := GetFullFilename(file_names, include_directories, prev_file);
end;
end;
function GetLineInfoDwarf(addr : ptruint; var func, source : string; var line : longint) : boolean;
var
current_offset : QWord;
end_offset : QWord;
found : Boolean;
begin
func := '';
source := '';
found := false;
GetLineInfoDwarf:=false;
if not e.isopen then exit;
addr := addr - e.processaddress;
current_offset := DwarfOffset;
end_offset := DwarfOffset + DwarfSize;
while (current_offset < end_offset) and (not found) do begin
Init(current_offset, end_offset - current_offset);
current_offset := ParseCompilationUnit(addr, current_offset,
source, line, found);
end;
GetLineInfoDwarf:=found;
end;
{%endregion ********************* lnfodwrf ************************************}
{%region ********************* lineinfo ************************************}
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 }
var
{ GDB after 4.18 uses offset to function begin
in text section but OS/2 version still uses 4.16 PM }
StabsFunctionRelative: boolean;
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 occurred in the program }
{$WARNING This code is not thread-safe, and needs improvement }
var
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 GetLineInfoStabs(addr:ptruint;var func,source:string;var line:longint) : boolean;
var
res,
stabsleft,
stabscnt,i : longint;
found : boolean;
lastfunc : tstab;
begin
GetLineInfoStabs:=false;
{$ifdef DEBUG_LINEINFO}
writeln(stderr,'GetLineInfo called');
{$endif DEBUG_LINEINFO}
fillchar(func,high(func)+1,0);
fillchar(source,high(source)+1,0);
line:=0;
if not e.isopen then exit;
{ correct the value to the correct address in the file }
{ processaddress is set in OpenStabs }
addr := dword(addr - e.processaddress);
{$ifdef DEBUG_LINEINFO}
writeln(stderr,'Addr: ',hexstr(addr,sizeof(addr)*2));
{$endif DEBUG_LINEINFO}
fillchar(funcstab,sizeof(tstab),0);
fillchar(filestab,sizeof(tstab),0);
fillchar(dirstab,sizeof(tstab),0);
fillchar(linestab,sizeof(tstab),0);
fillchar(lastfunc{%H-},sizeof(tstab),0);
found:=false;
system.seek(e.f,stabofs);
stabsleft:=stabcnt;
repeat
if stabsleft>maxstabs then
stabscnt:=maxstabs
else
stabscnt:=stabsleft;
blockread(e.f,stabs,stabscnt*sizeof(tstab),res{%H-});
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
system.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
system.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
system.seek(e.f,stabstrofs+funcstab.strpos);
blockread(e.f,func[1],high(func)-1,res);
func[0]:=chr(strlen(@func[1]));
i:=system.pos(':',func);
if i>0 then
Delete(func,i,255);
end;
GetLineInfoStabs:=found;
end;
{%endregion ********************* lineinfo ************************************}
function OpenSymbolFile(AFileName: string): boolean;
begin
Result := False;
HasStabs := False;
HasDwarf := False;
filename := AFileName;
if not OpenExeFile(e,filename) then
exit;
if ReadDebugLink(e,dbgfn) then
begin
CloseExeFile(e);
if not OpenExeFile(e,dbgfn) then
exit;
end;
e.processaddress:=0;
// e.processaddress:=ptruint(baseaddr)-e.processaddress;
{%region ********************* lnfodwrf ************************************}
if FindExeSection(e,'.debug_line',dwarfoffset,dwarfsize) then begin
HasDwarf := True;
Result:=true;
end;
{%endregion ********************* lnfodwrf ************************************}
{%region ********************* lineinfo ************************************}
StabsFunctionRelative := E.FunctionRelative;
if FindExeSection(e,'.stab',stabofs,stablen) and
FindExeSection(e,'.stabstr',stabstrofs,stabstrlen) then
begin
stabcnt:=stablen div sizeof(tstab);
HasStabs := True;
Result:=true;
end;
{%endregion ********************* lineinfo ************************************}
end;
procedure CloseSymbolFile;
begin
CloseExeFile(e);
end;
function GetLineInfo(addr: ptruint; out func, source: string; out line: longint): boolean;
begin
Result := False;
if HasDwarf then
Result := GetLineInfoDwarf(addr, func{%H-}, source{%H-}, line{%H-});
if (not Result) and HasStabs then
Result := GetLineInfoStabs(addr, func, source, line);
end;
end.