+ MIPS linker: support linking PIC object files with non-PIC ones, by routing absolute calls into PIC code through stubs that load R25.

git-svn-id: trunk@24098 -
This commit is contained in:
sergei 2013-03-31 19:58:52 +00:00
parent 9758f4fa6a
commit cded05ccef
2 changed files with 149 additions and 3 deletions

View File

@ -43,7 +43,13 @@ implementation
local_got_relocs: TFPObjectList;
local_got_slots: TFPHashObjectList;
got_content: array of pint;
procedure MaybeWriteGOTEntry(reltyp:byte;relocval:aint;objsym:TObjSymbol);
pic_stub_syms: TFPObjectList;
pic_stubs: THashSet;
nullstub: TObjSymbol;
stubcount: longint;
trampolinesection: TObjSection;
procedure MaybeWriteGOTEntry(relocval:aint;objsym:TObjSymbol);
procedure CreatePICStub(objsym:TObjSymbol);
protected
procedure PrepareGOT;override;
function AllocGOTSlot(objsym:TObjSymbol):boolean;override;
@ -163,6 +169,10 @@ implementation
ri_gp_value: longint; // signed
end;
TStubHashKey=record
objsec:TObjSection;
offset:aword;
end;
procedure MaybeSwapElfReginfo(var h:TElfReginfo);
var
@ -234,6 +244,8 @@ implementation
begin
inherited Create;
local_got_relocs:=TFPObjectList.Create(False);
pic_stub_syms:=TFPObjectList.Create(False);
pic_stubs:=THashSet.Create(64,True,False);
local_got_slots:=TFPHashObjectList.Create(True);
end;
@ -241,6 +253,8 @@ implementation
destructor TElfExeOutputMIPS.Destroy;
begin
local_got_slots.Free;
pic_stub_syms.Free;
pic_stubs.Free;
local_got_relocs.Free;
inherited Destroy;
end;
@ -248,6 +262,10 @@ implementation
procedure TElfExeOutputMIPS.CreateGOTSection;
begin
nullstub:=TObjSymbol.Create(internalobjdata.ObjSymbolList,'*null_pic_stub*');
nullstub.bind:=AB_LOCAL;
nullstub.typ:=AT_FUNCTION;
gotobjsec:=TElfObjSection.create_ext(internalObjData,'.got',
SHT_PROGBITS,SHF_ALLOC or SHF_WRITE or SHF_MIPS_GPREL,sizeof(pint),sizeof(pint));
gotobjsec.SecOptions:=[oso_keep];
@ -355,11 +373,33 @@ implementation
var
i: longint;
exesym: TExeSymbol;
exesec: TExeSection;
newsec,objsec:TObjSection;
list:TFPObjectList;
begin
inherited PrepareGOT;
{ !! maybe incorrect, where do 'unmapped globals' belong? }
dt_local_gotno_value:=gotobjsec.size div sizeof(pint);
{ Insert PIC stubs (slow...) }
if assigned(trampolinesection) then
begin
exesec:=FindExeSection('.text');
exesec.ObjSectionList.Add(trampolinesection);
trampolinesection.ExeSection:=exesec;
trampolinesection.Used:=true;
end;
for i:=0 to pic_stub_syms.count-1 do
begin
exesym:=TExeSymbol(pic_stub_syms[i]);
newsec:=exesym.stubsymbol.objsection;
objsec:=exesym.objsymbol.objsection;
list:=objsec.ExeSection.ObjSectionList;
list.insert(list.IndexOf(objsec),newsec);
newsec.ExeSection:=objsec.ExeSection;
newsec.Used:=true;
end;
if not dynamiclink then
exit;
{ make room for first R_MIPS_NONE entry }
@ -496,7 +536,7 @@ implementation
gotobjsec.writeZeros(gotsize-gotobjsec.size);
end;
procedure TElfExeOutputMIPS.MaybeWriteGOTEntry(reltyp:byte;relocval:aint;objsym:TObjSymbol);
procedure TElfExeOutputMIPS.MaybeWriteGOTEntry(relocval:aint;objsym:TObjSymbol);
var
gotoff:aword;
begin
@ -515,6 +555,46 @@ implementation
end;
end;
procedure TElfExeOutputMIPS.CreatePICStub(objsym:TObjSymbol);
var
textsec,newsec:TObjSection;
newsym:TObjSymbol;
use_trampoline:boolean;
begin
textsec:=objsym.objsection;
use_trampoline:=(objsym.offset>0) or (textsec.SecAlign>16);
if use_trampoline then
begin
if trampolinesection=nil then
trampolinesection:=internalObjData.createsection(sec_code);
newsec:=trampolinesection;
end
else
begin
inc(stubcount);
newsec:=TElfObjSection.create_ext(internalObjData,'.text.stub.'+tostr(stubcount),
SHT_PROGBITS,SHF_ALLOC or SHF_EXECINSTR,0,textsec.SecAlign);
if (newsec.SecAlign>8) then
newsec.WriteZeros(newsec.SecAlign-8);
pic_stub_syms.add(objsym.ExeSymbol);
end;
{ symbol for the stub }
internalObjData.SetSection(newsec);
newsym:=internalObjData.symboldefine('.pic.'+objsym.name,AB_LOCAL,AT_FUNCTION);
putword(newsec,$3c190000); // lui $t9,%hi(x)
newsec.addrawreloc(newsec.size-4,objsym,R_MIPS_HI16);
if use_trampoline then
begin
putword(newsec,$08000000); // j x
newsec.addrawreloc(newsec.size-4,objsym,R_MIPS_26);
end;
putword(newsec,$27390000); // addiu $t9,$t9,%lo(x)
newsec.addrawreloc(newsec.size-4,objsym,R_MIPS_LO16);
objsym.exesymbol.stubsymbol:=newsym;
end;
procedure TElfExeOutputMIPS.GOTRelocPass1(objsec:TObjSection;var idx:longint);
var
objreloc:TObjRelocation;
@ -524,6 +604,12 @@ implementation
found:boolean;
i:longint;
lopart,hipart:longword;
objdata:TElfObjData;
exesym:TExeSymbol;
targetsec:TObjSection;
tmp:array[0..2] of longword;
key:TStubHashKey;
entry:PHashSetItem;
begin
objreloc:=TObjRelocation(objsec.ObjRelocations[idx]);
if (ObjReloc.flags and rf_raw)=0 then
@ -547,6 +633,59 @@ implementation
end
end;
R_MIPS_PC16,
R_MIPS_26:
begin
{ Absolute calls into PIC code must go through stubs that load R25 }
exesym:=objreloc.symbol.exesymbol;
if (exesym=nil) or (exesym.dynindex<>0) then
exit;
{ Stub already created? Redirect to it and be done. }
if assigned(exesym.stubsymbol) then
begin
if (exesym.stubsymbol<>nullstub) then
begin
objreloc.symbol.offset:=exesym.stubsymbol.offset;
objreloc.symbol.objsection:=exesym.stubsymbol.objsection;
end;
exit;
end;
targetsec:=exesym.ObjSymbol.objsection;
objdata:=TElfObjData(targetsec.ObjData);
if (objdata.flags and EF_MIPS_PIC)=0 then
exit;
{ Same objdata? then it's responsibility of assembler, not linker }
if (objdata=objsec.objdata) then
exit;
{ Check if destination begins with PIC prologue. If not, mark symbol
with 'null' stub so we don't waste time on subsequent relocs to it. }
targetsec.data.seek(exesym.ObjSymbol.offset);
targetsec.data.read(tmp,3*sizeof(longword));
if (source_info.endian<>target_info.endian) then
for i:=0 to 2 do
tmp[i]:=swapendian(tmp[i]);
if ((tmp[0] and $FFFF0000)<>$3C1C0000) or
((tmp[1] and $FFFF0000)<>$279C0000) or
(tmp[2]<>$0399E021) then
begin
exesym.stubsymbol:=nullstub;
exit;
end;
{ Avoid creating several stubs for an address due to symbol aliasing }
key.objsec:=targetsec;
key.offset:=exesym.ObjSymbol.offset;
entry:=pic_stubs.FindOrAdd(@key,sizeof(TStubHashKey));
if assigned(entry^.Data) then
exesym:=TExeSymbol(entry^.Data)
else
begin
entry^.Data:=exesym;
CreatePICStub(exesym.objsymbol);
end;
objreloc.symbol.offset:=exesym.stubsymbol.offset;
objreloc.symbol.objsection:=exesym.stubsymbol.objsection;
end;
R_MIPS_CALL16,
R_MIPS_GOT16:
begin
@ -768,7 +907,7 @@ implementation
reloclist:=hr;
continue;
end;
MaybeWriteGOTEntry(reltyp,relocval,objreloc.symbol);
MaybeWriteGOTEntry(relocval,objreloc.symbol);
// !! this is correct only while _gp symbol is defined relative to .got !!
relocval:=-(gotsymbol.offset-(objreloc.symbol.exesymbol.gotoffset-sizeof(pint)));
// TODO: check overflow

View File

@ -400,6 +400,13 @@ interface
{ fields for ELF linking }
gotoffset : aword;
dynindex : aword;
{ A thunk used to redirect some references to symbol (like absolute
jumps/calls to PIC code).
This probably is also needed for ARM/Thumb interworking and alike.
TODO: consider reusing objsymbol.indsymbol for this purpose }
{$ifdef mips}
stubsymbol : TObjSymbol;
{$endif mips}
end;
TExeSection = class(TFPHashObject)