+ Support dynamic copy relocations.

* For unresolved weak symbols, provide dynamic binding only if they are referenced via GOT or PLT (ld-compatible behavior).
* Made more TElfExeOutput properties/methods usable by descendant classes.
* x86_64/cpuelf.pas: some refactoring, handle DTPOFF relocations, prohibit TPOFF relocations in shared objects.
* i386/cpuelf.pas: set symref_from_text flags for copy relocations to work correctly.

git-svn-id: trunk@23375 -
This commit is contained in:
sergei 2013-01-13 17:44:14 +00:00
parent a1503b51f2
commit af4935e346
4 changed files with 133 additions and 34 deletions

View File

@ -210,6 +210,20 @@ implementation
objsym.refs:=objsym.refs or symref_plt;
end;
R_386_32:
if (oso_executable in objsec.SecOptions) or
not (oso_write in objsec.SecOptions) then
begin
if assigned(objreloc.symbol) and assigned(objreloc.symbol.exesymbol) then
begin
objsym:=objreloc.symbol.exesymbol.ObjSymbol;
objsym.refs:=objsym.refs or symref_from_text;
end;
end;
end;
case reltyp of
R_386_TLS_IE:
begin

View File

@ -228,7 +228,6 @@ interface
shoffset: aword;
gotwritten: boolean;
{ dynamic linking }
dynamiclink: boolean;
dynsymnames: Plongword;
dynsymtable: TElfSymtab;
interpobjsec: TObjSection;
@ -241,7 +240,7 @@ interface
dynamicsec,
hashobjsec: TElfObjSection;
neededlist: TFPHashList;
gotsize: aword;
dyncopysyms: TFPObjectList;
dynrelsize: aword;
function AttachSection(objsec:TObjSection):TElfExeSection;
@ -257,12 +256,13 @@ interface
procedure MapSectionsToSegments;
procedure WriteStaticSymtable;
procedure InitDynlink;
procedure PrepareGOT;
protected
dynamiclink: boolean;
hastextrelocs: boolean;
gotsymbol: TObjSymbol;
dynsymlist: TFPObjectList;
gotobjsec: TObjSection;
dynbssobjsec,
pltobjsec,
gotpltobjsec,
pltrelocsec,
@ -271,7 +271,9 @@ interface
dynreloclist: TFPObjectList;
tlsseg: TElfSegment;
relative_reloc_count: longint;
function AllocGOTSlot(objsym: TObjSymbol):boolean;
gotsize: aword;
procedure PrepareGOT;virtual;
function AllocGOTSlot(objsym: TObjSymbol):boolean;virtual;
procedure CreateGOTSection;virtual;
procedure make_dynamic_if_undefweak(exesym:TExeSymbol);
procedure WriteDynRelocEntry(dataofs:aword;typ:byte;symidx:aword;addend:aword);
@ -1821,12 +1823,6 @@ implementation
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);
@ -1941,6 +1937,7 @@ implementation
destructor TElfExeOutput.Destroy;
begin
dyncopysyms.Free;
neededlist.Free;
segmentlist.Free;
dynsymlist.Free;
@ -2180,6 +2177,7 @@ implementation
exit;
gotobjsec.alloc(sizeof(pint));
exesym.GotOffset:=gotobjsec.size;
make_dynamic_if_undefweak(exesym);
{ In shared library, every GOT entry needs a RELATIVE dynamic reloc,
imported/exported symbols need GLOB_DAT instead. For executables,
only the latter applies. }
@ -2266,6 +2264,15 @@ implementation
begin
exesym.State:=symstate_defined;
exesym.dynindex:=dynsymlist.Add(exesym)+1;
{ The original binding, value and section of external symbol
must be preserved, therefore resolving directly to .so symbol
hurts more than it helps. Copy type and size, and store .so
symbol in objsym.indsymbol for later use. }
exesym.ObjSymbol.typ:=objsym.typ;
if objsym.typ<>AT_FUNCTION then
exesym.ObjSymbol.size:=objsym.size;
exesym.ObjSymbol.indsymbol:=objsym;
objsym.ExeSymbol:=exesym;
needed:=true;
end;
end;
@ -2368,8 +2375,10 @@ implementation
begin
if exesym.dynindex<>0 then
InternalError(2012062301);
exesym.dynindex:=dynsymlist.add(exesym)+1;
exesym.state:=symstate_defined;
{ Weak-referenced symbols are changed into dynamic ones
only if referenced through GOT or PLT (this is BFD-compatible) }
if exesym.state<>symstate_undefweak then
exesym.dynindex:=dynsymlist.add(exesym)+1;
end
else
UnresolvedExeSymbols[i]:=nil;
@ -2386,21 +2395,44 @@ implementation
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;
if ((exesym.ObjSymbol.refs and symref_plt)<>0) or
((exesym.ObjSymbol.typ=AT_FUNCTION) and (not IsSharedLibrary)) then
begin
make_dynamic_if_undefweak(exesym);
{ 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;
{ 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.indsymbol:=exesym.ObjSymbol.indsymbol;
objsym.offset:=pltobjsec.size;
objsym.objsection:=pltobjsec;
objsym.exesymbol:=exesym;
exesym.ObjSymbol:=objsym;
WritePLTEntry(exesym);
WritePLTEntry(exesym);
end
else if ((exesym.ObjSymbol.refs and symref_from_text)<>0) and
(exesym.ObjSymbol.typ<>AT_FUNCTION) and (not IsSharedLibrary) and
(exesym.state<>symstate_undefweak) then
begin
if exesym.ObjSymbol.size=0 then
Comment(v_error,'Dynamic variable '+exesym.name+' has zero size');
internalobjdata.setSection(dynbssobjsec);
internalobjdata.allocalign(var_align(exesym.ObjSymbol.size));
objsym:=internalobjdata.SymbolDefine(exesym.name,AB_GLOBAL,AT_DATA);
objsym.size:=exesym.ObjSymbol.size;
objsym.indsymbol:=exesym.ObjSymbol.indsymbol;
exesym.ObjSymbol:=objsym;
objsym.exesymbol:=exesym;
dynbssobjsec.alloc(objsym.size);
{ allocate space for R_xx_COPY relocation for this symbol;
we'll create it later, to be consistent with "-z combreloc" semantics }
dyncopysyms.add(objsym);
dynrelocsec.alloc(dynrelocsec.shentsize);
inc(dynrelsize,dynrelocsec.shentsize);
end;
end;
{ Handle indirect symbols }
@ -2439,7 +2471,8 @@ implementation
(.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
if assigned(dynrelocsec) and
((dynrelocsec.size<>0) or (dyncopysyms.count<>0)) then
Exclude(dynrelocsec.ExeSection.SecOptions,oso_disabled);
if assigned(pltrelocsec) and (pltrelocsec.size>0) then
Exclude(pltrelocsec.ExeSection.SecOptions,oso_disabled);
@ -2568,9 +2601,10 @@ implementation
if (not gotwritten) then
begin
{ reset size of .got and .rel[a].dyn, they will be refilled while fixing up relocations }
{ Reset size of .got and .rel[a].dyn, they will be refilled while fixing up relocations.
For .got, consider already written reserved entries. }
if assigned(gotobjsec) then
gotobjsec.size:=0;
gotobjsec.size:=gotobjsec.data.size;
if assigned(dynrelocsec) then
begin
dynrelocsec.size:=0;
@ -2615,6 +2649,7 @@ implementation
exesec: TExeSection;
seg: TElfSegment;
objreloc: TObjRelocation;
objsym: TObjSymbol;
begin
gotwritten:=true;
{ If target does not support sorted relocations, it is expected to write the
@ -2623,6 +2658,14 @@ implementation
should remain. }
if assigned(dynrelocsec) then
begin
{ Append R_xx_COPY relocations }
for i:=0 to dyncopysyms.count-1 do
begin
objsym:=TObjSymbol(dyncopysyms[i]);
dynreloclist.Add(TObjRelocation.CreateRaw(objsym.address,objsym,ElfTarget.dyn_reloc_codes[dr_copy]));
end;
dyncopysyms.Clear;
if (dynrelocsec.size+(dynreloclist.count*dynrelocsec.shentsize)<>dynrelsize) then
InternalError(2012110601);
{ Write out non-RELATIVE dynamic relocations
@ -2742,6 +2785,10 @@ implementation
dynrelocsec:=TElfObjSection.create_reloc(internalObjData,'.dyn',true);
dynrelocsec.SecOptions:=[oso_keep];
dynbssobjsec:=TElfObjSection.create_ext(internalObjData,'.dynbss',
SHT_NOBITS,SHF_ALLOC or SHF_WRITE,sizeof(pint){16??},0);
dynbssobjsec.SecOptions:=[oso_keep];
dynreloclist:=TFPObjectList.Create(true);
symversec:=TElfObjSection.create_ext(internalObjData,'.gnu.version',
@ -2753,6 +2800,7 @@ implementation
verneedsec:=TElfObjSection.create_ext(internalObjData,'.gnu.version_r',
SHT_GNU_VERNEED,SHF_ALLOC,sizeof(pint),0);
verneedsec.SecOptions:=[oso_keep];
dyncopysyms:=TFPObjectList.Create(False);
end;

View File

@ -1457,6 +1457,7 @@ begin
Concat(' PROVIDE edata');
Concat('ENDEXESECTION');
Concat('EXESECTION .bss');
Concat(' OBJSECTION .dynbss');
Concat(' OBJSECTION .bss*');
Concat(' OBJSECTION fpc.reshandles');
Concat(' PROVIDE end');

View File

@ -199,6 +199,7 @@ implementation
objsym:TObjSymbol;
objreloc:TObjRelocation;
reltyp:byte;
externsym,fromtext:boolean;
begin
objreloc:=TObjRelocation(objsec.ObjRelocations[idx]);
if (ObjReloc.flags and rf_raw)=0 then
@ -249,6 +250,7 @@ implementation
//R_X86_64_TLSGD,
//R_X86_64_TLSLD: { TODO: allocate two GOT slots }
R_X86_64_TPOFF32,
{ R_X86_64_32S cannot be used in DSOs at all }
R_X86_64_32S:
if IsSharedLibrary then
@ -275,17 +277,40 @@ implementation
R_X86_64_64:
begin
fromtext:=(oso_executable in objsec.SecOptions) or
not (oso_write in objsec.SecOptions);
externsym:=assigned(objreloc.symbol) and
assigned(objreloc.symbol.exesymbol) and
(objreloc.symbol.exesymbol.dynindex<>0);
if IsSharedLibrary then
begin
if (oso_executable in objsec.SecOptions) or
not (oso_write in objsec.SecOptions) then
if fromtext then
hastextrelocs:=True;
dynrelocsec.alloc(dynrelocsec.shentsize);
objreloc.flags:=objreloc.flags or rf_dynamic;
if (objreloc.symbol=nil) or
(objreloc.symbol.exesymbol=nil) or
(objreloc.symbol.exesymbol.dynindex=0) then
if (not externsym) then
Inc(relative_reloc_count);
end
else if externsym then
// TODO: R_X86_64_32 and R_X86_64_32S here?
begin
objsym:=objreloc.symbol.ExeSymbol.ObjSymbol;
{ If symbol has non-GOT references from readonly sections, then it needs a
copy reloc, which eliminates any dynamic relocations to this symbol from
writable sections as well. OTOH if it is referenced *only* from writable
sections, then it's better not to generate a copy reloc and keep dynamic
relocations. The following code is based on assumption that all readonly
sections are processed before writable ones (which is true for current
segment mapping).
For arbitrary segment mapping, this will probably require a separate pass. }
if fromtext then
objsym.refs:=objsym.refs or symref_from_text
else if (objsym.refs and symref_from_text)=0 then
begin
dynrelocsec.alloc(dynrelocsec.shentsize);
objreloc.flags:=objreloc.flags or rf_dynamic;
end;
end;
end;
end;
@ -403,9 +428,20 @@ implementation
address:=address+relocval-PC;
end;
//R_X86_64_DTPOFF64 is possible in data??
R_X86_64_DTPOFF32:
begin
{ In executable it behaves as TPOFF32, but data expressions
like ".long foo@dtpoff" resolve to positive offset }
if IsSharedLibrary or not (oso_executable in objsec.SecOptions) then
address:=address+relocval-tlsseg.MemPos
else
address:=address+relocval-(tlsseg.MemPos+tlsseg.MemSize);
end;
R_X86_64_TPOFF32,
R_X86_64_TPOFF64:
address:=relocval-(tlsseg.MemPos+tlsseg.MemSize);
address:=address+relocval-(tlsseg.MemPos+tlsseg.MemSize);
R_X86_64_GOTTPOFF,
R_X86_64_GOTPCREL,