mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-04-19 20:39:25 +02:00
+ ELF linker. Works on x86_64-linux and i386-linux good enough to pass the testsuite, but still requires a lot of work in nearly all aspects. In particular, no attempt to resolve symbols from shared libs is done, everything is just treated as imports. Symbol versioning isn't supported either.
x86_64 is the most elaborated, has some degree of indirect function (GNU_IFUNC) and TLS support, so it is even able to link with static libc/pthreads code (tw14265) and produce an executable that can launch (but still fails due to invalid DWARF unwind info). i386 produces working shared libraries if they are compiled with -Cg, without one your mileage may vary. tw14265 does not link yet due to missing COMDAT group support. git-svn-id: trunk@22775 -
This commit is contained in:
parent
92510963da
commit
6d70009f06
@ -28,20 +28,30 @@ interface
|
||||
implementation
|
||||
|
||||
uses
|
||||
globtype,cclasses,
|
||||
verbose,
|
||||
systems,ogbase,ogelf,assemble;
|
||||
|
||||
type
|
||||
TElfObjOutput386=class(TElfObjectOutput)
|
||||
function encodereloc(objrel:TObjRelocation):byte;override;
|
||||
TElfTarget386=class(TElfTarget)
|
||||
class function encodereloc(objrel:TObjRelocation):byte;override;
|
||||
class procedure loadreloc(objrel:TObjRelocation);override;
|
||||
end;
|
||||
|
||||
TElfAssembler386=class(TInternalAssembler)
|
||||
constructor create(smart:boolean);override;
|
||||
TElfExeOutput386=class(TElfExeOutput)
|
||||
private
|
||||
procedure MaybeWriteGOTEntry(reltyp:byte;relocval:aint;exesym:TExeSymbol);
|
||||
protected
|
||||
procedure WriteFirstPLTEntry;override;
|
||||
procedure WritePLTEntry(exesym:TExeSymbol);override;
|
||||
procedure WriteIndirectPLTEntry(exesym:TExeSymbol);override;
|
||||
procedure GOTRelocPass1(objsec:TObjSection;ObjReloc:TObjRelocation);override;
|
||||
procedure DoRelocationFixup(objsec:TObjSection);override;
|
||||
end;
|
||||
|
||||
const
|
||||
{ Relocation types }
|
||||
R_386_NONE = 0;
|
||||
R_386_32 = 1; { ordinary absolute relocation }
|
||||
R_386_PC32 = 2; { PC-relative relocation }
|
||||
R_386_GOT32 = 3; { an offset into GOT }
|
||||
@ -87,12 +97,14 @@ implementation
|
||||
|
||||
|
||||
{****************************************************************************
|
||||
TElfObjOutput386
|
||||
TElfTarget386
|
||||
****************************************************************************}
|
||||
|
||||
function TElfObjOutput386.encodereloc(objrel:TObjRelocation):byte;
|
||||
class function TElfTarget386.encodereloc(objrel:TObjRelocation):byte;
|
||||
begin
|
||||
case objrel.typ of
|
||||
RELOC_NONE :
|
||||
result:=R_386_NONE;
|
||||
RELOC_RELATIVE :
|
||||
result:=R_386_PC32;
|
||||
RELOC_ABSOLUTE :
|
||||
@ -109,14 +121,307 @@ implementation
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
class procedure TElfTarget386.loadreloc(objrel:TObjRelocation);
|
||||
begin
|
||||
end;
|
||||
|
||||
|
||||
{****************************************************************************
|
||||
TElfAssembler386
|
||||
TElfExeOutput386
|
||||
****************************************************************************}
|
||||
|
||||
constructor TElfAssembler386.create(smart:boolean);
|
||||
|
||||
procedure TElfExeOutput386.WriteFirstPLTEntry;
|
||||
begin
|
||||
inherited Create(smart);
|
||||
CObjOutput:=TElfObjOutput386;
|
||||
if IsSharedLibrary then
|
||||
// push 4(%ebx); jmp *8(%ebx)
|
||||
pltobjsec.writeBytes(#$FF#$B3#$04#$00#$00#$00#$FF#$A3#$08#$00#$00#$00)
|
||||
else
|
||||
begin
|
||||
pltobjsec.writeBytes(#$FF#$35); // push got+4
|
||||
pltobjsec.writeReloc_internal(gotpltobjsec,sizeof(pint),4,RELOC_ABSOLUTE);
|
||||
pltobjsec.writeBytes(#$FF#$25); // jmp *got+8
|
||||
pltobjsec.writeReloc_internal(gotpltobjsec,2*sizeof(pint),4,RELOC_ABSOLUTE);
|
||||
end;
|
||||
pltobjsec.writeBytes(#$90#$90#$90#$90); // nop
|
||||
end;
|
||||
|
||||
|
||||
procedure TElfExeOutput386.WritePLTEntry(exesym:TExeSymbol);
|
||||
var
|
||||
got_offset: aword;
|
||||
tmp:pint;
|
||||
begin
|
||||
got_offset:=gotpltobjsec.size;
|
||||
if IsSharedLibrary then
|
||||
begin
|
||||
pltobjsec.writeBytes(#$FF#$A3); // jmp got+x(%ebx)
|
||||
pltobjsec.write(got_offset,4);
|
||||
end
|
||||
else
|
||||
begin
|
||||
pltobjsec.writeBytes(#$FF#$25); // jmp *got+x
|
||||
pltobjsec.writeReloc_internal(gotpltobjsec,got_offset,4,RELOC_ABSOLUTE);
|
||||
end;
|
||||
pltobjsec.writeBytes(#$68); // push $index
|
||||
tmp:=pltrelocsec.size;
|
||||
pltobjsec.write(tmp,4);
|
||||
|
||||
pltobjsec.writeBytes(#$E9); // jmp .plt
|
||||
tmp:=-(4+pltobjsec.Size);
|
||||
pltobjsec.write(tmp,4);
|
||||
|
||||
{ write a .got.plt slot pointing back to the 'push' instruction }
|
||||
gotpltobjsec.writeReloc_internal(pltobjsec,pltobjsec.size-(16-6),sizeof(pint),RELOC_ABSOLUTE);
|
||||
|
||||
{ write a .rel.plt entry }
|
||||
pltrelocsec.writeReloc_internal(gotpltobjsec,got_offset,sizeof(pint),RELOC_ABSOLUTE);
|
||||
got_offset:=(exesym.dynindex shl 8) or R_386_JUMP_SLOT;
|
||||
pltrelocsec.write(got_offset,sizeof(pint));
|
||||
if relocs_use_addend then
|
||||
pltrelocsec.writezeros(sizeof(pint));
|
||||
end;
|
||||
|
||||
|
||||
procedure TElfExeOutput386.WriteIndirectPLTEntry(exesym:TExeSymbol);
|
||||
begin
|
||||
// TODO
|
||||
inherited WriteIndirectPLTEntry(exesym);
|
||||
end;
|
||||
|
||||
|
||||
procedure TElfExeOutput386.GOTRelocPass1(objsec:TObjSection;ObjReloc:TObjRelocation);
|
||||
var
|
||||
objsym:TObjSymbol;
|
||||
sym:TExeSymbol;
|
||||
reltyp:byte;
|
||||
begin
|
||||
if (ObjReloc.flags and rf_raw)=0 then
|
||||
reltyp:=ElfTarget.encodereloc(ObjReloc)
|
||||
else
|
||||
reltyp:=ObjReloc.ftype;
|
||||
case reltyp of
|
||||
|
||||
R_386_PLT32:
|
||||
begin
|
||||
objsym:=objreloc.symbol.exesymbol.ObjSymbol;
|
||||
objsym.refs:=objsym.refs or symref_plt;
|
||||
end;
|
||||
|
||||
R_386_GOT32:
|
||||
begin
|
||||
sym:=ObjReloc.symbol.exesymbol;
|
||||
|
||||
{ Although local symbols should not be accessed through GOT,
|
||||
this isn't strictly forbidden. In this case we need to fake up
|
||||
the exesym to store the GOT offset in it.
|
||||
TODO: name collision; maybe use a different symbol list object? }
|
||||
if sym=nil then
|
||||
begin
|
||||
sym:=TExeSymbol.Create(ExeSymbolList,objreloc.symbol.name+'*local*');
|
||||
sym.objsymbol:=objreloc.symbol;
|
||||
objreloc.symbol.exesymbol:=sym;
|
||||
end;
|
||||
if sym.GotOffset>0 then
|
||||
exit;
|
||||
gotobjsec.alloc(sizeof(pint));
|
||||
sym.GotOffset:=gotobjsec.size;
|
||||
{ In shared library, every GOT entry needs a RELATIVE dynamic reloc,
|
||||
imported/exported symbols need GLOB_DAT instead. For executables,
|
||||
only the latter applies. }
|
||||
if IsSharedLibrary or (sym.dynindex>0) then
|
||||
dynrelocsec.alloc(dynrelocsec.shentsize);
|
||||
end;
|
||||
|
||||
R_386_32:
|
||||
begin
|
||||
{ TODO: How to handle absolute relocation to *weak* external symbol
|
||||
from executable? See test/tweaklib2, symbol test2, ld handles it
|
||||
differently for PIC and non-PIC code. In non-PIC code it drops
|
||||
dynamic relocation altogether. }
|
||||
if not IsSharedLibrary then
|
||||
exit;
|
||||
if (oso_executable in objsec.SecOptions) or
|
||||
not (oso_write in objsec.SecOptions) then
|
||||
hastextrelocs:=True;
|
||||
dynrelocsec.alloc(dynrelocsec.shentsize);
|
||||
objreloc.flags:=objreloc.flags or rf_dynamic;
|
||||
end;
|
||||
|
||||
R_386_PC32:
|
||||
begin
|
||||
if not IsSharedLibrary then
|
||||
exit;
|
||||
{ In shared library PC32 reloc to external symbol cannot be redirected
|
||||
to PLT entry, because PIC PLT relies on ebx register set properly. }
|
||||
if assigned(objreloc.symbol) and
|
||||
(
|
||||
(objreloc.symbol.objsection=nil) or
|
||||
(oso_plt in objreloc.symbol.objsection.SecOptions)
|
||||
) then
|
||||
begin
|
||||
{ Must be a dynamic symbol }
|
||||
if not (assigned(objreloc.symbol.exesymbol) and
|
||||
(objreloc.symbol.exesymbol.dynindex<>0)) then
|
||||
InternalError(2012101201);
|
||||
if (oso_executable in objsec.SecOptions) or
|
||||
not (oso_write in objsec.SecOptions) then
|
||||
hastextrelocs:=True;
|
||||
dynrelocsec.alloc(dynrelocsec.shentsize);
|
||||
objreloc.flags:=objreloc.flags or rf_dynamic;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure TElfExeOutput386.MaybeWriteGOTEntry(reltyp:byte;relocval:aint;exesym:TExeSymbol);
|
||||
var
|
||||
gotoff,dynidx,tmp:aword;
|
||||
begin
|
||||
gotoff:=exesym.gotoffset;
|
||||
if gotoff=0 then
|
||||
InternalError(2012060902);
|
||||
|
||||
{ the GOT slot itself, and a dynamic relocation for it }
|
||||
{ TODO: only data symbols must get here }
|
||||
if gotoff=gotobjsec.Data.size+sizeof(pint) then
|
||||
begin
|
||||
dynidx:=exesym.dynindex;
|
||||
gotobjsec.write(relocval,sizeof(pint));
|
||||
|
||||
tmp:=gotobjsec.mempos+gotoff-sizeof(pint);
|
||||
if (dynidx>0) then
|
||||
WriteDynRelocEntry(tmp,R_386_GLOB_DAT,dynidx,0)
|
||||
else if IsSharedLibrary then
|
||||
WriteDynRelocEntry(tmp,R_386_RELATIVE,0,relocval);
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure TElfExeOutput386.DoRelocationFixup(objsec:TObjSection);
|
||||
var
|
||||
i,zero:longint;
|
||||
objreloc: TObjRelocation;
|
||||
address,
|
||||
relocval : aint;
|
||||
relocsec : TObjSection;
|
||||
data: TDynamicArray;
|
||||
reltyp: byte;
|
||||
begin
|
||||
data:=objsec.data;
|
||||
for i:=0 to objsec.ObjRelocations.Count-1 do
|
||||
begin
|
||||
objreloc:=TObjRelocation(objsec.ObjRelocations[i]);
|
||||
case objreloc.typ of
|
||||
RELOC_NONE:
|
||||
continue;
|
||||
RELOC_ZERO:
|
||||
begin
|
||||
data.Seek(objreloc.dataoffset);
|
||||
zero:=0;
|
||||
data.Write(zero,4);
|
||||
continue;
|
||||
end;
|
||||
end;
|
||||
|
||||
if (objreloc.flags and rf_raw)=0 then
|
||||
reltyp:=ElfTarget.encodereloc(objreloc)
|
||||
else
|
||||
reltyp:=objreloc.ftype;
|
||||
|
||||
if relocs_use_addend then
|
||||
address:=objreloc.orgsize
|
||||
else
|
||||
begin
|
||||
data.Seek(objreloc.dataoffset);
|
||||
data.Read(address,4);
|
||||
end;
|
||||
if assigned(objreloc.symbol) then
|
||||
begin
|
||||
relocsec:=objreloc.symbol.objsection;
|
||||
relocval:=objreloc.symbol.address;
|
||||
end
|
||||
else if assigned(objreloc.objsection) then
|
||||
begin
|
||||
relocsec:=objreloc.objsection;
|
||||
relocval:=objreloc.objsection.mempos
|
||||
end
|
||||
else
|
||||
internalerror(2012060702);
|
||||
|
||||
{ Only debug sections are allowed to have relocs pointing to unused sections }
|
||||
if assigned(relocsec) and not (relocsec.used and assigned(relocsec.exesection)) and
|
||||
not (oso_debug in objsec.secoptions) then
|
||||
begin
|
||||
writeln(objsec.fullname,' references ',relocsec.fullname);
|
||||
internalerror(2012060703);
|
||||
end;
|
||||
|
||||
{ TODO: if relocsec=nil, relocations must be copied to .rel.dyn section }
|
||||
if (relocsec=nil) or (relocsec.used) then
|
||||
case reltyp of
|
||||
R_386_PC32:
|
||||
begin
|
||||
if (objreloc.flags and rf_dynamic)<>0 then
|
||||
WriteDynRelocEntry(objreloc.dataoffset+objsec.mempos,R_386_PC32,objreloc.symbol.exesymbol.dynindex,0)
|
||||
else
|
||||
address:=address+relocval-(objsec.mempos+objreloc.dataoffset);
|
||||
end;
|
||||
|
||||
R_386_PLT32:
|
||||
begin
|
||||
{ If target is in current object, treat as RELOC_RELATIVE }
|
||||
address:=address+relocval-(objsec.mempos+objreloc.dataoffset);
|
||||
end;
|
||||
|
||||
R_386_32:
|
||||
begin
|
||||
if (objreloc.flags and rf_dynamic)<>0 then
|
||||
begin
|
||||
if (objreloc.symbol=nil) or
|
||||
(objreloc.symbol.exesymbol=nil) or
|
||||
(objreloc.symbol.exesymbol.dynindex=0) then
|
||||
begin
|
||||
address:=address+relocval;
|
||||
WriteDynRelocEntry(objreloc.dataoffset+objsec.mempos,R_386_RELATIVE,0,address);
|
||||
end
|
||||
else
|
||||
{ Don't modify address in this case, as it serves as addend for RTLD }
|
||||
WriteDynRelocEntry(objreloc.dataoffset+objsec.mempos,R_386_32,objreloc.symbol.exesymbol.dynindex,0);
|
||||
end
|
||||
else
|
||||
address:=address+relocval;
|
||||
end;
|
||||
|
||||
R_386_GOTPC:
|
||||
begin
|
||||
address:=address+gotsymbol.address-(objsec.mempos+objreloc.dataoffset);
|
||||
end;
|
||||
|
||||
R_386_GOT32:
|
||||
begin
|
||||
MaybeWriteGOTEntry(reltyp,relocval,objreloc.symbol.exesymbol);
|
||||
|
||||
relocval:=gotobjsec.mempos+objreloc.symbol.exesymbol.gotoffset-sizeof(pint)-gotsymbol.address;
|
||||
address:=address+relocval;
|
||||
end;
|
||||
|
||||
//R_386_GOTOFF: !!TODO
|
||||
|
||||
else
|
||||
begin
|
||||
writeln(objreloc.typ);
|
||||
internalerror(200604014);
|
||||
end;
|
||||
end
|
||||
else { not relocsec.Used }
|
||||
address:=0; { Relocation in debug section points to unused section, which is eliminated by linker }
|
||||
|
||||
data.Seek(objreloc.dataoffset);
|
||||
data.Write(address,4);
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
@ -143,7 +448,9 @@ implementation
|
||||
);
|
||||
|
||||
initialization
|
||||
RegisterAssembler(as_i386_elf32_info,TElfAssembler386);
|
||||
RegisterAssembler(as_i386_elf32_info,TElfAssembler);
|
||||
ElfExeOutputClass:=TElfExeOutput386;
|
||||
ElfTarget:=TElfTarget386;
|
||||
|
||||
end.
|
||||
|
||||
|
@ -1388,6 +1388,7 @@ Implementation
|
||||
STABS for empty linker scripts }
|
||||
exeoutput.MergeStabs;
|
||||
exeoutput.MarkEmptySections;
|
||||
exeoutput.AfterUnusedSectionRemoval;
|
||||
if ErrorCount>0 then
|
||||
goto myexit;
|
||||
|
||||
|
@ -154,8 +154,10 @@ interface
|
||||
typ : TAsmsymtype;
|
||||
{ Current assemble pass, used to detect duplicate labels }
|
||||
pass : byte;
|
||||
objsection : TObjSection;
|
||||
{ how the symbol is referenced (target-specific bitfield)
|
||||
refs : byte;
|
||||
symidx : longint;
|
||||
objsection : TObjSection;
|
||||
offset,
|
||||
size : aword;
|
||||
{ Used for external and common solving during linking }
|
||||
@ -375,6 +377,9 @@ interface
|
||||
State : TSymbolState;
|
||||
{ Used for vmt references optimization }
|
||||
VTable : TExeVTable;
|
||||
{ fields for ELF linking }
|
||||
gotoffset : aword;
|
||||
dynindex : aword;
|
||||
end;
|
||||
|
||||
TExeSection = class(TFPHashObject)
|
||||
@ -533,6 +538,7 @@ interface
|
||||
procedure RemoveUnreferencedSections;
|
||||
procedure RemoveDisabledSections;
|
||||
procedure RemoveDebugInfo;
|
||||
procedure AfterUnusedSectionRemoval;virtual;
|
||||
procedure GenerateLibraryImports(ImportLibraryList:TFPHashObjectList);virtual;
|
||||
procedure GenerateDebugLink(const dbgname:string;dbgcrc:cardinal);
|
||||
function WriteExeFile(const fn:string):boolean;
|
||||
@ -2556,6 +2562,11 @@ implementation
|
||||
end;
|
||||
|
||||
|
||||
procedure TExeOutput.AfterUnusedSectionRemoval;
|
||||
begin
|
||||
end;
|
||||
|
||||
|
||||
function ByAddress(item1,item2:pointer):longint;
|
||||
var
|
||||
sym1:TObjSymbol absolute item1;
|
||||
|
1671
compiler/ogelf.pas
1671
compiler/ogelf.pas
File diff suppressed because it is too large
Load Diff
@ -32,31 +32,50 @@ implementation
|
||||
systems,ogbase,ogelf,assemble;
|
||||
|
||||
type
|
||||
TElfObjOutputSparc=class(TElfObjectOutput)
|
||||
function encodereloc(objrel:TObjRelocation):byte;override;
|
||||
end;
|
||||
|
||||
TElfAssemblerSparc=class(TInternalAssembler)
|
||||
constructor create(smart:boolean);override;
|
||||
TElfTargetSparc=class(TElfTarget)
|
||||
class function encodereloc(objrel:TObjRelocation):byte;override;
|
||||
class procedure loadreloc(objrel:TObjRelocation);override;
|
||||
end;
|
||||
|
||||
const
|
||||
{ Relocation types }
|
||||
R_SPARC_NONE = 0;
|
||||
R_SPARC_8 = 1;
|
||||
R_SPARC_16 = 2;
|
||||
R_SPARC_32 = 3;
|
||||
R_SPARC_DISP8 = 4;
|
||||
R_SPARC_DISP16 = 5;
|
||||
R_SPARC_DISP32 = 6;
|
||||
R_SPARC_WDISP30 = 7;
|
||||
R_SPARC_WDISP22 = 8;
|
||||
R_SPARC_HI22 = 9;
|
||||
R_SPARC_22 = 10;
|
||||
R_SPARC_13 = 11;
|
||||
R_SPARC_LO10 = 12;
|
||||
R_SPARC_GOT10 = 13;
|
||||
R_SPARC_GOT13 = 14;
|
||||
R_SPARC_GOT22 = 15;
|
||||
R_SPARC_PC10 = 16;
|
||||
R_SPARC_PC22 = 17;
|
||||
R_SPARC_WPLT30 = 18;
|
||||
R_SPARC_COPY = 19;
|
||||
R_SPARC_GLOB_DAT = 20;
|
||||
R_SPARC_JMP_SLOT = 21;
|
||||
R_SPARC_RELATIVE = 22;
|
||||
R_SPARC_UA32 = 23;
|
||||
R_SPARC_GNU_VTINHERIT = 250;
|
||||
R_SPARC_GNU_VTENTRY = 251;
|
||||
|
||||
|
||||
{****************************************************************************
|
||||
TElfObjOutputSparc
|
||||
TElfTargetSparc
|
||||
****************************************************************************}
|
||||
|
||||
function TElfObjOutputSparc.encodereloc(objrel:TObjRelocation):byte;
|
||||
class function TElfTargetSparc.encodereloc(objrel:TObjRelocation):byte;
|
||||
begin
|
||||
case objrel.typ of
|
||||
RELOC_NONE :
|
||||
result:=R_SPARC_NONE;
|
||||
RELOC_ABSOLUTE :
|
||||
result:=R_SPARC_32;
|
||||
{ TODO }
|
||||
@ -67,15 +86,10 @@ implementation
|
||||
end;
|
||||
|
||||
|
||||
{****************************************************************************
|
||||
TElfAssemblerSparc
|
||||
****************************************************************************}
|
||||
class procedure TElfTargetSparc.loadreloc(objrel:TObjRelocation);
|
||||
begin
|
||||
end;
|
||||
|
||||
constructor TElfAssemblerSparc.create(smart:boolean);
|
||||
begin
|
||||
inherited Create(smart);
|
||||
CObjOutput:=TElfObjOutputSparc;
|
||||
end;
|
||||
|
||||
|
||||
{*****************************************************************************
|
||||
@ -99,7 +113,8 @@ implementation
|
||||
);
|
||||
|
||||
initialization
|
||||
RegisterAssembler(as_sparc_elf32_info,TElfAssemblerSparc);
|
||||
RegisterAssembler(as_sparc_elf32_info,TElfAssembler);
|
||||
ElfTarget:=TElfTargetSparc;
|
||||
|
||||
end.
|
||||
|
||||
|
@ -58,6 +58,18 @@ interface
|
||||
procedure LoadPredefinedLibraryOrder; override;
|
||||
end;
|
||||
|
||||
TInternalLinkerLinux=class(TInternalLinker)
|
||||
private
|
||||
libctype: TLibcType;
|
||||
reorder: boolean;
|
||||
linklibc: boolean;
|
||||
prtobj: string[20];
|
||||
dynlinker: string[100];
|
||||
public
|
||||
constructor Create;override;
|
||||
procedure DefaultLinkScript;override;
|
||||
procedure InitSysInitUnitName;override;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
@ -70,6 +82,7 @@ implementation
|
||||
aasmbase,aasmtai,aasmcpu,cpubase,
|
||||
cgbase,cgobj,cgutils,ogbase,ncgutil,
|
||||
comprsrc,
|
||||
ogelf,
|
||||
rescmn, i_linux
|
||||
;
|
||||
|
||||
@ -1128,6 +1141,324 @@ begin
|
||||
MakeSharedLibrary:=success; { otherwise a recursive call to link method }
|
||||
end;
|
||||
|
||||
{*****************************************************************************
|
||||
TINTERNALLINKERLINUX
|
||||
*****************************************************************************}
|
||||
|
||||
constructor TInternalLinkerLinux.Create;
|
||||
begin
|
||||
inherited Create;
|
||||
SetupLibrarySearchPath;
|
||||
SetupDynlinker(dynlinker,libctype);
|
||||
|
||||
CExeOutput:=ElfExeOutputClass;
|
||||
CObjInput:=TElfObjInput;
|
||||
|
||||
end;
|
||||
|
||||
procedure TInternalLinkerLinux.InitSysInitUnitName;
|
||||
begin
|
||||
linklibc:=ModulesLinkToLibc;
|
||||
reorder:=linklibc and ReOrderEntries;
|
||||
sysinitunit:=defsinames[current_module.islibrary];
|
||||
prtobj:=defprtnames[current_module.islibrary];
|
||||
|
||||
if cs_profile in current_settings.moduleswitches then
|
||||
begin
|
||||
prtobj:=gprtnames[libctype];
|
||||
sysinitunit:=gsinames[libctype];
|
||||
linklibc:=true;
|
||||
end
|
||||
else if linklibc then
|
||||
begin
|
||||
prtobj:=cprtnames[libctype];
|
||||
sysinitunit:=csinames[libctype];
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure TInternalLinkerLinux.DefaultLinkScript;
|
||||
var
|
||||
s,s1,s2:TCmdStr;
|
||||
found1,found2:boolean;
|
||||
linkToSharedLibs:boolean;
|
||||
|
||||
procedure AddLibraryStatement(const s:TCmdStr);
|
||||
var
|
||||
i:longint;
|
||||
s1,s2:TCmdStr;
|
||||
begin
|
||||
i:=pos(target_info.sharedClibext+'.',s);
|
||||
if (i>0) then
|
||||
s1:=target_info.sharedClibprefix+S
|
||||
else
|
||||
s1:=target_info.sharedClibprefix+S+target_info.sharedClibext;
|
||||
{ TODO: to be compatible with ld search algorithm, each found file
|
||||
must be tested for target compatibility, incompatible ones should be skipped. }
|
||||
{ TODO: shall we search library without suffix if one with suffix is not found? }
|
||||
if (not(cs_link_staticflag in current_settings.globalswitches)) and
|
||||
FindLibraryFile(s1,'','',s2) then
|
||||
LinkScript.Concat('READSTATICLIBRARY '+maybequoted(s2))
|
||||
{ TODO: static libraries never have numeric suffix in their names }
|
||||
else if FindLibraryFile(s,target_info.staticClibprefix,target_info.staticClibext,s2) then
|
||||
LinkScript.Concat('READSTATICLIBRARY '+maybequoted(s2))
|
||||
else
|
||||
Comment(V_Error,'Import library not found for '+S);
|
||||
end;
|
||||
|
||||
begin
|
||||
if cs_profile in current_settings.moduleswitches then
|
||||
begin
|
||||
if not(libctype in [glibc2,glibc21]) then
|
||||
AddSharedLibrary('gmon');
|
||||
AddSharedLibrary('c');
|
||||
end;
|
||||
|
||||
{ add objectfiles, start with prt0 always }
|
||||
if not (target_info.system in systems_internal_sysinit) and (prtobj<>'') then
|
||||
LinkScript.Concat('READOBJECT '+ maybequoted(FindObjectFile(prtobj,'',false)));
|
||||
|
||||
{ try to add crti and crtbegin if linking to C }
|
||||
if linklibc and (libctype<>uclibc) then
|
||||
begin
|
||||
{ crti.o must come first }
|
||||
if librarysearchpath.FindFile('crti.o',false,s) then
|
||||
LinkScript.Concat('READOBJECT '+maybequoted(s));
|
||||
{ then the crtbegin* }
|
||||
if cs_create_pic in current_settings.moduleswitches then
|
||||
begin
|
||||
if librarysearchpath.FindFile('crtbeginS.o',false,s) then
|
||||
LinkScript.Concat('READOBJECT '+maybequoted(s));
|
||||
end
|
||||
else
|
||||
if (cs_link_staticflag in current_settings.globalswitches) and
|
||||
librarysearchpath.FindFile('crtbeginT.o',false,s) then
|
||||
LinkScript.Concat('READOBJECT '+maybequoted(s))
|
||||
else if librarysearchpath.FindFile('crtbegin.o',false,s) then
|
||||
LinkScript.Concat('READOBJECT '+maybequoted(s));
|
||||
end;
|
||||
|
||||
ScriptAddSourceStatements(false);
|
||||
{ we must reorder here because the result could empty sharedlibfiles }
|
||||
if reorder then
|
||||
ExpandAndApplyOrder(SharedLibFiles);
|
||||
|
||||
{ See tw9089*.pp: if more than one pure-Pascal shared libs are loaded,
|
||||
and none have rtld in their DT_NEEDED, then rtld cannot finalize correctly. }
|
||||
if IsSharedLibrary then
|
||||
LinkScript.Concat('READSTATICLIBRARY '+maybequoted(dynlinker));
|
||||
|
||||
linkToSharedLibs:=(not SharedLibFiles.Empty);
|
||||
|
||||
{ Symbols declared as "external 'libx.so'" are added to ImportLibraryList, library
|
||||
prefix/extension *not* stripped. TImportLibLinux copies these to SharedLibFiles,
|
||||
stripping prefixes and extensions.
|
||||
However extension won't be stripped if library is specified with numeric suffix
|
||||
(like "libpango-1.0.so.0")
|
||||
Libraries specified with $LINKLIB directive are directly added to SharedLibFiles
|
||||
and won't be present in ImportLibraryList. }
|
||||
while not SharedLibFiles.Empty do
|
||||
begin
|
||||
S:=SharedLibFiles.GetFirst;
|
||||
if (S<>'c') or reorder then
|
||||
AddLibraryStatement(S);
|
||||
end;
|
||||
|
||||
if (cs_link_staticflag in current_settings.globalswitches) or
|
||||
(linklibc and not reorder) then
|
||||
begin
|
||||
if (cs_link_staticflag in current_settings.globalswitches) then
|
||||
begin
|
||||
AddLibraryStatement('gcc');
|
||||
AddLibraryStatement('gcc_eh');
|
||||
end;
|
||||
if linklibc and not reorder then
|
||||
AddLibraryStatement('c');
|
||||
end;
|
||||
|
||||
{ objects which must be at the end }
|
||||
if linklibc and (libctype<>uclibc) then
|
||||
begin
|
||||
if cs_create_pic in current_settings.moduleswitches then
|
||||
found1:=librarysearchpath.FindFile('crtendS.o',false,s1)
|
||||
else
|
||||
found1:=librarysearchpath.FindFile('crtend.o',false,s1);
|
||||
found2:=librarysearchpath.FindFile('crtn.o',false,s2);
|
||||
if found1 then
|
||||
LinkScript.Concat('READOBJECT '+maybequoted(s1));
|
||||
if found2 then
|
||||
LinkScript.Concat('READOBJECT '+maybequoted(s2));
|
||||
end;
|
||||
|
||||
if (not IsSharedLibrary) then
|
||||
if (linkToSharedLibs and not linklibc) then
|
||||
LinkScript.Concat('ENTRYNAME _dynamic_start')
|
||||
else
|
||||
LinkScript.Concat('ENTRYNAME _start')
|
||||
else
|
||||
LinkScript.Concat('ISSHAREDLIBRARY');
|
||||
|
||||
with LinkScript do
|
||||
begin
|
||||
Concat('HEADER');
|
||||
Concat('EXESECTION .interp');
|
||||
Concat(' OBJSECTION .interp');
|
||||
Concat('ENDEXESECTION');
|
||||
Concat('EXESECTION .note.ABI-tag');
|
||||
Concat(' OBJSECTION .note.ABI-tag');
|
||||
Concat('ENDEXESECTION');
|
||||
Concat('EXESECTION .note.gnu.build-id');
|
||||
Concat(' OBJSECTION .note.gnu.build-id');
|
||||
Concat('ENDEXESECTION');
|
||||
Concat('EXESECTION .hash');
|
||||
Concat(' OBJSECTION .hash');
|
||||
Concat('ENDEXESECTION');
|
||||
Concat('EXESECTION .dynsym');
|
||||
Concat(' OBJSECTION .dynsym');
|
||||
Concat('ENDEXESECTION');
|
||||
Concat('EXESECTION .dynstr');
|
||||
Concat(' OBJSECTION .dynstr');
|
||||
Concat('ENDEXESECTION');
|
||||
{$ifdef x86_64}
|
||||
Concat('EXESECTION .rela.dyn');
|
||||
Concat(' OBJSECTION .rela.dyn');
|
||||
{$else}
|
||||
Concat('EXESECTION .rel.dyn');
|
||||
Concat(' OBJSECTION .rel.dyn');
|
||||
{$endif}
|
||||
|
||||
Concat('ENDEXESECTION');
|
||||
{$ifdef x86_64}
|
||||
Concat('EXESECTION .rela.plt');
|
||||
Concat(' OBJSECTION .rela.plt');
|
||||
Concat(' PROVIDE __rela_iplt_start');
|
||||
Concat(' OBJSECTION .rela.iplt');
|
||||
Concat(' PROVIDE __rela_iplt_end');
|
||||
{$else}
|
||||
Concat('EXESECTION .rel.plt');
|
||||
Concat(' OBJSECTION .rel.plt');
|
||||
{$endif}
|
||||
Concat('ENDEXESECTION');
|
||||
Concat('EXESECTION .init');
|
||||
Concat(' OBJSECTION .init');
|
||||
Concat('ENDEXESECTION');
|
||||
Concat('EXESECTION .plt');
|
||||
Concat(' OBJSECTION .plt');
|
||||
Concat('ENDEXESECTION');
|
||||
Concat('EXESECTION .text');
|
||||
Concat(' OBJSECTION .text*');
|
||||
Concat('ENDEXESECTION');
|
||||
|
||||
{ This is not in standard ld scripts, it is handled by 'orphan section' functionality }
|
||||
Concat('EXESECTION __libc_thread_freeres_fn');
|
||||
Concat(' PROVIDE __start__libc_thread_freeres_fn');
|
||||
Concat(' OBJSECTION __libc_thread_freeres_fn');
|
||||
Concat(' PROVIDE __stop__libc_thread_freeres_fn');
|
||||
Concat('ENDEXESECTION');
|
||||
|
||||
Concat('EXESECTION __libc_freeres_fn');
|
||||
Concat(' PROVIDE __start__libc_freeres_fn');
|
||||
Concat(' OBJSECTION __libc_freeres_fn');
|
||||
Concat(' PROVIDE __stop__libc_freeres_fn');
|
||||
Concat('ENDEXESECTION');
|
||||
|
||||
Concat('EXESECTION .fini');
|
||||
Concat(' OBJSECTION .fini');
|
||||
Concat(' PROVIDE __etext');
|
||||
Concat(' PROVIDE _etext');
|
||||
Concat(' PROVIDE etext');
|
||||
Concat('ENDEXESECTION');
|
||||
Concat('EXESECTION .rodata');
|
||||
Concat(' OBJSECTION .rodata*');
|
||||
Concat('ENDEXESECTION');
|
||||
|
||||
Concat('EXESECTION .eh_frame');
|
||||
Concat(' OBJSECTION .eh_frame');
|
||||
Concat('ENDEXESECTION');
|
||||
Concat('EXESECTION .gcc_except_table');
|
||||
Concat(' OBJSECTION .gcc_except_table');
|
||||
Concat(' OBJSECTION .gcc_except_table.*');
|
||||
Concat('ENDEXESECTION');
|
||||
Concat('EXESECTION .tdata');
|
||||
Concat(' OBJSECTION .tdata');
|
||||
Concat(' OBJSECTION .tdata.*');
|
||||
Concat('ENDEXESECTION');
|
||||
Concat('EXESECTION .tbss');
|
||||
Concat(' OBJSECTION .tbss');
|
||||
Concat(' OBJSECTION .tbss.*');
|
||||
Concat('ENDEXESECTION');
|
||||
Concat('EXESECTION .preinit_array');
|
||||
Concat(' PROVIDE __preinit_array_start');
|
||||
Concat(' OBJSECTION .preinit_array');
|
||||
Concat(' PROVIDE __preinit_array_end');
|
||||
Concat('ENDEXESECTION');
|
||||
Concat('EXESECTION .init_array');
|
||||
Concat(' PROVIDE __init_array_start');
|
||||
{ why the hell .ctors are both here and exesection .ctors below?? }
|
||||
// KEEP ( *(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*)))
|
||||
Concat(' OBJSECTION .init_array');
|
||||
// KEEP ( *(EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors))
|
||||
Concat('PROVIDE __init_array_end');
|
||||
Concat('ENDEXESECTION');
|
||||
Concat('EXESECTION .fini_array');
|
||||
Concat(' PROVIDE __fini_array_start');
|
||||
// KEEP ( *(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*)))
|
||||
Concat(' OBJSECTION .fini_array');
|
||||
// KEEP ( *(EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors))
|
||||
Concat(' PROVIDE __fini_array_end');
|
||||
Concat('ENDEXESECTION');
|
||||
|
||||
Concat('EXESECTION .ctors');
|
||||
Concat(' OBJSECTION .ctors*');
|
||||
Concat('ENDEXESECTION');
|
||||
Concat('EXESECTION .dtors');
|
||||
Concat(' OBJSECTION .dtors*');
|
||||
Concat('ENDEXESECTION');
|
||||
Concat('EXESECTION .jcr');
|
||||
Concat(' OBJSECTION .jcr');
|
||||
Concat('ENDEXESECTION');
|
||||
Concat('EXESECTION .dynamic');
|
||||
Concat(' OBJSECTION .dynamic');
|
||||
Concat('ENDEXESECTION');
|
||||
Concat('EXESECTION .got');
|
||||
Concat(' OBJSECTION .got');
|
||||
Concat('ENDEXESECTION');
|
||||
Concat('EXESECTION .got.plt');
|
||||
Concat(' OBJSECTION .got.plt');
|
||||
Concat('ENDEXESECTION');
|
||||
Concat('EXESECTION .data');
|
||||
Concat(' OBJSECTION .data*');
|
||||
Concat(' OBJSECTION .fpc*');
|
||||
Concat(' OBJSECTION fpc.resources');
|
||||
Concat(' PROVIDE _edata');
|
||||
Concat(' PROVIDE edata');
|
||||
Concat('ENDEXESECTION');
|
||||
Concat('EXESECTION .bss');
|
||||
Concat(' OBJSECTION .bss*');
|
||||
Concat(' OBJSECTION fpc.reshandles');
|
||||
Concat(' PROVIDE end');
|
||||
Concat(' SYMBOL _end');
|
||||
Concat('ENDEXESECTION');
|
||||
|
||||
{ This is not in standard ld scripts, it is handled by 'orphan section' functionality }
|
||||
Concat('EXESECTION __libc_freeres_ptrs');
|
||||
Concat(' PROVIDE __start__libc_freeres_ptrs');
|
||||
Concat(' OBJSECTION __libc_freeres_ptrs');
|
||||
Concat(' PROVIDE __stop__libc_freeres_ptrs');
|
||||
Concat('ENDEXESECTION');
|
||||
|
||||
ScriptAddGenericSections('.debug_aranges,.debug_pubnames,.debug_info,'+
|
||||
'.debug_abbrev,.debug_line,.debug_frame,.debug_str,.debug_loc,'+
|
||||
'.debug_macinfo,.debug_weaknames,.debug_funcnames,.debug_typenames,.debug_varnames,.debug_ranges');
|
||||
Concat('EXESECTION .stab');
|
||||
Concat(' OBJSECTION .stab');
|
||||
Concat('ENDEXESECTION');
|
||||
Concat('EXESECTION .stabstr');
|
||||
Concat(' OBJSECTION .stabstr');
|
||||
Concat('ENDEXESECTION');
|
||||
end;
|
||||
end;
|
||||
|
||||
{*****************************************************************************
|
||||
Initialize
|
||||
*****************************************************************************}
|
||||
|
@ -28,16 +28,28 @@ interface
|
||||
implementation
|
||||
|
||||
uses
|
||||
globtype,cutils,cclasses,
|
||||
verbose,
|
||||
systems,ogbase,ogelf,assemble;
|
||||
systems,aasmbase,ogbase,ogelf,assemble;
|
||||
|
||||
type
|
||||
TElfObjOutputx86_64=class(TElfObjectOutput)
|
||||
function encodereloc(objrel:TObjRelocation):byte;override;
|
||||
TElfTargetx86_64=class(TElfTarget)
|
||||
class function encodereloc(objrel:TObjRelocation):byte;override;
|
||||
class procedure loadreloc(objrel:TObjRelocation);override;
|
||||
end;
|
||||
|
||||
TElfAssemblerx86_64=class(TInternalAssembler)
|
||||
constructor create(smart:boolean);override;
|
||||
TElfExeOutputx86_64=class(TElfExeOutput)
|
||||
private
|
||||
function RelocName(reltyp:byte):string;
|
||||
procedure MaybeWriteGOTEntry(reltyp:byte;relocval:aint;exesym:TExeSymbol);
|
||||
procedure ReportNonDSOReloc(reltyp:byte;objsec:TObjSection;ObjReloc:TObjRelocation);
|
||||
procedure ReportRelocOverflow(reltyp:byte;objsec:TObjSection;ObjReloc:TObjRelocation);
|
||||
protected
|
||||
procedure WriteFirstPLTEntry;override;
|
||||
procedure WritePLTEntry(exesym:TExeSymbol);override;
|
||||
procedure WriteIndirectPLTEntry(exesym:TExeSymbol);override;
|
||||
procedure GOTRelocPass1(objsec:TObjSection;ObjReloc:TObjRelocation);override;
|
||||
procedure DoRelocationFixup(objsec:TObjSection);override;
|
||||
end;
|
||||
|
||||
const
|
||||
@ -86,14 +98,64 @@ implementation
|
||||
R_X86_64_GNU_VTINHERIT = 250; { GNU extension to record C++ vtable hierarchy }
|
||||
R_X86_64_GNU_VTENTRY = 251; { GNU extension to record C++ vtable member usage }
|
||||
|
||||
type
|
||||
TRelocProp=record
|
||||
name: PChar;
|
||||
size: byte;
|
||||
end;
|
||||
|
||||
const
|
||||
relocprops: array[0..37] of TRelocProp=(
|
||||
(name: 'R_X86_64_NONE'; size:0),
|
||||
(name: 'R_X86_64_64'; size:8),
|
||||
(name: 'R_X86_64_PC32'; size:4),
|
||||
(name: 'R_X86_64_GOT32'; size:4),
|
||||
(name: 'R_X86_64_PLT32'; size:4),
|
||||
(name: 'R_X86_64_COPY'; size:0),
|
||||
(name: 'R_X86_64_GLOB_DAT'; size:8),
|
||||
(name: 'R_X86_64_JUMP_SLOT';size:8),
|
||||
(name: 'R_X86_64_RELATIVE'; size:8),
|
||||
(name: 'R_X86_64_GOTPCREL'; size:4),
|
||||
(name: 'R_X86_64_32'; size:4),
|
||||
(name: 'R_X86_64_32S'; size:4),
|
||||
(name: 'R_X86_64_16'; size:2),
|
||||
(name: 'R_X86_64_PC16'; size:2),
|
||||
(name: 'R_X86_64_8'; size:1),
|
||||
(name: 'R_X86_64_PC8'; size:1),
|
||||
(name: 'R_X86_64_DTPMOD64'; size:8),
|
||||
(name: 'R_X86_64_DTPOFF64'; size:8),
|
||||
(name: 'R_X86_64_TPOFF64'; size:8),
|
||||
(name: 'R_X86_64_TLSGD'; size:4),
|
||||
(name: 'R_X86_64_TLSLD'; size:4),
|
||||
(name: 'R_X86_64_DTPOFF32'; size:4),
|
||||
(name: 'R_X86_64_GOTTPOFF'; size:4),
|
||||
(name: 'R_X86_64_TPOFF32'; size:4),
|
||||
(name: 'R_X86_64_PC64'; size:8),
|
||||
(name: 'R_X86_64_GOTOFF64'; size:8),
|
||||
(name: 'R_X86_64_GOTPC32'; size:4),
|
||||
(name: 'R_X86_64_GOT64'; size:8),
|
||||
(name: 'R_X86_64_GOTPCREL64'; size:8),
|
||||
(name: 'R_X86_64_GOTPC64'; size:8),
|
||||
(name: 'R_X86_64_GOTPLT64'; size:8),
|
||||
(name: 'R_X86_64_PLTOFF64'; size:8),
|
||||
(name: 'R_X86_64_SIZE32'; size:4),
|
||||
(name: 'R_X86_64_SIZE64'; size:8),
|
||||
(name: 'R_X86_64_GOTPC32_TLSDESC'; size:4),
|
||||
(name: 'R_X86_64_TLSDESC_CALL'; size:0),
|
||||
(name: 'R_X86_64_TLSDESC'; size:8),
|
||||
(name: 'R_X86_64_IRELATIVE'; size:8)
|
||||
);
|
||||
|
||||
|
||||
{****************************************************************************
|
||||
TELFObjectOutputx86_64
|
||||
TELFTargetx86_64
|
||||
****************************************************************************}
|
||||
|
||||
function TElfObjOutputx86_64.encodereloc(objrel:TObjRelocation):byte;
|
||||
class function TElfTargetx86_64.encodereloc(objrel:TObjRelocation):byte;
|
||||
begin
|
||||
case objrel.typ of
|
||||
RELOC_NONE :
|
||||
result:=R_X86_64_NONE;
|
||||
{ Note: 8 and 16-bit relocations are known to be non-conformant with
|
||||
AMD64 ABI, so they aren't handled. }
|
||||
RELOC_RELATIVE :
|
||||
@ -122,17 +184,412 @@ implementation
|
||||
end;
|
||||
end;
|
||||
|
||||
{****************************************************************************
|
||||
TELFAssemblerx86_64
|
||||
****************************************************************************}
|
||||
|
||||
constructor TElfAssemblerx86_64.create(smart:boolean);
|
||||
class procedure TElfTargetx86_64.loadreloc(objrel:TObjRelocation);
|
||||
begin
|
||||
inherited Create(smart);
|
||||
CObjOutput:=TElfObjOutputx86_64;
|
||||
end;
|
||||
|
||||
|
||||
{****************************************************************************
|
||||
TELFExeOutputx86_64
|
||||
****************************************************************************}
|
||||
|
||||
function TElfExeOutputx86_64.RelocName(reltyp:byte):string;
|
||||
begin
|
||||
if reltyp<=high(relocprops) then
|
||||
result:=relocprops[reltyp].name
|
||||
else
|
||||
result:='unknown ('+tostr(reltyp)+')';
|
||||
end;
|
||||
|
||||
|
||||
procedure TElfExeOutputx86_64.ReportNonDSOReloc(reltyp:byte;objsec:TObjSection;ObjReloc:TObjRelocation);
|
||||
begin
|
||||
{ TODO: include objsec properties into message }
|
||||
Comment(v_error,'Relocation '+RelocName(reltyp)+' against '''+objreloc.TargetName+''' cannot be used when linking a shared object; recompile with -Cg');
|
||||
end;
|
||||
|
||||
|
||||
procedure TElfExeOutputx86_64.ReportRelocOverflow(reltyp:byte;objsec:TObjSection;ObjReloc:TObjRelocation);
|
||||
begin
|
||||
{ TODO: include objsec properties into message }
|
||||
Comment(v_error,'Relocation truncated to fit: '+RelocName(reltyp)+' against '''+objreloc.TargetName+'''');
|
||||
end;
|
||||
|
||||
|
||||
procedure TElfExeOutputx86_64.GOTRelocPass1(objsec:TObjSection;ObjReloc:TObjRelocation);
|
||||
var
|
||||
objsym:TObjSymbol;
|
||||
sym:TExeSymbol;
|
||||
reltyp:byte;
|
||||
begin
|
||||
if (ObjReloc.flags and rf_raw)=0 then
|
||||
reltyp:=ElfTarget.encodereloc(ObjReloc)
|
||||
else
|
||||
reltyp:=ObjReloc.ftype;
|
||||
|
||||
case reltyp of
|
||||
R_X86_64_PLT32,
|
||||
R_X86_64_GOTPLT64:
|
||||
begin
|
||||
objsym:=ObjReloc.symbol.exesymbol.ObjSymbol;
|
||||
objsym.refs:=objsym.refs or symref_plt;
|
||||
end;
|
||||
end;
|
||||
|
||||
case reltyp of
|
||||
R_X86_64_GOT32,
|
||||
R_X86_64_GOT64,
|
||||
R_X86_64_GOTTPOFF,
|
||||
R_X86_64_GOTPCREL,
|
||||
R_X86_64_GOTPCREL64:
|
||||
begin
|
||||
sym:=ObjReloc.symbol.exesymbol;
|
||||
{ TLS IE to locally defined symbol, convert into LE so GOT entry isn't needed
|
||||
(Is TLS IE allowed in shared libs at all? Yes it is, when lib is accessing
|
||||
a threadvar in main program or in other *statically* loaded lib; TLS IE access to
|
||||
own threadvars may render library not loadable dynamically) }
|
||||
(*
|
||||
if (reltyp=R_X86_64_GOTTPOFF) and not
|
||||
(IsSharedLibrary or (sym.dynindex>0)) then
|
||||
begin
|
||||
if not IsValidIEtoLE(objsec,ObjReloc) then
|
||||
Comment(v_error,'Cannot transform TLS IE to LE');
|
||||
TranslateIEtoLE(objsec,ObjReloc);
|
||||
ObjReloc.ftype:=R_X86_64_TPOFF32;
|
||||
exit;
|
||||
end;
|
||||
*)
|
||||
{ Although local symbols should not be accessed through GOT,
|
||||
this isn't strictly forbidden. In this case we need to fake up
|
||||
the exesym to store the GOT offset in it.
|
||||
TODO: name collision; maybe use a different symbol list object? }
|
||||
if sym=nil then
|
||||
begin
|
||||
sym:=TExeSymbol.Create(ExeSymbolList,objreloc.symbol.name+'*local*');
|
||||
sym.objsymbol:=objreloc.symbol;
|
||||
objreloc.symbol.exesymbol:=sym;
|
||||
end;
|
||||
if sym.GotOffset>0 then
|
||||
exit;
|
||||
gotobjsec.alloc(sizeof(pint));
|
||||
sym.GotOffset:=gotobjsec.size;
|
||||
{ In shared library, every GOT entry needs a RELATIVE dynamic reloc,
|
||||
imported/exported symbols need GLOB_DAT instead. For executables,
|
||||
only the latter applies. }
|
||||
if IsSharedLibrary or (sym.dynindex>0) then
|
||||
dynrelocsec.alloc(dynrelocsec.shentsize);
|
||||
end;
|
||||
|
||||
//R_X86_64_TLSGD,
|
||||
//R_X86_64_TLSLD: { TODO: allocate two GOT slots }
|
||||
|
||||
{ R_X86_64_32S cannot be used in DSOs at all }
|
||||
R_X86_64_32S:
|
||||
if IsSharedLibrary then
|
||||
ReportNonDSOReloc(reltyp,objsec,objreloc);
|
||||
|
||||
{ R_X86_64_32 is processed by rtld, but binutils accept it in data sections only.
|
||||
Relocating the against local symbols is tricky: changing into RELATIVE is not possible,
|
||||
so it is changed into relocation against section symbol. This requires adding
|
||||
the appropriate section symbol to dynamic symtable. BFD also has some obscure logic
|
||||
behind, e.g. it uses .text section for symbols from .data section.
|
||||
|
||||
For now, leave this situation unhandled, as 32-bit relocations aren't commonly
|
||||
used in 64-bit code. }
|
||||
|
||||
R_X86_64_32:
|
||||
if IsSharedLibrary then
|
||||
begin
|
||||
if (oso_executable in objsec.SecOptions) or
|
||||
not (oso_write in objsec.SecOptions) then
|
||||
ReportNonDSOReloc(reltyp,objsec,objreloc)
|
||||
else
|
||||
InternalError(2012092601);
|
||||
end;
|
||||
|
||||
R_X86_64_64:
|
||||
begin
|
||||
if IsSharedLibrary then
|
||||
begin
|
||||
if (oso_executable in objsec.SecOptions) or
|
||||
not (oso_write in objsec.SecOptions) then
|
||||
hastextrelocs:=True;
|
||||
dynrelocsec.alloc(dynrelocsec.shentsize);
|
||||
objreloc.flags:=objreloc.flags or rf_dynamic;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure TElfExeOutputx86_64.MaybeWriteGOTEntry(reltyp:byte;relocval:aint;exesym:TExeSymbol);
|
||||
var
|
||||
gotoff,dynidx,tmp:aword;
|
||||
begin
|
||||
gotoff:=exesym.gotoffset;
|
||||
if gotoff=0 then
|
||||
InternalError(2012060902);
|
||||
|
||||
{ the GOT slot itself, and a dynamic relocation for it }
|
||||
{ TODO: only data symbols must get here }
|
||||
if gotoff=gotobjsec.Data.size+sizeof(pint) then
|
||||
begin
|
||||
dynidx:=exesym.dynindex;
|
||||
gotobjsec.write(relocval,sizeof(pint));
|
||||
|
||||
tmp:=gotobjsec.mempos+gotoff-sizeof(pint);
|
||||
if (dynidx>0) then
|
||||
begin
|
||||
if (reltyp=R_X86_64_GOTTPOFF) then
|
||||
if IsSharedLibrary then
|
||||
WriteDynRelocEntry(tmp,R_X86_64_TPOFF64,dynidx,0) // probably incorrect
|
||||
else
|
||||
else
|
||||
WriteDynRelocEntry(tmp,R_X86_64_GLOB_DAT,dynidx,0);
|
||||
end
|
||||
else if IsSharedLibrary then
|
||||
WriteDynRelocEntry(tmp,R_X86_64_RELATIVE,0,relocval);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TElfExeOutputx86_64.DoRelocationFixup(objsec:TObjSection);
|
||||
var
|
||||
i,zero:longint;
|
||||
objreloc: TObjRelocation;
|
||||
address,
|
||||
relocval : aint;
|
||||
relocsec : TObjSection;
|
||||
data: TDynamicArray;
|
||||
reltyp,relsize: byte;
|
||||
begin
|
||||
data:=objsec.data;
|
||||
for i:=0 to objsec.ObjRelocations.Count-1 do
|
||||
begin
|
||||
objreloc:=TObjRelocation(objsec.ObjRelocations[i]);
|
||||
case objreloc.typ of
|
||||
RELOC_NONE:
|
||||
continue;
|
||||
RELOC_ZERO:
|
||||
begin
|
||||
data.Seek(objreloc.dataoffset);
|
||||
zero:=0;
|
||||
data.Write(zero,4);
|
||||
continue;
|
||||
end;
|
||||
end;
|
||||
|
||||
if (objreloc.flags and rf_raw)=0 then
|
||||
reltyp:=ElfTarget.encodereloc(objreloc)
|
||||
else
|
||||
reltyp:=objreloc.ftype;
|
||||
|
||||
if reltyp<=high(relocprops) then
|
||||
relsize:=relocprops[reltyp].size
|
||||
else
|
||||
InternalError(2012092103);
|
||||
|
||||
if relocs_use_addend then
|
||||
address:=objreloc.orgsize
|
||||
else
|
||||
begin
|
||||
data.Seek(objreloc.dataoffset);
|
||||
data.Read(address,relsize);
|
||||
end;
|
||||
if assigned(objreloc.symbol) then
|
||||
begin
|
||||
relocsec:=objreloc.symbol.objsection;
|
||||
relocval:=objreloc.symbol.address;
|
||||
end
|
||||
else if assigned(objreloc.objsection) then
|
||||
begin
|
||||
relocsec:=objreloc.objsection;
|
||||
relocval:=objreloc.objsection.mempos
|
||||
end
|
||||
else
|
||||
internalerror(2012060702);
|
||||
|
||||
{ Only debug sections are allowed to have relocs pointing to unused sections }
|
||||
if assigned(relocsec) and not (relocsec.used and assigned(relocsec.exesection)) and
|
||||
not (oso_debug in objsec.secoptions) then
|
||||
begin
|
||||
writeln(objsec.fullname,' references ',relocsec.fullname);
|
||||
internalerror(2012060703);
|
||||
end;
|
||||
|
||||
{ TODO: if relocsec=nil, relocations must be copied to .rela.dyn section }
|
||||
if (relocsec=nil) or (relocsec.used) then
|
||||
case reltyp of
|
||||
R_X86_64_PC32,
|
||||
R_X86_64_PC64:
|
||||
begin
|
||||
address:=address+relocval-(objsec.mempos+objreloc.dataoffset);
|
||||
end;
|
||||
|
||||
R_X86_64_PLT32:
|
||||
begin
|
||||
{ If target is in current object, treat as RELOC_RELATIVE }
|
||||
address:=address+relocval-(objsec.mempos+objreloc.dataoffset);
|
||||
end;
|
||||
|
||||
R_X86_64_TPOFF32,
|
||||
R_X86_64_TPOFF64:
|
||||
address:=relocval-(tlsseg.MemPos+tlsseg.MemSize);
|
||||
|
||||
R_X86_64_GOTTPOFF,
|
||||
R_X86_64_GOTPCREL,
|
||||
R_X86_64_GOTPCREL64:
|
||||
begin
|
||||
if (reltyp=R_X86_64_GOTTPOFF) then
|
||||
relocval:=relocval-(tlsseg.MemPos+tlsseg.MemSize);
|
||||
|
||||
MaybeWriteGOTEntry(reltyp,relocval,objreloc.symbol.exesymbol);
|
||||
|
||||
{ resolves to PC-relative offset to GOT slot }
|
||||
relocval:=gotobjsec.mempos+objreloc.symbol.exesymbol.gotoffset-sizeof(pint);
|
||||
address:=address+relocval-(objsec.mempos+objreloc.dataoffset);
|
||||
end;
|
||||
|
||||
R_X86_64_32S,
|
||||
R_X86_64_32:
|
||||
inc(address,relocval);
|
||||
|
||||
R_X86_64_64:
|
||||
begin
|
||||
inc(address,relocval);
|
||||
|
||||
if (objreloc.flags and rf_dynamic)<>0 then
|
||||
begin
|
||||
if (objreloc.symbol=nil) or
|
||||
(objreloc.symbol.exesymbol=nil) or
|
||||
(objreloc.symbol.exesymbol.dynindex=0) then
|
||||
WriteDynRelocEntry(objreloc.dataoffset+objsec.mempos,R_X86_64_RELATIVE,0,address)
|
||||
else
|
||||
WriteDynRelocEntry(objreloc.dataoffset+objsec.mempos,R_X86_64_64,objreloc.symbol.exesymbol.dynindex,0);
|
||||
end;
|
||||
end;
|
||||
|
||||
R_X86_64_GOTPC32,
|
||||
R_X86_64_GOTPC64:
|
||||
begin
|
||||
address:=address+gotsymbol.address-(objsec.mempos+objreloc.dataoffset);
|
||||
end;
|
||||
|
||||
R_X86_64_GOT32,
|
||||
R_X86_64_GOT64:
|
||||
begin
|
||||
MaybeWriteGOTEntry(reltyp,relocval,objreloc.symbol.exesymbol);
|
||||
|
||||
relocval:=gotobjsec.mempos+objreloc.symbol.exesymbol.gotoffset-sizeof(pint)-gotsymbol.address;
|
||||
address:=address+relocval;
|
||||
end;
|
||||
|
||||
else
|
||||
begin
|
||||
writeln(objreloc.typ);
|
||||
internalerror(200604014);
|
||||
end;
|
||||
end
|
||||
else { not relocsec.Used }
|
||||
address:=0; { Relocation in debug section points to unused section, which is eliminated by linker }
|
||||
|
||||
case relsize of
|
||||
8: ;
|
||||
4:
|
||||
begin
|
||||
case reltyp of
|
||||
R_X86_64_32:
|
||||
if qword(address)>qword($FFFFFFFF) then
|
||||
ReportRelocOverflow(reltyp,objsec,objreloc);
|
||||
else
|
||||
if (address>high(longint)) or (address<low(longint)) then
|
||||
ReportRelocOverflow(reltyp,objsec,objreloc);
|
||||
end;
|
||||
end;
|
||||
else
|
||||
InternalError(2012101102);
|
||||
end;
|
||||
|
||||
data.Seek(objreloc.dataoffset);
|
||||
data.Write(address,relsize);
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure TElfExeOutputx86_64.WriteFirstPLTEntry;
|
||||
begin
|
||||
pltobjsec.writeBytes(#$FF#$35); // push got+8(%rip)
|
||||
pltobjsec.writeReloc_internal(gotpltobjsec,sizeof(pint),4,RELOC_RELATIVE);
|
||||
pltobjsec.writeBytes(#$FF#$25); // jmp *got+16(%rip)
|
||||
pltobjsec.writeReloc_internal(gotpltobjsec,2*sizeof(pint),4,RELOC_RELATIVE);
|
||||
pltobjsec.writeBytes(#$0F#$1F#$40#$00); // nopl 0(%rax)
|
||||
end;
|
||||
|
||||
|
||||
procedure TElfExeOutputx86_64.WritePLTEntry(exesym:TExeSymbol);
|
||||
var
|
||||
got_offset: aword;
|
||||
tmp: pint;
|
||||
begin
|
||||
pltobjsec.writeBytes(#$FF#$25); // jmp *got+x(%rip)
|
||||
pltobjsec.writeReloc_internal(gotpltobjsec,gotpltobjsec.size,4,RELOC_RELATIVE);
|
||||
pltobjsec.writeBytes(#$68); // push $index
|
||||
tmp:=pltrelocsec.size div pltrelocsec.shentsize;
|
||||
pltobjsec.write(tmp,4);
|
||||
|
||||
pltobjsec.writeBytes(#$E9); // jmp .plt
|
||||
tmp:=-(4+pltobjsec.Size);
|
||||
pltobjsec.write(tmp,4);
|
||||
|
||||
{ write a .got.plt slot pointing back to the 'push' instruction }
|
||||
gotpltobjsec.writeReloc_internal(pltobjsec,pltobjsec.size-(16-6),sizeof(pint),RELOC_ABSOLUTE);
|
||||
|
||||
{ write a .rela.plt entry (Elf64_rela record) }
|
||||
pltrelocsec.writeReloc_internal(gotpltobjsec,gotpltobjsec.size-sizeof(pint),sizeof(pint),RELOC_ABSOLUTE);
|
||||
got_offset:=(qword(exesym.dynindex) shl 32) or R_X86_64_JUMP_SLOT;
|
||||
pltrelocsec.write(got_offset,sizeof(pint));
|
||||
if relocs_use_addend then
|
||||
pltrelocsec.writezeros(sizeof(pint));
|
||||
end;
|
||||
|
||||
|
||||
procedure TElfExeOutputx86_64.WriteIndirectPLTEntry(exesym:TExeSymbol);
|
||||
var
|
||||
tmp: pint;
|
||||
objsym:TObjSymbol;
|
||||
targetsym:TObjSymbol;
|
||||
begin
|
||||
targetsym:=exesym.ObjSymbol;
|
||||
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;
|
||||
|
||||
pltobjsec.writeBytes(#$FF#$25); // jmp *got+x(%rip)
|
||||
pltobjsec.writeReloc_internal(gotpltobjsec,gotpltobjsec.size,4,RELOC_RELATIVE);
|
||||
{ TODO: Are these entries relevant when linking dynamic?
|
||||
(for static linking, they don't matter) }
|
||||
pltobjsec.writeBytes(#$68); // push $index
|
||||
tmp:=pltrelocsec.size div pltrelocsec.shentsize;
|
||||
pltobjsec.write(tmp,4);
|
||||
|
||||
pltobjsec.writeBytes(#$E9); // jmp .plt
|
||||
tmp:=-(4+pltobjsec.Size);
|
||||
pltobjsec.write(tmp,4);
|
||||
|
||||
{ write a .got.plt slot pointing back to the 'push' instruction }
|
||||
gotpltobjsec.writeReloc_internal(pltobjsec,pltobjsec.size-(16-6),sizeof(pint),RELOC_ABSOLUTE);
|
||||
|
||||
{ write a .rela.iplt entry (Elf64_rela record) }
|
||||
ipltrelocsec.writeReloc_internal(gotpltobjsec,gotpltobjsec.size-sizeof(pint),sizeof(pint),RELOC_ABSOLUTE);
|
||||
tmp:=R_X86_64_IRELATIVE;
|
||||
ipltrelocsec.write(tmp,sizeof(pint));
|
||||
if relocs_use_addend then
|
||||
ipltrelocsec.writeReloc_internal(targetsym.objsection,targetsym.offset,sizeof(pint),RELOC_ABSOLUTE);
|
||||
end;
|
||||
|
||||
{*****************************************************************************
|
||||
Initialize
|
||||
*****************************************************************************}
|
||||
@ -153,7 +610,9 @@ implementation
|
||||
);
|
||||
|
||||
initialization
|
||||
RegisterAssembler(as_x86_64_elf64_info,TElfAssemblerx86_64);
|
||||
RegisterAssembler(as_x86_64_elf64_info,TElfAssembler);
|
||||
ElfTarget:=TElfTargetx86_64;
|
||||
ElfExeOutputClass:=TElfExeOutputx86_64;
|
||||
|
||||
end.
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user