mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-04-08 16:28:24 +02:00
+ 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:
parent
a1503b51f2
commit
af4935e346
@ -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
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
||||
|
@ -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');
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user