fpc/utils/fpcres/elfresfix.pas
2005-09-23 09:58:16 +00:00

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.