lazarus/components/fpdebug/fpimgreaderelf.pas

395 lines
11 KiB
ObjectPascal

{
This unit contains the types needed for reading Elf images.
This file was ported from DUBY. See svn log for details
---------------------------------------------------------------------------
***************************************************************************
* *
* This source is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This code 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. See the GNU *
* General Public License for more details. *
* *
* A copy of the GNU General Public License is available on the World *
* Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also *
* obtain it by writing to the Free Software Foundation, *
* Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1335, USA. *
* *
***************************************************************************
}
unit FpImgReaderElf;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils,
FpImgReaderBase,
fpDbgSymTable, DbgIntfBaseTypes,
FpImgReaderElfTypes, LCLProc; // these files are part of
type
TElfSection = packed record
name : AnsiString;
FileOfs : QWord;
Address : QWord;
Size : QWord;
end;
{ TElfFile }
TElfFile = class(TObject)
private
FIs64Bit: boolean;
protected
function Load32BitFile(ALoader: TDbgFileLoader): Boolean;
function Load64BitFile(ALoader: TDbgFileLoader): Boolean;
procedure AddSection(const name: AnsiString; FileOffset, Address, Size: Qword);
public
sections : array of TElfSection;
seccount : Integer;
function LoadFromFile(ALoader: TDbgFileLoader): Boolean;
function FindSection(const Name: String): Integer;
property Is64Bit: boolean read FIs64Bit;
end;
{ TElfDbgSource }
TElfDbgSource = class(TDbgImageReader) // executable parser
private
FSections: TStringList;
FFileLoader : TDbgFileLoader;
fOwnSource : Boolean;
fElfFile : TElfFile;
protected
function GetSection(const AName: String): PDbgImageSection; override;
public
class function isValid(ASource: TDbgFileLoader): Boolean; override;
class function UserName: AnsiString; override;
constructor Create(ASource: TDbgFileLoader; ADebugMap: TObject; OwnSource: Boolean); override;
destructor Destroy; override;
procedure ParseSymbolTable(AFpSymbolInfo: TfpSymbolList); override;
//function GetSectionInfo(const SectionName: AnsiString; var Size: int64): Boolean; override;
//function GetSectionData(const SectionName: AnsiString; Offset, Size: Int64; var Buf: array of byte): Int64; override;
end;
implementation
type
TElf32symbol=record
st_name : longword;
st_value : longword;
st_size : longword;
st_info : byte; { bit 0-3: type, 4-7: bind }
st_other : byte;
st_shndx : word;
end;
PElf32symbolArray = ^TElf32symbolArray;
TElf32symbolArray = array[0..maxSmallint] of TElf32symbol;
TElf64symbol=record
st_name : longword;
st_info : byte; { bit 0-3: type, 4-7: bind }
st_other : byte;
st_shndx : word;
st_value : qword;
st_size : qword;
end;
PElf64symbolArray = ^TElf64symbolArray;
TElf64symbolArray = array[0..maxSmallint] of TElf64symbol;
const
// Symbol-map section name
_symbol = '.symtab';
_symbolstrings = '.strtab';
{ TElfFile }
function TElfFile.Load32BitFile(ALoader: TDbgFileLoader): Boolean;
var
hdr : Elf32_Ehdr;
sect : array of Elf32_shdr;
i, j : integer;
nm : string;
sz : LongWord;
strs : array of byte;
begin
Result := ALoader.Read(0, sizeof(hdr), @hdr) = sizeof(hdr);
if not Result then Exit;
SetLength(sect, hdr.e_shnum);
//ALoader.Position := hdr.e_shoff;
sz := hdr.e_shetsize * hdr.e_shnum;
if sz > LongWord(length(sect)*sizeof(Elf32_shdr)) then begin
debugln(['TElfFile.Load64BitFile Size of SectHdrs is ', sz, ' expected ', LongWord(length(sect)*sizeof(Elf32_shdr))]);
sz := LongWord(length(sect)*sizeof(Elf32_shdr));
end;
//ALoader.Read(sect[0], sz);
ALoader.Read(hdr.e_shoff, sz, @sect[0]);
i := sect[hdr.e_shstrndx].sh_offset;
j := sect[hdr.e_shstrndx].sh_size;
SetLength(strs, j);
//ALoader.Position:=i;
//ALoader.Read(strs[0], j);
ALoader.Read(i, j, @strs[0]);
for i := 0 to hdr.e_shnum - 1 do
with sect[i] do begin
nm := PChar( @strs[sh_name] );
AddSection(nm, sh_offset, sh_addr, sh_size );
end;
end;
function TElfFile.Load64BitFile(ALoader: TDbgFileLoader): Boolean;
var
hdr : Elf64_Ehdr;
sect : array of Elf64_shdr;
i, j : integer;
nm : string;
sz : LongWord;
strs : array of byte;
begin
Result := ALoader.Read(0, sizeof(hdr), @hdr) = sizeof(hdr);
if not Result then Exit;
FIs64Bit:=true;
SetLength(sect, hdr.e_shnum);
//ALoader.Position := hdr.e_shoff;
sz := hdr.e_shentsize * hdr.e_shnum;
if sz > LongWord(length(sect)*sizeof(Elf64_shdr)) then begin
debugln(['TElfFile.Load64BitFile Size of SectHdrs is ', sz, ' expected ', LongWord(length(sect)*sizeof(Elf64_shdr))]);
sz := LongWord(length(sect)*sizeof(Elf64_shdr));
end;
//ALoader.Read(sect[0], sz);
ALoader.Read(hdr.e_shoff, sz, @sect[0]);
i := sect[hdr.e_shstrndx].sh_offset;
j := sect[hdr.e_shstrndx].sh_size;
SetLength(strs, j);
//ALoader.Position:=i;
//ALoader.Read(strs[0], j);
ALoader.Read(i, j, @strs[0]);
for i := 0 to hdr.e_shnum - 1 do
with sect[i] do begin
nm := PChar( @strs[sh_name] );
AddSection(nm, sh_offset, sh_address, sh_size );
end;
Result := False;
end;
procedure TElfFile.AddSection(const name: AnsiString; FileOffset, Address,
Size: Qword);
begin
if seccount=Length(sections) then begin
if seccount = 0 then SetLength(sections, 4)
else SetLength(sections, seccount*2);
end;
sections[seccount].Address:= Address;
sections[seccount].name:=name;
sections[seccount].FileOfs:=FileOffset;
sections[seccount].Size:=Size;
inc(seccount);
end;
function TElfFile.LoadFromFile(ALoader: TDbgFileLoader): Boolean;
var
ident : array [0..EINDENT-1] of byte;
begin
try
Result := ALoader.Read(0, sizeof(ident), @ident[0]) = sizeof(ident);
if not Result then Exit;
Result := (ident[EI_MAG0] = $7f) and
(ident[EI_MAG1] = byte('E')) and
(ident[EI_MAG2] = byte('L')) and
(ident[EI_MAG3] = byte('F'));
if not Result then Exit;
Result := False;
if ident[EI_CLASS] = ELFCLASS32 then begin
Result := Load32BitFile(ALoader);
exit;
end;
if ident[EI_CLASS] = ELFCLASS64 then begin
Result := Load64BitFile(ALoader);
exit;
end;
except
Result := false;
end;
end;
function TElfFile.FindSection(const Name: String): Integer;
var
i : Integer;
begin
Result := -1;
for i := 0 to seccount - 1 do
if sections[i].name = Name then begin
Result := i;
Exit;
end;
end;
{ TElfDbgSource }
function TElfDbgSource.GetSection(const AName: String): PDbgImageSection;
var
i: Integer;
ex: PDbgImageSectionEx;
begin
Result := nil;
i := FSections.IndexOf(AName);
if i < 0 then
exit;
ex := PDbgImageSectionEx(FSections.Objects[i]);
Result := @ex^.Sect;
if ex^.Loaded then
exit;
ex^.Loaded := True;
FFileLoader.LoadMemory(ex^.Offs, Result^.Size, Result^.RawData);
end;
class function TElfDbgSource.isValid(ASource: TDbgFileLoader): Boolean;
var
buf : array [0..3+sizeof(Elf32_EHdr)] of byte;
begin
try
Result := Assigned(ASource) and
(ASource.Read(0, sizeof(Elf32_EHdr), @buf[0]) = sizeof(Elf32_EHdr));
if not Result then Exit;
Result := (buf[EI_MAG0] = $7f) and (buf[EI_MAG1] = byte('E')) and
(buf[EI_MAG2] = byte('L')) and (buf[EI_MAG3] = byte('F'));
except
Result := false;
end;
end;
class function TElfDbgSource.UserName: AnsiString;
begin
Result := 'ELF executable';
end;
constructor TElfDbgSource.Create(ASource: TDbgFileLoader; ADebugMap: TObject; OwnSource: Boolean);
var
p: PDbgImageSectionEx;
idx: integer;
i: Integer;
fs: TElfSection;
begin
FSections := TStringList.Create;
FSections.Sorted := True;
//FSections.Duplicates := dupError;
FSections.CaseSensitive := False;
FFileLoader := ASource;
fOwnSource := OwnSource;
fElfFile := TElfFile.Create;
fElfFile.LoadFromFile(ASource);
for i := 0 to fElfFile.seccount - 1 do begin
fs := fElfFile.sections[i];
idx := FSections.AddObject(fs.name, nil);
New(p);
P^.Offs := fs.FileOfs;
p^.Sect.Size := fs.Size;
p^.Sect.VirtualAddress := 0; // Todo? fs.Address - ImageBase
p^.Loaded := False;
FSections.Objects[idx] := TObject(p);
end;
SetImage64Bit(fElfFile.Is64Bit);
inherited Create(ASource, ADebugMap, OwnSource);
end;
destructor TElfDbgSource.Destroy;
begin
if fOwnSource then FFileLoader.Free;
fElfFile.Free;
while FSections.Count > 0 do begin
Freemem(FSections.Objects[0]);
FSections.Delete(0);
end;
FreeAndNil(FSections);
inherited Destroy;
end;
procedure TElfDbgSource.ParseSymbolTable(AFpSymbolInfo: TfpSymbolList);
var
p: PDbgImageSection;
ps: PDbgImageSection;
SymbolArr32: PElf32symbolArray;
SymbolArr64: PElf64symbolArray;
SymbolStr: pointer;
i: integer;
SymbolCount: integer;
SymbolName: AnsiString;
begin
p := Section[_symbol];
ps := Section[_symbolstrings];
if assigned(p) and assigned(ps) then
begin
SymbolStr:=PDbgImageSectionEx(ps)^.Sect.RawData;
if Image64Bit then
begin
SymbolArr64:=PDbgImageSectionEx(p)^.Sect.RawData;
SymbolCount := PDbgImageSectionEx(p)^.Sect.Size div sizeof(TElf64symbol);
for i := 0 to SymbolCount-1 do
begin
begin
{$push}
{$R-}
if SymbolArr64^[i].st_name<>0 then
begin
SymbolName:=pchar(SymbolStr+SymbolArr64^[i].st_name);
AfpSymbolInfo.Add(SymbolName, TDbgPtr(SymbolArr64^[i].st_value+ImageBase));
end;
{$pop}
end
end;
end
else
begin
SymbolArr32:=PDbgImageSectionEx(p)^.Sect.RawData;
SymbolCount := PDbgImageSectionEx(p)^.Sect.Size div sizeof(TElf32symbol);
for i := 0 to SymbolCount-1 do
begin
begin
if SymbolArr32^[i].st_name<>0 then
begin
SymbolName:=pchar(SymbolStr+SymbolArr32^[i].st_name);
AfpSymbolInfo.Add(SymbolName, TDBGPtr(SymbolArr32^[i].st_value+ImageBase));
end;
end
end;
end;
end;
end;
initialization
RegisterImageReaderClass( TElfDbgSource );
end.