x86_64 internal ELF linker:

* Split generation of regular and TLSIE GOT entries. Although code for both is similar to some extent, mixing them in a single method turns it into spaghetti.
* Several fixes to content and dynamic relocations of TLSIE entries.

git-svn-id: trunk@25180 -
This commit is contained in:
sergei 2013-07-29 08:34:00 +00:00
parent db0585f388
commit 11b72b5515

View File

@ -35,7 +35,8 @@ implementation
type type
TElfExeOutputx86_64=class(TElfExeOutput) TElfExeOutputx86_64=class(TElfExeOutput)
private private
procedure MaybeWriteGOTEntry(reltyp:byte;relocval:aint;objsym:TObjSymbol); procedure MaybeWriteGOTEntry(relocval:aint;objsym:TObjSymbol);
procedure MaybeWriteTLSIEGotEntry(relocval:aint;objsym:TObjSymbol);
protected protected
procedure WriteFirstPLTEntry;override; procedure WriteFirstPLTEntry;override;
procedure WritePLTEntry(exesym:TExeSymbol);override; procedure WritePLTEntry(exesym:TExeSymbol);override;
@ -260,7 +261,7 @@ implementation
ReportNonDSOReloc(reltyp,objsec,objreloc); ReportNonDSOReloc(reltyp,objsec,objreloc);
{ R_X86_64_32 is processed by rtld, but binutils accept it in data sections only. { 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, Relocating against local symbols is tricky: changing into RELATIVE is not possible,
so it is changed into relocation against section symbol. This requires adding 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 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. behind, e.g. it uses .text section for symbols from .data section.
@ -320,7 +321,7 @@ implementation
end; end;
procedure TElfExeOutputx86_64.MaybeWriteGOTEntry(reltyp:byte;relocval:aint;objsym:TObjSymbol); procedure TElfExeOutputx86_64.MaybeWriteGOTEntry(relocval:aint;objsym:TObjSymbol);
var var
gotoff,tmp:aword; gotoff,tmp:aword;
begin begin
@ -334,18 +335,43 @@ implementation
begin begin
gotobjsec.write(relocval,sizeof(pint)); gotobjsec.write(relocval,sizeof(pint));
tmp:=gotobjsec.mempos+gotoff-sizeof(pint);
if (objsym.exesymbol.dynindex>0) then
dynreloclist.Add(TObjRelocation.CreateRaw(tmp,objsym,R_X86_64_GLOB_DAT))
else
if IsSharedLibrary then
WriteDynRelocEntry(tmp,R_X86_64_RELATIVE,0,relocval);
end;
end;
procedure TElfExeOutputx86_64.MaybeWriteTLSIEGotEntry(relocval:aint;objsym:TObjSymbol);
var
gotoff,tmp: aword;
objrel: TObjRelocation;
begin
gotoff:=objsym.exesymbol.gotoffset;
if gotoff=0 then
InternalError(2012060903);
if gotoff=gotobjsec.Data.size+sizeof(pint) then
begin
tmp:=gotobjsec.mempos+gotoff-sizeof(pint); tmp:=gotobjsec.mempos+gotoff-sizeof(pint);
if (objsym.exesymbol.dynindex>0) then if (objsym.exesymbol.dynindex>0) then
begin begin
if (reltyp=R_X86_64_GOTTPOFF) then gotobjsec.writezeros(sizeof(pint));
if IsSharedLibrary then dynreloclist.Add(TObjRelocation.CreateRaw(tmp,objsym,R_X86_64_TPOFF64));
dynreloclist.Add(TObjRelocation.CreateRaw(tmp,objsym,R_X86_64_TPOFF64)) // probably incorrect
else
else
dynreloclist.Add(TObjRelocation.CreateRaw(tmp,objsym,R_X86_64_GLOB_DAT));
end end
else if IsSharedLibrary then else
WriteDynRelocEntry(tmp,R_X86_64_RELATIVE,0,relocval); begin
gotobjsec.write(relocval,sizeof(pint));
if IsSharedLibrary then
begin
objrel:=TObjRelocation.CreateRaw(tmp,nil,R_X86_64_TPOFF64);
objrel.orgsize:=relocval;
dynreloclist.Add(objrel);
end;
end;
end; end;
end; end;
@ -434,8 +460,8 @@ implementation
//R_X86_64_DTPOFF64 is possible in data?? //R_X86_64_DTPOFF64 is possible in data??
R_X86_64_DTPOFF32: R_X86_64_DTPOFF32:
begin begin
{ In executable it behaves as TPOFF32, but data expressions { In executable it behaves as TPOFF32 (i.e. generates negative offset),
like ".long foo@dtpoff" resolve to positive offset } but data expressions like ".long foo@dtpoff" resolve to positive offset }
if IsSharedLibrary or not (oso_executable in objsec.SecOptions) then if IsSharedLibrary or not (oso_executable in objsec.SecOptions) then
address:=address+relocval-tlsseg.MemPos address:=address+relocval-tlsseg.MemPos
else else
@ -446,14 +472,24 @@ implementation
R_X86_64_TPOFF64: R_X86_64_TPOFF64:
address:=address+relocval-(tlsseg.MemPos+tlsseg.MemSize); address:=address+relocval-(tlsseg.MemPos+tlsseg.MemSize);
R_X86_64_GOTTPOFF, R_X86_64_GOTTPOFF:
begin
if IsSharedLibrary then
relocval:=relocval-tlsseg.MemPos
else
relocval:=relocval-(tlsseg.MemPos+tlsseg.MemSize);
MaybeWriteTLSIEGotEntry(relocval,objreloc.symbol);
{ resolves to PC-relative offset to GOT slot }
relocval:=gotobjsec.mempos+objreloc.symbol.exesymbol.gotoffset-sizeof(pint);
address:=address+relocval-PC;
end;
R_X86_64_GOTPCREL, R_X86_64_GOTPCREL,
R_X86_64_GOTPCREL64: R_X86_64_GOTPCREL64:
begin begin
if (reltyp=R_X86_64_GOTTPOFF) then MaybeWriteGOTEntry(relocval,objreloc.symbol);
relocval:=relocval-(tlsseg.MemPos+tlsseg.MemSize);
MaybeWriteGOTEntry(reltyp,relocval,objreloc.symbol);
{ resolves to PC-relative offset to GOT slot } { resolves to PC-relative offset to GOT slot }
relocval:=gotobjsec.mempos+objreloc.symbol.exesymbol.gotoffset-sizeof(pint); relocval:=gotobjsec.mempos+objreloc.symbol.exesymbol.gotoffset-sizeof(pint);
@ -488,7 +524,7 @@ implementation
R_X86_64_GOT32, R_X86_64_GOT32,
R_X86_64_GOT64: R_X86_64_GOT64:
begin begin
MaybeWriteGOTEntry(reltyp,relocval,objreloc.symbol); MaybeWriteGOTEntry(relocval,objreloc.symbol);
relocval:=gotobjsec.mempos+objreloc.symbol.exesymbol.gotoffset-sizeof(pint)-gotsymbol.address; relocval:=gotobjsec.mempos+objreloc.symbol.exesymbol.gotoffset-sizeof(pint)-gotsymbol.address;
address:=address+relocval; address:=address+relocval;