+ 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; objsym.refs:=objsym.refs or symref_plt;
end; 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: R_386_TLS_IE:
begin begin

View File

@ -228,7 +228,6 @@ interface
shoffset: aword; shoffset: aword;
gotwritten: boolean; gotwritten: boolean;
{ dynamic linking } { dynamic linking }
dynamiclink: boolean;
dynsymnames: Plongword; dynsymnames: Plongword;
dynsymtable: TElfSymtab; dynsymtable: TElfSymtab;
interpobjsec: TObjSection; interpobjsec: TObjSection;
@ -241,7 +240,7 @@ interface
dynamicsec, dynamicsec,
hashobjsec: TElfObjSection; hashobjsec: TElfObjSection;
neededlist: TFPHashList; neededlist: TFPHashList;
gotsize: aword; dyncopysyms: TFPObjectList;
dynrelsize: aword; dynrelsize: aword;
function AttachSection(objsec:TObjSection):TElfExeSection; function AttachSection(objsec:TObjSection):TElfExeSection;
@ -257,12 +256,13 @@ interface
procedure MapSectionsToSegments; procedure MapSectionsToSegments;
procedure WriteStaticSymtable; procedure WriteStaticSymtable;
procedure InitDynlink; procedure InitDynlink;
procedure PrepareGOT;
protected protected
dynamiclink: boolean;
hastextrelocs: boolean; hastextrelocs: boolean;
gotsymbol: TObjSymbol; gotsymbol: TObjSymbol;
dynsymlist: TFPObjectList; dynsymlist: TFPObjectList;
gotobjsec: TObjSection; gotobjsec: TObjSection;
dynbssobjsec,
pltobjsec, pltobjsec,
gotpltobjsec, gotpltobjsec,
pltrelocsec, pltrelocsec,
@ -271,7 +271,9 @@ interface
dynreloclist: TFPObjectList; dynreloclist: TFPObjectList;
tlsseg: TElfSegment; tlsseg: TElfSegment;
relative_reloc_count: longint; relative_reloc_count: longint;
function AllocGOTSlot(objsym: TObjSymbol):boolean; gotsize: aword;
procedure PrepareGOT;virtual;
function AllocGOTSlot(objsym: TObjSymbol):boolean;virtual;
procedure CreateGOTSection;virtual; procedure CreateGOTSection;virtual;
procedure make_dynamic_if_undefweak(exesym:TExeSymbol); procedure make_dynamic_if_undefweak(exesym:TExeSymbol);
procedure WriteDynRelocEntry(dataofs:aword;typ:byte;symidx:aword;addend:aword); procedure WriteDynRelocEntry(dataofs:aword;typ:byte;symidx:aword;addend:aword);
@ -1821,12 +1823,6 @@ implementation
if dynndx=0 then if dynndx=0 then
InternalError(2012071401); 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 } { load the symtable }
FReader.Seek(symtaboffset+sizeof(TElfSymbol)); FReader.Seek(symtaboffset+sizeof(TElfSymbol));
LoadSymbols(objdata,syms,localsyms); LoadSymbols(objdata,syms,localsyms);
@ -1941,6 +1937,7 @@ implementation
destructor TElfExeOutput.Destroy; destructor TElfExeOutput.Destroy;
begin begin
dyncopysyms.Free;
neededlist.Free; neededlist.Free;
segmentlist.Free; segmentlist.Free;
dynsymlist.Free; dynsymlist.Free;
@ -2180,6 +2177,7 @@ implementation
exit; exit;
gotobjsec.alloc(sizeof(pint)); gotobjsec.alloc(sizeof(pint));
exesym.GotOffset:=gotobjsec.size; exesym.GotOffset:=gotobjsec.size;
make_dynamic_if_undefweak(exesym);
{ In shared library, every GOT entry needs a RELATIVE dynamic reloc, { In shared library, every GOT entry needs a RELATIVE dynamic reloc,
imported/exported symbols need GLOB_DAT instead. For executables, imported/exported symbols need GLOB_DAT instead. For executables,
only the latter applies. } only the latter applies. }
@ -2266,6 +2264,15 @@ implementation
begin begin
exesym.State:=symstate_defined; exesym.State:=symstate_defined;
exesym.dynindex:=dynsymlist.Add(exesym)+1; 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; needed:=true;
end; end;
end; end;
@ -2368,8 +2375,10 @@ implementation
begin begin
if exesym.dynindex<>0 then if exesym.dynindex<>0 then
InternalError(2012062301); InternalError(2012062301);
exesym.dynindex:=dynsymlist.add(exesym)+1; { Weak-referenced symbols are changed into dynamic ones
exesym.state:=symstate_defined; only if referenced through GOT or PLT (this is BFD-compatible) }
if exesym.state<>symstate_undefweak then
exesym.dynindex:=dynsymlist.add(exesym)+1;
end end
else else
UnresolvedExeSymbols[i]:=nil; UnresolvedExeSymbols[i]:=nil;
@ -2386,21 +2395,44 @@ implementation
if assigned(exesym.ObjSymbol.objsection) then // an exported symbol if assigned(exesym.ObjSymbol.objsection) then // an exported symbol
continue; continue;
{ if there's no PLT references to symbol, then PLT entry isn't needed } if ((exesym.ObjSymbol.refs and symref_plt)<>0) or
{ !! Does not work correctly yet !! } ((exesym.ObjSymbol.typ=AT_FUNCTION) and (not IsSharedLibrary)) then
// if (exesym.objsymbol.refs and symref_plt)=0 then begin
// continue; make_dynamic_if_undefweak(exesym);
{ This symbol has a valid address to which relocations are resolved, { This symbol has a valid address to which relocations are resolved,
but it remains (weak)external when written to dynamic symtable. } but it remains (weak)external when written to dynamic symtable. }
objsym:=internalobjdata.CreateSymbol(exesym.name); objsym:=internalobjdata.CreateSymbol(exesym.name);
objsym.typ:=AT_FUNCTION; objsym.typ:=AT_FUNCTION;
objsym.bind:=exesym.ObjSymbol.bind; { AB_EXTERNAL or AB_WEAK_EXTERNAL } objsym.bind:=exesym.ObjSymbol.bind; { AB_EXTERNAL or AB_WEAK_EXTERNAL }
objsym.offset:=pltobjsec.size; objsym.indsymbol:=exesym.ObjSymbol.indsymbol;
objsym.objsection:=pltobjsec; objsym.offset:=pltobjsec.size;
exesym.ObjSymbol:=objsym; 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; end;
{ Handle indirect symbols } { Handle indirect symbols }
@ -2439,7 +2471,8 @@ implementation
(.got, .rel[a].dyn, .rel[a].plt (includes .rel[a].iplt) and .hash } (.got, .rel[a].dyn, .rel[a].plt (includes .rel[a].iplt) and .hash }
if gotobjsec.size<>0 then if gotobjsec.size<>0 then
Exclude(gotobjsec.ExeSection.SecOptions,oso_disabled); 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); Exclude(dynrelocsec.ExeSection.SecOptions,oso_disabled);
if assigned(pltrelocsec) and (pltrelocsec.size>0) then if assigned(pltrelocsec) and (pltrelocsec.size>0) then
Exclude(pltrelocsec.ExeSection.SecOptions,oso_disabled); Exclude(pltrelocsec.ExeSection.SecOptions,oso_disabled);
@ -2568,9 +2601,10 @@ implementation
if (not gotwritten) then if (not gotwritten) then
begin 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 if assigned(gotobjsec) then
gotobjsec.size:=0; gotobjsec.size:=gotobjsec.data.size;
if assigned(dynrelocsec) then if assigned(dynrelocsec) then
begin begin
dynrelocsec.size:=0; dynrelocsec.size:=0;
@ -2615,6 +2649,7 @@ implementation
exesec: TExeSection; exesec: TExeSection;
seg: TElfSegment; seg: TElfSegment;
objreloc: TObjRelocation; objreloc: TObjRelocation;
objsym: TObjSymbol;
begin begin
gotwritten:=true; gotwritten:=true;
{ If target does not support sorted relocations, it is expected to write the { If target does not support sorted relocations, it is expected to write the
@ -2623,6 +2658,14 @@ implementation
should remain. } should remain. }
if assigned(dynrelocsec) then if assigned(dynrelocsec) then
begin 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 if (dynrelocsec.size+(dynreloclist.count*dynrelocsec.shentsize)<>dynrelsize) then
InternalError(2012110601); InternalError(2012110601);
{ Write out non-RELATIVE dynamic relocations { Write out non-RELATIVE dynamic relocations
@ -2742,6 +2785,10 @@ implementation
dynrelocsec:=TElfObjSection.create_reloc(internalObjData,'.dyn',true); dynrelocsec:=TElfObjSection.create_reloc(internalObjData,'.dyn',true);
dynrelocsec.SecOptions:=[oso_keep]; 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); dynreloclist:=TFPObjectList.Create(true);
symversec:=TElfObjSection.create_ext(internalObjData,'.gnu.version', symversec:=TElfObjSection.create_ext(internalObjData,'.gnu.version',
@ -2753,6 +2800,7 @@ implementation
verneedsec:=TElfObjSection.create_ext(internalObjData,'.gnu.version_r', verneedsec:=TElfObjSection.create_ext(internalObjData,'.gnu.version_r',
SHT_GNU_VERNEED,SHF_ALLOC,sizeof(pint),0); SHT_GNU_VERNEED,SHF_ALLOC,sizeof(pint),0);
verneedsec.SecOptions:=[oso_keep]; verneedsec.SecOptions:=[oso_keep];
dyncopysyms:=TFPObjectList.Create(False);
end; end;

View File

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

View File

@ -199,6 +199,7 @@ implementation
objsym:TObjSymbol; objsym:TObjSymbol;
objreloc:TObjRelocation; objreloc:TObjRelocation;
reltyp:byte; reltyp:byte;
externsym,fromtext:boolean;
begin begin
objreloc:=TObjRelocation(objsec.ObjRelocations[idx]); objreloc:=TObjRelocation(objsec.ObjRelocations[idx]);
if (ObjReloc.flags and rf_raw)=0 then if (ObjReloc.flags and rf_raw)=0 then
@ -249,6 +250,7 @@ implementation
//R_X86_64_TLSGD, //R_X86_64_TLSGD,
//R_X86_64_TLSLD: { TODO: allocate two GOT slots } //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 cannot be used in DSOs at all }
R_X86_64_32S: R_X86_64_32S:
if IsSharedLibrary then if IsSharedLibrary then
@ -275,17 +277,40 @@ implementation
R_X86_64_64: R_X86_64_64:
begin 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 if IsSharedLibrary then
begin begin
if (oso_executable in objsec.SecOptions) or if fromtext then
not (oso_write in objsec.SecOptions) then
hastextrelocs:=True; hastextrelocs:=True;
dynrelocsec.alloc(dynrelocsec.shentsize); dynrelocsec.alloc(dynrelocsec.shentsize);
objreloc.flags:=objreloc.flags or rf_dynamic; objreloc.flags:=objreloc.flags or rf_dynamic;
if (objreloc.symbol=nil) or if (not externsym) then
(objreloc.symbol.exesymbol=nil) or
(objreloc.symbol.exesymbol.dynindex=0) then
Inc(relative_reloc_count); 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; end;
end; end;
@ -403,9 +428,20 @@ implementation
address:=address+relocval-PC; address:=address+relocval-PC;
end; 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_TPOFF32,
R_X86_64_TPOFF64: R_X86_64_TPOFF64:
address:=relocval-(tlsseg.MemPos+tlsseg.MemSize); address:=address+relocval-(tlsseg.MemPos+tlsseg.MemSize);
R_X86_64_GOTTPOFF, R_X86_64_GOTTPOFF,
R_X86_64_GOTPCREL, R_X86_64_GOTPCREL,