123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604 |
- { *********************************************************************** }
- { }
- { 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;
- type
- TSectionKind = (skSymtab, skStrtab, skShstrtab, skText, skData, skBss, skFpcRessym, skFpcResstr,
- skFpcReshash, skFpcResdata, skFpcResspare);
- // Do not change the following consts, they are dummy tables to generate an .o that makes ld happy
- const 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;
- // 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;
- FShStrTab: string;
- FShStrOffsets: array[TSectionKind] of Longint;
- 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;
- procedure AddSection(aKind: TSectionKind; atype, aflags, aaddr, aoffset, asize, alink, ainfo, aaddralign, aentsize: longint);
- 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 }
- const
- sectionNames: array[TSectionKind] of PChar = (
- '.symtab',
- '.strtab',
- '.shstrtab',
- '.text',
- '.data',
- '.bss',
- '.fpc.ressym',
- '.fpc.resstr',
- '.fpc.reshash',
- '.fpc.resdata',
- '.fpc.resspare'
- );
- procedure TElfResCreator.AllocateData;
- var
- i: TSectionKind;
- begin
- FSectionStream:=TMemoryStream.Create;
- FDataStream:=TMemoryStream.Create;
- FSymStream:=TMemoryStream.Create;
- FHashStream:=TMemoryStream.Create;
- for i := Low(TSectionKind) to High(TSectionKind) do
- begin
- FShStrOffsets[i] := Length(FShStrTab) + 1;
- FShStrTab := FShStrTab + #0 + string(sectionNames[i]);
- end;
- FShStrTab := FShStrTab + #0#0;
- 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.AddSection(aKind: TSectionKind; atype, aflags, aaddr, aoffset, asize, alink, ainfo, aaddralign, aentsize: longint);
- var
- sechdr: TElf32sechdr;
- begin
- sechdr.sh_name := FShStrOffsets[aKind];
- sechdr.sh_type := atype;
- sechdr.sh_flags := aflags;
- sechdr.sh_addr := aaddr;
- sechdr.sh_offset := aoffset;
- sechdr.sh_size := asize;
- sechdr.sh_link := alink;
- sechdr.sh_info := ainfo;
- sechdr.sh_addralign := aaddralign;
- sechdr.sh_entsize := aentsize;
- FSectionStream.Write(sechdr, sizeOf(sechdr));
- 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(FShStrtab[1], length(FShStrtab));
- // 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));
- AddSection(skText, SHT_PROGBITS, 6 {AX}, 0, sizeof(TElf32Header), 0, 0, 0, 4, 0);
- AddSection(skData, SHT_PROGBITS, 3 {WA}, 0, sizeof(TElf32Header), 0, 0, 0, 4, 0);
- AddSection(skBss, SHT_NOBITS, 3 {WA}, 0, sizeof(TElf32Header), 0, 0, 0, 4, 0);
- AddSection(skFpcRessym, SHT_PROGBITS, 2 {A}, 0, ressym.ptr, FSymStream.Size, 0, 0, 1, 0);
- AddSection(skFpcResstr, SHT_PROGBITS, 2 {A}, 0, resstr.ptr, 0, 0, 0, 4, 0);
- AddSection(skFpcReshash, SHT_PROGBITS, 2 {A}, 0, reshash.ptr, length(ResourceEntries)*sizeof(TELF32ResourceInfo), 0, 0, 4, 0);
- AddSection(skFpcResdata, SHT_PROGBITS, 2 {A}, 0, resdata.ptr, FDataStream.Size, 0, 0, 4, 0);
- AddSection(skFpcResspare, SHT_NOBITS, 2 {A}, 0, resspare.ptr, 0, 0, 0, 4, 0);
- AddSection(skShstrtab, SHT_STRTAB, 0, 0, shstrtab_ofs, length(FShStrtab), 0, 0, 1, 0);
- AddSection(skSymtab, SHT_SYMTAB, 0, 0, FSectionStream.Position+sizeof(TElf32Header) + 2 * sizeof(SectionHeader), length(symtab), $0B, $09, 4, $10);
- AddSection(skStrtab, SHT_STRTAB, 0, 0, FSectionStream.Position+sizeof(TElf32Header) + sizeof(SectionHeader) + length(symtab), 1, 0, 0, 1, 0);
- // 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.
|