mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-06-03 21:38:32 +02:00
+ 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:
parent
9758f4fa6a
commit
cded05ccef
@ -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
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user