1{
2    This file is part of the Free Pascal run time library.
3    Copyright (c) 2008 by Giulio Bernardi
4
5    Resource writer for ELF files
6
7    See the file COPYING.FPC, included in this distribution,
8    for details about the copyright.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13
14 **********************************************************************}
15
16unit elfwriter;
17
18{$MODE OBJFPC} {$H+}
19
20interface
21
22uses
23  Classes, SysUtils, resource, elfconsts, elftypes;
24
25type
26  EElfResourceWriterException = class(EResourceWriterException);
27  EElfResourceWriterUnknownMachineException = class(EElfResourceWriterException);
28  EElfResourceWriterUnknownClassException = class(EElfResourceWriterException);
29  EElfResourceWriterUnknownSectionException = class(EElfResourceWriterException);
30
31type
32  { TElfResourceWriter }
33
34  TElfResourceWriter = class (TAbstractResourceWriter)
35  private
36    fExtensions : string;
37    fDescription : string;
38    fMachineTypeInt : integer;
39    fMachineType : TElfMachineType;
40    fOrder : byte;
41    fBits : byte;
42    fNativeOrder : integer;
43    fOppositeEndianess : boolean;
44    procedure SetDefaultTarget;
45    procedure SetMachineType(const aMachineType : TElfMachineType);
46
47    procedure WriteElfIdent(aStream : TStream);
48  protected
49    function GetExtensions : string; override;
50    function GetDescription : string; override;
51    procedure Write(aResources : TResources; aStream : TStream); override;
52  public
53    constructor Create; override;
54    destructor Destroy; override;
55    property MachineType : TElfMachineType read fMachineType write SetMachineType;
56  end;
57
58
59implementation
60
61uses resourcetree, strtable, fpcrestypes;
62
63type
64
65  { TElfSections }
66
67  TElfSections = class
68  private
69    fList : TFPList;
70    fStringTable : TObjectStringTable;
71    function GetCount : integer;
72    function GetItem(index : integer) : PElf64SectHdr;
73  protected
74  public
75    constructor Create(aStringTable : TObjectStringTable);
76    destructor Destroy; override;
77    function Add(const aName: string; const aType, aFlags : longword;
78      const aOffset, aSize : qword; const aAddrAlign : longword) : integer; overload;
79    function Add(const aName: string; const aType, aFlags : longword;
80      const aOffset, aSize, aEntSize : qword; const aLink, aInfo,aAddrAlign
81      : longword) : integer; overload;
82    procedure Clear;
83    property Count : integer read GetCount;
84    property Items[index : integer] : PElf64SectHdr read GetItem; default;
85  end;
86
87  { TElfSymbolTable }
88
89  TElfSymbolTable = class
90  private
91    fList : TFPList;
92    fSectFree : integer;
93    fLocFree : integer;
94    fStringTable : TObjectStringTable;
95    function CreateSym(const aName : string; const aValue, aSize : qword;
96      const aBind, aType : byte; const aSectIdx : integer) : PElf64Symbol;
97    procedure Clear;
98    function GetCount : integer;
99    function GetItem(index : integer) : PElf64Symbol;
100  protected
101  public
102    constructor Create(aStringTable : TObjectStringTable);
103    destructor Destroy; override;
104    procedure AddSection(const aSectIdx : integer);
105    procedure AddLocal(const aName : string; const aValue, aSize : qword;
106      const aType : byte; const aSectIdx : integer);
107    procedure AddGlobal(const aName : string; const aValue, aSize : qword;
108      const aType : byte; const aSectIdx : integer);
109    property Count : integer read GetCount;
110    property Items[index : integer] : PElf64Symbol read GetItem; default;
111    property FirstGlobal : integer read fLocFree;
112  end;
113
114  { TAbstractElfSubWriter }
115
116  TAbstractElfSubWriter = class
117  private
118  protected
119    fParent : TElfResourceWriter;
120    fOppositeEndianess : boolean;
121    fMachineType : integer;
122    fDataAlignment : longword;
123    fMachineFlags : longword;
124    fRoot : TRootResTreeNode;
125    fSectStringTable : TObjectStringTable;
126    fSymStringTable  : TObjectStringTable;
127    fResStringTable : TResStringTable;
128    fSymbolTable : TElfSymbolTable;
129    fSections : TElfSections;
130    fShStrTabIdx : integer;
131    fSymStrTabIdx : integer;
132    fSymTabIdx : integer;
133    fSectHdrOffset : qword;
134    fCurOfs : longword;
135    fDataCurOfs : longword;
136    fSectionStart : qword;
137    procedure Align(aBound : integer; aStream : TStream);
138    function NextAligned(aBound, aValue : longword) : longword;
139    procedure PrescanResourceTree; virtual; abstract;
140    function PrescanNode(aNode : TResourceTreeNode; aNodeSize : longword) : longword;
141    procedure WriteNodeInfo(aStream : TStream; aNode : TResourceTreeNode); virtual; abstract;
142    procedure WriteSubNodes(aStream : TStream; aNode : TResourceTreeNode);
143    procedure WriteResStringTable(aStream : TStream);
144    procedure WriteRawData(aStream : TStream);
145    procedure WriteResData(aStream : TStream; aNode : TResourceTreeNode);
146    procedure AddEmptySections(aResources : TResources; aStream : TStream);
147    procedure WriteStrTab(aStream : TStream);
148    procedure WriteShStrTab(aStream : TStream);
149    procedure Write(aResources : TResources; aStream : TStream); virtual; abstract;
150  public
151    constructor Create(aParent : TElfResourceWriter; const aMachineType : integer;
152      const aOppositeEndianess : boolean); virtual;
153    destructor Destroy; override;
154  end;
155
156type
157  TElfRelocInfo = record
158    RelocType : longword;
159    SectionType : integer;
160  end;
161(*
162Almost all differences in 32 and 64 bit elf files lie in record sizes.
163Generics don't work with record types, so use macros to do this task
164(uglier, but should be the same)
165*)
166
167{$MACRO ON}
168
169//Define TElf32RelocTable and TElf32SubWriter
170
171{$DEFINE _TElfRelocTable_:=TElf32RelocTable}
172{$DEFINE _TPElfRela_:=PElf32Rela}
173{$DEFINE _TElfRela_:=TElf32Rela}
174{$DEFINE _TPElfRel_:=PElf32Rel}
175{$DEFINE _TElfRel_:=TElf32Rel}
176{$DEFINE _Tword_:=longword}
177{$DEFINE _TElfSubWriter_:=TElf32SubWriter}
178{$DEFINE _TElfHdr_:=TElf32Hdr}
179{$DEFINE _TElfSectHdr_:=TElf32SectHdr}
180{$DEFINE _TElfSymbol_:=TElf32Symbol}
181{$DEFINE _TResHdr_:=TResHdr32}
182{$DEFINE _TResInfoNode_:=TResInfoNode32}
183{$INCLUDE elfsubwriter.inc}
184
185
186//Define TElf64RelocTable and TElf32SubWriter
187
188{$DEFINE _TElfRelocTable_:=TElf64RelocTable}
189{$DEFINE _TPElfRela_:=PElf64Rela}
190{$DEFINE _TElfRela_:=TElf64Rela}
191{$DEFINE _TPElfRel_:=PElf64Rel}
192{$DEFINE _TElfRel_:=TElf64Rel}
193{$DEFINE _Tword_:=qword}
194{$DEFINE _TElfSubWriter_:=TElf64SubWriter}
195{$DEFINE _TElfHdr_:=TElf64Hdr}
196{$DEFINE _TElfSectHdr_:=TElf64SectHdr}
197{$DEFINE _TElfSymbol_:=TElf64Symbol}
198{$DEFINE _TResHdr_:=TResHdr64}
199{$DEFINE _TResInfoNode_:=TResInfoNode64}
200{$INCLUDE elfsubwriter.inc}
201
202
203//Clean all this stuff...
204
205{$UNDEF _TElfRelocTable_}
206{$UNDEF _TPElfRela_}
207{$UNDEF _TElfRela_}
208{$UNDEF _TPElfRel_}
209{$UNDEF _TElfRel_}
210{$UNDEF _Tword_}
211{$UNDEF _TElfSubWriter_}
212{$UNDEF _TElfHdr_}
213{$UNDEF _TElfSectHdr_}
214{$UNDEF _TElfSymbol_}
215{$UNDEF _TResHdr_}
216{$UNDEF _TResInfoNode_}
217
218
219{ TElfSections }
220
221function TElfSections.GetCount: integer;
222begin
223  Result:=fList.Count;
224end;
225
226function TElfSections.GetItem(index: integer): PElf64SectHdr;
227begin
228  Result:=PElf64SectHdr(fList[index]);
229end;
230
231constructor TElfSections.Create(aStringTable : TObjectStringTable);
232begin
233  fList:=TFPList.Create;
234  fStringTable:=aStringTable;
235  Add('',0,0,0,0,0); //empty section
236end;
237
238destructor TElfSections.Destroy;
239var i : integer;
240    p : PElf64SectHdr;
241begin
242  for i:=0 to fList.Count-1 do
243  begin
244    p:=PElf64SectHdr(fList[i]);
245    FreeMem(p);
246  end;
247
248  fList.Free;
249end;
250
251function TElfSections.Add(const aName: string; const aType, aFlags : longword;
252  const aOffset, aSize : qword; const aAddrAlign : longword) : integer;
253begin
254  Result:=Add(aName,aType,aFlags,aOffset,aSize,0,0,0,aAddrAlign);
255end;
256
257function TElfSections.Add(const aName: string; const aType, aFlags: longword;
258  const aOffset, aSize, aEntSize: qword; const aLink, aInfo, aAddrAlign: longword): integer;
259var p : PElf64SectHdr;
260begin
261  Result:=fList.Count;
262  p:=GetMem(sizeof(TElf64SectHdr));
263  p^.NameIdx:=fStringTable.Add(aName);
264  p^._Type:=aType;
265  p^.Flags:=aFlags;
266  p^.Address:=0;
267  p^.Offset:=aOffset;;
268  p^.Size:=aSize;
269  p^.Link:=aLink;
270  p^.Info:=aInfo;
271  p^.AddrAlign:=aAddrAlign;
272  p^.EntSize:=aEntSize;
273  fList.Add(p);
274end;
275
276procedure TElfSections.Clear;
277var i : integer;
278    p, first : PElf64SectHdr;
279begin
280  first:=PElf64SectHdr(fList[0]);
281  for i:=1 to fList.Count-1 do
282  begin
283    p:=PElf64SectHdr(fList[i]);
284    FreeMem(p);
285  end;
286  fList.Clear;
287  fList.Add(first);
288end;
289
290{ TElfSymbolTable }
291
292constructor TElfSymbolTable.Create(aStringTable: TObjectStringTable);
293var p : PElf64Symbol;
294begin
295  fList:=TFPList.Create;
296  fStringTable:=aStringTable;
297  p:=CreateSym('',0,0,0,0,0);
298  fList.Add(p);
299  fSectFree:=1;
300  fLocFree:=1;
301end;
302
303destructor TElfSymbolTable.Destroy;
304begin
305  Clear;
306  fList.Free;
307end;
308
309procedure TElfSymbolTable.AddSection(const aSectIdx: integer);
310var p : PElf64Symbol;
311begin
312  p:=CreateSym('',0,0,STB_LOCAL,STT_SECTION,aSectIdx);
313  if fSectFree=fList.Count then
314    fList.Add(p)
315  else
316    fList.Insert(fSectFree,p);
317  inc(fSectFree);
318  inc(fLocFree);
319end;
320
321procedure TElfSymbolTable.AddLocal(const aName: string; const aValue,
322  aSize: qword; const aType: byte; const aSectIdx: integer);
323var p : PElf64Symbol;
324begin
325  p:=CreateSym(aName,aValue,aSize,STB_LOCAL,aType,aSectIdx);
326  if fLocFree=fList.Count then
327    fList.Add(p)
328  else
329    fList.Insert(fLocFree,p);
330  inc(fLocFree);
331end;
332
333procedure TElfSymbolTable.AddGlobal(const aName: string; const aValue,
334  aSize: qword; const aType: byte; const aSectIdx: integer);
335var p : PElf64Symbol;
336begin
337  p:=CreateSym(aName,aValue,aSize,STB_GLOBAL,aType,aSectIdx);
338  fList.Add(p)
339end;
340
341procedure TElfSymbolTable.Clear;
342var p : PElf64Symbol;
343    i : integer;
344begin
345  for i:=0 to fList.Count-1 do
346  begin
347    p:=PElf64Symbol(fList[i]);
348    FreeMem(p);
349  end;
350  fList.Clear;
351end;
352
353function TElfSymbolTable.GetCount: integer;
354begin
355  Result:=fList.Count;
356end;
357
358function TElfSymbolTable.GetItem(index: integer): PElf64Symbol;
359begin
360  Result:=PElf64Symbol(fList[index]);
361end;
362
363function TElfSymbolTable.CreateSym(const aName : string; const aValue, aSize : qword;
364      const aBind, aType : byte; const aSectIdx : integer) : PElf64Symbol;
365var p : PElf64Symbol;
366begin
367  p:=GetMem(sizeof(TElf64Symbol));
368  p^.Name:=fStringTable.Add(aName);
369  p^.Value:=aValue;
370  p^.Size:=aSize;
371  p^.Info:=aBind shl 4;
372  p^.Info:=p^.Info or (aType and $0F);
373  p^.Other:=0;
374  p^.SectIdx:=aSectIdx;
375  Result:=p;
376end;
377
378{ TAbstractElfSubWriter }
379
380procedure TAbstractElfSubWriter.Align(aBound : integer; aStream : TStream);
381var topad,tmp : integer;
382    qw : qword;
383begin
384  qw:=0;
385  topad:=aBound-(aStream.Position mod aBound);
386  if topad<>aBound then
387    while topad>0 do
388    begin
389      if topad>8 then tmp:=8 else tmp:=topad;
390      aStream.WriteBuffer(qw,tmp);
391      dec(topad,tmp);
392    end;
393end;
394
395function TAbstractElfSubWriter.NextAligned(aBound, aValue : longword) : longword;
396var topad : longword;
397begin
398  Result:=aValue;
399  topad:=aBound-(aValue mod aBound);
400  if topad<>aBound then inc(Result,topad);
401end;
402
403function TAbstractElfSubWriter.PrescanNode(aNode: TResourceTreeNode;
404  aNodeSize: longword): longword;
405var curofs : longword;
406    i : integer;
407    subnode : TResourceTreeNode;
408begin
409  if aNode.IsLeaf then
410  begin
411    Result:=aNode.SubDirRVA;
412    exit;
413  end;
414
415  if aNode.Desc.DescType=dtName then
416    aNode.NameRVA:=fResStringTable.Add(aNode.Desc.Name);
417
418  //first node subnodes begin at curofs (after all node headers)
419  curofs:=aNode.SubDirRva+(aNode.NamedCount+aNode.IDCount)*aNodeSize;
420  for i:=0 to aNode.NamedCount-1 do
421  begin
422    subnode:=aNode.NamedEntries[i];
423    subnode.SubDirRVA:=curofs;
424    curofs:=PrescanNode(subnode,aNodeSize);
425  end;
426  for i:=0 to aNode.IDCount-1 do
427  begin
428    subnode:=aNode.IDEntries[i];
429    subnode.SubDirRVA:=curofs;
430    curofs:=PrescanNode(subnode,aNodeSize);
431  end;
432  Result:=curofs;
433end;
434
435procedure TAbstractElfSubWriter.WriteSubNodes(aStream: TStream;
436  aNode: TResourceTreeNode);
437var i : integer;
438begin
439  for i:=0 to aNode.NamedCount-1 do
440    WriteNodeInfo(aStream,aNode.NamedEntries[i]);
441  for i:=0 to aNode.IDCount-1 do
442    WriteNodeInfo(aStream,aNode.IDEntries[i]);
443
444  for i:=0 to aNode.NamedCount-1 do
445    WriteSubNodes(aStream,aNode.NamedEntries[i]);
446  for i:=0 to aNode.IDCount-1 do
447    WriteSubNodes(aStream,aNode.IDEntries[i]);
448end;
449
450procedure TAbstractElfSubWriter.WriteResStringTable(aStream: TStream);
451begin
452  if fResStringTable.Used then
453    fResStringTable.WriteToStream(aStream);
454  Align(fDataAlignment,aStream);
455end;
456
457
458procedure TAbstractElfSubWriter.WriteRawData(aStream: TStream);
459begin
460  WriteResData(aStream,fRoot);
461end;
462
463procedure TAbstractElfSubWriter.WriteResData(aStream: TStream;
464  aNode: TResourceTreeNode);
465var rawdata : TStream;
466    i : integer;
467begin
468  if aNode.IsLeaf then
469  begin
470    rawdata:=aNode.Data.RawData;
471    rawdata.Position:=0;
472    aStream.CopyFrom(rawdata,rawdata.Size);
473    Align(fDataAlignment,aStream);
474    exit;
475  end;
476  for i:=0 to aNode.NamedCount-1 do
477    WriteResData(aStream,aNode.NamedEntries[i]);
478  for i:=0 to aNode.IDCount-1 do
479    WriteResData(aStream,aNode.IDEntries[i]);
480end;
481
482procedure TAbstractElfSubWriter.AddEmptySections(aResources : TResources; aStream: TStream);
483begin
484  Align(fDataAlignment,aStream);
485  fSections.Add(HandlesSectName,SHT_NOBITS,SHF_ALLOC or SHF_WRITE,
486    aStream.Position,fDataAlignment*aResources.Count,fDataAlignment);
487  fSections.Add('.text',SHT_PROGBITS,SHF_ALLOC or SHF_EXECINSTR,aStream.Position,0,4);
488  fSections.Add('.data',SHT_PROGBITS,SHF_ALLOC or SHF_WRITE,aStream.Position,0,4);
489  fSections.Add('.bss', SHT_NOBITS,SHF_ALLOC or SHF_WRITE,aStream.Position,0,4);
490  fSections.Add('.note.GNU-stack', SHT_PROGBITS,0,aStream.Position,0,1);
491end;
492
493procedure TAbstractElfSubWriter.WriteStrTab(aStream: TStream);
494begin
495  fSymStrTabIdx:=fSections.Add('.strtab',SHT_STRTAB,0,aStream.Position,
496    fSymStringTable.Size,1);
497  fSymStringTable.WriteToStream(aStream);
498end;
499
500procedure TAbstractElfSubWriter.WriteShStrTab(aStream: TStream);
501const namelen = length('.shstrtab')+1;
502begin
503  fShStrTabIdx:=fSections.Add('.shstrtab',SHT_STRTAB,0,aStream.Position,
504    fSectStringTable.Size+namelen,1);
505  fSectStringTable.WriteToStream(aStream);
506end;
507
508constructor TAbstractElfSubWriter.Create(aParent : TElfResourceWriter;
509  const aMachineType: integer; const aOppositeEndianess: boolean);
510begin
511  fMachineType:=aMachineType;
512  fOppositeEndianess:=aOppositeEndianess;
513  fRoot:=nil;
514  fParent:=aParent;
515  fSectStringTable:=TObjectStringTable.Create(nil,0);
516  fSymStringTable:=TObjectStringTable.Create(nil,0);
517  fResStringTable:=TResStringTable.Create;
518  fSymbolTable:=TElfSymbolTable.Create(fSymStringTable);
519  fSections:=TElfSections.Create(fSectStringTable);
520  fShStrTabIdx:=0;
521  fSymStrTabIdx:=0;
522  fSectHdrOffset:=0;
523  fCurOfs:=0;
524  fDataCurOfs:=0;
525  fSectionStart:=0;
526end;
527
528destructor TAbstractElfSubWriter.Destroy;
529begin
530  fSectStringTable.Free;
531  fSymStringTable.Free;
532  fResStringTable.Free;
533  fSymbolTable.Free;
534  fSections.Free;
535end;
536
537{ TElfResourceWriter }
538
539procedure TElfResourceWriter.SetDefaultTarget;
540begin
541  {$INCLUDE elfdefaulttarget.inc}
542end;
543
544procedure TElfResourceWriter.SetMachineType(const aMachineType: TElfMachineType);
545begin
546  case aMachineType of
547    emtsparc  : begin fMachineTypeInt:=EM_SPARC; fBits:=ELFCLASS32; fOrder:=ELFDATA2MSB; end;
548    emti386   : begin fMachineTypeInt:=EM_386; fBits:=ELFCLASS32; fOrder:=ELFDATA2LSB; end;
549    emtm68k   : begin fMachineTypeInt:=EM_68K; fBits:=ELFCLASS32; fOrder:=ELFDATA2MSB; end;
550    emtppc    : begin fMachineTypeInt:=EM_PPC; fBits:=ELFCLASS32; fOrder:=ELFDATA2MSB; end;
551    emtppc64  : begin fMachineTypeInt:=EM_PPC64; fBits:=ELFCLASS64;fOrder:=ELFDATA2MSB; end;
552    emtppc64le: begin fMachineTypeInt:=EM_PPC64; fBits:=ELFCLASS64;fOrder:=ELFDATA2LSB; end;
553    emtarm    : begin fMachineTypeInt:=EM_ARM; fBits:=ELFCLASS32; fOrder:=ELFDATA2LSB; end;
554    emtarmeb  : begin fMachineTypeInt:=EM_ARM; fBits:=ELFCLASS32; fOrder:=ELFDATA2MSB; end;
555    emtaarch64: begin fMachineTypeInt:=EM_AARCH64; fBits:=ELFCLASS64; fOrder:=ELFDATA2LSB; end;
556    emtalpha  : begin fMachineTypeInt:=EM_ALPHA; fBits:=ELFCLASS64; fOrder:=ELFDATA2LSB; end;
557    emtia64   : begin fMachineTypeInt:=EM_IA_64; fBits:=ELFCLASS64; fOrder:=ELFDATA2LSB; end;
558    emtx86_64 : begin fMachineTypeInt:=EM_X86_64; fBits:=ELFCLASS64; fOrder:=ELFDATA2LSB; end;
559    emtmips   : begin fMachineTypeInt:=EM_MIPS; fBits:=ELFCLASS32; fOrder:=ELFDATA2MSB; end;
560    emtmipsel : begin fMachineTypeInt:=EM_MIPS; fBits:=ELFCLASS32; fOrder:=ELFDATA2LSB; end
561    else
562      raise EElfResourceWriterUnknownMachineException.Create('');
563  end;
564  fMachineType:=aMachineType;
565  fOppositeEndianess:=fNativeOrder<>fOrder;
566end;
567
568procedure TElfResourceWriter.WriteElfIdent(aStream: TStream);
569var ident : TElfIdent;
570begin
571  ident.Magic:=ELFMAGIC;
572  ident.ElfClass:=fBits;
573  ident.ElfData:=fOrder;
574  ident.ElfVersion:=EV_CURRENT;
575  ident.OsAbi:=ELFOSABI_NONE; // UNIX System V ABI
576  ident.AbiVersion:=0;
577  FillByte(ident.Padding[9],length(ident.Padding),0);
578
579  aStream.WriteBuffer(ident,sizeof(ident));
580end;
581
582function TElfResourceWriter.GetExtensions: string;
583begin
584  Result:=fExtensions;
585end;
586
587function TElfResourceWriter.GetDescription: string;
588begin
589  Result:=fDescription;
590end;
591
592procedure TElfResourceWriter.Write(aResources: TResources; aStream: TStream);
593var subwriter : TAbstractElfSubWriter;
594begin
595  WriteElfIdent(aStream);
596  case fBits of
597    ELFCLASS32 : subwriter:=TElf32SubWriter.Create(self,fMachineTypeInt,fOppositeEndianess);
598    ELFCLASS64 : subwriter:=TElf64SubWriter.Create(self,fMachineTypeInt,fOppositeEndianess)
599  else
600    raise EElfResourceWriterUnknownClassException.Create('');
601  end;
602  try
603    subwriter.Write(aResources,aStream);
604  finally
605    subwriter.Free;
606  end;
607end;
608
609constructor TElfResourceWriter.Create;
610begin
611  fExtensions:='.o .or';
612  fDescription:='ELF resource writer';
613  SetDefaultTarget;
614end;
615
616destructor TElfResourceWriter.Destroy;
617begin
618
619end;
620
621initialization
622  TResources.RegisterWriter('.o',TElfResourceWriter);
623  TResources.RegisterWriter('.or',TElfResourceWriter);
624
625end.
626