mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-06-18 08:18:42 +02:00
694 lines
23 KiB
ObjectPascal
694 lines
23 KiB
ObjectPascal
{ *********************************************************************** }
|
|
{ }
|
|
{ fpcres2elf - Free Pascal Resource to ELF object compiler }
|
|
{ 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 unit will compile an ELF object file out of a supplied .res resource
|
|
file. Optionally, Delphi/Kylix dfm/xfm form files are also accepted as
|
|
input. These will automatically be converted into resources internally.
|
|
|
|
fpcres2elf 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.
|
|
|
|
Format used for the various resource sections:
|
|
|
|
fpc.resptrs: This section is contained in resptrs.o and always linked to the executable by
|
|
FPC. It containes an exported label fpcrespointers, which is used at runtime
|
|
to find the resptrs section in memory. The resptrs contains pointers to all the
|
|
sections and their sizes. These are updated in a post-precessing step by the
|
|
compiler and by the external resource embedder when applied to an ELF file.
|
|
This section always is 128 Bytes long and initially filled with zeros.
|
|
The first integer (32/64 Bit) value in this section contains the version
|
|
number of the resource system. Currently this value is 1.
|
|
The second integer (32/64 Bit) value in this section contains the number of
|
|
resources.
|
|
After this follows a version-defined number of TFPCResourceSectionInfo entries.
|
|
fpc.ressym: Contains the resource names. This simply is a stream of zero-terminated strings.
|
|
Only textual names are supported, numeric IDs get autoconverted to #ID's.
|
|
The reshash table has got a byte index into ressym to quickly get that data if needed
|
|
fpc.reshash: n TFPCResourceInfo records. (number of entries is defined in fpc.resptrs)
|
|
fpc.resdata: Contains the plain resource data stream. A byte index into the data stream
|
|
is given for each resource entry in TResourceInfo
|
|
fpc.resspare: An empty section which is resized to make room if the size of any of the previous
|
|
sections gets changed. NOT USED IN VERSION 1 (SIZE WILL BE 0)
|
|
fpc.resstr: This section is completely seperated from the rest and contains a block of
|
|
resourcestrings in internal FPC format.
|
|
|
|
resptr TFPCResourceSectionInfo list for FPC resources version 1:
|
|
|
|
Index Pointer to
|
|
0 ressym
|
|
1 reshash
|
|
2 resdata
|
|
3 resspare
|
|
4 resstr
|
|
5 reserved for future extension (stabs)
|
|
6 reserved for future extension
|
|
}
|
|
{$ifdef fpc}
|
|
{$mode objfpc}
|
|
{$endif}
|
|
{$h+}
|
|
|
|
unit elfres;
|
|
|
|
interface
|
|
|
|
uses
|
|
elfbfd,
|
|
SysUtils,
|
|
Classes;
|
|
|
|
const fpcres2elf_version=1;
|
|
|
|
// Do not change the following consts, they are dummy tables to generate an .o that makes ld happy
|
|
const shstrtab = #0+'.symtab'+#0+'.strtab'+#0+'.shstrtab'+#0+'.text'+#0+'.data'+#0+
|
|
'.bss'+#0+'fpc.ressym'+#0+'fpc.resstr'+#0+'fpc.reshash'+#0+
|
|
'fpc.resdata'+#0+'fpc.resspare'+#0+#0;
|
|
symtab = #$00#$00#$00#$00#$00#$00#$00#$00#$00#$00#$00#$00#$00#$00#$00#$00+
|
|
#$00#$00#$00#$00#$00#$00#$00#$00#$00#$00#$00#$00#$03#$00#$01#$00+
|
|
#$00#$00#$00#$00#$00#$00#$00#$00#$00#$00#$00#$00#$03#$00#$02#$00+
|
|
#$00#$00#$00#$00#$00#$00#$00#$00#$00#$00#$00#$00#$03#$00#$03#$00+
|
|
#$00#$00#$00#$00#$00#$00#$00#$00#$00#$00#$00#$00#$03#$00#$04#$00+
|
|
#$00#$00#$00#$00#$00#$00#$00#$00#$00#$00#$00#$00#$03#$00#$05#$00+
|
|
#$00#$00#$00#$00#$00#$00#$00#$00#$00#$00#$00#$00#$03#$00#$06#$00+
|
|
#$00#$00#$00#$00#$00#$00#$00#$00#$00#$00#$00#$00#$03#$00#$07#$00+
|
|
#$00#$00#$00#$00#$00#$00#$00#$00#$00#$00#$00#$00#$03#$00#$08#$00;
|
|
strtab = #$00#$00; // this actually is just one byte long
|
|
zeros = #$00#$00#$00#$00#$00#$00#$00#$00#$00#$00#$00#$00#$00#$00#$00#$00;
|
|
fake = 'fakefakefakefakefakefakefakefake';
|
|
|
|
// header of a windows 32 bit .res file (16 bytes)
|
|
reshdr = #$00#$00#$00#$00#$20#$00#$00#$00#$FF#$FF#$00#$00#$FF#$FF#$00#$00+
|
|
#$00#$00#$00#$00#$00#$00#$00#$00#$00#$00#$00#$00#$00#$00#$00#$00;
|
|
|
|
FilerSignature: array[1..4] of Char = 'TPF0';
|
|
|
|
SDefaultExtension = '.or';
|
|
|
|
Type
|
|
{ TElfResCreator }
|
|
|
|
TElfResCreator = Class(TObject)
|
|
private
|
|
FDestFileName: String;
|
|
FExtension: string;
|
|
FSourceFileName: String;
|
|
FOverwrite: Boolean;
|
|
FVerbose: Boolean;
|
|
FVersion: Integer;
|
|
Protected
|
|
FSectionStream: TMemoryStream;
|
|
FDataStream: TMemoryStream;
|
|
FSymStream: TMemoryStream;
|
|
FHashStream: TMemoryStream;
|
|
sectionheader_ofs: integer;
|
|
shstrtab_ofs: integer;
|
|
CurrentResource:integer;
|
|
resheader: string;
|
|
Signature: byte;
|
|
Procedure AllocateData; virtual;
|
|
Procedure FreeData; virtual;
|
|
Procedure DoAlign(const a: integer);
|
|
Public
|
|
Constructor Create;
|
|
Procedure Convert(Const Source,Destination : String);
|
|
Procedure ConvertStreams(Source,Dest : TStream);
|
|
Procedure DoConvertStreams(Source,Dest : TStream); virtual;Abstract;
|
|
Property Verbose : Boolean Read FVerbose Write FVerbose;
|
|
Property SourceFileName : String Read FSourceFileName;
|
|
Property DestFileName : String Read FDestFileName;
|
|
Property Overwrite : Boolean Read FOverwrite Write FOverWrite;
|
|
Property Version : Integer Read FVersion Write FVersion;
|
|
Property Extension : string Read FExtension Write FExtension;
|
|
end;
|
|
|
|
{ TElf32ResCreator }
|
|
|
|
TElf32ResCreator = Class(TElfResCreator)
|
|
Private
|
|
ResourceEntries: array of TELF32ResourceInfo;
|
|
Protected
|
|
Procedure AllocateData; override;
|
|
Procedure FreeData; override;
|
|
public
|
|
procedure LoadBinaryDFMEntry(const rs:TStream; const DataStream:TMemoryStream; const SymStream:TMemoryStream; var resinfo:TELF32ResourceInfo);
|
|
procedure LoadTextDFMEntry(const rs:TStream; const DataStream:TMemoryStream; const SymStream:TMemoryStream; var resinfo:TELF32ResourceInfo);
|
|
procedure LoadRESEntry(const rs:TStream; const DataStream:TMemoryStream; const SymStream:TMemoryStream; var resinfo:TELF32ResourceInfo);
|
|
Procedure DoConvertStreams(Source,Dest : TStream); override;
|
|
end;
|
|
|
|
{ TElf64Creator }
|
|
|
|
TElf64ResCreator = Class(TElfResCreator)
|
|
Procedure DoConvertStreams(Source,Dest : TStream); override;
|
|
end;
|
|
|
|
EElfResError = Class(Exception);
|
|
|
|
implementation
|
|
|
|
resourcestring
|
|
SErrUnrecognizedFormat = 'Unrecognized file format for input file "%s"';
|
|
|
|
Procedure DoError (Msg : String);
|
|
|
|
begin
|
|
Raise EElfResError.Create(Msg);
|
|
end;
|
|
|
|
Procedure DoErrorFmt (Msg : String; Args : Array of const);
|
|
|
|
begin
|
|
Raise EElfResError.CreateFmt(Msg,Args);
|
|
end;
|
|
|
|
function HashELF(const S : string) : longint;
|
|
{Note: this hash function is described in "Practical Algorithms For
|
|
Programmers" by Andrew Binstock and John Rex, Addison Wesley,
|
|
with modifications in Dr Dobbs Journal, April 1996}
|
|
var
|
|
G : longint;
|
|
i : integer;
|
|
begin
|
|
Result := 0;
|
|
for i := 1 to length(S) do begin
|
|
Result := (Result shl 4) + ord(S[i]);
|
|
G := Result and $F0000000;
|
|
if (G <> 0) then
|
|
Result := Result xor (G shr 24);
|
|
Result := Result and (not G);
|
|
end;
|
|
end;
|
|
|
|
{ TElfResCreator }
|
|
|
|
procedure TElfResCreator.AllocateData;
|
|
begin
|
|
FSectionStream:=TMemoryStream.Create;
|
|
FDataStream:=TMemoryStream.Create;
|
|
FSymStream:=TMemoryStream.Create;
|
|
FHashStream:=TMemoryStream.Create;
|
|
end;
|
|
|
|
procedure TElfResCreator.FreeData;
|
|
begin
|
|
FreeAndNil(FSectionStream);
|
|
FreeAndNil(FDataStream);
|
|
FreeAndNil(FSymStream);
|
|
FreeAndNil(FHashStream);
|
|
end;
|
|
|
|
constructor TElfResCreator.Create;
|
|
begin
|
|
FVersion:=fpcres2elf_version;
|
|
FOverwrite:=True;
|
|
FVerbose:=False;
|
|
FExtension:=SDefaultExtension;
|
|
end;
|
|
|
|
// fill the memorystream so it is aligned, max supported align is 16
|
|
procedure TElfResCreator.DoAlign(const a: integer);
|
|
var i: integer;
|
|
begin
|
|
i:=(4 - (FSectionStream.position MOD a)) MOD a;
|
|
if (i>0) then FSectionStream.Write(zeros[1],i);
|
|
end;
|
|
|
|
procedure TElfResCreator.Convert(const Source, Destination: String);
|
|
|
|
Var
|
|
Src,Dest : TFileStream;
|
|
|
|
begin
|
|
FSourceFileName:=Source;
|
|
FDestFileName:=Destination;
|
|
if FDestFileName='' then
|
|
FDestFileName:=ChangeFileExt(Source,FExtension);
|
|
Src:=TFileStream.Create(FSourceFileName,fmOpenRead or fmShareDenyWrite);
|
|
try
|
|
Dest:=TFileStream.Create(FDestFileName,fmCreate or fmShareDenyWrite);
|
|
Try
|
|
ConvertStreams(Src,Dest);
|
|
Finally
|
|
Dest.Free;
|
|
end;
|
|
Finally
|
|
Src.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure TElfResCreator.ConvertStreams(Source, Dest: TStream);
|
|
begin
|
|
AllocateData;
|
|
Try
|
|
DoConvertStreams(Source,Dest);
|
|
Finally
|
|
FreeData;
|
|
end;
|
|
end;
|
|
|
|
{ ---------------------------------------------------------------------
|
|
TElf32ResCreator
|
|
---------------------------------------------------------------------}
|
|
|
|
procedure TElf32ResCreator.AllocateData;
|
|
begin
|
|
inherited AllocateData;
|
|
// reserve space for 1024 resource entries for now
|
|
SetLength(ResourceEntries,1024);
|
|
CurrentResource:=0;
|
|
end;
|
|
|
|
procedure TElf32ResCreator.FreeData;
|
|
begin
|
|
inherited FreeData;
|
|
SetLength(ResourceEntries,0);
|
|
end;
|
|
|
|
procedure TElf32ResCreator.LoadRESEntry(const rs:TStream; const DataStream:TMemoryStream; const SymStream:TMemoryStream; var resinfo:TELF32ResourceInfo);
|
|
var l:longint;
|
|
w:word;
|
|
ws:WideString;
|
|
wc:WideChar;
|
|
name:string;
|
|
i,nl: integer;
|
|
headersize:integer;
|
|
headerstart:integer;
|
|
begin
|
|
headerstart:=rs.Position;
|
|
resinfo.ptr:=DataStream.Position;
|
|
rs.Read(resinfo.size,4);
|
|
rs.Read(headersize,4);
|
|
rs.Read(l,4); // Type
|
|
if (l AND $0000FFFF)=$0000FFFF then
|
|
begin // Type is stored as ID
|
|
resinfo.restype:=(l AND $FFFF0000) shr 16; // kill the marker, we always use IDs
|
|
end
|
|
else
|
|
begin
|
|
// we don't support text IDs for now, skip until we have reached the end
|
|
// of the widestring, and set rcdata (10) in this case.
|
|
repeat
|
|
rs.Read(w,2);
|
|
until w=0;
|
|
resinfo.restype:=10;
|
|
end;
|
|
|
|
rs.Read(l,4); // Name
|
|
if (l AND $0000FFFF)=$0000FFFF then
|
|
begin // Name is stored as ID.
|
|
l:=(l AND $FFFF0000) shr 16; // kill the marker
|
|
// We don't want to support integer names, we'll instead convert them to a #id string
|
|
// which is more common.
|
|
name:='#'+inttostr(l);
|
|
end
|
|
else
|
|
begin
|
|
// Ok, it's a widestring ID
|
|
ws:=widechar(l AND $0000FFFF);
|
|
ws:=ws+widechar((l AND $FFFF0000) shr 16);
|
|
// get the rest of it
|
|
repeat
|
|
rs.Read(wc,2);
|
|
if wc<>#0 then ws:=ws+wc;
|
|
until wc=#0;
|
|
// convert to ANSI
|
|
name:=ws;
|
|
end;
|
|
|
|
// create a hash of the name
|
|
resinfo.reshash:=HashELF(name);
|
|
// save the name plus a trailing #0 to the SymStream, also save
|
|
// the position of this name in the SymStream
|
|
resinfo.name:=SymStream.Position;
|
|
name:=name+#0;
|
|
nl:=length(name);
|
|
SymStream.Write(name[1],length(name));
|
|
|
|
// We don't care about the rest of the header
|
|
rs.Seek(headersize-(rs.position-headerstart),soFromCurrent);
|
|
|
|
// Now copy over the resource data into our internal memory stream
|
|
DataStream.CopyFrom(rs,resinfo.size);
|
|
|
|
// Align the resource stream on a dword boundary
|
|
i:=(4 - (rs.Position MOD 4)) MOD 4;
|
|
if (i>0) then rs.Seek(i,soFromCurrent);
|
|
|
|
|
|
// Align the data stream on a dword boundary
|
|
i:=(4 - (DataStream.Position MOD 4)) MOD 4;
|
|
if (i>0) then DataStream.Write(zeros[1],i);
|
|
end;
|
|
|
|
procedure TElf32ResCreator.LoadBinaryDFMEntry(const rs:TStream; const DataStream:TMemoryStream; const SymStream:TMemoryStream; var resinfo:TELF32ResourceInfo);
|
|
var name: string;
|
|
i: integer;
|
|
begin
|
|
resinfo.ptr:=0;
|
|
resinfo.restype:=10; // RCDATA
|
|
|
|
// Skip the header
|
|
rs.Position:=3;
|
|
|
|
// Read the name
|
|
setlength(name,64); // Component names can be 64 chars at max
|
|
rs.Read(name[1],64);
|
|
|
|
// Find end of name
|
|
i:=pos(#0,name);
|
|
name:=copy(name,1,i-1);
|
|
|
|
// Seek to after the name and skip other crap
|
|
rs.Position:=i+9;
|
|
|
|
resinfo.size:=rs.Size-rs.Position;
|
|
|
|
// ...this is followed by the data.
|
|
|
|
// create a hash of the name
|
|
resinfo.reshash:=HashELF(name);
|
|
|
|
// save the name plus a trailing #0 to the SymStream, also save
|
|
// the position of this name in the SymStream
|
|
resinfo.name:=SymStream.Position;
|
|
name:=name+#0;
|
|
SymStream.Write(name[1],length(name));
|
|
|
|
// Now copy over the resource data into our internal memory stream
|
|
DataStream.CopyFrom(rs,resinfo.size);
|
|
|
|
// Align the data stream on a dword boundary
|
|
i:=(4 - (DataStream.Position MOD 4)) MOD 4;
|
|
if (i>0) then DataStream.Write(zeros,i);
|
|
end;
|
|
|
|
procedure TElf32ResCreator.LoadTextDFMEntry(const rs:TStream; const DataStream:TMemoryStream; const SymStream:TMemoryStream; var resinfo:TELF32ResourceInfo);
|
|
var ms:TMemoryStream;
|
|
begin
|
|
ms:=nil;
|
|
try
|
|
ms:=TMemoryStream.Create;
|
|
ObjectTextToResource(rs,ms);
|
|
LoadBinaryDFMEntry(ms, DataStream, SymStream, resinfo);
|
|
finally
|
|
ms.free;
|
|
end;
|
|
end;
|
|
|
|
procedure TElf32ResCreator.DoConvertStreams(Source, Dest: TStream);
|
|
|
|
Var
|
|
I : Integer;
|
|
ElfHeader: TElf32Header;
|
|
SectionHeader: TElf32sechdr;
|
|
ressym: TELF32ResourceSectionInfo;
|
|
resstr: TELF32ResourceSectionInfo;
|
|
reshash: TELF32ResourceSectionInfo;
|
|
resdata: TELF32ResourceSectionInfo;
|
|
resspare: TELF32ResourceSectionInfo;
|
|
|
|
begin
|
|
// Read and check the header of the input file. First check if it's a 32bit resource
|
|
// file...
|
|
SetLength(resheader,32);
|
|
Source.Read(resheader[1],32);
|
|
if (resheader<>reshdr) then
|
|
begin
|
|
// ...not a 32Bit resource file. Now let's see if it's a text or binary dfm/xfm/lfm file.
|
|
Source.Position:=0;
|
|
Source.Read(Signature,1);
|
|
if (Signature=$FF) then
|
|
begin
|
|
Source.Position:=0;
|
|
LoadBinaryDFMEntry(Source, FDataStream, FSymStream, ResourceEntries[CurrentResource]);
|
|
end
|
|
else if char(Signature) in ['o','O','i','I',' ',#13,#11,#9] then
|
|
begin
|
|
Source.Position:=0;
|
|
LoadTextDFMEntry(Source, FDataStream, FSymStream, ResourceEntries[CurrentResource]);
|
|
end
|
|
else
|
|
DoErrorFmt(SErrUnrecognizedFormat,[SourceFileName]);
|
|
inc(CurrentResource,1);
|
|
end
|
|
else // ...yes, it's a resource file.
|
|
while Source.Position<Source.Size do
|
|
begin
|
|
// Load Resource info, and copy the resource data into the DataStream
|
|
LoadRESEntry(Source, FDataStream, FSymStream, ResourceEntries[CurrentResource]);
|
|
inc(CurrentResource,1);
|
|
// if we hit the current limit of allocated ResourceEntries in the
|
|
// array, allocate some more space
|
|
if (CurrentResource>=length(ResourceEntries)) then
|
|
setlength(ResourceEntries,length(ResourceEntries)+1024);
|
|
end;
|
|
|
|
// downsize the ResourceEntries to the really needed size
|
|
SetLength(ResourceEntries,CurrentResource);
|
|
|
|
// Write the symbol table - ressym
|
|
ressym.ptr:=FSectionStream.Position+sizeof(TElf32Header);
|
|
FSymStream.Position:=0;
|
|
FSectionStream.CopyFrom(FSymStream,FSymStream.Size);
|
|
|
|
// resstr
|
|
resstr.ptr:=FSectionStream.Position+sizeof(TElf32Header);
|
|
resstr.size:=0;
|
|
// TODO: Load string data here
|
|
doalign(4);
|
|
|
|
// Now write the ResourceInfos.
|
|
reshash.ptr:=FSectionStream.Position+sizeof(TElf32Header);
|
|
for i:=0 to high(ResourceEntries) do
|
|
begin
|
|
FSectionStream.Write(ResourceEntries[i],sizeof(TELF32ResourceInfo));
|
|
end;
|
|
doalign(4);
|
|
|
|
// Next write the resource data stream
|
|
resdata.ptr:=FSectionStream.Position+sizeof(TElf32Header);
|
|
FDataStream.Position:=0;
|
|
FSectionStream.CopyFrom(FDataStream,FDataStream.Size);
|
|
doalign(4);
|
|
|
|
// resspare
|
|
resspare.ptr:=FSectionStream.Position+sizeof(TElf32Header);
|
|
// don't write anything, this is an empty section
|
|
|
|
// shstrtab - this is not aligned
|
|
shstrtab_ofs:=FSectionStream.Position+sizeof(TElf32Header);
|
|
FSectionStream.Write(shstrtab[1],length(shstrtab));
|
|
|
|
// Write 12 section headers. The headers itself don't need to be aligned,
|
|
// as their size can be divided by 4. As shstrtab is uneven and not aligned,
|
|
// we however need to align the start of the section header table
|
|
doalign(4);
|
|
sectionheader_ofs:=FSectionStream.Position+sizeof(TElf32Header);;
|
|
|
|
// empty one
|
|
fillchar(SectionHeader,sizeof(SectionHeader),0);
|
|
FSectionStream.Write(SectionHeader,sizeOf(SectionHeader));
|
|
|
|
// .text
|
|
SectionHeader.sh_name:=$1B;
|
|
SectionHeader.sh_type:=1; // PROGBITS
|
|
SectionHeader.sh_flags:=6; // AX
|
|
SectionHeader.sh_addr:=0;
|
|
SectionHeader.sh_offset:=sizeof(TElf32Header); // after header, dummy as size is 0
|
|
SectionHeader.sh_size:=0; // yep, pretty empty it is
|
|
SectionHeader.sh_link:=0;
|
|
SectionHeader.sh_info:=0;
|
|
SectionHeader.sh_addralign:=4; // alignment
|
|
SectionHeader.sh_entsize:=0;
|
|
FSectionStream.Write(SectionHeader,sizeOf(SectionHeader));
|
|
|
|
// .data
|
|
SectionHeader.sh_name:=$21;
|
|
SectionHeader.sh_type:=1; // PROGBITS
|
|
SectionHeader.sh_flags:=3; // WA
|
|
SectionHeader.sh_addr:=0;
|
|
SectionHeader.sh_offset:=sizeof(TElf32Header); // after header, dummy as size is 0
|
|
SectionHeader.sh_size:=0; // yep, pretty empty it is
|
|
SectionHeader.sh_link:=0;
|
|
SectionHeader.sh_info:=0;
|
|
SectionHeader.sh_addralign:=4; // alignment
|
|
SectionHeader.sh_entsize:=0;
|
|
FSectionStream.Write(SectionHeader,sizeOf(SectionHeader));
|
|
|
|
// .bss
|
|
SectionHeader.sh_name:=$27;
|
|
SectionHeader.sh_type:=8; // NOBITS
|
|
SectionHeader.sh_flags:=3; // WA
|
|
SectionHeader.sh_addr:=0;
|
|
SectionHeader.sh_offset:=sizeof(TElf32Header); // after header, dummy as size is 0
|
|
SectionHeader.sh_size:=0; // yep, pretty empty it is
|
|
SectionHeader.sh_link:=0;
|
|
SectionHeader.sh_info:=0;
|
|
SectionHeader.sh_addralign:=4; // alignment
|
|
SectionHeader.sh_entsize:=0;
|
|
FSectionStream.Write(SectionHeader,sizeOf(SectionHeader));
|
|
|
|
// fpc.ressym
|
|
SectionHeader.sh_name:=$2C;
|
|
SectionHeader.sh_type:=1; // PROGBITS
|
|
SectionHeader.sh_flags:=2; // A
|
|
SectionHeader.sh_addr:=0;
|
|
SectionHeader.sh_offset:=ressym.ptr; // directly after header
|
|
SectionHeader.sh_size:=FSymStream.Size;
|
|
SectionHeader.sh_link:=0;
|
|
SectionHeader.sh_info:=0;
|
|
SectionHeader.sh_addralign:=1; // DON'T align this, as this section will be merged by ld
|
|
SectionHeader.sh_entsize:=0;
|
|
FSectionStream.Write(SectionHeader,sizeOf(SectionHeader));
|
|
|
|
// fpc.resstr
|
|
SectionHeader.sh_name:=$37;
|
|
SectionHeader.sh_type:=1; // PROGBITS
|
|
SectionHeader.sh_flags:=2; // A
|
|
SectionHeader.sh_addr:=0;
|
|
SectionHeader.sh_offset:=resstr.ptr;
|
|
SectionHeader.sh_size:=0; // currently empty
|
|
SectionHeader.sh_link:=0;
|
|
SectionHeader.sh_info:=0;
|
|
SectionHeader.sh_addralign:=4; // alignment
|
|
SectionHeader.sh_entsize:=0;
|
|
FSectionStream.Write(SectionHeader,sizeOf(SectionHeader));
|
|
|
|
// fpc.reshash
|
|
SectionHeader.sh_name:=$42;
|
|
SectionHeader.sh_type:=1; // PROGBITS
|
|
SectionHeader.sh_flags:=2; // A
|
|
SectionHeader.sh_addr:=0;
|
|
SectionHeader.sh_offset:=reshash.ptr;
|
|
SectionHeader.sh_size:=length(ResourceEntries)*sizeof(TELF32ResourceInfo);
|
|
SectionHeader.sh_link:=0;
|
|
SectionHeader.sh_info:=0;
|
|
SectionHeader.sh_addralign:=4; // alignment
|
|
SectionHeader.sh_entsize:=0;
|
|
FSectionStream.Write(SectionHeader,sizeOf(SectionHeader));
|
|
|
|
// fpc.resdata
|
|
SectionHeader.sh_name:=$4e;
|
|
SectionHeader.sh_type:=1; // PROGBITS
|
|
SectionHeader.sh_flags:=2; // A
|
|
SectionHeader.sh_addr:=0;
|
|
SectionHeader.sh_offset:=resdata.ptr;
|
|
SectionHeader.sh_size:=FDataStream.Size;
|
|
SectionHeader.sh_link:=0;
|
|
SectionHeader.sh_info:=0;
|
|
SectionHeader.sh_addralign:=4; // alignment
|
|
SectionHeader.sh_entsize:=0;
|
|
FSectionStream.Write(SectionHeader,sizeOf(SectionHeader));
|
|
|
|
// fpc.resspare
|
|
// Not used in V1
|
|
SectionHeader.sh_name:=$5a;
|
|
SectionHeader.sh_type:=8; // NOBITS
|
|
SectionHeader.sh_flags:=2; // A
|
|
SectionHeader.sh_addr:=0;
|
|
SectionHeader.sh_offset:=resspare.ptr; // fake, as it's empty, should be equal to shstrtab's offset
|
|
SectionHeader.sh_size:=0; //DataStream.Size; // Leave as much room as we currently have in resdata section
|
|
SectionHeader.sh_link:=0;
|
|
SectionHeader.sh_info:=0;
|
|
SectionHeader.sh_addralign:=4; // alignment
|
|
SectionHeader.sh_entsize:=0;
|
|
FSectionStream.Write(SectionHeader,sizeOf(SectionHeader));
|
|
|
|
// .shstrtab
|
|
SectionHeader.sh_name:=$11;
|
|
SectionHeader.sh_type:=3; // STRTAB
|
|
SectionHeader.sh_flags:=0;
|
|
SectionHeader.sh_addr:=0;
|
|
SectionHeader.sh_offset:=shstrtab_ofs; // $3E
|
|
SectionHeader.sh_size:=$67;
|
|
SectionHeader.sh_link:=0;
|
|
SectionHeader.sh_info:=0;
|
|
SectionHeader.sh_addralign:=1; // alignment
|
|
SectionHeader.sh_entsize:=0;
|
|
FSectionStream.Write(SectionHeader,sizeOf(SectionHeader));
|
|
|
|
// .symtab
|
|
SectionHeader.sh_name:=$01;
|
|
SectionHeader.sh_type:=2; // SYMTAB
|
|
SectionHeader.sh_flags:=0;
|
|
SectionHeader.sh_addr:=0;
|
|
SectionHeader.sh_offset:=FSectionStream.Position+sizeof(TElf32Header)+sizeOf(SectionHeader)+sizeOf(SectionHeader); // will come directly after this and the next section. $0288;
|
|
SectionHeader.sh_size:=$90;
|
|
SectionHeader.sh_link:=$0B;
|
|
SectionHeader.sh_info:=$09;
|
|
SectionHeader.sh_addralign:=4; // alignment
|
|
SectionHeader.sh_entsize:=$10;
|
|
FSectionStream.Write(SectionHeader,sizeOf(SectionHeader));
|
|
|
|
// .strtab
|
|
SectionHeader.sh_name:=$09;
|
|
SectionHeader.sh_type:=3; // STRTAB
|
|
SectionHeader.sh_flags:=0;
|
|
SectionHeader.sh_addr:=0;
|
|
SectionHeader.sh_offset:=FSectionStream.Position+sizeof(TElf32Header)+sizeOf(SectionHeader)+$90; // will come after this sectionheader and the $90 bytes symtab - $0318; end of file
|
|
SectionHeader.sh_size:=1;
|
|
SectionHeader.sh_link:=0;
|
|
SectionHeader.sh_info:=0;
|
|
SectionHeader.sh_addralign:=1; // alignment
|
|
SectionHeader.sh_entsize:=$0;
|
|
FSectionStream.Write(SectionHeader,sizeOf(SectionHeader));
|
|
|
|
// now write the symbol table
|
|
FSectionStream.Write(symtab[1],length(symtab));
|
|
// We don't need to align it, as it's $90 in size
|
|
|
|
// now write the string table, it's just a single byte
|
|
FSectionStream.Write(strtab[1],1);
|
|
|
|
// Ok, we are done, now let's really write something to disk...
|
|
|
|
// First write the ELF header
|
|
|
|
fillchar(ElfHeader,sizeof(ElfHeader),0);
|
|
ElfHeader.magic0123:=$464c457f; { = #127'ELF' }
|
|
ElfHeader.file_class:=1;
|
|
ElfHeader.data_encoding:=1;
|
|
ElfHeader.file_version:=1;
|
|
ElfHeader.e_type:=1;
|
|
ElfHeader.e_machine:=3;
|
|
ElfHeader.e_version:=1;
|
|
ElfHeader.e_shoff:=sectionheader_ofs;
|
|
ElfHeader.e_shstrndx:=9;
|
|
ElfHeader.e_shnum:=12;
|
|
ElfHeader.e_ehsize:=sizeof(TElf32header);
|
|
ElfHeader.e_shentsize:=sizeof(TElf32sechdr);
|
|
|
|
Dest.Write(ElfHeader,sizeof(TElf32header));
|
|
|
|
// And now let's dump our whole memorystream into it.
|
|
FSectionStream.Position:=0;
|
|
Dest.CopyFrom(FSectionStream,FsectionStream.Size);
|
|
end;
|
|
|
|
|
|
{ TElf64Creator }
|
|
|
|
procedure TElf64ResCreator.DoConvertStreams(Source, Dest: TStream);
|
|
begin
|
|
DoError('64 bits resources not yet supported')
|
|
end;
|
|
|
|
end.
|