mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-06-18 05:18:38 +02:00
312 lines
9.3 KiB
ObjectPascal
312 lines
9.3 KiB
ObjectPascal
|
|
{ *********************************************************************** }
|
|
{ }
|
|
{ elfresfix - Free Pascal Resource to ELF object compiler - fixup tool }
|
|
{ Part of the Free Pascal and CrossFPC distributions }
|
|
{ }
|
|
{ Copyright (C) 2005 Simon Kissel }
|
|
{ }
|
|
{ See the file COPYING.FPC, included in the FPC 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. }
|
|
{ }
|
|
{ *********************************************************************** }
|
|
|
|
{
|
|
This tool will update the fpc.resptrs section of an ELF executable to point
|
|
to the various resource sections in the file. This is done so that the FPC
|
|
RTL at runtime is able to get pointers to these sections.
|
|
|
|
This tool is automatically run on any fpc compiled ELF executable that
|
|
contains ELF resources.
|
|
|
|
fpcresfix builds with Delphi, Kylix and FPC.
|
|
|
|
Currently this only works on 32Bit targets, but support for 64Bit targets
|
|
is in the works. Support for big endian systems is completely missing,
|
|
though.
|
|
}
|
|
{$ifdef fpc}
|
|
{$mode objfpc}
|
|
{$endif}
|
|
{$h+}
|
|
|
|
unit elfresfix;
|
|
|
|
interface
|
|
|
|
uses
|
|
SysUtils, Classes, elfbfd;
|
|
|
|
Type
|
|
|
|
TLogEvent = Procedure(Const Msg : String) of object;
|
|
|
|
{ TElfResourceFixer }
|
|
|
|
TElfResourceFixer = Class(TObject)
|
|
private
|
|
FFileName: String;
|
|
FOnVerbose: TLogEvent;
|
|
FVerbose: Boolean;
|
|
Procedure DoVerbose(Msg : String);
|
|
public
|
|
Procedure FixFile(AFileName : String);
|
|
Procedure DoFixStream(Stream : TStream); virtual; abstract;
|
|
Property Verbose : Boolean read FVerbose write FVerbose;
|
|
Property FileName : String Read FFileName;
|
|
Property Onverbose : TLogEvent Read FOnVerbose Write FOnVerbose;
|
|
end;
|
|
|
|
{ TElf32ResourceFixer }
|
|
|
|
TElf32ResourceFixer = Class(TElfResourceFixer)
|
|
Procedure DoFixStream(Stream : TStream); override;
|
|
end;
|
|
|
|
{ TElf64ResourceFixer }
|
|
|
|
TElf64ResourceFixer = Class(TElfResourceFixer)
|
|
Procedure DoFixStream(Stream : TStream); override;
|
|
end;
|
|
|
|
EElfResFixError = Class(Exception);
|
|
|
|
Implementation
|
|
|
|
ResourceString
|
|
SCheckingHeader = 'Checking ELF Header... ';
|
|
SReadingSectionHeaders = 'Reading Section Headers...';
|
|
SHeaderOK = 'ELF Header is OK';
|
|
SCheckingHeaderTable = 'Checking Section Header table...';
|
|
SStrTabFound = 'Found strtab...';
|
|
SProcessingSection = 'Processing section: ';
|
|
SUpdatingResptrs = 'Updating resptrs section...';
|
|
SFileFixed = 'File fixed successfully!';
|
|
SNothingToFix = 'There was nothing to fix in this file.';
|
|
|
|
SErrUnsupportedHeaderSize = 'Unsupported Section Header size.';
|
|
SErrInvalidELFHeader = 'Not a valid linux ELF binary.';
|
|
SErrResPtrsNotFound = 'Unable to find resptrs section.';
|
|
|
|
Procedure DoError (Msg : String);
|
|
|
|
begin
|
|
Raise EElfResFixError.Create(Msg);
|
|
end;
|
|
|
|
Procedure DoErrorFmt (Msg : String; Args : Array of const);
|
|
|
|
begin
|
|
Raise EElfResFixError.CreateFmt(Msg,Args);
|
|
end;
|
|
|
|
{ TElfResourceFixer }
|
|
|
|
procedure TElfResourceFixer.DoVerbose(Msg: String);
|
|
begin
|
|
If FVerbose and Assigned(FOnVerbose) then
|
|
FOnVerbose(Msg);
|
|
end;
|
|
|
|
procedure TElfResourceFixer.FixFile(AFileName: String);
|
|
|
|
Var
|
|
F : TStream;
|
|
|
|
begin
|
|
FFileName:=AFileName;
|
|
F:=TFileStream.Create(AFilename,fmOpenReadWrite or fmShareDenyWrite);
|
|
Try
|
|
DoFixStream(F);
|
|
Finally
|
|
F.Free;
|
|
end;
|
|
end;
|
|
|
|
{ TElf32ResourceFixer }
|
|
|
|
procedure TElf32ResourceFixer.DoFixStream(Stream: TStream);
|
|
var
|
|
ElfHeader:TElf32header;
|
|
ResourceSectionTable: TElf32ResourceSectionTable;
|
|
SectionHeaders: array of TElf32sechdr;
|
|
|
|
i:integer;
|
|
sn:string;
|
|
SectionHeaderOffset:integer;
|
|
fixed: boolean;
|
|
strtab:string;
|
|
SectionName: string;
|
|
ResPtrsSection: integer;
|
|
ResHashSection: integer;
|
|
ResSymSection: integer;
|
|
|
|
ResourceInfo: TELF32ResourceInfo;
|
|
DataIndex, StringIndex: integer;
|
|
SymString: string;
|
|
|
|
procedure DoAlign(var value:integer; const a: integer);
|
|
var i: integer;
|
|
begin
|
|
i:=(4 - (value MOD a)) MOD a;
|
|
if (i>0) then inc(value,i);
|
|
end;
|
|
|
|
begin
|
|
Fixed:=False;
|
|
Stream.Read(ElfHeader,sizeof(TElf32header));
|
|
DoVerbose(SCheckingHeader);
|
|
if (ElfHeader.magic0123<>$464C457F) then
|
|
DoError(SErrInvalidELFheader);
|
|
if ElfHeader.e_shentsize=sizeof(TElf32sechdr) then
|
|
DoVerbose(SHeaderOK)
|
|
else
|
|
DoError(SErrUnSupportedHeaderSize);
|
|
DoVerbose(SReadingSectionHeaders);
|
|
|
|
setlength(SectionHeaders,ElfHeader.e_shnum);
|
|
SectionHeaderOffset:=ElfHeader.e_shoff;
|
|
Stream.Position:=SectionHeaderOffset;
|
|
|
|
for i:=0 to ElfHeader.e_shnum-1 do
|
|
begin
|
|
Stream.Read(SectionHeaders[i],sizeof(TElf32sechdr));
|
|
end;
|
|
|
|
DoVerbose(SCheckingHeaderTable);
|
|
|
|
// Get the section header strtab
|
|
i:=ElfHeader.e_shstrndx;
|
|
if SectionHeaders[i].sh_type=SHT_STRTAB then
|
|
begin
|
|
DoVerbose(SStrTabFound);
|
|
// read the strtab
|
|
Stream.Position:=SectionHeaders[i].sh_offset;
|
|
setlength(strtab,SectionHeaders[i].sh_size);
|
|
Stream.Read(strtab[1],SectionHeaders[i].sh_size);
|
|
end
|
|
else
|
|
begin
|
|
writeln('Error: Unable to find strtab.');
|
|
halt(5);
|
|
end;
|
|
|
|
ResPtrsSection:=-1;
|
|
ResHashSection:=-1;
|
|
ResourceSectionTable.version:=66;
|
|
|
|
// Next cycle through all sections to gather pointers to all the resource
|
|
// sections, and note the index of the resptrs section
|
|
for i:=0 to ElfHeader.e_shnum-1 do
|
|
begin
|
|
SectionName:=copy(strtab,SectionHeaders[i].sh_name+1,32);
|
|
SectionName:=copy(SectionName,1,pos(#0,SectionName)-1);
|
|
DoVerbose(SProcessingSection+SectionName);
|
|
sn:=Copy(SectionName,1,4);
|
|
// FPC section ?
|
|
if (sn='fpc.') then
|
|
begin
|
|
sn:=SectionName;
|
|
Delete(SN,1,4);
|
|
if SN='resptrs' then
|
|
begin
|
|
ResPtrsSection:=i;
|
|
end
|
|
else if sn='ressym' then
|
|
begin
|
|
ResSymSection:=i;
|
|
ResourceSectionTable.ressym.ptr:=SectionHeaders[i].sh_addr;
|
|
ResourceSectionTable.ressym.size:=SectionHeaders[i].sh_size;
|
|
end
|
|
else if sn='reshash' then
|
|
begin
|
|
ResHashSection:=i;
|
|
ResourceSectionTable.reshash.ptr:=SectionHeaders[i].sh_addr;
|
|
ResourceSectionTable.reshash.size:=SectionHeaders[i].sh_size;
|
|
ResourceSectionTable.resentries:=SectionHeaders[i].sh_size DIV sizeof(TELF32ResourceInfo);
|
|
end
|
|
else if sn='resdata' then
|
|
begin
|
|
ResourceSectionTable.resdata.ptr:=SectionHeaders[i].sh_addr;
|
|
ResourceSectionTable.resdata.size:=SectionHeaders[i].sh_size;
|
|
end
|
|
else if sn='resspare' then
|
|
begin
|
|
ResourceSectionTable.resspare.ptr:=SectionHeaders[i].sh_addr;
|
|
ResourceSectionTable.resspare.size:=SectionHeaders[i].sh_size;
|
|
end
|
|
else if SectionName='resstr' then
|
|
begin
|
|
ResourceSectionTable.resstr.ptr:=SectionHeaders[i].sh_addr;
|
|
ResourceSectionTable.resstr.size:=SectionHeaders[i].sh_size;
|
|
end;
|
|
end
|
|
end;
|
|
|
|
// Ok, we now have pointers to all resource sections and also
|
|
// know the number of resources.
|
|
// Now update the resptrs table
|
|
if (ResPtrsSection>-1) and (ResHashSection>-1) and (ResSymSection>-1) then
|
|
begin
|
|
Doverbose(SUpdatingResPtrs);
|
|
Stream.Position:=SectionHeaders[ResPtrsSection].sh_offset;
|
|
Stream.Write(ResourceSectionTable,sizeof(TELF32ResourceSectionTable));
|
|
|
|
// LD might have merged the sections of several linked .or together
|
|
// Therefore our data and stringtable offsets might be messed up and need to recalculated
|
|
|
|
// First get the symbol string
|
|
Stream.Position:=SectionHeaders[ResSymSection].sh_offset;
|
|
setlength(SymString, SectionHeaders[ResSymSection].sh_size);
|
|
Stream.Read(SymString[1], SectionHeaders[ResSymSection].sh_size);
|
|
|
|
DataIndex:=0;
|
|
StringIndex:=0;
|
|
Stream.Position:=SectionHeaders[ResHashSection].sh_offset;
|
|
|
|
for i:=0 to ResourceSectionTable.resentries-1 do
|
|
begin
|
|
Stream.Position:=SectionHeaders[ResHashSection].sh_offset+i*sizeof(TELF32ResourceInfo);
|
|
Stream.Read(ResourceInfo, sizeof(TELF32ResourceInfo));
|
|
ResourceInfo.ptr:=DataIndex;
|
|
ResourceInfo.name:=StringIndex;
|
|
|
|
// advance for next entry
|
|
DataIndex:=DataIndex+ResourceInfo.size;
|
|
DoAlign(DataIndex,4); // The data blocks are 32bit aligned
|
|
|
|
// find end of current string
|
|
while SymString[StringIndex+1]<>#0 do
|
|
inc(StringIndex,1);
|
|
inc(StringIndex,1);
|
|
// this should be the start of the next string
|
|
|
|
// write back the entry
|
|
Stream.Position:=SectionHeaders[ResHashSection].sh_offset+i*sizeof(TELF32ResourceInfo);
|
|
Stream.Write(ResourceInfo, sizeof(TELF32ResourceInfo));
|
|
end;
|
|
fixed:=true;
|
|
end
|
|
else
|
|
DoError(SErrREsptrsNotFound);
|
|
|
|
|
|
if fixed then
|
|
DoVerbose(SFileFixed)
|
|
else
|
|
writeln(SNothingToFix);
|
|
end;
|
|
|
|
{ TElf64ResourceFixer }
|
|
|
|
procedure TElf64ResourceFixer.DoFixStream(Stream: TStream);
|
|
begin
|
|
DoError('64-bit resources not yet supported');
|
|
end;
|
|
|
|
end.
|