{ *********************************************************************** }
{                                                                         }
{  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.
