+ MIPS: doing progress with linker, implemented processing of local symbols in PIC code and stuff needed to link shared libraries.

git-svn-id: trunk@23690 -
This commit is contained in:
sergei 2013-03-04 20:48:33 +00:00
parent 268b2d56d7
commit 6fcd9979dd
2 changed files with 242 additions and 35 deletions

View File

@ -39,6 +39,10 @@ implementation
gnugpsym: TObjSymbol;
dt_gotsym_value: longint;
dt_local_gotno_value: longint;
dt_local_gotno_offset: aword;
local_got_relocs: TFPObjectList;
local_got_slots: TFPHashObjectList;
got_content: array of pint;
procedure MaybeWriteGOTEntry(reltyp:byte;relocval:aint;objsym:TObjSymbol);
protected
procedure PrepareGOT;override;
@ -51,8 +55,11 @@ implementation
// procedure WriteIndirectPLTEntry(exesym:TExeSymbol);override;
procedure GOTRelocPass1(objsec:TObjSection;var idx:longint);override;
procedure DoRelocationFixup(objsec:TObjSection);override;
procedure Do_Mempos;override;
public
procedure DataPos_Start;override;
constructor Create;override;
destructor Destroy;override;
procedure FixupRelocations;override;
end;
const
@ -176,6 +183,22 @@ implementation
TElfExeOutputMIPS
*****************************************************************************}
constructor TElfExeOutputMIPS.Create;
begin
inherited Create;
local_got_relocs:=TFPObjectList.Create(False);
local_got_slots:=TFPHashObjectList.Create(True);
end;
destructor TElfExeOutputMIPS.Destroy;
begin
local_got_slots.Free;
local_got_relocs.Free;
inherited Destroy;
end;
procedure TElfExeOutputMIPS.CreateGOTSection;
var
tmp: longword;
@ -222,6 +245,7 @@ implementation
writeDynTag(DT_MIPS_BASE_ADDRESS,0)
else
writeDynTag(DT_MIPS_BASE_ADDRESS,ElfTarget.exe_image_base);
dt_local_gotno_offset:=dynamicsec.size;
writeDynTag(DT_MIPS_LOCAL_GOTNO,dt_local_gotno_value);
writeDynTag(DT_MIPS_SYMTABNO,dynsymlist.count+1);
{ ABI says: "Index of first external dynamic symbol not referenced locally" }
@ -248,16 +272,8 @@ implementation
result:=false;
exesym:=objsym.exesymbol;
{ Although local symbols should not be accessed through GOT,
this isn't strictly forbidden. In this case we need to fake up
the exesym to store the GOT offset in it.
TODO: name collision; maybe use a different symbol list object? }
if exesym=nil then
begin
exesym:=TExeSymbol.Create(ExeSymbolList,objsym.name+'*local*');
exesym.objsymbol:=objsym;
objsym.exesymbol:=exesym;
end;
if (exesym=nil) then
InternalError(2013030406);
if exesym.GotOffset>0 then
exit;
make_dynamic_if_undefweak(exesym);
@ -284,14 +300,32 @@ implementation
end;
function address_ascending(p1,p2:pointer):longint;
var
reloc1: TObjRelocation absolute p1;
reloc2: TObjRelocation absolute p2;
begin
result:=(reloc1.symbol.address+reloc1.orgsize)-(reloc2.symbol.address+reloc2.orgsize);
end;
procedure TElfExeOutputMIPS.PrepareGOT;
var
i: longint;
exesym: TExeSymbol;
begin
inherited PrepareGOT;
{ !! maybe incorrect, where do 'unmapped globals' belong? }
dt_local_gotno_value:=gotobjsec.size div sizeof(pint);
if not dynamiclink then
exit;
{ make room for first R_MIPS_NONE entry }
if dynrelsize>0 then
begin
dynrelocsec.alloc(dynrelocsec.shentsize);
inc(dynrelsize,dynrelocsec.shentsize);
end;
dynsymlist.sort(@put_externals_last);
{ reindex, as sorting could changed the order }
for i:=0 to dynsymlist.count-1 do
@ -306,8 +340,6 @@ implementation
break;
end;
end;
{ !! maybe incorrect, where do 'unmapped globals' belong? }
dt_local_gotno_value:=gotobjsec.size div sizeof(pint);
{ actually allocate GOT slots for imported symbols }
for i:=dt_gotsym_value to dynsymlist.count-1 do
@ -320,25 +352,119 @@ implementation
end;
procedure TElfExeOutputMIPS.DataPos_Start;
procedure TElfExeOutputMIPS.Do_Mempos;
var
i:longint;
objrel:TObjRelocation;
addr,page:aword;
numpages,tmp:longint;
objsym:TObjSymbol;
exesym:TExeSymbol;
got_local_area_start:aword;
begin
inherited Do_Mempos;
{ determine required amount of 64k page entries }
local_got_relocs.Sort(@address_ascending);
numpages:=0;
page:=high(aword);
for i:=0 to local_got_relocs.count-1 do
begin
objrel:=TObjRelocation(local_got_relocs[i]);
addr:=objrel.symbol.address+objrel.orgsize;
addr:=addr-smallint(addr);
if (page<>addr shr 16) then
inc(numpages);
page:=addr shr 16;
end;
if (numpages=0) then
exit;
{ An additional page may be consumed when we add slots to GOT }
inc(numpages);
{ Make space in GOT }
got_local_area_start:=dt_local_gotno_value;
inc(gotsize,numpages*sizeof(pint));
gotobjsec.alloc(numpages*sizeof(pint));
{ Redo layout }
inherited Do_Mempos;
{ Now assign GOT offsets to local slots }
SetLength(got_content,numpages);
page:=high(aword);
tmp:=-1;
objsym:=nil;
for i:=0 to local_got_relocs.count-1 do
begin
objrel:=TObjRelocation(local_got_relocs[i]);
addr:=objrel.symbol.address+objrel.orgsize;
{ the contents of slot }
addr:=addr-smallint(addr);
if (page<>addr) then
begin
Inc(tmp);
if (tmp>=numpages) then
InternalError(2013030402);
{ replace relocation symbol with one pointing to GOT slot }
objsym:=TObjSymbol.Create(local_got_slots,hexstr(addr,8));
objsym.offset:=(got_local_area_start+tmp+1)*sizeof(pint);
objsym.bind:=AB_LOCAL;
if (source_info.endian=target_info.endian) then
got_content[tmp]:=addr
else
got_content[tmp]:=swapendian(addr);
page:=addr;
end;
objrel.symbol:=objsym;
end;
if dynamiclink then
begin
{ Patch DT_LOCAL_GOTNO value }
if (dt_local_gotno_offset=0) then
InternalError(2013030401);
i:=dynamicsec.size;
dynamicsec.Data.Seek(dt_local_gotno_offset);
writeDynTag(DT_MIPS_LOCAL_GOTNO,dt_local_gotno_value+numpages);
dynamicsec.size:=i;
{ Increase gotoffset of exesymbols that come after dt_gotsym }
for i:=dt_gotsym_value to dynsymlist.count-1 do
begin
exesym:=TExeSymbol(dynsymlist[i]);
exesym.GotOffset:=exesym.GotOffset+(numpages*sizeof(pint));
end;
end;
end;
procedure TElfExeOutputMIPS.FixupRelocations;
begin
if dynrelsize>0 then
WriteDynRelocEntry(0,R_MIPS_NONE,0,0);
inherited FixupRelocations;
{ Since we omit GOT slots for imported symbols during inherited PrepareGOT, they don't
get written in ResolveRelocations either. This must be compensated here.
Or better override ResolveRelocations and handle there. }
get written in FixupRelocations either. This must be compensated here. }
gotobjsec.write(got_content[0],length(got_content)*sizeof(pint));
{ TODO: shouldn't be zeroes, but address of stubs if address taken, etc. }
gotobjsec.writeZeros(gotsize-gotobjsec.size);
inherited DataPos_Start;
end;
procedure TElfExeOutputMIPS.MaybeWriteGOTEntry(reltyp:byte;relocval:aint;objsym:TObjSymbol);
var
gotoff:aword;
begin
if (objsym.bind=AB_LOCAL) then
InternalError(2013030403);
gotoff:=objsym.exesymbol.gotoffset;
if gotoff=0 then
InternalError(2012060902);
{ the GOT slot itself, and a dynamic relocation for it }
{ On MIPS, GOT does not need dynamic relocations }
if gotoff=gotobjsec.Data.size+sizeof(pint) then
begin
if source_info.endian<>target_info.endian then
@ -350,7 +476,12 @@ implementation
procedure TElfExeOutputMIPS.GOTRelocPass1(objsec:TObjSection;var idx:longint);
var
objreloc:TObjRelocation;
lowreloc:TObjRelocation;
reltyp:byte;
externsym:boolean;
found:boolean;
i:longint;
lopart,hipart:longword;
begin
objreloc:=TObjRelocation(objsec.ObjRelocations[idx]);
if (ObjReloc.flags and rf_raw)=0 then
@ -359,11 +490,56 @@ implementation
reltyp:=ObjReloc.ftype;
case reltyp of
R_MIPS_32:
begin
externsym:=assigned(objreloc.symbol) and
assigned(objreloc.symbol.exesymbol) and
(objreloc.symbol.exesymbol.dynindex<>0);
if IsSharedLibrary then
begin
dynrelocsec.alloc(dynrelocsec.shentsize);
objreloc.flags:=objreloc.flags or rf_dynamic;
if (not externsym) then
Inc(relative_reloc_count);
end
end;
R_MIPS_CALL16,
R_MIPS_GOT16:
begin
//TODO: GOT16 against local symbols need specialized handling
AllocGOTSlot(objreloc.symbol);
if objreloc.symbol.bind<>AB_LOCAL then
AllocGOTSlot(objreloc.symbol)
else
begin
{ Extract the addend, which is stored split between this relocation and
the following (maybe not immediately) R_MIPS_LO16 one. }
found:=false;
for i:=idx+1 to objsec.ObjRelocations.Count-1 do
begin
lowreloc:=TObjRelocation(objsec.ObjRelocations[i]);
if (lowreloc.flags and rf_raw)=0 then
InternalError(2013030101);
if (lowreloc.ftype=R_MIPS_LO16) then
begin;
found:=true;
break;
end;
end;
if not found then
InternalError(2013030102);
objsec.Data.Seek(objreloc.DataOffset);
objsec.Data.Read(hipart,sizeof(hipart));
objsec.Data.Seek(lowreloc.DataOffset);
objsec.Data.Read(lopart,sizeof(lopart));
if (source_info.endian<>target_info.endian) then
begin
hipart:=swapendian(hipart);
lopart:=swapendian(lopart);
end;
objreloc.orgsize:=(hipart shl 16)+SmallInt(lopart);
local_got_relocs.add(objreloc);
end;
end;
end;
end;
@ -450,19 +626,17 @@ implementation
R_MIPS_32:
begin
address:=address+relocval;
if (objreloc.flags and rf_dynamic)<>0 then
begin
if (objreloc.symbol=nil) or
(objreloc.symbol.exesymbol=nil) or
(objreloc.symbol.exesymbol.dynindex=0) then
begin
end
WriteDynRelocEntry(curloc,R_MIPS_REL32,0,address)
else
;
end
else
address:=address+relocval;
end;
dynreloclist.add(TObjRelocation.CreateRaw(curloc,objreloc.symbol,R_MIPS_REL32));
end;
end;
R_MIPS_26:
begin
@ -486,6 +660,13 @@ implementation
R_MIPS_LO16:
begin
{ LO16 may be without pair, e.g. in following sequence:
lui $v0, %hi(foo)
lw $a0, %lo(foo)($v0)
lw $a1, %lo(foo+4)($v0)
}
AHL_S:=SmallInt(address)+relocval;
is_gp_disp:=false;
while assigned(reloclist) do
begin
hr:=reloclist;
@ -494,33 +675,57 @@ implementation
// InternalError();
{ _gp_disp and __gnu_local_gp magic }
if assigned(hr^.objrel.symbol) and
assigned(hr^.objrel.symbol.exesymbol) then
(hr^.objrel.symbol.bind<>AB_LOCAL) then
begin
is_gp_disp:=(hr^.objrel.symbol.exesymbol.objsymbol=gpdispsym);
if (hr^.objrel.symbol.exesymbol.objsymbol=gnugpsym) then
relocval:=gotsymbol.address;
end;
{ in case of _gp_disp, non-zero addend is not possible? }
{ 4 must be added right here, so possible overflow in low half
is propagated into high one (e.g if displacement is $37ffc,
high part must be 4, not 3) }
if is_gp_disp then
relocval:=gotsymbol.address-curloc;
relocval:=gotsymbol.address-curloc+4;
AHL_S:=(hr^.addend shl 16)+SmallInt(address)+relocval;
{ formula: ((AHL + S) (short)(AHL + S)) >> 16 }
tmp:=(hr^.addend and $FFFF0000) or ((AHL_S-SmallInt(AHL_S)) shr 16);
case hr^.objrel.ftype of
R_MIPS_HI16:
tmp:=(AHL_S-SmallInt(AHL_S)) shr 16;
R_MIPS_GOT16:
tmp:=-(gotsymbol.offset-(hr^.objrel.symbol.offset-sizeof(pint)));
else
InternalError(2013030404);
end;
tmp:=(hr^.addend and $FFFF0000) or (tmp and $FFFF);
data.seek(hr^.objrel.dataoffset);
if source_info.endian<>target_info.endian then
tmp:=swapendian(tmp);
data.Write(tmp,4);
dispose(hr);
end;
if is_gp_disp then
Inc(AHL_S,4);
address:=(address and $FFFF0000) or (AHL_S and $FFFF);
end;
R_MIPS_CALL16,
R_MIPS_GOT16:
begin
//TODO: GOT16 relocations against local symbols need specialized handling
{ GOT16 relocations against local symbols are followed by LO16 }
if (objreloc.symbol.bind=AB_LOCAL) then
begin
new(hr);
hr^.next:=reloclist;
hr^.objrel:=objreloc;
hr^.objsec:=objsec;
hr^.addend:=address; //TODO: maybe it can be saved in objrel.orgsize field
reloclist:=hr;
continue;
end;
MaybeWriteGOTEntry(reltyp,relocval,objreloc.symbol);
// !! this is correct only while _gp symbol is defined relative to .got !!
relocval:=-(gotsymbol.offset-(objreloc.symbol.exesymbol.gotoffset-sizeof(pint)));

View File

@ -237,7 +237,6 @@ interface
symversec,
verdefsec,
verneedsec,
dynamicsec,
hashobjsec: TElfObjSection;
neededlist: TFPHashList;
dyncopysyms: TFPObjectList;
@ -262,6 +261,7 @@ interface
hastextrelocs: boolean;
gotsymbol: TObjSymbol;
dynsymlist: TFPObjectList;
dynamicsec,
gotobjsec: TObjSection;
dynbssobjsec,
pltobjsec,
@ -3021,8 +3021,10 @@ implementation
writeDynTag(pltreltags[rela],dynrelocsec);
writeDynTag(relsztags[rela],dynrelocsec.Size);
writeDynTag(relenttags[rela],dynrelocsec.shentsize);
{$ifndef MIPS}
if (relative_reloc_count>0) then
writeDynTag(relcnttags[rela],relative_reloc_count);
{$endif MIPS}
end;
WriteTargetDynamicTags;