mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-04-17 14:19:41 +02:00
3173 lines
107 KiB
ObjectPascal
3173 lines
107 KiB
ObjectPascal
{
|
|
Copyright (c) 1998-2006 by Peter Vreman
|
|
|
|
Contains the binary elf writer
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
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. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
****************************************************************************
|
|
}
|
|
unit ogelf;
|
|
|
|
{$i fpcdefs.inc}
|
|
|
|
interface
|
|
|
|
uses
|
|
{ common }
|
|
cclasses,globtype,
|
|
{ target }
|
|
systems,
|
|
{ assembler }
|
|
cpuinfo,cpubase,aasmbase,aasmtai,aasmdata,assemble,
|
|
{ output }
|
|
ogbase,
|
|
owbase;
|
|
|
|
type
|
|
TElfObjSection = class(TObjSection)
|
|
public
|
|
shstridx,
|
|
shtype,
|
|
shflags,
|
|
shlink,
|
|
shinfo,
|
|
shentsize : longint;
|
|
constructor create(AList:TFPHashObjectList;const Aname:string;Aalign:shortint;Aoptions:TObjSectionOptions);override;
|
|
constructor create_ext(aobjdata:TObjData;const Aname:string;Ashtype,Ashflags:longint;Aalign:shortint;Aentsize:longint);
|
|
constructor create_reloc(aobjdata:TObjData;const Aname:string;allocflag:boolean);
|
|
procedure writeReloc_internal(aTarget:TObjSection;offset:aword;len:byte;reltype:TObjRelocationType);override;
|
|
end;
|
|
|
|
TElfSymtabKind = (esk_obj,esk_exe,esk_dyn);
|
|
|
|
TElfSymtab = class(TElfObjSection)
|
|
public
|
|
kind: TElfSymtabKind;
|
|
fstrsec: TObjSection;
|
|
symidx: longint;
|
|
constructor create(aObjData:TObjData;aKind:TElfSymtabKind);reintroduce;
|
|
procedure writeSymbol(objsym:TObjSymbol;nameidx:longword=0);
|
|
procedure writeInternalSymbol(avalue:aword;astridx:longword;ainfo:byte;ashndx:word);
|
|
end;
|
|
|
|
TElfObjData = class(TObjData)
|
|
public
|
|
constructor create(const n:string);override;
|
|
function sectionname(atype:TAsmSectiontype;const aname:string;aorder:TAsmSectionOrder):string;override;
|
|
procedure CreateDebugSections;override;
|
|
procedure writereloc(data:aint;len:aword;p:TObjSymbol;reltype:TObjRelocationType);override;
|
|
end;
|
|
|
|
TElfObjectOutput = class(tObjOutput)
|
|
private
|
|
symtabsect: TElfSymtab;
|
|
shstrtabsect: TElfObjSection;
|
|
procedure createrelocsection(s:TElfObjSection;data:TObjData);
|
|
procedure createshstrtab(data:TObjData);
|
|
procedure createsymtab(data: TObjData);
|
|
procedure writesectionheader(s:TElfObjSection);
|
|
procedure section_write_symbol(p:TObject;arg:pointer);
|
|
procedure section_count_sections(p:TObject;arg:pointer);
|
|
procedure section_create_relocsec(p:TObject;arg:pointer);
|
|
procedure section_write_sechdr(p:TObject;arg:pointer);
|
|
protected
|
|
function writedata(data:TObjData):boolean;override;
|
|
public
|
|
constructor Create(AWriter:TObjectWriter);override;
|
|
end;
|
|
|
|
TElfAssembler = class(tinternalassembler)
|
|
constructor create(smart:boolean);override;
|
|
end;
|
|
|
|
TElfTarget=class(TObject)
|
|
public
|
|
class function encodereloc(objrel:TObjRelocation):byte;virtual;abstract;
|
|
class procedure loadreloc(objrel:TObjRelocation);virtual;abstract;
|
|
end;
|
|
|
|
TElfTargetClass=class of TElfTarget;
|
|
|
|
PSectionRec=^TSectionRec;
|
|
TSectionRec=record
|
|
sec: TObjSection;
|
|
relocpos: aword;
|
|
relocs: longint;
|
|
relentsize: longint;
|
|
end;
|
|
|
|
TElfObjInput=class(TObjInput)
|
|
private
|
|
FSecTbl: PSectionRec;
|
|
FSymTbl: PPointer;
|
|
FLoaded: PBoolean;
|
|
nsects: longword;
|
|
shentsize: longword;
|
|
shoffset: aword;
|
|
shstrndx: longword;
|
|
symtabndx: longword;
|
|
shstrtab: PChar;
|
|
strtab: PChar;
|
|
shstrtablen: longword;
|
|
strtablen: longword;
|
|
symtaboffset: aword;
|
|
syms: longword;
|
|
localsyms: longword;
|
|
symversions: PWord;
|
|
dynobj: boolean;
|
|
function LoadHeader:word;
|
|
procedure LoadSection(const hdr;index:longint;objdata:TObjData);
|
|
procedure LoadRelocations(const secrec:TSectionRec);
|
|
procedure LoadSymbols(objdata:TObjData;count,locals:longword);
|
|
procedure LoadDynamic(const hdr;objdata:TObjData);
|
|
public
|
|
constructor Create;override;
|
|
destructor Destroy;override;
|
|
function ReadObjData(AReader:TObjectreader;objdata:TObjData):boolean;override;
|
|
class function CanReadObjData(AReader:TObjectreader):boolean;override;
|
|
end;
|
|
|
|
TElfExeSection=class(TExeSection)
|
|
public
|
|
secshidx : longword; { index of the section header }
|
|
shstridx,
|
|
shtype,
|
|
shflags,
|
|
shlink,
|
|
shinfo,
|
|
shentsize : longword;
|
|
procedure AddObjSection(objsec:TObjSection;ignoreprops:boolean=false);override;
|
|
end;
|
|
|
|
TElfSegment=class
|
|
public
|
|
ptype: longword;
|
|
pflags: longword;
|
|
DataPos: aword;
|
|
DataSize: aword;
|
|
MemPos: aword;
|
|
MemSize: aword;
|
|
align: longword;
|
|
//physaddr: aword;
|
|
FSectionList: TFPObjectList;
|
|
constructor Create(atype,aflags,aalign:longword);
|
|
destructor Destroy; override;
|
|
procedure Add(exesec:TExeSection);
|
|
end;
|
|
|
|
TElfExeOutput=class(TExeOutput)
|
|
private
|
|
segmentlist: TFPObjectList;
|
|
textseg,
|
|
dataseg,
|
|
noteseg,
|
|
phdrseg: TElfSegment;
|
|
shstrtabsect: TElfObjSection;
|
|
symtab: TElfSymtab;
|
|
shoffset: aword;
|
|
gotwritten: boolean;
|
|
{ dynamic linking }
|
|
dynamiclink: boolean;
|
|
dynsymnames: Plongword;
|
|
dynsymtable: TElfSymtab;
|
|
interpobjsec: TObjSection;
|
|
dynamicsec,
|
|
hashobjsec: TElfObjSection;
|
|
neededlist: TFPHashList;
|
|
gotsize: aword;
|
|
dynrelsize: aword;
|
|
|
|
function AttachSection(objsec:TObjSection):TElfExeSection;
|
|
function CreateSegment(atype,aflags,aalign:longword):TElfSegment;
|
|
procedure CreatePLT;
|
|
procedure WriteHeader;
|
|
procedure WriteDynamicSymbolsHash;
|
|
procedure WriteDynamicTags;
|
|
procedure FinishDynamicTags;
|
|
procedure exesection_write_header(p:TObject;arg:Pointer);
|
|
procedure segment_write_header(p:TObject;arg:Pointer);
|
|
procedure mempos_segment(seg:TElfSegment);
|
|
procedure datapos_segment(seg:TElfSegment);
|
|
procedure MapSectionsToSegments;
|
|
procedure WriteStaticSymtable;
|
|
procedure InitDynlink;
|
|
procedure PrepareGOT;
|
|
procedure WriteDynTag(aTag:longword;aValue:longword);
|
|
procedure WriteDynTag(aTag:longword;aSection:TObjSection;aOffs:aword=0);
|
|
protected
|
|
hastextrelocs: boolean;
|
|
gotsymbol: TObjSymbol;
|
|
dynsymlist: TFPObjectList;
|
|
gotobjsec: TObjSection;
|
|
pltobjsec,
|
|
gotpltobjsec,
|
|
pltrelocsec,
|
|
ipltrelocsec,
|
|
dynrelocsec: TElfObjSection;
|
|
dynreloclist: TFPObjectList;
|
|
tlsseg: TElfSegment;
|
|
relative_reloc_count: longint;
|
|
procedure WriteDynRelocEntry(dataofs:aword;typ:byte;symidx:aword;addend:aword);
|
|
procedure WriteFirstPLTEntry;virtual;abstract;
|
|
procedure WritePLTEntry(exesym:TExeSymbol);virtual;
|
|
procedure WriteIndirectPLTEntry(exesym:TExeSymbol);virtual;
|
|
procedure GOTRelocPass1(objsec:TObjSection;ObjReloc:TObjRelocation);virtual;abstract;
|
|
public
|
|
constructor Create;override;
|
|
destructor Destroy;override;
|
|
procedure Load_Start;override;
|
|
procedure Load_DynamicObject(ObjData:TObjData);override;
|
|
procedure Order_Start;override;
|
|
procedure Order_end;override;
|
|
procedure AfterUnusedSectionRemoval;override;
|
|
procedure MemPos_Start;override;
|
|
procedure MemPos_ExeSection(const aname:string);override;
|
|
procedure DataPos_Start;override;
|
|
procedure DataPos_ExeSection(const aname:string);override;
|
|
function writeData:boolean;override;
|
|
procedure GenerateLibraryImports(ImportLibraryList:TFPHashObjectList);override;
|
|
end;
|
|
|
|
var
|
|
ElfExeOutputClass: TExeOutputClass;
|
|
ElfTarget: TElfTargetClass;
|
|
|
|
const
|
|
{ Bits of TObjSymbol.refs field }
|
|
symref_plt = 1;
|
|
|
|
{TODO: should become property of back-end }
|
|
{$ifdef x86_64}
|
|
const
|
|
relocs_use_addend:Boolean=True;
|
|
{$else x86_64}
|
|
const
|
|
relocs_use_addend:Boolean=False;
|
|
{$endif x86_64}
|
|
|
|
|
|
implementation
|
|
|
|
uses
|
|
SysUtils,
|
|
verbose,
|
|
export,expunix,
|
|
cutils,globals,fmodule;
|
|
|
|
const
|
|
symbolresize = 200*18;
|
|
|
|
const
|
|
EI_MAG0 = 0;
|
|
ELFMAG0 = $7F;
|
|
EI_MAG1 = 1;
|
|
ELFMAG1 = ord('E');
|
|
EI_MAG2 = 2;
|
|
ELFMAG2 = ord('L');
|
|
EI_MAG3 = 3;
|
|
ELFMAG3 = ord('F');
|
|
EI_CLASS = 4;
|
|
ELFCLASSNONE = 0;
|
|
ELFCLASS32 = 1;
|
|
ELFCLASS64 = 2;
|
|
EI_DATA = 5;
|
|
ELFDATANONE = 0;
|
|
ELFDATA2LSB = 1;
|
|
ELFDATA2MSB = 2;
|
|
EI_VERSION = 6;
|
|
EI_OSABI = 7;
|
|
EI_ABIVERSION = 8;
|
|
EI_PAD = 9;
|
|
|
|
{ ELFHeader.e_type }
|
|
ET_NONE = 0;
|
|
ET_REL = 1;
|
|
ET_EXEC = 2;
|
|
ET_DYN = 3;
|
|
ET_CORE = 4;
|
|
|
|
{ ELFHeader.e_machine }
|
|
EM_SPARC = 2;
|
|
EM_386 = 3;
|
|
EM_M68K = 4;
|
|
EM_MIPS = 8;
|
|
EM_PPC = 20;
|
|
EM_ARM = 40;
|
|
EM_X86_64 = 62;
|
|
|
|
{$ifdef sparc}
|
|
ELFMACHINE = EM_SPARC;
|
|
{$endif sparc}
|
|
{$ifdef i386}
|
|
ELFMACHINE = EM_386;
|
|
{$endif i386}
|
|
{$ifdef m68k}
|
|
ELFMACHINE = EM_M68K;
|
|
{$endif m68k}
|
|
{$ifdef powerpc}
|
|
ELFMACHINE = EM_PPC;
|
|
{$endif powerpc}
|
|
{$ifdef powerpc64}
|
|
ELFMACHINE = EM_PPC; // TODO
|
|
{$endif}
|
|
{$ifdef mips}
|
|
ELFMACHINE = EM_MIPS;
|
|
{$endif}
|
|
{$ifdef arm}
|
|
ELFMACHINE = EM_ARM;
|
|
{$endif arm}
|
|
{$ifdef x86_64}
|
|
ELFMACHINE = EM_X86_64;
|
|
{$endif x86_64}
|
|
|
|
SHN_UNDEF = 0;
|
|
SHN_LORESERVE = $FF00;
|
|
SHN_ABS = $fff1;
|
|
SHN_COMMON = $fff2;
|
|
|
|
SHT_NULL = 0;
|
|
SHT_PROGBITS = 1;
|
|
SHT_SYMTAB = 2;
|
|
SHT_STRTAB = 3;
|
|
SHT_RELA = 4;
|
|
SHT_HASH = 5;
|
|
SHT_DYNAMIC = 6;
|
|
SHT_NOTE = 7;
|
|
SHT_NOBITS = 8;
|
|
SHT_REL = 9;
|
|
SHT_SHLIB = 10;
|
|
SHT_DYNSYM = 11;
|
|
SHT_INIT_ARRAY = 14;
|
|
SHT_FINI_ARRAY = 15;
|
|
SHT_PREINIT_ARRAY = 16;
|
|
SHT_GROUP = 17;
|
|
SHT_SYMTAB_SHNDX = 18;
|
|
SHT_GNU_verdef = $6ffffffd;
|
|
SHT_GNU_verneed = $6ffffffe;
|
|
SHT_GNU_versym = $6fffffff;
|
|
|
|
SHF_WRITE = 1;
|
|
SHF_ALLOC = 2;
|
|
SHF_EXECINSTR = 4;
|
|
SHF_MERGE = 16;
|
|
SHF_STRINGS = 32;
|
|
SHF_INFO_LINK = 64;
|
|
SHF_LINK_ORDER = 128;
|
|
SHF_OS_NONCONFORMING = 256;
|
|
SHF_GROUP = 512;
|
|
SHF_TLS = 1024;
|
|
|
|
STB_LOCAL = 0;
|
|
STB_GLOBAL = 1;
|
|
STB_WEAK = 2;
|
|
|
|
STT_NOTYPE = 0;
|
|
STT_OBJECT = 1;
|
|
STT_FUNC = 2;
|
|
STT_SECTION = 3;
|
|
STT_FILE = 4;
|
|
STT_COMMON = 5;
|
|
STT_TLS = 6;
|
|
STT_GNU_IFUNC = 10;
|
|
|
|
{ program header types }
|
|
PT_NULL = 0;
|
|
PT_LOAD = 1;
|
|
PT_DYNAMIC = 2;
|
|
PT_INTERP = 3;
|
|
PT_NOTE = 4;
|
|
PT_SHLIB = 5;
|
|
PT_PHDR = 6;
|
|
PT_TLS = 7;
|
|
PT_LOOS = $60000000;
|
|
PT_HIOS = $6FFFFFFF;
|
|
PT_LOPROC = $70000000;
|
|
PT_HIPROC = $7FFFFFFF;
|
|
PT_GNU_EH_FRAME = PT_LOOS + $474e550; { Frame unwind information }
|
|
PT_GNU_STACK = PT_LOOS + $474e551; { Stack flags }
|
|
PT_GNU_RELRO = PT_LOOS + $474e552; { Read-only after relocation }
|
|
|
|
{ program header flags }
|
|
PF_X = 1;
|
|
PF_W = 2;
|
|
PF_R = 4;
|
|
PF_MASKOS = $0FF00000; { OS-specific reserved bits }
|
|
PF_MASKPROC = $F0000000; { Processor-specific reserved bits }
|
|
|
|
{ .dynamic tags }
|
|
DT_NULL = 0;
|
|
DT_NEEDED = 1;
|
|
DT_PLTRELSZ = 2;
|
|
DT_PLTGOT = 3;
|
|
DT_HASH = 4;
|
|
DT_STRTAB = 5;
|
|
DT_SYMTAB = 6;
|
|
DT_RELA = 7;
|
|
DT_RELASZ = 8;
|
|
DT_RELAENT = 9;
|
|
DT_STRSZ = 10;
|
|
DT_SYMENT = 11;
|
|
DT_INIT = 12;
|
|
DT_FINI = 13;
|
|
DT_SONAME = 14;
|
|
DT_RPATH = 15;
|
|
DT_SYMBOLIC = 16;
|
|
DT_REL = 17;
|
|
DT_RELSZ = 18;
|
|
DT_RELENT = 19;
|
|
DT_PLTREL = 20;
|
|
DT_DEBUG = 21;
|
|
DT_TEXTREL = 22;
|
|
DT_JMPREL = 23;
|
|
DT_BIND_NOW = 24;
|
|
DT_INIT_ARRAY = 25;
|
|
DT_FINI_ARRAY = 26;
|
|
DT_INIT_ARRAYSZ = 27;
|
|
DT_FINI_ARRAYSZ = 28;
|
|
DT_RUNPATH = 29;
|
|
DT_FLAGS = 30;
|
|
DT_ENCODING = 32;
|
|
DT_PREINIT_ARRAY = 32;
|
|
DT_PREINIT_ARRAYSZ = 33;
|
|
DT_NUM = 34;
|
|
DT_LOOS = $6000000D;
|
|
DT_HIOS = $6ffff000;
|
|
DT_LOPROC = $70000000;
|
|
DT_HIPROC = $7fffffff;
|
|
|
|
DT_RELACOUNT = $6ffffff9;
|
|
DT_RELCOUNT = $6ffffffa;
|
|
DT_FLAGS_1 = $6ffffffb;
|
|
DT_VERDEF = $6ffffffc;
|
|
DT_VERDEFNUM = $6ffffffd;
|
|
DT_VERNEED = $6ffffffe;
|
|
DT_VERNEEDNUM = $6fffffff;
|
|
|
|
GRP_COMDAT = 1;
|
|
|
|
type
|
|
{ Structures which are written directly to the output file }
|
|
TElf32header=packed record
|
|
e_ident : array[0..15] of byte;
|
|
e_type : word;
|
|
e_machine : word;
|
|
e_version : longword;
|
|
e_entry : longword; { entrypoint }
|
|
e_phoff : longword; { program header offset }
|
|
e_shoff : longword; { sections header offset }
|
|
e_flags : longword;
|
|
e_ehsize : word; { elf header size in bytes }
|
|
e_phentsize : word; { size of an entry in the program header array }
|
|
e_phnum : word; { 0..e_phnum-1 of entrys }
|
|
e_shentsize : word; { size of an entry in sections header array }
|
|
e_shnum : word; { 0..e_shnum-1 of entrys }
|
|
e_shstrndx : word; { index of string section header }
|
|
end;
|
|
TElf32sechdr=packed record
|
|
sh_name : longword;
|
|
sh_type : longword;
|
|
sh_flags : longword;
|
|
sh_addr : longword;
|
|
sh_offset : longword;
|
|
sh_size : longword;
|
|
sh_link : longword;
|
|
sh_info : longword;
|
|
sh_addralign : longword;
|
|
sh_entsize : longword;
|
|
end;
|
|
TElf32proghdr=packed record
|
|
p_type : longword;
|
|
p_offset : longword;
|
|
p_vaddr : longword;
|
|
p_paddr : longword;
|
|
p_filesz : longword;
|
|
p_memsz : longword;
|
|
p_flags : longword;
|
|
p_align : longword;
|
|
end;
|
|
TElf32reloc=packed record
|
|
address : longword;
|
|
info : longword; { bit 0-7: type, 8-31: symbol }
|
|
addend : longint;
|
|
end;
|
|
TElf32symbol=packed record
|
|
st_name : longword;
|
|
st_value : longword;
|
|
st_size : longword;
|
|
st_info : byte; { bit 0-3: type, 4-7: bind }
|
|
st_other : byte;
|
|
st_shndx : word;
|
|
end;
|
|
TElf32Dyn=packed record
|
|
d_tag: longword;
|
|
case integer of
|
|
0: (d_val: longword);
|
|
1: (d_ptr: longword);
|
|
end;
|
|
|
|
|
|
telf64header=packed record
|
|
e_ident : array[0..15] of byte;
|
|
e_type : word;
|
|
e_machine : word;
|
|
e_version : longword;
|
|
e_entry : qword; { entrypoint }
|
|
e_phoff : qword; { program header offset }
|
|
e_shoff : qword; { sections header offset }
|
|
e_flags : longword;
|
|
e_ehsize : word; { elf header size in bytes }
|
|
e_phentsize : word; { size of an entry in the program header array }
|
|
e_phnum : word; { 0..e_phnum-1 of entrys }
|
|
e_shentsize : word; { size of an entry in sections header array }
|
|
e_shnum : word; { 0..e_shnum-1 of entrys }
|
|
e_shstrndx : word; { index of string section header }
|
|
end;
|
|
telf64sechdr=packed record
|
|
sh_name : longword;
|
|
sh_type : longword;
|
|
sh_flags : qword;
|
|
sh_addr : qword;
|
|
sh_offset : qword;
|
|
sh_size : qword;
|
|
sh_link : longword;
|
|
sh_info : longword;
|
|
sh_addralign : qword;
|
|
sh_entsize : qword;
|
|
end;
|
|
telf64proghdr=packed record
|
|
p_type : longword;
|
|
p_flags : longword;
|
|
p_offset : qword;
|
|
p_vaddr : qword;
|
|
p_paddr : qword;
|
|
p_filesz : qword;
|
|
p_memsz : qword;
|
|
p_align : qword;
|
|
end;
|
|
telf64reloc=packed record
|
|
address : qword;
|
|
info : qword; { bit 0-31: type, 32-63: symbol }
|
|
addend : int64; { signed! }
|
|
end;
|
|
telf64symbol=packed record
|
|
st_name : longword;
|
|
st_info : byte; { bit 0-3: type, 4-7: bind }
|
|
st_other : byte;
|
|
st_shndx : word;
|
|
st_value : qword;
|
|
st_size : qword;
|
|
end;
|
|
TElf64Dyn=packed record
|
|
d_tag: qword;
|
|
case integer of
|
|
0: (d_val: qword);
|
|
1: (d_ptr: qword);
|
|
end;
|
|
|
|
TElfVerdef=record { same for 32 and 64 bits }
|
|
vd_version: word; { =1 }
|
|
vd_flags: word;
|
|
vd_ndx: word;
|
|
vd_cnt: word; { number of verdaux records }
|
|
vd_hash: longword; { ELF hash of version name }
|
|
vd_aux: longword; { offset to verdaux records }
|
|
vd_next: longword; { offset to next verdef record }
|
|
end;
|
|
|
|
TElfVerdaux=record
|
|
vda_name: longword;
|
|
vda_next: longword;
|
|
end;
|
|
|
|
TElfVerneed=record
|
|
vn_version: word; { =VER_NEED_CURRENT }
|
|
vn_cnt: word;
|
|
vn_file: longword;
|
|
vn_aux: longword;
|
|
vn_next: longword;
|
|
end;
|
|
|
|
TElfVernaux=record
|
|
vna_hash: longword;
|
|
vna_flags: word;
|
|
vna_other: word;
|
|
vna_name: longword;
|
|
vna_next: longword;
|
|
end;
|
|
|
|
{$ifdef cpu64bitaddr}
|
|
const
|
|
ELFCLASS = ELFCLASS64;
|
|
type
|
|
telfheader = telf64header;
|
|
telfreloc = telf64reloc;
|
|
telfsymbol = telf64symbol;
|
|
telfsechdr = telf64sechdr;
|
|
telfproghdr = telf64proghdr;
|
|
telfdyn = telf64dyn;
|
|
|
|
function ELF_R_INFO(sym:longword;typ:byte):qword;inline;
|
|
begin
|
|
result:=(qword(sym) shl 32) or typ;
|
|
end;
|
|
|
|
{$else cpu64bitaddr}
|
|
const
|
|
ELFCLASS = ELFCLASS32;
|
|
type
|
|
telfheader = telf32header;
|
|
telfreloc = telf32reloc;
|
|
telfsymbol = telf32symbol;
|
|
telfsechdr = telf32sechdr;
|
|
telfproghdr = telf32proghdr;
|
|
telfdyn = telf32dyn;
|
|
|
|
function ELF_R_INFO(sym:longword;typ:byte):longword;inline;
|
|
begin
|
|
result:=(sym shl 8) or typ;
|
|
end;
|
|
{$endif cpu64bitaddr}
|
|
|
|
{$ifdef x86_64}
|
|
const
|
|
ELF_MAXPAGESIZE:longint=$200000;
|
|
TEXT_SEGMENT_START:longint=$400000;
|
|
{$else x86_64}
|
|
const
|
|
ELF_MAXPAGESIZE:longint=$1000;
|
|
TEXT_SEGMENT_START:longint=$8048000;
|
|
{$endif x86_64}
|
|
|
|
procedure MayBeSwapHeader(var h : telf32header);
|
|
begin
|
|
if source_info.endian<>target_info.endian then
|
|
with h do
|
|
begin
|
|
e_type:=swapendian(e_type);
|
|
e_machine:=swapendian(e_machine);
|
|
e_version:=swapendian(e_version);
|
|
e_entry:=swapendian(e_entry);
|
|
e_phoff:=swapendian(e_phoff);
|
|
e_shoff:=swapendian(e_shoff);
|
|
e_flags:=swapendian(e_flags);
|
|
e_ehsize:=swapendian(e_ehsize);
|
|
e_phentsize:=swapendian(e_phentsize);
|
|
e_phnum:=swapendian(e_phnum);
|
|
e_shentsize:=swapendian(e_shentsize);
|
|
e_shnum:=swapendian(e_shnum);
|
|
e_shstrndx:=swapendian(e_shstrndx);
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure MayBeSwapHeader(var h : telf64header);
|
|
begin
|
|
if source_info.endian<>target_info.endian then
|
|
with h do
|
|
begin
|
|
e_type:=swapendian(e_type);
|
|
e_machine:=swapendian(e_machine);
|
|
e_version:=swapendian(e_version);
|
|
e_entry:=swapendian(e_entry);
|
|
e_phoff:=swapendian(e_phoff);
|
|
e_shoff:=swapendian(e_shoff);
|
|
e_flags:=swapendian(e_flags);
|
|
e_ehsize:=swapendian(e_ehsize);
|
|
e_phentsize:=swapendian(e_phentsize);
|
|
e_phnum:=swapendian(e_phnum);
|
|
e_shentsize:=swapendian(e_shentsize);
|
|
e_shnum:=swapendian(e_shnum);
|
|
e_shstrndx:=swapendian(e_shstrndx);
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure MayBeSwapHeader(var h : telf32proghdr);
|
|
begin
|
|
if source_info.endian<>target_info.endian then
|
|
with h do
|
|
begin
|
|
p_align:=swapendian(p_align);
|
|
p_filesz:=swapendian(p_filesz);
|
|
p_flags:=swapendian(p_flags);
|
|
p_memsz:=swapendian(p_memsz);
|
|
p_offset:=swapendian(p_offset);
|
|
p_paddr:=swapendian(p_paddr);
|
|
p_type:=swapendian(p_type);
|
|
p_vaddr:=swapendian(p_vaddr);
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure MayBeSwapHeader(var h : telf64proghdr);
|
|
begin
|
|
if source_info.endian<>target_info.endian then
|
|
with h do
|
|
begin
|
|
p_align:=swapendian(p_align);
|
|
p_filesz:=swapendian(p_filesz);
|
|
p_flags:=swapendian(p_flags);
|
|
p_memsz:=swapendian(p_memsz);
|
|
p_offset:=swapendian(p_offset);
|
|
p_paddr:=swapendian(p_paddr);
|
|
p_type:=swapendian(p_type);
|
|
p_vaddr:=swapendian(p_vaddr);
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure MaybeSwapSecHeader(var h : telf32sechdr);
|
|
begin
|
|
if source_info.endian<>target_info.endian then
|
|
with h do
|
|
begin
|
|
sh_name:=swapendian(sh_name);
|
|
sh_type:=swapendian(sh_type);
|
|
sh_flags:=swapendian(sh_flags);
|
|
sh_addr:=swapendian(sh_addr);
|
|
sh_offset:=swapendian(sh_offset);
|
|
sh_size:=swapendian(sh_size);
|
|
sh_link:=swapendian(sh_link);
|
|
sh_info:=swapendian(sh_info);
|
|
sh_addralign:=swapendian(sh_addralign);
|
|
sh_entsize:=swapendian(sh_entsize);
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure MaybeSwapSecHeader(var h : telf64sechdr);
|
|
begin
|
|
if source_info.endian<>target_info.endian then
|
|
with h do
|
|
begin
|
|
sh_name:=swapendian(sh_name);
|
|
sh_type:=swapendian(sh_type);
|
|
sh_flags:=swapendian(sh_flags);
|
|
sh_addr:=swapendian(sh_addr);
|
|
sh_offset:=swapendian(sh_offset);
|
|
sh_size:=swapendian(sh_size);
|
|
sh_link:=swapendian(sh_link);
|
|
sh_info:=swapendian(sh_info);
|
|
sh_addralign:=swapendian(sh_addralign);
|
|
sh_entsize:=swapendian(sh_entsize);
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure MaybeSwapElfSymbol(var h : telf32symbol);
|
|
begin
|
|
if source_info.endian<>target_info.endian then
|
|
with h do
|
|
begin
|
|
st_name:=swapendian(st_name);
|
|
st_value:=swapendian(st_value);
|
|
st_size:=swapendian(st_size);
|
|
st_shndx:=swapendian(st_shndx);
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure MaybeSwapElfSymbol(var h : telf64symbol);
|
|
begin
|
|
if source_info.endian<>target_info.endian then
|
|
with h do
|
|
begin
|
|
st_name:=swapendian(st_name);
|
|
st_value:=swapendian(st_value);
|
|
st_size:=swapendian(st_size);
|
|
st_shndx:=swapendian(st_shndx);
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure MaybeSwapElfReloc(var h : telf32reloc);
|
|
begin
|
|
if source_info.endian<>target_info.endian then
|
|
with h do
|
|
begin
|
|
address:=swapendian(address);
|
|
info:=swapendian(info);
|
|
addend:=swapendian(addend);
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure MaybeSwapElfReloc(var h : telf64reloc);
|
|
begin
|
|
if source_info.endian<>target_info.endian then
|
|
with h do
|
|
begin
|
|
address:=swapendian(address);
|
|
info:=swapendian(info);
|
|
addend:=swapendian(addend);
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure MaybeSwapElfDyn(var h : telf32dyn);
|
|
begin
|
|
if source_info.endian<>target_info.endian then
|
|
with h do
|
|
begin
|
|
d_tag:=swapendian(d_tag);
|
|
d_val:=swapendian(d_val);
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure MaybeSwapElfDyn(var h : telf64dyn);
|
|
begin
|
|
if source_info.endian<>target_info.endian then
|
|
with h do
|
|
begin
|
|
d_tag:=swapendian(d_tag);
|
|
d_val:=swapendian(d_val);
|
|
end;
|
|
end;
|
|
|
|
|
|
{****************************************************************************
|
|
Helpers
|
|
****************************************************************************}
|
|
|
|
procedure encodesechdrflags(aoptions:TObjSectionOptions;out AshType:longint;out Ashflags:longint);
|
|
begin
|
|
{ Section Type }
|
|
AshType:=SHT_PROGBITS;
|
|
if oso_strings in aoptions then
|
|
AshType:=SHT_STRTAB
|
|
else if not(oso_data in aoptions) then
|
|
AshType:=SHT_NOBITS;
|
|
{ Section Flags }
|
|
Ashflags:=0;
|
|
if oso_load in aoptions then
|
|
Ashflags:=Ashflags or SHF_ALLOC;
|
|
if oso_executable in aoptions then
|
|
Ashflags:=Ashflags or SHF_EXECINSTR;
|
|
if oso_write in aoptions then
|
|
Ashflags:=Ashflags or SHF_WRITE;
|
|
end;
|
|
|
|
|
|
procedure decodesechdrflags(AshType:longint;Ashflags:longint;out aoptions:TObjSectionOptions);
|
|
begin
|
|
aoptions:=[];
|
|
{ Section Type }
|
|
if AshType<>SHT_NOBITS then
|
|
include(aoptions,oso_data);
|
|
if AshType=SHT_STRTAB then
|
|
include(aoptions,oso_strings);
|
|
{ Section Flags }
|
|
if Ashflags and SHF_ALLOC<>0 then
|
|
include(aoptions,oso_load);
|
|
if Ashflags and SHF_WRITE<>0 then
|
|
include(aoptions,oso_write);
|
|
if Ashflags and SHF_EXECINSTR<>0 then
|
|
include(aoptions,oso_executable);
|
|
end;
|
|
|
|
|
|
{****************************************************************************
|
|
TElfObjSection
|
|
****************************************************************************}
|
|
|
|
constructor TElfObjSection.create(AList:TFPHashObjectList;const Aname:string;Aalign:shortint;Aoptions:TObjSectionOptions);
|
|
begin
|
|
inherited create(AList,Aname,Aalign,aoptions);
|
|
index:=0;
|
|
shstridx:=0;
|
|
encodesechdrflags(aoptions,shtype,shflags);
|
|
shlink:=0;
|
|
shinfo:=0;
|
|
if name='.stab' then
|
|
shentsize:=sizeof(TObjStabEntry);
|
|
end;
|
|
|
|
|
|
constructor TElfObjSection.create_ext(aobjdata:TObjData;const Aname:string;Ashtype,Ashflags:longint;Aalign:shortint;Aentsize:longint);
|
|
var
|
|
aoptions : TObjSectionOptions;
|
|
begin
|
|
decodesechdrflags(Ashtype,Ashflags,aoptions);
|
|
inherited create(aobjdata.ObjSectionList,Aname,Aalign,aoptions);
|
|
objdata:=aobjdata;
|
|
index:=0;
|
|
shstridx:=0;
|
|
shtype:=AshType;
|
|
shflags:=AshFlags;
|
|
shentsize:=Aentsize;
|
|
end;
|
|
|
|
|
|
const
|
|
relsec_prefix:array[boolean] of string[5] = ('.rel','.rela');
|
|
relsec_shtype:array[boolean] of longword = (SHT_REL,SHT_RELA);
|
|
|
|
constructor TElfObjSection.create_reloc(aobjdata:TObjData;const Aname:string;allocflag:boolean);
|
|
begin
|
|
create_ext(aobjdata,
|
|
relsec_prefix[relocs_use_addend]+aname,
|
|
relsec_shtype[relocs_use_addend],
|
|
SHF_ALLOC*ord(allocflag),
|
|
sizeof(pint),
|
|
(2+ord(relocs_use_addend))*sizeof(pint));
|
|
end;
|
|
|
|
|
|
procedure TElfObjSection.writeReloc_internal(aTarget:TObjSection;offset:aword;len:byte;reltype:TObjRelocationType);
|
|
var
|
|
reloc: TObjRelocation;
|
|
begin
|
|
reloc:=TObjRelocation.CreateSection(Size,aTarget,reltype);
|
|
reloc.size:=len;
|
|
ObjRelocations.Add(reloc);
|
|
if reltype=RELOC_RELATIVE then
|
|
dec(offset,len)
|
|
else if reltype<>RELOC_ABSOLUTE then
|
|
InternalError(2012062401);
|
|
if relocs_use_addend then
|
|
begin
|
|
reloc.orgsize:=offset;
|
|
offset:=0;
|
|
end;
|
|
write(offset,len);
|
|
end;
|
|
|
|
|
|
{****************************************************************************
|
|
TElfObjData
|
|
****************************************************************************}
|
|
|
|
constructor TElfObjData.create(const n:string);
|
|
begin
|
|
inherited create(n);
|
|
CObjSection:=TElfObjSection;
|
|
end;
|
|
|
|
|
|
function TElfObjData.sectionname(atype:TAsmSectiontype;const aname:string;aorder:TAsmSectionOrder):string;
|
|
const
|
|
secnames : array[TAsmSectiontype] of string[length('__DATA, __datacoal_nt,coalesced')] = ('','',
|
|
{$ifdef userodata}
|
|
'.text','.data','.data','.rodata','.bss','.threadvar',
|
|
{$else userodata}
|
|
'.text','.data','.data','.data','.bss','.threadvar',
|
|
{$endif userodata}
|
|
'.pdata',
|
|
'.text', { darwin stubs }
|
|
'__DATA,__nl_symbol_ptr',
|
|
'__DATA,__la_symbol_ptr',
|
|
'__DATA,__mod_init_func',
|
|
'__DATA,__mod_term_func',
|
|
'.stab','.stabstr',
|
|
'.idata$2','.idata$4','.idata$5','.idata$6','.idata$7','.edata',
|
|
'.eh_frame',
|
|
'.debug_frame','.debug_info','.debug_line','.debug_abbrev',
|
|
'.fpc',
|
|
'.toc',
|
|
'.init',
|
|
'.fini',
|
|
'.objc_class',
|
|
'.objc_meta_class',
|
|
'.objc_cat_cls_meth',
|
|
'.objc_cat_inst_meth',
|
|
'.objc_protocol',
|
|
'.objc_string_object',
|
|
'.objc_cls_meth',
|
|
'.objc_inst_meth',
|
|
'.objc_cls_refs',
|
|
'.objc_message_refs',
|
|
'.objc_symbols',
|
|
'.objc_category',
|
|
'.objc_class_vars',
|
|
'.objc_instance_vars',
|
|
'.objc_module_info',
|
|
'.objc_class_names',
|
|
'.objc_meth_var_types',
|
|
'.objc_meth_var_names',
|
|
'.objc_selector_strs',
|
|
'.objc_protocol_ext',
|
|
'.objc_class_ext',
|
|
'.objc_property',
|
|
'.objc_image_info',
|
|
'.objc_cstring_object',
|
|
'.objc_sel_fixup',
|
|
'__DATA,__objc_data',
|
|
'__DATA,__objc_const',
|
|
'.objc_superrefs',
|
|
'__DATA, __datacoal_nt,coalesced',
|
|
'.objc_classlist',
|
|
'.objc_nlclasslist',
|
|
'.objc_catlist',
|
|
'.obcj_nlcatlist',
|
|
'.objc_protolist'
|
|
);
|
|
var
|
|
sep : string[3];
|
|
secname : string;
|
|
begin
|
|
{ section type user gives the user full controll on the section name }
|
|
if atype=sec_user then
|
|
result:=aname
|
|
else
|
|
begin
|
|
secname:=secnames[atype];
|
|
if (atype=sec_fpc) and (Copy(aname,1,3)='res') then
|
|
begin
|
|
result:=secname+'.'+aname;
|
|
exit;
|
|
end;
|
|
if create_smartlink_sections and (aname<>'') then
|
|
begin
|
|
case aorder of
|
|
secorder_begin :
|
|
sep:='.b_';
|
|
secorder_end :
|
|
sep:='.z_';
|
|
else
|
|
sep:='.n_';
|
|
end;
|
|
result:=secname+sep+aname
|
|
end
|
|
else
|
|
result:=secname;
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure TElfObjData.CreateDebugSections;
|
|
begin
|
|
if target_dbg.id=dbg_stabs then
|
|
begin
|
|
stabssec:=createsection(sec_stab);
|
|
stabstrsec:=createsection(sec_stabstr);
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure TElfObjData.writereloc(data:aint;len:aword;p:TObjSymbol;reltype:TObjRelocationType);
|
|
var
|
|
symaddr : aint;
|
|
objreloc: TObjRelocation;
|
|
begin
|
|
if CurrObjSec=nil then
|
|
internalerror(200403292);
|
|
objreloc:=nil;
|
|
if assigned(p) then
|
|
begin
|
|
{ real address of the symbol }
|
|
symaddr:=p.address;
|
|
{ Local ObjSymbols can be resolved already or need a section reloc }
|
|
if (p.bind=AB_LOCAL) and
|
|
(reltype in [RELOC_RELATIVE,RELOC_ABSOLUTE{$ifdef x86_64},RELOC_ABSOLUTE32{$endif x86_64}]) then
|
|
begin
|
|
{ For a reltype relocation in the same section the
|
|
value can be calculated }
|
|
if (p.objsection=CurrObjSec) and
|
|
(reltype=RELOC_RELATIVE) then
|
|
inc(data,symaddr-len-CurrObjSec.Size)
|
|
else
|
|
begin
|
|
objreloc:=TObjRelocation.CreateSection(CurrObjSec.Size,p.objsection,reltype);
|
|
CurrObjSec.ObjRelocations.Add(objreloc);
|
|
inc(data,symaddr);
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
objreloc:=TObjRelocation.CreateSymbol(CurrObjSec.Size,p,reltype);
|
|
CurrObjSec.ObjRelocations.Add(objreloc);
|
|
{ If target is a local label and it isn't handled above,
|
|
patch its type in order to get it written to symtable.
|
|
This may happen e.g. when taking address of Pascal label in PIC mode. }
|
|
if (p.bind=AB_LOCAL) and (p.typ=AT_LABEL) then
|
|
p.typ:=AT_ADDR;
|
|
end;
|
|
end;
|
|
if assigned(objreloc) then
|
|
begin
|
|
objreloc.size:=len;
|
|
if reltype in [RELOC_RELATIVE{$ifdef x86},RELOC_PLT32{$endif}{$ifdef x86_64},RELOC_GOTPCREL{$endif}] then
|
|
dec(data,len);
|
|
if relocs_use_addend then
|
|
begin
|
|
objreloc.orgsize:=data;
|
|
data:=0;
|
|
end;
|
|
end;
|
|
CurrObjSec.write(data,len);
|
|
end;
|
|
|
|
|
|
{****************************************************************************
|
|
TElfSymtab
|
|
****************************************************************************}
|
|
|
|
const
|
|
symsecnames: array[boolean] of string[8] = ('.symtab','.dynsym');
|
|
strsecnames: array[boolean] of string[8] = ('.strtab','.dynstr');
|
|
symsectypes: array[boolean] of longword = (SHT_SYMTAB,SHT_DYNSYM);
|
|
symsecattrs: array[boolean] of longword = (0,SHF_ALLOC);
|
|
|
|
|
|
constructor TElfSymtab.create(aObjData:TObjData;aKind:TElfSymtabKind);
|
|
var
|
|
dyn:boolean;
|
|
begin
|
|
dyn:=(aKind=esk_dyn);
|
|
create_ext(aObjData,symsecnames[dyn],symsectypes[dyn],symsecattrs[dyn],sizeof(pint),sizeof(TElfSymbol));
|
|
fstrsec:=TElfObjSection.create_ext(aObjData,strsecnames[dyn],SHT_STRTAB,symsecattrs[dyn],1,0);
|
|
fstrsec.writezeros(1);
|
|
writezeros(sizeof(TElfSymbol));
|
|
symidx:=1;
|
|
shinfo:=1;
|
|
kind:=aKind;
|
|
end;
|
|
|
|
procedure TElfSymtab.writeInternalSymbol(avalue:aword;astridx:longword;ainfo:byte;ashndx:word);
|
|
var
|
|
elfsym:TElfSymbol;
|
|
begin
|
|
fillchar(elfsym,sizeof(elfsym),0);
|
|
elfsym.st_value:=avalue;
|
|
elfsym.st_name:=astridx;
|
|
elfsym.st_info:=ainfo;
|
|
elfsym.st_shndx:=ashndx;
|
|
inc(symidx);
|
|
inc(shinfo);
|
|
MaybeSwapElfSymbol(elfsym);
|
|
write(elfsym,sizeof(elfsym));
|
|
end;
|
|
|
|
procedure TElfSymtab.writeSymbol(objsym:TObjSymbol;nameidx:longword);
|
|
var
|
|
elfsym:TElfSymbol;
|
|
begin
|
|
fillchar(elfsym,sizeof(elfsym),0);
|
|
if nameidx=0 then
|
|
elfsym.st_name:=fstrsec.writestr(objsym.name)
|
|
else
|
|
elfsym.st_name:=nameidx;
|
|
elfsym.st_size:=objsym.size;
|
|
case objsym.bind of
|
|
AB_LOCAL :
|
|
begin
|
|
elfsym.st_value:=objsym.address;
|
|
elfsym.st_info:=STB_LOCAL shl 4;
|
|
inc(shinfo);
|
|
end;
|
|
AB_COMMON :
|
|
begin
|
|
elfsym.st_value:=var_align(objsym.size);
|
|
elfsym.st_info:=STB_GLOBAL shl 4;
|
|
elfsym.st_shndx:=SHN_COMMON;
|
|
end;
|
|
AB_EXTERNAL :
|
|
elfsym.st_info:=STB_GLOBAL shl 4;
|
|
AB_WEAK_EXTERNAL :
|
|
begin
|
|
elfsym.st_info:=STB_WEAK shl 4;
|
|
elfsym.st_value:=objsym.address;
|
|
end;
|
|
AB_GLOBAL :
|
|
begin
|
|
elfsym.st_value:=objsym.address;
|
|
elfsym.st_info:=STB_GLOBAL shl 4;
|
|
end;
|
|
end;
|
|
if (objsym.bind<>AB_EXTERNAL) {and
|
|
not(assigned(objsym.objsection) and
|
|
not(oso_data in objsym.objsection.secoptions))} then
|
|
begin
|
|
case objsym.typ of
|
|
AT_FUNCTION :
|
|
elfsym.st_info:=elfsym.st_info or STT_FUNC;
|
|
AT_DATA :
|
|
elfsym.st_info:=elfsym.st_info or STT_OBJECT;
|
|
AT_TLS:
|
|
elfsym.st_info:=elfsym.st_info or STT_TLS;
|
|
AT_GNU_IFUNC:
|
|
elfsym.st_info:=elfsym.st_info or STT_GNU_IFUNC;
|
|
end;
|
|
end;
|
|
if objsym.bind<>AB_COMMON then
|
|
begin
|
|
if kind<>esk_obj then
|
|
begin
|
|
if assigned(objsym.objsection) and assigned(objsym.objsection.ExeSection) then
|
|
begin
|
|
if (oso_plt in objsym.objsection.SecOptions) then
|
|
elfsym.st_value:=0
|
|
else
|
|
elfsym.st_shndx:=TElfExeSection(objsym.objsection.ExeSection).secshidx;
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
if assigned(objsym.objsection) then
|
|
elfsym.st_shndx:=objsym.objsection.index
|
|
else
|
|
elfsym.st_shndx:=SHN_UNDEF;
|
|
objsym.symidx:=symidx;
|
|
end;
|
|
end;
|
|
inc(symidx);
|
|
MaybeSwapElfSymbol(elfsym);
|
|
write(elfsym,sizeof(TElfSymbol));
|
|
end;
|
|
|
|
{****************************************************************************
|
|
TElfObjectOutput
|
|
****************************************************************************}
|
|
|
|
constructor TElfObjectOutput.create(AWriter:TObjectWriter);
|
|
begin
|
|
inherited Create(AWriter);
|
|
CObjData:=TElfObjData;
|
|
end;
|
|
|
|
|
|
procedure TElfObjectOutput.createrelocsection(s:TElfObjSection;data:TObjData);
|
|
var
|
|
i : longint;
|
|
rel : telfreloc;
|
|
objreloc : TObjRelocation;
|
|
relsym : longint;
|
|
relocsect : TElfObjSection;
|
|
begin
|
|
{ create the reloc section }
|
|
relocsect:=TElfObjSection.create_reloc(data,s.name,false);
|
|
relocsect.shlink:=symtabsect.index;
|
|
relocsect.shinfo:=s.index;
|
|
{ add the relocations }
|
|
for i:=0 to s.Objrelocations.count-1 do
|
|
begin
|
|
objreloc:=TObjRelocation(s.Objrelocations[i]);
|
|
|
|
{ Symbol }
|
|
if assigned(objreloc.symbol) then
|
|
begin
|
|
if objreloc.symbol.symidx=-1 then
|
|
begin
|
|
writeln(objreloc.symbol.Name);
|
|
internalerror(200603012);
|
|
end;
|
|
relsym:=objreloc.symbol.symidx;
|
|
end
|
|
else
|
|
begin
|
|
if objreloc.objsection<>nil then
|
|
relsym:=objreloc.objsection.secsymidx
|
|
else
|
|
relsym:=SHN_UNDEF;
|
|
end;
|
|
|
|
rel.address:=objreloc.dataoffset;
|
|
rel.info:=ELF_R_INFO(relsym,ElfTarget.encodereloc(objreloc));
|
|
rel.addend:=objreloc.orgsize;
|
|
|
|
{ write reloc }
|
|
{ ElfXX_Rel is essentially ElfXX_Rela without the addend field. }
|
|
MaybeSwapElfReloc(rel);
|
|
relocsect.write(rel,relocsect.shentsize);
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure TElfObjectOutput.section_write_symbol(p:TObject;arg:pointer);
|
|
begin
|
|
{ Must not write symbols for internal sections like .symtab }
|
|
{ TODO: maybe use inclusive list of section types instead }
|
|
if (TElfObjSection(p).shtype in [SHT_SYMTAB,SHT_STRTAB,SHT_REL,SHT_RELA]) then
|
|
exit;
|
|
TObjSection(p).secsymidx:=symtabsect.symidx;
|
|
symtabsect.writeInternalSymbol(0,0,STT_SECTION,TObjSection(p).index);
|
|
end;
|
|
|
|
|
|
procedure TElfObjectOutput.createsymtab(data: TObjData);
|
|
var
|
|
i : longint;
|
|
objsym : TObjSymbol;
|
|
begin
|
|
with data do
|
|
begin
|
|
{ filename entry }
|
|
symtabsect.writeInternalSymbol(0,1,STT_FILE,SHN_ABS);
|
|
{ section }
|
|
ObjSectionList.ForEachCall(@section_write_symbol,nil);
|
|
{ First the Local Symbols, this is required by ELF. The localsyms
|
|
count stored in shinfo is used to skip the local symbols
|
|
when traversing the symtab }
|
|
for i:=0 to ObjSymbolList.Count-1 do
|
|
begin
|
|
objsym:=TObjSymbol(ObjSymbolList[i]);
|
|
if (objsym.bind=AB_LOCAL) and (objsym.typ<>AT_LABEL) then
|
|
symtabsect.WriteSymbol(objsym);
|
|
end;
|
|
{ Global Symbols }
|
|
for i:=0 to ObjSymbolList.Count-1 do
|
|
begin
|
|
objsym:=TObjSymbol(ObjSymbolList[i]);
|
|
if (objsym.bind<>AB_LOCAL) then
|
|
symtabsect.WriteSymbol(objsym);
|
|
end;
|
|
{ update the .symtab section header }
|
|
symtabsect.shlink:=symtabsect.fstrsec.index;
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure TElfObjectOutput.createshstrtab(data: TObjData);
|
|
var
|
|
i,prefixlen:longint;
|
|
objsec,target:TElfObjSection;
|
|
begin
|
|
shstrtabsect.writezeros(1);
|
|
prefixlen:=length('.rel')+ord(relocs_use_addend);
|
|
for i:=0 to data.ObjSectionList.Count-1 do
|
|
begin
|
|
objsec:=TElfObjSection(data.ObjSectionList[i]);
|
|
{ Alias section names into names of corresponding reloc sections,
|
|
this is allowed by ELF specs and saves good half of .shstrtab space. }
|
|
if objsec.shtype=relsec_shtype[relocs_use_addend] then
|
|
begin
|
|
target:=TElfObjSection(data.ObjSectionList[objsec.shinfo-1]);
|
|
if (target.ObjRelocations.Count=0) or
|
|
(target.shstridx<prefixlen) then
|
|
InternalError(2012101204);
|
|
objsec.shstridx:=target.shstridx-prefixlen;
|
|
end
|
|
else
|
|
begin
|
|
if objsec.ObjRelocations.Count<>0 then
|
|
shstrtabsect.write(relsec_prefix[true][1],prefixlen);
|
|
objsec.shstridx:=shstrtabsect.writestr(objsec.name);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure TElfObjectOutput.writesectionheader(s:TElfObjSection);
|
|
var
|
|
sechdr : telfsechdr;
|
|
begin
|
|
fillchar(sechdr,sizeof(sechdr),0);
|
|
sechdr.sh_name:=s.shstridx;
|
|
sechdr.sh_type:=s.shtype;
|
|
sechdr.sh_flags:=s.shflags;
|
|
sechdr.sh_offset:=s.datapos;
|
|
sechdr.sh_size:=s.Size;
|
|
sechdr.sh_link:=s.shlink;
|
|
sechdr.sh_info:=s.shinfo;
|
|
sechdr.sh_addralign:=s.secalign;
|
|
sechdr.sh_entsize:=s.shentsize;
|
|
MaybeSwapSecHeader(sechdr);
|
|
writer.write(sechdr,sizeof(sechdr));
|
|
end;
|
|
|
|
|
|
procedure TElfObjectOutput.section_count_sections(p:TObject;arg:pointer);
|
|
begin
|
|
TElfObjSection(p).index:=pword(arg)^;
|
|
inc(pword(arg)^);
|
|
end;
|
|
|
|
|
|
procedure TElfObjectOutput.section_create_relocsec(p:TObject;arg:pointer);
|
|
begin
|
|
if (TElfObjSection(p).ObjRelocations.count>0) then
|
|
createrelocsection(TElfObjSection(p),TObjData(arg));
|
|
end;
|
|
|
|
|
|
procedure TElfObjectOutput.section_write_sechdr(p:TObject;arg:pointer);
|
|
begin
|
|
writesectionheader(TElfObjSection(p));
|
|
end;
|
|
|
|
|
|
function TElfObjectOutput.writedata(data:TObjData):boolean;
|
|
var
|
|
header : telfheader;
|
|
shoffset,
|
|
datapos : aword;
|
|
nsections : word;
|
|
begin
|
|
result:=false;
|
|
with data do
|
|
begin
|
|
{ default sections }
|
|
symtabsect:=TElfSymtab.create(data,esk_obj);
|
|
shstrtabsect:=TElfObjSection.create_ext(data,'.shstrtab',SHT_STRTAB,0,1,0);
|
|
{ "no executable stack" marker for Linux }
|
|
if (target_info.system in systems_linux) and
|
|
not(cs_executable_stack in current_settings.moduleswitches) then
|
|
TElfObjSection.create_ext(data,'.note.GNU-stack',SHT_PROGBITS,0,1,0);
|
|
{ insert filename as first in strtab }
|
|
symtabsect.fstrsec.writestr(ExtractFileName(current_module.mainsource));
|
|
{ calc amount of sections we have }
|
|
nsections:=1;
|
|
{ also create the index in the section header table }
|
|
ObjSectionList.ForEachCall(@section_count_sections,@nsections);
|
|
{ create .symtab and .strtab }
|
|
createsymtab(data);
|
|
{ Create the relocation sections, this needs valid secidx and symidx }
|
|
ObjSectionList.ForEachCall(@section_create_relocsec,data);
|
|
{ recalc nsections to incude the reloc sections }
|
|
nsections:=1;
|
|
ObjSectionList.ForEachCall(@section_count_sections,@nsections);
|
|
{ create .shstrtab }
|
|
createshstrtab(data);
|
|
|
|
{ Calculate the filepositions }
|
|
datapos:=$40; { elfheader + alignment }
|
|
{ section data }
|
|
layoutsections(datapos);
|
|
{ section headers }
|
|
shoffset:=datapos;
|
|
inc(datapos,(nsections+1)*sizeof(telfsechdr));
|
|
|
|
{ Write ELF Header }
|
|
fillchar(header,sizeof(header),0);
|
|
header.e_ident[EI_MAG0]:=ELFMAG0; { = #127'ELF' }
|
|
header.e_ident[EI_MAG1]:=ELFMAG1;
|
|
header.e_ident[EI_MAG2]:=ELFMAG2;
|
|
header.e_ident[EI_MAG3]:=ELFMAG3;
|
|
header.e_ident[EI_CLASS]:=ELFCLASS;
|
|
if target_info.endian=endian_big then
|
|
header.e_ident[EI_DATA]:=ELFDATA2MSB
|
|
else
|
|
header.e_ident[EI_DATA]:=ELFDATA2LSB;
|
|
|
|
header.e_ident[EI_VERSION]:=1;
|
|
header.e_type:=ET_REL;
|
|
header.e_machine:=ELFMACHINE;
|
|
{$ifdef arm}
|
|
if (current_settings.fputype=fpu_soft) then
|
|
header.e_flags:=$600;
|
|
{$endif arm}
|
|
header.e_version:=1;
|
|
header.e_shoff:=shoffset;
|
|
header.e_shstrndx:=shstrtabsect.index;
|
|
|
|
header.e_shnum:=nsections;
|
|
header.e_ehsize:=sizeof(telfheader);
|
|
header.e_shentsize:=sizeof(telfsechdr);
|
|
MaybeSwapHeader(header);
|
|
writer.write(header,sizeof(header));
|
|
writer.writezeros($40-sizeof(header)); { align }
|
|
{ Sections }
|
|
WriteSectionContent(data);
|
|
{ section headers, start with an empty header for sh_undef }
|
|
writer.writezeros(sizeof(telfsechdr));
|
|
ObjSectionList.ForEachCall(@section_write_sechdr,nil);
|
|
end;
|
|
result:=true;
|
|
end;
|
|
|
|
|
|
{****************************************************************************
|
|
TELFAssembler
|
|
****************************************************************************}
|
|
|
|
constructor TElfAssembler.Create(smart:boolean);
|
|
begin
|
|
inherited Create(smart);
|
|
CObjOutput:=TElfObjectOutput;
|
|
end;
|
|
|
|
|
|
{****************************************************************************
|
|
TELFObjectInput
|
|
****************************************************************************}
|
|
|
|
constructor TElfObjInput.Create;
|
|
begin
|
|
inherited Create;
|
|
CObjData:=TElfObjData;
|
|
end;
|
|
|
|
|
|
destructor TElfObjInput.Destroy;
|
|
begin
|
|
if Assigned(FSymTbl) then
|
|
FreeMem(FSymTbl);
|
|
if Assigned(FSecTbl) then
|
|
FreeMem(FSecTbl);
|
|
if Assigned(strtab) then
|
|
FreeMem(strtab);
|
|
if Assigned(shstrtab) then
|
|
FreeMem(shstrtab);
|
|
if Assigned(symversions) then
|
|
FreeMem(symversions);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
|
|
procedure TElfObjInput.LoadRelocations(const secrec:TSectionRec);
|
|
var
|
|
i: longint;
|
|
rel: TElfReloc;
|
|
reltyp: byte;
|
|
relsym: longint;
|
|
objrel: TObjRelocation;
|
|
p: TObjSymbol;
|
|
begin
|
|
FReader.Seek(secrec.relocpos);
|
|
if secrec.sec=nil then
|
|
InternalError(2012060203);
|
|
for i:=0 to secrec.relocs-1 do
|
|
begin
|
|
FReader.Read(rel,secrec.relentsize);
|
|
MaybeSwapElfReloc(rel);
|
|
reltyp:=rel.info and $FF;
|
|
{$ifdef cpu64bitaddr}
|
|
relsym:=rel.info shr 32;
|
|
{$else cpu64bitaddr}
|
|
relsym:=(rel.info shr 8) and $FFFFFF;
|
|
{$endif cpu64bitaddr}
|
|
if relsym>=syms then
|
|
InternalError(2012060204);
|
|
p:=TObjSymbol(FSymTbl[relsym]);
|
|
if assigned(p) then
|
|
begin
|
|
objrel:=TObjRelocation.CreateRaw(rel.address-secrec.sec.mempos,p,reltyp);
|
|
if relocs_use_addend then
|
|
objrel.orgsize:=rel.addend;
|
|
{ perform target-specific actions }
|
|
ElfTarget.loadreloc(objrel);
|
|
secrec.sec.ObjRelocations.add(objrel);
|
|
end
|
|
else
|
|
begin
|
|
InputError('Unable to resolve symbol of relocation');
|
|
exit;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure TElfObjInput.LoadSymbols(objdata:TObjData;count,locals:longword);
|
|
var
|
|
i: longint;
|
|
sym: TElfSymbol;
|
|
bind: TAsmSymBind;
|
|
typ: TAsmSymType;
|
|
objsym: TObjSymbol;
|
|
ver: word;
|
|
begin
|
|
FSymTbl:=AllocMem(count*sizeof(Pointer));
|
|
for i:=1 to count-1 do
|
|
begin
|
|
FReader.Read(sym,sizeof(TElfSymbol));
|
|
MaybeSwapElfSymbol(sym);
|
|
if sym.st_name>=strtablen then
|
|
InternalError(2012060205);
|
|
|
|
if sym.st_shndx=SHN_ABS then { ignore absolute symbols (should we really do it???) }
|
|
Continue
|
|
else if sym.st_shndx=SHN_COMMON then
|
|
bind:=AB_COMMON
|
|
else if (sym.st_shndx>=nsects) then
|
|
InternalError(2012060206)
|
|
else
|
|
case (sym.st_info shr 4) of
|
|
STB_LOCAL:
|
|
bind:=AB_LOCAL;
|
|
STB_GLOBAL:
|
|
if sym.st_shndx=SHN_UNDEF then
|
|
bind:=AB_EXTERNAL
|
|
else
|
|
bind:=AB_GLOBAL;
|
|
STB_WEAK:
|
|
bind:=AB_WEAK_EXTERNAL;
|
|
else
|
|
InternalError(2012060207);
|
|
end;
|
|
|
|
{ Ignore section symbol if we didn't create the corresponding objsection
|
|
(examples are SHT_GROUP or .note.GNU-stack sections). }
|
|
if (sym.st_shndx>0) and (sym.st_shndx<SHN_LORESERVE) and
|
|
(FSecTbl[sym.st_shndx].sec=nil) and
|
|
(not dynobj) then
|
|
if ((sym.st_info and $0F)=STT_SECTION) then
|
|
Continue
|
|
else
|
|
begin
|
|
writeln(objdata.name,' ',i);
|
|
InternalError(2012110701)
|
|
end;
|
|
|
|
case (sym.st_info and $0F) of
|
|
STT_NOTYPE:
|
|
typ:=AT_NONE;
|
|
STT_OBJECT:
|
|
typ:=AT_DATA;
|
|
STT_FUNC:
|
|
typ:=AT_FUNCTION;
|
|
STT_SECTION:
|
|
typ:=AT_SECTION;
|
|
STT_FILE:
|
|
continue;
|
|
STT_TLS:
|
|
typ:=AT_TLS;
|
|
STT_GNU_IFUNC:
|
|
typ:=AT_GNU_IFUNC;
|
|
else
|
|
writeln(objdata.name,' ',sym.st_info and $0F);
|
|
InternalError(2012060208);
|
|
end;
|
|
{ If reading DSO, we're interested only in global symbols defined there.
|
|
Symbols with non-current version should also be ignored. }
|
|
if dynobj then
|
|
begin
|
|
if assigned(symversions) then
|
|
begin
|
|
ver:=symversions[i];
|
|
if (ver=0) or (ver > $7FFF) then
|
|
continue;
|
|
end;
|
|
if (bind= AB_LOCAL) or (sym.st_shndx=SHN_UNDEF) then
|
|
continue;
|
|
end;
|
|
{ validity of name and objsection has been checked above }
|
|
{ !! all AT_SECTION symbols have duplicate (null) name,
|
|
therefore TObjSection.CreateSymbol cannot be used here }
|
|
objsym:=TObjSymbol.Create(objdata.ObjSymbolList,string(PChar(@strtab[sym.st_name])));
|
|
objsym.bind:=bind;
|
|
objsym.typ:=typ;
|
|
if bind<>AB_COMMON then
|
|
objsym.objsection:=FSecTbl[sym.st_shndx].sec;
|
|
objsym.offset:=sym.st_value;
|
|
objsym.size:=sym.st_size;
|
|
FSymTbl[i]:=objsym;
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure TElfObjInput.LoadSection(const hdr;index:longint;objdata:tobjdata);
|
|
var
|
|
shdr: TElfsechdr absolute hdr;
|
|
sec: TElfObjSection;
|
|
sym: TElfSymbol;
|
|
secname: string;
|
|
begin
|
|
case shdr.sh_type of
|
|
SHT_NULL:
|
|
{ignore};
|
|
|
|
{ SHT_STRTAB may appear for .stabstr and other debug sections.
|
|
.shstrtab and .strtab are processed separately and don't appear here. }
|
|
SHT_PROGBITS,SHT_NOBITS,SHT_NOTE,SHT_STRTAB:
|
|
begin
|
|
if shdr.sh_name>=shstrtablen then
|
|
InternalError(2012060210);
|
|
secname:=string(PChar(@shstrtab[shdr.sh_name]));
|
|
|
|
sec:=TElfObjSection.create_ext(objdata,secname,
|
|
shdr.sh_type,shdr.sh_flags,shdr.sh_addralign,shdr.sh_entsize);
|
|
|
|
if (Length(secname)>3) and (secname[2] in ['d','f','n','s']) then
|
|
begin
|
|
if (Pos('.stub',secname)=1) or
|
|
(Pos('.fpc',secname)=1) then
|
|
sec.SecOptions:=[oso_keep]
|
|
{ ELF does not have any flags specific to debug sections,
|
|
but reserves names starting with '.debug' for this purpose }
|
|
else if (Pos('.debug',secname)=1) or
|
|
(secname='.stab') or
|
|
(secname='.stabstr') then
|
|
sec.SecOptions:=[oso_debug]
|
|
else if (secname='.note.GNU-stack') and (shdr.sh_type=SHT_PROGBITS) then
|
|
begin
|
|
if (shdr.sh_flags and SHF_EXECINSTR)=0 then
|
|
objdata.ExecStack:=False;
|
|
end;
|
|
end;
|
|
|
|
if (shdr.sh_type=SHT_NOTE) and (shdr.sh_size<>0) then
|
|
sec.SecOptions:=[oso_keep];
|
|
|
|
sec.index:=index;
|
|
sec.DataPos:=shdr.sh_offset;
|
|
sec.MemPos:=shdr.sh_addr;
|
|
sec.Size:=shdr.sh_size;
|
|
FSecTbl[index].sec:=sec;
|
|
end;
|
|
|
|
SHT_REL,SHT_RELA:
|
|
begin
|
|
if shdr.sh_info>=nsects then
|
|
InternalError(2012060211);
|
|
if shdr.sh_entsize<>longword((2+ord(shdr.sh_type=SHT_RELA))*sizeof(pint)) then
|
|
InternalError(2012060212);
|
|
|
|
with FSecTbl[shdr.sh_info] do
|
|
begin
|
|
relocpos:=shdr.sh_offset;
|
|
relocs:=shdr.sh_size div shdr.sh_entsize;
|
|
relentsize:=shdr.sh_entsize;
|
|
end;
|
|
end;
|
|
|
|
SHT_GROUP:
|
|
if (shdr.sh_size>=2*sizeof(longword)) and
|
|
(shdr.sh_entsize=sizeof(longword)) and
|
|
((shdr.sh_size mod shdr.sh_entsize)=0) then
|
|
begin
|
|
{ Groups are identified by name of symbol pointed to by
|
|
sh_link and sh_info, not by sh_name. This symbol
|
|
may as well be STT_SECTION symbol of this section,
|
|
in which case we end up using sh_name. }
|
|
if dynobj then
|
|
InternalError(2012110801);
|
|
if (shdr.sh_link<>symtabndx) then
|
|
InternalError(2012110703);
|
|
if (shdr.sh_info>=syms) then
|
|
InternalError(2012110704);
|
|
|
|
FReader.Seek(symtaboffset+shdr.sh_info*sizeof(TElfSymbol));
|
|
FReader.Read(sym,sizeof(TElfSymbol));
|
|
MaybeSwapElfSymbol(sym);
|
|
if sym.st_name>=strtablen then
|
|
InternalError(2012110705);
|
|
if (sym.st_shndx=index) and (sym.st_info=((STB_LOCAL shl 4) or STT_SECTION)) then
|
|
secname:=string(PChar(@shstrtab[shdr.sh_name]))
|
|
else
|
|
secname:=string(PChar(@strtab[sym.st_name]));
|
|
|
|
{ Postpone further processing until all sections are loaded,
|
|
we'll need to access correct section header.
|
|
Since ABI requires SHT_GROUP sections to come first in the file,
|
|
we assume that group number x has header index x+1.
|
|
If we ever encounter files where this is not true, we'll have
|
|
to maintain a separate index. }
|
|
objdata.CreateSectionGroup(secname);
|
|
if (index<>objdata.GroupsList.Count) then
|
|
InternalError(2012110802);
|
|
end
|
|
else
|
|
InternalError(2012110706);
|
|
else
|
|
InternalError(2012072603);
|
|
end;
|
|
FLoaded[index]:=True;
|
|
end;
|
|
|
|
|
|
function TElfObjInput.LoadHeader:word;
|
|
var
|
|
header:TElfHeader;
|
|
begin
|
|
result:=ET_NONE;
|
|
if not FReader.read(header,sizeof(header)) then
|
|
begin
|
|
InputError('Can''t read ELF header');
|
|
exit;
|
|
end;
|
|
if (header.e_ident[EI_MAG0]<>ELFMAG0) or (header.e_ident[EI_MAG1]<>ELFMAG1) or
|
|
(header.e_ident[EI_MAG2]<>ELFMAG2) or (header.e_ident[EI_MAG3]<>ELFMAG3) then
|
|
begin
|
|
InputError('Illegal ELF magic');
|
|
exit;
|
|
end;
|
|
if (header.e_ident[EI_VERSION]<>1) then
|
|
begin
|
|
InputError('Unknown ELF file version');
|
|
exit;
|
|
end;
|
|
if (header.e_ident[EI_CLASS]<>ELFCLASS) then
|
|
begin
|
|
InputError('Wrong ELF file class (32/64 bit mismatch)');
|
|
exit;
|
|
end;
|
|
if (header.e_ident[EI_DATA]<>1+ord(target_info.endian=endian_big)) then
|
|
begin
|
|
InputError('ELF endianness does not match target');
|
|
exit;
|
|
end;
|
|
MaybeSwapHeader(header);
|
|
if (header.e_version<>1) then
|
|
begin
|
|
InputError('Unknown ELF data version');
|
|
exit;
|
|
end;
|
|
if (header.e_machine<>ELFMACHINE) then
|
|
begin
|
|
InputError('ELF file is for different CPU');
|
|
exit;
|
|
end;
|
|
|
|
nsects:=header.e_shnum;
|
|
shentsize:=header.e_shentsize;
|
|
shoffset:=header.e_shoff;
|
|
shstrndx:=header.e_shstrndx;
|
|
result:=header.e_type;
|
|
end;
|
|
|
|
|
|
procedure TElfObjInput.LoadDynamic(const hdr;objdata:TObjData);
|
|
var
|
|
shdr: TElfsechdr absolute hdr;
|
|
dt: TElfDyn;
|
|
i: longint;
|
|
begin
|
|
if (shdr.sh_entsize<>sizeof(TElfDyn)) then
|
|
InternalError(2012071403);
|
|
|
|
FReader.Seek(shdr.sh_offset);
|
|
for i:=0 to (shdr.sh_size div shdr.sh_entsize)-1 do
|
|
begin
|
|
FReader.Read(dt,sizeof(TElfDyn));
|
|
MaybeSwapElfDyn(dt);
|
|
case dt.d_tag of
|
|
DT_NULL:
|
|
break;
|
|
DT_SONAME:
|
|
TElfObjData(objdata).FName:=string(PChar(@strtab[dt.d_ptr]));
|
|
DT_NEEDED:
|
|
;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
function TElfObjInput.ReadObjData(AReader:TObjectreader;objdata:TObjData):boolean;
|
|
var
|
|
i,j,strndx,dynndx,
|
|
versymndx,verdefndx,verneedndx: longint;
|
|
objsec: TObjSection;
|
|
shdrs: array of TElfsechdr;
|
|
grp: TObjSectionGroup;
|
|
tmp: longword;
|
|
count: longint;
|
|
begin
|
|
FReader:=AReader;
|
|
InputFileName:=AReader.FileName;
|
|
result:=false;
|
|
|
|
i:=LoadHeader;
|
|
if (i=ET_NONE) then { error message already given in this case }
|
|
exit;
|
|
if (i<>ET_REL) and (i<>ET_DYN) then
|
|
begin
|
|
InputError('Not a relocatable or dynamic ELF file');
|
|
exit;
|
|
end;
|
|
dynobj:=(i=ET_DYN);
|
|
|
|
if shentsize<>sizeof(TElfsechdr) then
|
|
InternalError(2012062701);
|
|
|
|
FSecTbl:=AllocMem(nsects*sizeof(TSectionRec));
|
|
FLoaded:=AllocMem(nsects*sizeof(boolean));
|
|
SetLength(shdrs,nsects);
|
|
|
|
FReader.Seek(shoffset);
|
|
if not FReader.Read(shdrs[0],nsects*sizeof(TElfsechdr)) then
|
|
begin
|
|
InputError('Can''t read ELF section headers');
|
|
exit;
|
|
end;
|
|
if source_info.endian<>target_info.endian then
|
|
for i:=0 to nsects-1 do
|
|
MaybeSwapSecHeader(shdrs[i]);
|
|
|
|
{ First, load the .shstrtab section }
|
|
if shstrndx>=nsects then
|
|
InternalError(2012060201);
|
|
if shdrs[shstrndx].sh_type<>SHT_STRTAB then
|
|
InternalError(2012060202);
|
|
shstrtablen:=shdrs[shstrndx].sh_size;
|
|
GetMem(shstrtab,shstrtablen);
|
|
FReader.seek(shdrs[shstrndx].sh_offset);
|
|
FReader.read(shstrtab^,shstrtablen);
|
|
FLoaded[shstrndx]:=True;
|
|
|
|
{ Locate the symtable, it is typically at the end so loop backwards.
|
|
Load the strings, postpone symtable itself until done with sections.
|
|
Note that is is legal to have no symtable.
|
|
For DSO, locate .dynsym instead, this one is near the beginning, but
|
|
overall number of sections won't be big. }
|
|
symtabndx:=0;
|
|
for i:=nsects-1 downto 1 do
|
|
begin
|
|
if (shdrs[i].sh_type<>symsectypes[dynobj]) then
|
|
continue;
|
|
if (shdrs[i].sh_entsize<>sizeof(TElfSymbol)) then
|
|
InternalError(2012060213);
|
|
if shdrs[i].sh_link>=nsects then
|
|
InternalError(2012062702);
|
|
strndx:=shdrs[i].sh_link;
|
|
if shdrs[strndx].sh_type<>SHT_STRTAB then
|
|
InternalError(2012062703);
|
|
strtablen:=shdrs[strndx].sh_size;
|
|
GetMem(strtab,strtablen);
|
|
FReader.seek(shdrs[strndx].sh_offset);
|
|
FReader.read(strtab^,strtablen);
|
|
|
|
symtaboffset:=shdrs[i].sh_offset;
|
|
syms:=shdrs[i].sh_size div sizeof(TElfSymbol);
|
|
localsyms:=shdrs[i].sh_info;
|
|
FLoaded[i]:=True;
|
|
FLoaded[strndx]:=True;
|
|
symtabndx:=i;
|
|
break;
|
|
end;
|
|
|
|
if symtabndx=0 then
|
|
InternalError(2012110706);
|
|
|
|
if dynobj then
|
|
begin
|
|
{ Locate .dynamic and version sections. Expect a single one of a kind. }
|
|
dynndx:=0;
|
|
versymndx:=0;
|
|
verdefndx:=0;
|
|
verneedndx:=0;
|
|
for i:=nsects-1 downto 0 do
|
|
begin
|
|
case shdrs[i].sh_type of
|
|
SHT_DYNAMIC:
|
|
begin
|
|
if dynndx<>0 then
|
|
InternalError(2012102001);
|
|
dynndx:=i;
|
|
if (shdrs[dynndx].sh_link<>strndx) then
|
|
InternalError(2012071402);
|
|
LoadDynamic(shdrs[dynndx],objdata);
|
|
end;
|
|
|
|
SHT_GNU_versym:
|
|
begin
|
|
if versymndx<>0 then
|
|
InternalError(2012102002);
|
|
versymndx:=i;
|
|
if shdrs[i].sh_entsize<>sizeof(word) then
|
|
InternalError(2012102003);
|
|
if shdrs[i].sh_link<>symtabndx then
|
|
InternalError(2012102004);
|
|
if shdrs[i].sh_size<>syms*sizeof(word) then
|
|
InternalError(2012102005);
|
|
GetMem(symversions,shdrs[i].sh_size);
|
|
FReader.seek(shdrs[i].sh_offset);
|
|
FReader.read(symversions^,shdrs[i].sh_size);
|
|
if source_info.endian<>target_info.endian then
|
|
for j:=0 to syms-1 do
|
|
SwapEndian(symversions[j]);
|
|
end;
|
|
|
|
SHT_GNU_verdef:
|
|
begin
|
|
if verdefndx<>0 then
|
|
InternalError(2012102006);
|
|
verdefndx:=i;
|
|
//sh_link->.dynstr
|
|
//sh_info->.hash
|
|
end;
|
|
|
|
SHT_GNU_verneed:
|
|
begin
|
|
if verneedndx<>0 then
|
|
InternalError(2012102007);
|
|
verneedndx:=i;
|
|
//sh_link->.dynstr
|
|
//sh_info->hash
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
if dynndx=0 then
|
|
InternalError(2012071401);
|
|
|
|
{ for DSO, we aren't interested in actual sections, but need to a dummy one
|
|
to maintain integrity. }
|
|
objsec:=TElfObjSection.create_ext(objdata,'*DSO*',SHT_PROGBITS,0,0,0);
|
|
for i:=1 to nsects-1 do
|
|
FSecTbl[i].sec:=objsec;
|
|
|
|
{ load the symtable }
|
|
FReader.Seek(symtaboffset+sizeof(TElfSymbol));
|
|
LoadSymbols(objdata,syms,localsyms);
|
|
|
|
result:=True;
|
|
exit;
|
|
end;
|
|
|
|
{ assume stack is executable until proven otherwise }
|
|
objdata.ExecStack:=True;
|
|
|
|
{ Process section headers }
|
|
for i:=1 to nsects-1 do
|
|
if not FLoaded[i] then
|
|
LoadSection(shdrs[i],i,objdata);
|
|
|
|
{ load the content }
|
|
ReadSectionContent(objdata);
|
|
|
|
{ load the symtable }
|
|
FReader.Seek(symtaboffset+sizeof(TElfSymbol));
|
|
LoadSymbols(objdata,syms,localsyms);
|
|
|
|
{ finish relocations }
|
|
for i:=0 to objdata.ObjSectionList.Count-1 do
|
|
begin
|
|
objsec:=TObjSection(objdata.ObjsectionList[i]);
|
|
{ skip debug sections }
|
|
if (oso_debug in objsec.SecOptions) and
|
|
(cs_link_strip in current_settings.globalswitches) and
|
|
not(cs_link_separate_dbg_file in current_settings.globalswitches) then
|
|
continue;
|
|
|
|
if FSecTbl[objsec.index].relocpos>0 then
|
|
LoadRelocations(FSecTbl[objsec.index]);
|
|
end;
|
|
|
|
{ finish processing section groups, if any }
|
|
if Assigned(objdata.GroupsList) then
|
|
begin
|
|
for i:=0 to objdata.GroupsList.Count-1 do
|
|
begin
|
|
grp:=TObjSectionGroup(objData.GroupsList[i]);
|
|
FReader.Seek(shdrs[i+1].sh_offset);
|
|
{ first dword is flags }
|
|
FReader.Read(tmp,sizeof(longword));
|
|
if source_info.endian<>target_info.endian then
|
|
tmp:=SwapEndian(tmp);
|
|
if (tmp and GRP_COMDAT)<>0 then
|
|
grp.IsComdat:=true;
|
|
|
|
count:=(shdrs[i+1].sh_size div sizeof(longword))-1;
|
|
SetLength(grp.members,count);
|
|
for j:=0 to count-1 do
|
|
begin
|
|
FReader.Read(tmp,sizeof(longword));
|
|
if source_info.endian<>target_info.endian then
|
|
tmp:=SwapEndian(tmp);
|
|
if (tmp>=nsects) then
|
|
InternalError(2012110805);
|
|
objsec:=FSecTbl[tmp].sec;
|
|
if (objsec=nil) then
|
|
InternalError(2012110806);
|
|
if (TElfObjSection(objsec).shflags and SHF_GROUP)=0 then
|
|
InternalError(2012110807);
|
|
grp.members[j]:=objsec;
|
|
objsec.Group:=grp;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
result:=True;
|
|
end;
|
|
|
|
|
|
class function TElfObjInput.CanReadObjData(AReader:TObjectreader):boolean;
|
|
var
|
|
header: TElfHeader;
|
|
begin
|
|
result:=false;
|
|
if AReader.Read(header,sizeof(header)) then
|
|
begin;
|
|
if (header.e_ident[EI_MAG0]=ELFMAG0) and (header.e_ident[EI_MAG1]=ELFMAG1) and
|
|
(header.e_ident[EI_MAG2]=ELFMAG2) and (header.e_ident[EI_MAG3]=ELFMAG3) then
|
|
{ TODO: check additional fields }
|
|
result:=true;
|
|
end;
|
|
AReader.Seek(0);
|
|
end;
|
|
|
|
{*****************************************************************************
|
|
TElfExeOutput
|
|
*****************************************************************************}
|
|
|
|
constructor TElfExeOutput.Create;
|
|
begin
|
|
inherited Create;
|
|
CObjData:=TElfObjData;
|
|
CExeSection:=TElfExeSection;
|
|
{$ifdef cpu64}
|
|
MaxMemPos:=Qword($FFFFFFFFFFFFFFFF);
|
|
//MaxMemPos:=$7EFFFFFF; { As specified by SysV AMD64 ABI for small memory model }
|
|
{$else cpu64}
|
|
MaxMemPos:=$7FFFFFFF;
|
|
{$endif cpu64}
|
|
SectionMemAlign:=$20;
|
|
SectionDataAlign:=$20;
|
|
segmentlist:=TFPObjectList.Create(True);
|
|
neededlist:=TFPHashList.Create;
|
|
end;
|
|
|
|
|
|
destructor TElfExeOutput.Destroy;
|
|
begin
|
|
neededlist.Free;
|
|
segmentlist.Free;
|
|
dynsymlist.Free;
|
|
dynreloclist.Free;
|
|
if assigned(dynsymnames) then
|
|
FreeMem(dynsymnames);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
|
|
function TElfExeOutput.AttachSection(objsec:TObjSection):TElfExeSection;
|
|
begin
|
|
objsec.SecOptions:=[oso_keep];
|
|
result:=TElfExeSection(FindExeSection(objsec.name));
|
|
if result=nil then
|
|
result:=TElfExeSection.Create(ExeSectionList,objsec.name);
|
|
result.AddObjSection(objsec);
|
|
end;
|
|
|
|
|
|
function TElfExeOutput.CreateSegment(atype,aflags,aalign:longword):TElfSegment;
|
|
begin
|
|
result:=TElfSegment.Create(atype,aflags,aalign);
|
|
segmentlist.add(result);
|
|
end;
|
|
|
|
|
|
procedure TElfExeOutput.WriteHeader;
|
|
var
|
|
header: TElfHeader;
|
|
begin
|
|
FillChar(header,sizeof(header),0);
|
|
header.e_ident[EI_MAG0]:=ELFMAG0; { = #127'ELF' }
|
|
header.e_ident[EI_MAG1]:=ELFMAG1;
|
|
header.e_ident[EI_MAG2]:=ELFMAG2;
|
|
header.e_ident[EI_MAG3]:=ELFMAG3;
|
|
header.e_ident[EI_CLASS]:=ELFCLASS;
|
|
if target_info.endian=endian_big then
|
|
header.e_ident[EI_DATA]:=ELFDATA2MSB
|
|
else
|
|
header.e_ident[EI_DATA]:=ELFDATA2LSB;
|
|
|
|
header.e_ident[EI_VERSION]:=1;
|
|
if IsSharedLibrary then
|
|
header.e_type:=ET_DYN
|
|
else
|
|
header.e_type:=ET_EXEC;
|
|
header.e_machine:=ELFMACHINE;
|
|
{$ifdef arm}
|
|
if (current_settings.fputype=fpu_soft) then
|
|
header.e_flags:=$600;
|
|
{$endif arm}
|
|
header.e_version:=1;
|
|
header.e_phoff:=sizeof(TElfHeader);
|
|
header.e_shoff:=shoffset;
|
|
header.e_shstrndx:=ExeSectionList.IndexOf(shstrtabsect.ExeSection)+1;
|
|
|
|
header.e_shnum:=ExeSectionList.Count+1;
|
|
header.e_phnum:=segmentlist.count;
|
|
header.e_ehsize:=sizeof(telfheader);
|
|
if assigned(EntrySym) then
|
|
header.e_entry:=EntrySym.Address;
|
|
header.e_shentsize:=sizeof(telfsechdr);
|
|
header.e_phentsize:=sizeof(telfproghdr);
|
|
MaybeSwapHeader(header);
|
|
FWriter.Write(header,sizeof(header));
|
|
end;
|
|
|
|
|
|
procedure TElfExeOutput.exesection_write_header(p:TObject;arg:Pointer);
|
|
var
|
|
shdr: TElfsechdr;
|
|
exesec: TElfExeSection absolute p;
|
|
begin
|
|
FillChar(shdr,sizeof(shdr),0);
|
|
shdr.sh_name:=exesec.shstridx;
|
|
if (ExeWriteMode=ewm_dbgonly) and
|
|
(exesec.SecOptions*[oso_debug,oso_debug_copy]=[]) then
|
|
shdr.sh_type:=SHT_NOBITS
|
|
else
|
|
shdr.sh_type:=exesec.shtype;
|
|
shdr.sh_flags:=exesec.shflags;
|
|
if (oso_load in exesec.SecOptions) then
|
|
shdr.sh_addr:=exesec.MemPos;
|
|
shdr.sh_offset:=exesec.DataPos;
|
|
shdr.sh_size:=exesec.Size;
|
|
shdr.sh_link:=exesec.shlink;
|
|
shdr.sh_info:=exesec.shinfo;
|
|
shdr.sh_addralign:=exesec.SecAlign;
|
|
shdr.sh_entsize:=exesec.shentsize;
|
|
MaybeSwapSecHeader(shdr);
|
|
FWriter.Write(shdr,sizeof(shdr));
|
|
end;
|
|
|
|
|
|
procedure TElfExeOutput.segment_write_header(p:TObject;arg:Pointer);
|
|
var
|
|
phdr: TElfproghdr;
|
|
seg: TElfSegment absolute p;
|
|
begin
|
|
FillChar(phdr,sizeof(phdr),0);
|
|
phdr.p_type:=seg.ptype;
|
|
phdr.p_flags:=seg.pflags;
|
|
phdr.p_align:=seg.align;
|
|
phdr.p_offset:=seg.DataPos;
|
|
phdr.p_filesz:=seg.DataSize;
|
|
phdr.p_memsz:=seg.MemSize;
|
|
phdr.p_vaddr:=seg.MemPos;
|
|
phdr.p_paddr:=seg.MemPos;
|
|
|
|
MaybeSwapHeader(phdr);
|
|
FWriter.Write(phdr,sizeof(phdr));
|
|
end;
|
|
|
|
|
|
procedure TElfExeOutput.WriteStaticSymtable;
|
|
var
|
|
i: longint;
|
|
sec: TElfExeSection;
|
|
exesym: TExeSymbol;
|
|
begin
|
|
for i:=0 to ExeSectionList.Count-1 do
|
|
begin
|
|
sec:=TElfExeSection(ExeSectionList[i]);
|
|
{ Must not write symbols for internal sections like .symtab }
|
|
if (sec.shtype in [SHT_SYMTAB,SHT_STRTAB,SHT_REL,SHT_RELA]) then
|
|
continue;
|
|
sec.secsymidx:=symtab.symidx;
|
|
symtab.writeInternalSymbol(sec.mempos,0,STT_SECTION,sec.secshidx);
|
|
end;
|
|
{ local symbols first }
|
|
for i:=0 to ExeSymbolList.Count-1 do
|
|
begin
|
|
exesym:=TExeSymbol(ExeSymbolList[i]);
|
|
if (exesym.objsymbol.bind=AB_LOCAL) and (exesym.objsymbol.typ<>AT_LABEL) then
|
|
symtab.WriteSymbol(exesym.objsymbol);
|
|
end;
|
|
{ Global Symbols }
|
|
for i:=0 to ExeSymbolList.Count-1 do
|
|
begin
|
|
exesym:=TExeSymbol(ExeSymbolList[i]);
|
|
if (exesym.objsymbol.bind<>AB_LOCAL) then
|
|
symtab.WriteSymbol(exesym.objsymbol);
|
|
end;
|
|
{ update exe section properties }
|
|
symtab.ExeSection.size:=symtab.size;
|
|
TElfExeSection(symtab.ExeSection).shinfo:=symtab.shinfo;
|
|
TElfExeSection(symtab.ExeSection).shlink:=ExeSectionList.IndexOf(symtab.fstrsec.ExeSection)+1;
|
|
symtab.fstrsec.ExeSection.Size:=symtab.fstrsec.size;
|
|
end;
|
|
|
|
|
|
procedure TElfExeOutput.MapSectionsToSegments;
|
|
var
|
|
seg: TElfSegment;
|
|
exesec: TExeSection;
|
|
i: longint;
|
|
begin
|
|
if (not IsSharedLibrary) and assigned(interpobjsec) then
|
|
begin
|
|
phdrseg:=CreateSegment(PT_PHDR,PF_R or PF_X,sizeof(pint));
|
|
seg:=CreateSegment(PT_INTERP,PF_R,1);
|
|
seg.Add(interpobjsec.ExeSection);
|
|
end;
|
|
|
|
textseg:=CreateSegment(PT_LOAD,PF_X or PF_R,ELF_MAXPAGESIZE);
|
|
dataseg:=CreateSegment(PT_LOAD,PF_R or PF_W,ELF_MAXPAGESIZE);
|
|
for i:=0 to ExeSectionList.Count-1 do
|
|
begin
|
|
exesec:=TExeSection(ExeSectionList[i]);
|
|
if (oso_load in exesec.SecOptions) then
|
|
begin
|
|
if (TElfExeSection(exesec).shflags and SHF_TLS)<>0 then
|
|
begin
|
|
if tlsseg=nil then
|
|
tlsseg:=CreateSegment(PT_TLS,PF_R,sizeof(pint));
|
|
tlsseg.add(exesec);
|
|
end;
|
|
|
|
{ TODO: at least on Linux, ld seems to drop .note.ABI-tag for static executables.
|
|
(Logic is as follows: there is no .note.ABI-tag section in ld script, so it
|
|
is processed as orphan section. As such, it is placed after .interp.
|
|
For static executables .interp is dropped, and it looks like there's nowhere to
|
|
place .note.ABI-tag in this case)
|
|
Always including it doesn't harm though (except increasing file size). }
|
|
if TElfExeSection(exesec).shtype=SHT_NOTE then
|
|
begin
|
|
if noteseg=nil then
|
|
noteseg:=CreateSegment(PT_NOTE,PF_R,4);
|
|
noteseg.Add(exesec);
|
|
Include(exesec.SecOptions,oso_debug_copy);
|
|
end;
|
|
if (oso_executable in exesec.SecOptions) or
|
|
not (oso_write in exesec.SecOptions) then
|
|
textseg.add(exesec)
|
|
else
|
|
dataseg.add(exesec);
|
|
end;
|
|
end;
|
|
|
|
if dynamiclink then
|
|
begin
|
|
seg:=CreateSegment(PT_DYNAMIC,PF_R or PF_W,sizeof(pint));
|
|
seg.add(dynamicsec.ExeSection);
|
|
end;
|
|
|
|
{ stack flags }
|
|
CreateSegment(PT_GNU_STACK,PF_R or PF_W or (PF_X*ord(ExecStack)),sizeof(pint));
|
|
end;
|
|
|
|
|
|
procedure TElfExeOutput.PrepareGOT;
|
|
var
|
|
i,j,k: longint;
|
|
objsec: TElfObjSection;
|
|
exesec: TExeSection;
|
|
begin
|
|
for i:=0 to ExeSectionList.Count-1 do
|
|
begin
|
|
exesec:=TExeSection(ExeSectionList[i]);
|
|
for j:=0 to exesec.ObjSectionlist.count-1 do
|
|
begin
|
|
objsec:=TElfObjSection(exesec.ObjSectionlist[j]);
|
|
{ ignore linker-generated and debug sections }
|
|
if (objsec.objdata=internalobjdata) or (oso_debug in objsec.SecOptions) then
|
|
continue;
|
|
if not objsec.Used then
|
|
internalerror(2012060901);
|
|
for k:=0 to objsec.ObjRelocations.Count-1 do
|
|
GOTRelocPass1(objsec,TObjRelocation(objsec.ObjRelocations[k]));
|
|
end;
|
|
end;
|
|
{ remember sizes for sanity checking }
|
|
gotsize:=gotobjsec.size;
|
|
if assigned(dynrelocsec) then
|
|
dynrelsize:=dynrelocsec.size
|
|
else
|
|
dynrelsize:=0;
|
|
end;
|
|
|
|
|
|
procedure TElfExeOutput.Load_Start;
|
|
begin
|
|
inherited Load_Start;
|
|
dynsymlist:=TFPObjectList.Create(False);
|
|
|
|
gotpltobjsec:=TElfObjSection.create_ext(internalObjData,'.got.plt',
|
|
SHT_PROGBITS,SHF_ALLOC or SHF_WRITE,sizeof(pint),sizeof(pint));
|
|
|
|
gotobjsec:=TElfObjSection.create_ext(internalObjData,'.got',
|
|
SHT_PROGBITS,SHF_ALLOC or SHF_WRITE,sizeof(pint),sizeof(pint));
|
|
gotobjsec.SecOptions:=[oso_keep];
|
|
|
|
{ GOT symbol and reserved .got.plt entries }
|
|
internalObjData.SetSection(gotpltobjsec);
|
|
gotsymbol:=internalObjData.SymbolDefine('_GLOBAL_OFFSET_TABLE_',AB_GLOBAL,AT_DATA);
|
|
gotpltobjsec.writeZeros(3*sizeof(pint));
|
|
end;
|
|
|
|
|
|
procedure TElfExeOutput.Load_DynamicObject(objdata:TObjData);
|
|
var
|
|
i: longint;
|
|
exesym: TExeSymbol;
|
|
objsym: TObjSymbol;
|
|
begin
|
|
Comment(v_debug,'Dynamic object: '+objdata.name);
|
|
if neededlist.Find(objdata.name)=nil then
|
|
neededlist.Add(objdata.name,objdata);
|
|
for i:=0 to UnresolvedExeSymbols.Count-1 do
|
|
begin
|
|
exesym:=TExeSymbol(UnresolvedExeSymbols[i]);
|
|
if exesym.State<>symstate_undefined then
|
|
continue;
|
|
objsym:=TObjSymbol(objdata.ObjSymbolList.Find(exesym.name));
|
|
if assigned(objsym) then
|
|
begin
|
|
exesym.State:=symstate_defined;
|
|
exesym.dynindex:=dynsymlist.Add(exesym)+1;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure TElfExeOutput.Order_Start;
|
|
begin
|
|
inherited Order_Start;
|
|
dynamiclink:=IsSharedLibrary or (dynsymlist.count>0) or
|
|
(
|
|
(UnresolvedExeSymbols.Count>0) and
|
|
not (cs_link_staticflag in current_settings.globalswitches)
|
|
);
|
|
if dynamiclink then
|
|
InitDynlink;
|
|
if dynamiclink or (IndirectObjSymbols.Count>0) then
|
|
CreatePLT;
|
|
end;
|
|
|
|
|
|
procedure TElfExeOutput.Order_end;
|
|
|
|
procedure set_oso_keep(const s:string);
|
|
var
|
|
exesec:TExeSection;
|
|
objsec:TObjSection;
|
|
i:longint;
|
|
begin
|
|
exesec:=TExeSection(ExeSectionList.Find(s));
|
|
if assigned(exesec) then
|
|
begin
|
|
for i:=0 to exesec.ObjSectionList.Count-1 do
|
|
begin
|
|
objsec:=TObjSection(exesec.ObjSectionList[i]);
|
|
{ ignore sections used for symbol definition }
|
|
if oso_data in objsec.SecOptions then
|
|
objsec.SecOptions:=[oso_keep];
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
begin
|
|
inherited Order_end;
|
|
set_oso_keep('.init');
|
|
set_oso_keep('.fini');
|
|
set_oso_keep('.jcr');
|
|
set_oso_keep('.ctors');
|
|
set_oso_keep('.dtors');
|
|
set_oso_keep('.preinit_array');
|
|
set_oso_keep('.init_array');
|
|
set_oso_keep('.fini_array');
|
|
set_oso_keep('.eh_frame');
|
|
|
|
{ let .dynamic reference other dynamic sections so they aren't marked
|
|
for removal as unused }
|
|
if dynamiclink then
|
|
WriteDynamicTags;
|
|
end;
|
|
|
|
|
|
procedure TElfExeOutput.AfterUnusedSectionRemoval;
|
|
var
|
|
i:longint;
|
|
exesym:TExeSymbol;
|
|
objsym:TObjSymbol;
|
|
objsec: TObjSection;
|
|
begin
|
|
{ Unused section removal changes state of referenced exesymbols
|
|
to symstate_dynamic. Remaining ones can be removed. }
|
|
for i:=0 to dynsymlist.count-1 do
|
|
begin
|
|
exesym:=TExeSymbol(dynsymlist[i]);
|
|
if assigned(exesym.ObjSymbol.ObjSection) then // an exported symbol
|
|
continue;
|
|
if exesym.state<>symstate_dynamic then
|
|
begin
|
|
dynsymlist[i]:=nil;
|
|
exesym.dynindex:=0;
|
|
end;
|
|
end;
|
|
dynsymlist.Pack;
|
|
{ reindex }
|
|
for i:=0 to dynsymlist.count-1 do
|
|
TExeSymbol(dynsymlist[i]).dynindex:=i+1;
|
|
|
|
{ Drop unresolved symbols that aren't referenced, assign dynamic
|
|
indices to remaining ones, but not if linking with -Xt.
|
|
TODO: behavior of .so with -Xt ? }
|
|
if (cs_link_staticflag in current_settings.globalswitches) then
|
|
UnresolvedExeSymbols.Clear
|
|
else
|
|
for i:=0 to UnresolvedExeSymbols.Count-1 do
|
|
begin
|
|
exesym:=TExeSymbol(UnresolvedExeSymbols[i]);
|
|
if exesym.state=symstate_dynamic then
|
|
begin
|
|
if exesym.dynindex<>0 then
|
|
InternalError(2012062301);
|
|
exesym.dynindex:=dynsymlist.add(exesym)+1;
|
|
exesym.state:=symstate_defined;
|
|
end
|
|
else
|
|
UnresolvedExeSymbols[i]:=nil;
|
|
end;
|
|
UnresolvedExeSymbols.Pack;
|
|
|
|
{ Scan relocations to determine size of GOT, dynamic reloc section, etc. }
|
|
PrepareGOT;
|
|
|
|
{ Write required PLT entries }
|
|
for i:=0 to dynsymlist.Count-1 do
|
|
begin
|
|
exesym:=TExeSymbol(dynsymlist[i]);
|
|
if assigned(exesym.ObjSymbol.objsection) then // an exported symbol
|
|
continue;
|
|
|
|
{ if there's no PLT references to symbol, then PLT entry isn't needed }
|
|
{ !! Does not work correctly yet !! }
|
|
// if (exesym.objsymbol.refs and symref_plt)=0 then
|
|
// continue;
|
|
|
|
{ This symbol has a valid address to which relocations are resolved,
|
|
but it remains (weak)external when written to dynamic symtable. }
|
|
objsym:=internalobjdata.CreateSymbol(exesym.name);
|
|
objsym.typ:=AT_FUNCTION;
|
|
objsym.bind:=exesym.ObjSymbol.bind; { AB_EXTERNAL or AB_WEAK_EXTERNAL }
|
|
objsym.offset:=pltobjsec.size;
|
|
objsym.objsection:=pltobjsec;
|
|
exesym.ObjSymbol:=objsym;
|
|
|
|
WritePLTEntry(exesym);
|
|
end;
|
|
|
|
{ Handle indirect symbols }
|
|
for i:=0 to IndirectObjSymbols.Count-1 do
|
|
begin
|
|
objsym:=TObjSymbol(IndirectObjSymbols[i]);
|
|
objsec:=objsym.ExeSymbol.ObjSymbol.objsection;
|
|
objsym.bind:=AB_EXTERNAL; { cheat FixupSymbols }
|
|
if (oso_plt in objsec.SecOptions) then
|
|
continue;
|
|
WriteIndirectPLTEntry(objsym.ExeSymbol);
|
|
end;
|
|
|
|
FixupSymbols;
|
|
|
|
if dynamiclink then
|
|
WriteDynamicSymbolsHash;
|
|
|
|
{ Create .shstrtab section, which is needed in both exe and .dbg files }
|
|
shstrtabsect:=TElfObjSection.Create_ext(internalObjData,'.shstrtab',SHT_STRTAB,0,1,0);
|
|
shstrtabsect.SecOptions:=[oso_debug_copy];
|
|
AttachSection(shstrtabsect);
|
|
|
|
{ Create the static symtable (.symtab and .strtab) }
|
|
if (cs_link_separate_dbg_file in current_settings.globalswitches) or
|
|
not(cs_link_strip in current_settings.globalswitches) then
|
|
begin
|
|
symtab:=TElfSymtab.Create(internalObjData,esk_exe);
|
|
symtab.SecOptions:=[oso_debug];
|
|
symtab.fstrsec.SecOptions:=[oso_debug];
|
|
AttachSection(symtab);
|
|
AttachSection(symtab.fstrsec);
|
|
end;
|
|
|
|
{ Re-enable sections which end up to contain some data
|
|
(.got, .rel[a].dyn, .rel[a].plt (includes .rel[a].iplt) and .hash }
|
|
if gotobjsec.size<>0 then
|
|
Exclude(gotobjsec.ExeSection.SecOptions,oso_disabled);
|
|
if assigned(dynrelocsec) and (dynrelocsec.size<>0) then
|
|
Exclude(dynrelocsec.ExeSection.SecOptions,oso_disabled);
|
|
if assigned(pltrelocsec) and (pltrelocsec.size>0) then
|
|
Exclude(pltrelocsec.ExeSection.SecOptions,oso_disabled);
|
|
if assigned(ipltrelocsec) and (ipltrelocsec.size>0) then
|
|
Exclude(ipltrelocsec.ExeSection.SecOptions,oso_disabled);
|
|
if assigned(hashobjsec) then
|
|
Exclude(hashobjsec.ExeSection.SecOptions,oso_disabled);
|
|
|
|
RemoveDisabledSections;
|
|
MapSectionsToSegments;
|
|
if dynamiclink then
|
|
FinishDynamicTags;
|
|
end;
|
|
|
|
|
|
procedure TElfExeOutput.MemPos_Start;
|
|
var
|
|
i,j: longint;
|
|
seg: TElfSegment;
|
|
exesec: TElfExeSection;
|
|
objsec: TObjSection;
|
|
tempmempos: qword;
|
|
begin
|
|
{ Remove any existing .shstrtab contents }
|
|
if (shstrtabsect.size>0) then
|
|
begin
|
|
shstrtabsect.ReleaseData;
|
|
shstrtabsect.Size:=0;
|
|
shstrtabsect.SecOptions:=[oso_data];
|
|
end;
|
|
|
|
{ Assign section indices and fill .shstrtab
|
|
List of sections cannot be modified after this point. }
|
|
shstrtabsect.writezeros(1);
|
|
for i:=0 to ExeSectionList.Count-1 do
|
|
begin
|
|
exesec:=TElfExeSection(ExeSectionList[i]);
|
|
exesec.shstridx:=shstrtabsect.writestr(exesec.Name);
|
|
exesec.secshidx:=i+1;
|
|
end;
|
|
|
|
if dynamiclink then
|
|
begin
|
|
{ fixup sh_link/sh_info members of various dynamic sections }
|
|
TElfExeSection(hashobjsec.ExeSection).shlink:=TElfExeSection(dynsymtable.ExeSection).secshidx;
|
|
i:=TElfExeSection(dynsymtable.fstrsec.ExeSection).secshidx;
|
|
TElfExeSection(dynamicsec.ExeSection).shlink:=i;
|
|
TElfExeSection(dynsymtable.ExeSection).shlink:=i;
|
|
|
|
if assigned(pltrelocsec) then
|
|
begin
|
|
TElfExeSection(pltrelocsec.ExeSection).shlink:=TElfExeSection(dynsymtable.ExeSection).secshidx;
|
|
TElfExeSection(pltrelocsec.ExeSection).shinfo:=TElfExeSection(pltobjsec.ExeSection).secshidx;
|
|
end;
|
|
|
|
if assigned(dynrelocsec) and assigned(dynrelocsec.ExeSection) then
|
|
TElfExeSection(dynrelocsec.ExeSection).shlink:=TElfExeSection(dynsymtable.ExeSection).secshidx;
|
|
end
|
|
else if assigned(ipltrelocsec) then
|
|
TElfExeSection(ipltrelocsec.ExeSection).shinfo:=TElfExeSection(pltobjsec.ExeSection).secshidx;
|
|
|
|
{ The actual layout }
|
|
if IsSharedLibrary then
|
|
CurrMemPos:=0
|
|
else
|
|
CurrMemPos:=TEXT_SEGMENT_START;
|
|
textseg.MemPos:=CurrMemPos;
|
|
if assigned(phdrseg) then
|
|
begin
|
|
phdrseg.Mempos:=CurrMemPos+sizeof(TElfHeader);
|
|
phdrseg.Memsize:=sizeof(TElfproghdr)*segmentlist.count;
|
|
end;
|
|
CurrMemPos:=CurrMemPos+sizeof(TElfHeader)+segmentlist.count*sizeof(TElfproghdr);
|
|
MemPos_Segment(textseg);
|
|
CurrMemPos:=Align(CurrMemPos,SectionDataAlign); {! Data,not MemAlign}
|
|
CurrMemPos:=CurrMemPos+ELF_MAXPAGESIZE;
|
|
dataseg.MemPos:=CurrMemPos;
|
|
MemPos_Segment(dataseg);
|
|
{ Mempos of unmapped sections is forced to zero, but we have to set positions
|
|
of its objsections and update sizes }
|
|
for i:=0 to ExeSectionList.Count-1 do
|
|
begin
|
|
exesec:=TElfExeSection(ExeSectionList[i]);
|
|
if not (oso_load in exesec.SecOptions) then
|
|
begin
|
|
tempmempos:=0;
|
|
exesec.MemPos:=tempmempos;
|
|
for j:=0 to exesec.ObjSectionList.Count-1 do
|
|
begin
|
|
objsec:=TObjSection(exesec.ObjSectionList[j]);
|
|
tempmempos:=objsec.setmempos(tempmempos);
|
|
end;
|
|
exesec.Size:=tempmempos;
|
|
end;
|
|
end;
|
|
|
|
{ Update MemPos and MemSize of non-load segments,
|
|
in particular, TLS sizes are needed to resolve relocations }
|
|
for i:=0 to segmentlist.count-1 do
|
|
begin
|
|
seg:=TElfSegment(segmentlist[i]);
|
|
if (seg.ptype=PT_LOAD) or (seg.FSectionList.Count=0) then
|
|
continue;
|
|
seg.MemPos:=TExeSection(seg.FSectionList.First).MemPos;
|
|
for j:=0 to seg.FSectionList.Count-1 do
|
|
begin
|
|
exesec:=TElfExeSection(seg.FSectionList[j]);
|
|
seg.MemSize:=exesec.MemPos+exesec.Size-seg.MemPos;
|
|
end;
|
|
end;
|
|
|
|
if (not gotwritten) then
|
|
begin
|
|
{ reset size of .got and .rel[a].dyn, they will be refilled while fixing up relocations }
|
|
if assigned(gotobjsec) then
|
|
gotobjsec.size:=0;
|
|
if assigned(dynrelocsec) then
|
|
begin
|
|
dynrelocsec.size:=0;
|
|
{ write actual .dynsym content (needs valid symbol addresses) }
|
|
dynsymtable.size:=sizeof(TElfsymbol);
|
|
for i:=0 to dynsymlist.count-1 do
|
|
dynsymtable.writeSymbol(TExeSymbol(dynsymlist[i]).objsymbol,dynsymnames[i]);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure TElfExeOutput.MemPos_Segment(seg:TElfSegment);
|
|
var
|
|
i: longint;
|
|
exesec: TElfExeSection;
|
|
begin
|
|
for i:=0 to seg.FSectionList.Count-1 do
|
|
begin
|
|
exesec:=TElfExeSection(seg.FSectionList[i]);
|
|
inherited MemPos_ExeSection(exesec);
|
|
{ .tbss should not contribute to address space }
|
|
if (exesec.shtype=SHT_NOBITS) and ((exesec.shflags and SHF_TLS)<>0) then
|
|
CurrMemPos:=exesec.MemPos;
|
|
end;
|
|
{ calculate size of the segment }
|
|
seg.MemSize:=CurrMemPos-seg.MemPos;
|
|
end;
|
|
|
|
|
|
procedure TElfExeOutput.MemPos_ExeSection(const aname:string);
|
|
begin
|
|
// Ignore. All layout is done in mempos_start
|
|
end;
|
|
|
|
|
|
procedure TElfExeOutput.DataPos_Start;
|
|
var
|
|
i,j: longint;
|
|
exesec: TExeSection;
|
|
seg: TElfSegment;
|
|
objreloc: TObjRelocation;
|
|
begin
|
|
gotwritten:=true;
|
|
{ If target does not support sorted relocations, it is expected to write the
|
|
entire .rel[a].dyn section during FixupRelocations. Otherwise, only RELATIVE ones
|
|
should be written, space for non-relative relocations should remain. }
|
|
if assigned(dynrelocsec) and (relative_reloc_count>0) then
|
|
begin
|
|
if (dynrelocsec.size+(dynreloclist.count*dynrelocsec.shentsize)<>dynrelsize) then
|
|
InternalError(2012110601);
|
|
{ Write out non-RELATIVE dynamic relocations
|
|
TODO: additional sorting? }
|
|
for i:=0 to dynreloclist.count-1 do
|
|
begin
|
|
objreloc:=TObjRelocation(dynreloclist[i]);
|
|
WriteDynRelocEntry(objreloc.dataoffset,objreloc.ftype,objreloc.symbol.exesymbol.dynindex,0);
|
|
end;
|
|
end;
|
|
|
|
{ sanity checks }
|
|
if assigned(gotobjsec) and (gotsize<>gotobjsec.size) then
|
|
InternalError(2012092501);
|
|
if assigned(dynrelocsec) and (dynrelsize<>dynrelocsec.size) then
|
|
InternalError(2012092502);
|
|
|
|
if (ExeWriteMode=ewm_dbgonly) or
|
|
(
|
|
(ExeWriteMode=ewm_exefull) and
|
|
not(cs_link_strip in current_settings.globalswitches)
|
|
) then
|
|
WriteStaticSymtable;
|
|
|
|
{ first handle primary segments }
|
|
textseg.DataPos:=0;
|
|
CurrDataPos:=sizeof(TElfHeader)+sizeof(TElfproghdr)*segmentlist.count;
|
|
if assigned(phdrseg) then
|
|
begin
|
|
phdrseg.DataPos:=sizeof(TElfHeader);
|
|
phdrseg.DataSize:=sizeof(TElfproghdr)*segmentlist.count;
|
|
end;
|
|
DataPos_Segment(textseg);
|
|
CurrDataPos:=align(CurrDataPos,SectionDataAlign);
|
|
dataseg.DataPos:=CurrDataPos;
|
|
DataPos_Segment(dataseg);
|
|
{ then unmapped sections }
|
|
for i:=0 to ExeSectionList.Count-1 do
|
|
begin
|
|
exesec:=TExeSection(ExeSectionList[i]);
|
|
if not (oso_load in exesec.SecOptions) then
|
|
inherited DataPos_ExeSection(exesec);
|
|
end;
|
|
{ finally, update size/position of non-load segments }
|
|
for i:=0 to segmentlist.count-1 do
|
|
begin
|
|
seg:=TElfSegment(segmentlist[i]);
|
|
if (seg.ptype=PT_LOAD) or (seg.FSectionList.Count=0) then
|
|
continue;
|
|
seg.DataPos:=TExeSection(seg.FSectionList.First).DataPos;
|
|
for j:=0 to seg.FSectionList.Count-1 do
|
|
begin
|
|
exesec:=TExeSection(seg.FSectionList[j]);
|
|
if oso_data in exesec.SecOptions then
|
|
seg.DataSize:=exesec.DataPos+exesec.Size-seg.DataPos;
|
|
end;
|
|
end;
|
|
{ place section headers after the data }
|
|
shoffset:=CurrDataPos;
|
|
CurrDataPos:=CurrDataPos+ExeSectionList.Count*sizeof(TElfsechdr);
|
|
end;
|
|
|
|
|
|
procedure TElfExeOutput.DataPos_Segment(seg:TElfSegment);
|
|
var
|
|
i: longint;
|
|
exesec: TElfExeSection;
|
|
begin
|
|
for i:=0 to seg.FSectionList.Count-1 do
|
|
begin
|
|
exesec:=TElfExeSection(seg.FSectionList[i]);
|
|
{ ELF needs DataPos set to 'would-be' value for sections that
|
|
don't have data, and for non-debug sections in .dbg file, too.
|
|
This slightly differs from generic approach. }
|
|
if not (oso_data in exesec.SecOptions) or
|
|
(
|
|
(ExeWriteMode=ewm_dbgonly) and
|
|
(exesec.SecOptions*[oso_debug,oso_debug_copy]=[])
|
|
) then
|
|
begin
|
|
CurrDataPos:=align(CurrDataPos,SectionDataAlign);
|
|
exesec.DataPos:=CurrDataPos;
|
|
end
|
|
else
|
|
inherited DataPos_ExeSection(exesec);
|
|
|
|
end;
|
|
{ calculate size of the segment }
|
|
seg.DataSize:=CurrDataPos-seg.DataPos;
|
|
end;
|
|
|
|
|
|
procedure TElfExeOutput.DataPos_ExeSection(const aname:string);
|
|
begin
|
|
// Ignore. Work is done entirely in datapos_start.
|
|
end;
|
|
|
|
|
|
procedure TElfExeOutput.InitDynlink;
|
|
begin
|
|
if not IsSharedLibrary then
|
|
begin
|
|
interpobjsec:=internalObjData.createsection('.interp',1,[oso_data,oso_load,oso_keep]);
|
|
{ TODO: supply target-specific default }
|
|
{$ifdef x86_64}
|
|
interpobjsec.writestr('/lib64/ld-linux-x86-64.so.2');
|
|
{$else}
|
|
interpobjsec.writestr('/lib/ld-linux.so.2');
|
|
{$endif x86_64}
|
|
end;
|
|
|
|
hashobjsec:=TElfObjSection.create_ext(internalObjData,'.hash',
|
|
SHT_HASH,SHF_ALLOC,sizeof(pint),4);
|
|
hashobjsec.secoptions:=[oso_keep];
|
|
|
|
dynsymtable:=TElfSymtab.create(internalObjData,esk_dyn);
|
|
|
|
dynamicsec:=TElfObjSection.create_ext(internalObjData,'.dynamic',
|
|
SHT_DYNAMIC,SHF_ALLOC or SHF_WRITE,sizeof(pint),sizeof(TElfDyn));
|
|
dynamicsec.SecOptions:=[oso_keep];
|
|
|
|
dynrelocsec:=TElfObjSection.create_reloc(internalObjData,'.dyn',true);
|
|
dynrelocsec.SecOptions:=[oso_keep];
|
|
|
|
dynreloclist:=TFPObjectList.Create(true);
|
|
end;
|
|
|
|
|
|
const
|
|
hashbuckets: array[0..15] of longint=(
|
|
1, 3, 17, 37, 67, 97, 131, 197, 263, 521, 1031, 2053, 4099, 8209,
|
|
16411, 32771);
|
|
|
|
{$push}{$r-,q-}
|
|
function elfhash(const name:string):longword;
|
|
var
|
|
g: longword;
|
|
i: longint;
|
|
begin
|
|
result:=0;
|
|
for i:=1 to length(name) do
|
|
begin
|
|
result:=(result shl 4)+ord(name[i]);
|
|
g:=result and $F0000000;
|
|
if g>0 then
|
|
result:=result xor (g shr 24);
|
|
result:=result and (not g);
|
|
end;
|
|
end;
|
|
{$pop}
|
|
|
|
procedure TElfExeOutput.WriteDynamicSymbolsHash;
|
|
var
|
|
nchains,nbuckets: longint;
|
|
i,j: longint;
|
|
hashdata: plongint;
|
|
sym: TExeSymbol;
|
|
begin
|
|
dynsymnames:=AllocMem(dynsymlist.count*sizeof(longword));
|
|
nchains:=dynsymlist.Count+1;
|
|
{ determine suitable bucket count }
|
|
i:=high(hashbuckets);
|
|
while (i>=0) and (nchains<hashbuckets[i]) do
|
|
dec(i);
|
|
nbuckets:=hashbuckets[i];
|
|
|
|
hashdata:=AllocMem((2+nchains+nbuckets)*sizeof(longint));
|
|
hashdata[0]:=nbuckets;
|
|
hashdata[1]:=nchains;
|
|
{ The contents of .dynsym can be written only after mempos pass
|
|
because it needs valid symbol virtual addresses and section indices.
|
|
Here we preset .dynsym size and write names, in order to get
|
|
correct size of .dynstr section. }
|
|
dynsymtable.size:=(dynsymlist.count+1)*sizeof(TElfsymbol);
|
|
for i:=0 to dynsymlist.Count-1 do
|
|
begin
|
|
sym:=TExeSymbol(dynsymlist[i]);
|
|
dynsymnames[i]:=dynsymtable.fstrsec.writestr(sym.objsymbol.name);
|
|
j:=(elfhash(sym.objsymbol.name) mod nbuckets)+2;
|
|
|
|
while hashdata[j]<>0 do
|
|
j:=2+nbuckets+hashdata[j];
|
|
hashdata[j]:=i+1;
|
|
end;
|
|
if source_info.endian<>target_info.endian then
|
|
for i:=0 to nchains+nbuckets+1 do
|
|
hashdata[i]:=swapendian(hashdata[i]);
|
|
hashobjsec.write(hashdata^,(2+nchains+nbuckets)*sizeof(longint));
|
|
freemem(hashdata);
|
|
end;
|
|
|
|
|
|
procedure TElfExeOutput.WriteDynRelocEntry(dataofs:aword;typ:byte;symidx:aword;addend:aword);
|
|
var
|
|
rel:telfreloc;
|
|
begin
|
|
rel.address:=dataofs;
|
|
rel.info:=ELF_R_INFO(symidx,typ);
|
|
rel.addend:=addend;
|
|
MaybeSwapElfReloc(rel);
|
|
dynrelocsec.write(rel,dynrelocsec.shentsize);
|
|
end;
|
|
|
|
|
|
procedure TElfExeOutput.WriteDynTag(aTag:longword;aValue:longword);
|
|
var
|
|
d: TElfDyn;
|
|
begin
|
|
d.d_tag:=aTag;
|
|
d.d_val:=aValue;
|
|
MaybeSwapElfDyn(d);
|
|
dynamicsec.write(d,sizeof(TElfDyn));
|
|
end;
|
|
|
|
|
|
procedure TElfExeOutput.WriteDynTag(aTag:longword;aSection:TObjSection;aOffs:aword);
|
|
var
|
|
d: TElfDyn;
|
|
begin
|
|
d.d_tag:=aTag;
|
|
if source_info.endian<>target_info.endian then
|
|
d.d_tag:=swapendian(d.d_tag);
|
|
dynamicsec.write(d.d_tag,sizeof(d.d_tag));
|
|
{ TODO: ignores endianness! }
|
|
dynamicsec.writeReloc_internal(aSection,aOffs,sizeof(d.d_ptr),RELOC_ABSOLUTE);
|
|
end;
|
|
|
|
|
|
procedure TElfExeOutput.WriteDynamicTags;
|
|
var
|
|
s: aword;
|
|
i: longint;
|
|
sym: TExeSymbol;
|
|
hs:string;
|
|
begin
|
|
for i:=0 to neededlist.Count-1 do
|
|
begin
|
|
s:=dynsymtable.fstrsec.writestr(neededlist.NameOfIndex(i));
|
|
WriteDynTag(DT_NEEDED,s);
|
|
end;
|
|
|
|
if IsSharedLibrary then
|
|
begin
|
|
s:=dynsymtable.fstrsec.writestr(ExtractFileName(current_module.sharedlibfilename));
|
|
WriteDynTag(DT_SONAME,s);
|
|
{ TODO: names hardcoded here }
|
|
sym:=TExeSymbol(ExeSymbolList.Find('FPC_SHARED_LIB_START'));
|
|
if assigned(sym) then
|
|
WriteDynTag(DT_INIT,sym.objsymbol.objsection,sym.objsymbol.offset);
|
|
sym:=TExeSymbol(ExeSymbolList.Find('FPC_LIB_EXIT'));
|
|
if assigned(sym) then
|
|
WriteDynTag(DT_FINI,sym.objsymbol.objsection,sym.objsymbol.offset);
|
|
end;
|
|
|
|
{ TODO: we need a dedicated parameter to pass runpath, instead of this hack
|
|
(-Xr is a different thing, it passes "-rpath-link"). }
|
|
if (ParaLinkOptions<>'') then
|
|
begin
|
|
hs:=ParaLinkOptions;
|
|
while (hs<>'') do
|
|
begin
|
|
if (GetToken(hs,' ')='-rpath') then
|
|
begin
|
|
s:=dynsymtable.fstrsec.writestr(GetToken(hs,' '));
|
|
WriteDynTag(DT_RPATH,s);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
writeDynTag(DT_HASH,hashobjsec);
|
|
writeDynTag(DT_STRTAB,dynsymtable.fstrsec);
|
|
writeDynTag(DT_SYMTAB,dynsymtable);
|
|
|
|
writeDynTag(DT_SYMENT,sizeof(TElfSymbol));
|
|
|
|
if Assigned(gotpltobjsec) then
|
|
writeDynTag(DT_PLTGOT,gotpltobjsec);
|
|
end;
|
|
|
|
|
|
const
|
|
pltreltags: array[boolean] of longword=(DT_REL,DT_RELA);
|
|
relsztags: array[boolean] of longword=(DT_RELSZ,DT_RELASZ);
|
|
relenttags: array[boolean] of longword=(DT_RELENT,DT_RELAENT);
|
|
relcnttags: array[boolean] of longword=(DT_RELCOUNT,DT_RELACOUNT);
|
|
|
|
procedure TElfExeOutput.FinishDynamicTags;
|
|
begin
|
|
if assigned(dynsymtable) then
|
|
writeDynTag(DT_STRSZ,dynsymtable.fstrsec.size);
|
|
|
|
if hastextrelocs then
|
|
writeDynTag(DT_TEXTREL,0);
|
|
|
|
if Assigned(pltrelocsec) and (pltrelocsec.size>0) then
|
|
begin
|
|
writeDynTag(DT_PLTRELSZ,pltrelocsec.Size);
|
|
writeDynTag(DT_PLTREL,pltreltags[relocs_use_addend]);
|
|
writeDynTag(DT_JMPREL,pltrelocsec);
|
|
end;
|
|
|
|
if Assigned(dynrelocsec) and (dynrelocsec.size>0) then
|
|
begin
|
|
writeDynTag(pltreltags[relocs_use_addend],dynrelocsec);
|
|
writeDynTag(relsztags[relocs_use_addend],dynrelocsec.Size);
|
|
writeDynTag(relenttags[relocs_use_addend],dynrelocsec.shentsize);
|
|
end;
|
|
if (relative_reloc_count>0) then
|
|
writeDynTag(relcnttags[relocs_use_addend],relative_reloc_count);
|
|
writeDynTag(DT_NULL,0);
|
|
end;
|
|
|
|
|
|
procedure TElfExeOutput.CreatePLT;
|
|
var
|
|
reloc: TObjRelocation;
|
|
begin
|
|
pltobjsec:=TElfObjSection.create_ext(internalObjData,'.plt',
|
|
SHT_PROGBITS,SHF_ALLOC or SHF_EXECINSTR,4,16);
|
|
pltobjsec.SecOptions:=[oso_keep,oso_plt];
|
|
|
|
pltrelocsec:=TElfObjSection.create_reloc(internalObjData,'.plt',true);
|
|
pltrelocsec.SecOptions:=[oso_keep];
|
|
|
|
ipltrelocsec:=TElfObjSection.create_reloc(internalObjData,'.iplt',true);
|
|
ipltrelocsec.SecOptions:=[oso_keep];
|
|
|
|
{ reference .dynamic from .got.plt, this isn't necessary if linking statically }
|
|
{ TODO: maybe move writing initial .got.plt entries here completely
|
|
(needs testing --- GOT symbol may get lost if .got.plt is empty)}
|
|
if dynamiclink then
|
|
begin
|
|
reloc:=TObjRelocation.CreateSection(0,dynamicsec,RELOC_ABSOLUTE);
|
|
reloc.size:=sizeof(pint);
|
|
gotpltobjsec.ObjRelocations.Add(reloc);
|
|
end;
|
|
|
|
{ Initial PLT entry, CPU-specific }
|
|
WriteFirstPLTEntry;
|
|
end;
|
|
|
|
|
|
procedure TElfExeOutput.WritePLTEntry(exesym:TExeSymbol);
|
|
begin
|
|
// must be implemented by CPU-specific descendant
|
|
InternalError(2012092102);
|
|
end;
|
|
|
|
|
|
procedure TElfExeOutput.WriteIndirectPLTEntry(exesym:TExeSymbol);
|
|
begin
|
|
// must be implemented by CPU-specific descendant
|
|
InternalError(2012092101);
|
|
end;
|
|
|
|
|
|
function TElfExeOutput.WriteData:boolean;
|
|
begin
|
|
WriteHeader;
|
|
segmentlist.ForEachCall(@segment_write_header,nil);
|
|
WriteExeSectionContent;
|
|
FWriter.WriteZeros(sizeof(TElfsechdr));
|
|
ExeSectionList.ForEachCall(@exesection_write_header,nil);
|
|
result:=true;
|
|
end;
|
|
|
|
|
|
procedure TElfExeOutput.GenerateLibraryImports(ImportLibraryList:TFPHashObjectList);
|
|
var
|
|
i:longint;
|
|
exportlist: TCmdStrList;
|
|
sym: TExeSymbol;
|
|
begin
|
|
{ add exported symbols to dynamic list }
|
|
exportlist:=texportlibunix(exportlib).exportedsymnames;
|
|
if not exportlist.empty then
|
|
repeat
|
|
sym:=TExeSymbol(ExeSymbolList.Find(exportlist.getfirst));
|
|
if assigned(sym) then
|
|
begin
|
|
if assigned(sym.objsymbol.objsection) then
|
|
sym.objsymbol.objsection.SecOptions:=[oso_keep];
|
|
sym.dynindex:=dynsymlist.add(sym)+1
|
|
end
|
|
else
|
|
InternalError(2012071801);
|
|
until exportlist.empty;
|
|
|
|
{ Mark unresolved weak symbols as defined, they will become dynamic further on.
|
|
If compiling a .so, make all symbols dynamic, since shared library may reference
|
|
symbols from executable which does not participate in linking. }
|
|
for i:=0 to UnresolvedExeSymbols.Count-1 do
|
|
begin
|
|
sym:=TExeSymbol(UnresolvedExeSymbols[i]);
|
|
if (sym.objsymbol.bind=AB_WEAK_EXTERNAL) or IsSharedLibrary then
|
|
sym.state:=symstate_defined;
|
|
end;
|
|
end;
|
|
|
|
|
|
{****************************************************************************
|
|
TElfExeSection
|
|
****************************************************************************}
|
|
|
|
procedure TElfExeSection.AddObjSection(objsec:TObjSection;ignoreprops:boolean);
|
|
begin
|
|
inherited AddObjSection(objsec,ignoreprops);
|
|
if ignoreprops then
|
|
exit;
|
|
if (shtype=SHT_NULL) then
|
|
begin
|
|
shtype:=TElfObjSection(objsec).shtype;
|
|
shflags:=TElfObjSection(objsec).shflags;
|
|
shentsize:=TElfObjSection(objsec).shentsize;
|
|
end;
|
|
end;
|
|
|
|
{****************************************************************************
|
|
TElfSegment
|
|
****************************************************************************}
|
|
|
|
constructor TElfSegment.Create(atype,aflags,aalign:longword);
|
|
begin
|
|
ptype:=atype;
|
|
pflags:=aflags;
|
|
align:=aalign;
|
|
FSectionList:=TFPObjectList.Create(false);
|
|
end;
|
|
|
|
destructor TElfSegment.Destroy;
|
|
begin
|
|
FSectionList.Free;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TElfSegment.Add(exesec:TExeSection);
|
|
begin
|
|
FSectionList.Add(exesec);
|
|
end;
|
|
|
|
|
|
end.
|