From cded05cceffbd438aece0a38843aa15eb03a8a03 Mon Sep 17 00:00:00 2001 From: sergei Date: Sun, 31 Mar 2013 19:58:52 +0000 Subject: [PATCH] + 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 - --- compiler/mips/cpuelf.pas | 145 ++++++++++++++++++++++++++++++++++++++- compiler/ogbase.pas | 7 ++ 2 files changed, 149 insertions(+), 3 deletions(-) diff --git a/compiler/mips/cpuelf.pas b/compiler/mips/cpuelf.pas index ff6e8634a3..8422332192 100644 --- a/compiler/mips/cpuelf.pas +++ b/compiler/mips/cpuelf.pas @@ -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 diff --git a/compiler/ogbase.pas b/compiler/ogbase.pas index 242a884403..78905fc9ad 100644 --- a/compiler/ogbase.pas +++ b/compiler/ogbase.pas @@ -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)