mirror of
				https://gitlab.com/freepascal.org/fpc/source.git
				synced 2025-10-31 20:31:51 +01:00 
			
		
		
		
	 578348817b
			
		
	
	
		578348817b
		
	
	
	
	
		
			
			* Fixed values of R_MIPS_GOT_HI16/LO16. + Implemented elf_mips_relocname + Check R_MIPS_PC16 relocations for overflows + Handle R_MIPS_JALR optimization hint. git-svn-id: trunk@33240 -
		
			
				
	
	
		
			1114 lines
		
	
	
		
			36 KiB
		
	
	
	
		
			ObjectPascal
		
	
	
	
	
	
			
		
		
	
	
			1114 lines
		
	
	
		
			36 KiB
		
	
	
	
		
			ObjectPascal
		
	
	
	
	
	
| {
 | |
|     Copyright (c) 2012 by Sergei Gorelkin
 | |
| 
 | |
|     Includes ELF-related code specific to MIPS
 | |
| 
 | |
|     This program is free software; you can redistribute it and/or modify
 | |
|     it under the terms of the GNU General Public License as published by
 | |
|     the Free Software Foundation; either version 2 of the License, or
 | |
|     (at your option) any later version.
 | |
| 
 | |
|     This program is distributed in the hope that it will be useful,
 | |
|     but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | |
|     GNU General Public License for more details.
 | |
| 
 | |
|     You should have received a copy of the GNU General Public License
 | |
|     along with this program; if not, write to the Free Software
 | |
|     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 | |
| 
 | |
|  ****************************************************************************
 | |
| }
 | |
| unit cpuelf;
 | |
| 
 | |
| interface
 | |
| 
 | |
| {$i fpcdefs.inc}
 | |
| 
 | |
| implementation
 | |
| 
 | |
|   uses
 | |
|     globtype,sysutils,cutils,cclasses,
 | |
|     verbose, elfbase,
 | |
|     systems,aasmbase,ogbase,ogelf,assemble;
 | |
| 
 | |
|   type
 | |
|     TElfExeOutputMIPS=class(TElfExeOutput)
 | |
|     private
 | |
|       gpdispsym: TObjSymbol;
 | |
|       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;
 | |
|       pic_stub_syms: TFPObjectList;
 | |
|       pic_stubs: THashSet;
 | |
|       nullstub: TObjSymbol;
 | |
|       stubcount: longint;
 | |
|       trampolinesection: TObjSection;
 | |
|       procedure MaybeWriteGOTEntry(relocval:aint;objsym:TObjSymbol);
 | |
|       procedure MaybeWriteTLSIEGotEntry(relocval:aint;objsym:TObjSymbol);
 | |
|       procedure CreatePICStub(objsym:TObjSymbol);
 | |
|     protected
 | |
|       procedure PrepareGOT;override;
 | |
|       function AllocGOTSlot(objsym:TObjSymbol):boolean;override;
 | |
|       procedure CreateGOTSection;override;
 | |
|       procedure CreatePLT;override;
 | |
|       procedure WriteTargetDynamicTags;override;
 | |
|     //  procedure WriteFirstPLTEntry;override;
 | |
|       procedure WritePLTEntry(exesym:TExeSymbol);override;
 | |
|     //  procedure WriteIndirectPLTEntry(exesym:TExeSymbol);override;
 | |
|       procedure GOTRelocPass1(objsec:TObjSection;var idx:longint);override;
 | |
|       procedure DoRelocationFixup(objsec:TObjSection);override;
 | |
|       procedure Do_Mempos;override;
 | |
|     public
 | |
|       constructor Create;override;
 | |
|       destructor Destroy;override;
 | |
|       procedure FixupRelocations;override;
 | |
|     end;
 | |
| 
 | |
|   const
 | |
|     { ELF header e_flags }
 | |
|     EF_MIPS_NOREORDER = 1;
 | |
|     EF_MIPS_PIC       = 2;
 | |
|     EF_MIPS_CPIC      = 4;
 | |
|     EF_MIPS_ABI       = $0000F000;
 | |
|       E_MIPS_ABI_O32    = $00001000;
 | |
|       E_MIPS_ABI_O64    = $00002000;
 | |
|       E_MIPS_ABI_EABI32 = $00003000;
 | |
|       E_MIPS_ABI_EABI64 = $00004000;
 | |
|     EF_MIPS_ARCH      = $F0000000;
 | |
|       E_MIPS_ARCH_1   = $00000000;    // -mips1
 | |
|       E_MIPS_ARCH_2   = $10000000;
 | |
|       E_MIPS_ARCH_3   = $20000000;
 | |
|       E_MIPS_ARCH_4   = $30000000;
 | |
|       E_MIPS_ARCH_5   = $40000000;
 | |
|       E_MIPS_ARCH_32  = $50000000;    // -mips32
 | |
|       E_MIPS_ARCH_64  = $60000000;
 | |
|       E_MIPS_ARCH_32R2= $70000000;    // -mips32r2
 | |
|       E_MIPS_ARCH_64R2= $80000000;
 | |
| 
 | |
| 
 | |
|     { section types }
 | |
|     SHT_MIPS_LIBLIST  = $70000000;
 | |
|     SHT_MIPS_CONFLICT = $70000002;
 | |
|     SHT_MIPS_GPTAB    = $70000003;
 | |
|     SHT_MIPS_UCODE    = $70000004;
 | |
|     SHT_MIPS_DEBUG    = $70000005;
 | |
|     SHT_MIPS_REGINFO  = $70000006;
 | |
|     SHT_MIPS_DWARF    = $7000001e;
 | |
| 
 | |
|     { section flags }
 | |
|     SHF_MIPS_GPREL    = $10000000;
 | |
| 
 | |
|     { relocations }
 | |
|     R_MIPS_NONE  = 0;
 | |
|     R_MIPS_16    = 1;
 | |
|     R_MIPS_32    = 2;
 | |
|     R_MIPS_REL32 = 3;
 | |
|     R_MIPS_26    = 4;
 | |
|     R_MIPS_HI16  = 5;
 | |
|     R_MIPS_LO16  = 6;
 | |
|     R_MIPS_GPREL16 = 7;
 | |
|     R_MIPS_LITERAL = 8;
 | |
|     R_MIPS_GOT16   = 9;
 | |
|     R_MIPS_PC16    = 10;
 | |
|     R_MIPS_CALL16  = 11;
 | |
|     R_MIPS_GPREL32 = 12;
 | |
|     R_MIPS_GOT_HI16 = 22;
 | |
|     R_MIPS_GOT_LO16 = 23;
 | |
|     R_MIPS_CALL_HI16 = 30;
 | |
|     R_MIPS_CALL_LO16 = 31;
 | |
|     R_MIPS_JALR    = 37;
 | |
| 
 | |
|     R_MIPS_TLS_DTPMOD32 = 38;
 | |
|     R_MIPS_TLS_DTPREL32 = 39;
 | |
|     R_MIPS_TLS_DTPMOD64 = 40;
 | |
|     R_MIPS_TLS_DTPREL64 = 41;
 | |
|     R_MIPS_TLS_GD = 42;
 | |
|     R_MIPS_TLS_LDM = 43;
 | |
|     R_MIPS_TLS_DTPREL_HI16 = 44;
 | |
|     R_MIPS_TLS_DTPREL_LO16 = 45;
 | |
|     R_MIPS_TLS_GOTTPREL = 46;
 | |
|     R_MIPS_TLS_TPREL32 = 47;
 | |
|     R_MIPS_TLS_TPREL64 = 48;
 | |
|     R_MIPS_TLS_TPREL_HI16 = 49;
 | |
|     R_MIPS_TLS_TPREL_LO16 = 50;
 | |
| 
 | |
|     { dynamic tags }
 | |
|     DT_MIPS_RLD_VERSION  = $70000001;
 | |
|     DT_MIPS_TIME_STAMP   = $70000002;
 | |
|     DT_MIPS_ICHECKSUM    = $70000003;
 | |
|     DT_MIPS_IVERSION     = $70000004;
 | |
|     DT_MIPS_FLAGS        = $70000005;
 | |
|     DT_MIPS_BASE_ADDRESS = $70000006;
 | |
|     DT_MIPS_CONFLICT     = $70000008;
 | |
|     DT_MIPS_LIBLIST      = $70000009;
 | |
|     DT_MIPS_LOCAL_GOTNO  = $7000000A;
 | |
|     DT_MIPS_CONFLICTNO   = $7000000B;
 | |
|     DT_MIPS_LIBLISTNO    = $70000010;
 | |
|     DT_MIPS_SYMTABNO     = $70000011;
 | |
|     DT_MIPS_UNREFEXTNO   = $70000012;
 | |
|     DT_MIPS_GOTSYM       = $70000013;
 | |
|     DT_MIPS_HIPAGENO     = $70000014;
 | |
|     DT_MIPS_RLD_MAP      = $70000016;
 | |
| 
 | |
|     { values of DT_MIPS_FLAGS }
 | |
|     RHF_QUICKSTART = 1;
 | |
|     RHF_NOTPOT     = 2;
 | |
| 
 | |
|     { TLS layout }
 | |
|     TP_OFFSET = $7000;
 | |
|     DTP_OFFSET = $8000;
 | |
| 
 | |
|   type
 | |
|     TElfReginfo=record
 | |
|       ri_gprmask: longword;
 | |
|       ri_cprmask: array[0..3] of longword;
 | |
|       ri_gp_value: longint;     // signed
 | |
|     end;
 | |
| 
 | |
|     TStubHashKey=record
 | |
|       objsec:TObjSection;
 | |
|       offset:aword;
 | |
|     end;
 | |
| 
 | |
| 
 | |
|   const
 | |
|     relocnames: array[0..50] of PChar = (
 | |
|       'R_MIPS_NONE',
 | |
|       'R_MIPS_16',
 | |
|       'R_MIPS_32',
 | |
|       'R_MIPS_REL32',
 | |
|       'R_MIPS_26',
 | |
|       'R_MIPS_HI16',
 | |
|       'R_MIPS_LO16',
 | |
|       'R_MIPS_GPREL16',
 | |
|       'R_MIPS_LITERAL',
 | |
|       'R_MIPS_GOT16',
 | |
|       'R_MIPS_PC16',
 | |
|       'R_MIPS_CALL16',
 | |
|       'R_MIPS_GPREL32',
 | |
|       nil,  {13}
 | |
|       nil,  {14}
 | |
|       nil,  {15}
 | |
|       nil,  {16}
 | |
|       nil,  {17}
 | |
|       nil,  {18}
 | |
|       nil,  {19}
 | |
|       nil,  {20}
 | |
|       nil,  {21}
 | |
|       'R_MIPS_GOT_HI16',
 | |
|       'R_MIPS_GOT_LO16',
 | |
|       nil,  {24}
 | |
|       nil,  {25}
 | |
|       nil,  {26}
 | |
|       nil,  {27}
 | |
|       nil,  {28}
 | |
|       nil,  {29}
 | |
|       'R_MIPS_CALL_HI16',
 | |
|       'R_MIPS_CALL_LO16',
 | |
|       nil,  {32}
 | |
|       nil,  {33}
 | |
|       nil,  {34}
 | |
|       nil,  {35}
 | |
|       nil,  {36}
 | |
|       'R_MIPS_JALR',
 | |
|       'R_MIPS_TLS_DTPMOD32',
 | |
|       'R_MIPS_TLS_DTPREL32',
 | |
|       'R_MIPS_TLS_DTPMOD64',
 | |
|       'R_MIPS_TLS_DTPREL64',
 | |
|       'R_MIPS_TLS_GD',
 | |
|       'R_MIPS_TLS_LDM',
 | |
|       'R_MIPS_TLS_DTPREL_HI16',
 | |
|       'R_MIPS_TLS_DTPREL_LO16',
 | |
|       'R_MIPS_TLS_GOTTPREL',
 | |
|       'R_MIPS_TLS_TPREL32',
 | |
|       'R_MIPS_TLS_TPREL64',
 | |
|       'R_MIPS_TLS_TPREL_HI16',
 | |
|       'R_MIPS_TLS_TPREL_LO16'
 | |
|     );
 | |
| 
 | |
|   procedure MaybeSwapElfReginfo(var h:TElfReginfo);
 | |
|     var
 | |
|       i: longint;
 | |
|     begin
 | |
|       if source_info.endian<>target_info.endian then
 | |
|         begin
 | |
|           h.ri_gprmask:=swapendian(h.ri_gprmask);
 | |
|           for i:=0 to 3 do
 | |
|             h.ri_cprmask[i]:=swapendian(h.ri_cprmask[i]);
 | |
|           h.ri_gp_value:=swapendian(h.ri_gp_value);
 | |
|         end;
 | |
|     end;
 | |
| 
 | |
| 
 | |
|   procedure putword(sec:TObjSection;d:longword);
 | |
|     begin
 | |
|       if source_info.endian<>target_info.endian then
 | |
|         d:=swapendian(d);
 | |
|       sec.write(d,4);
 | |
|     end;
 | |
| 
 | |
| {****************************************************************************
 | |
|                               ELF Target methods
 | |
| ****************************************************************************}
 | |
| 
 | |
|   function elf_mips_encodereloc(objrel:TObjRelocation):byte;
 | |
|     begin
 | |
|       case objrel.typ of
 | |
|         RELOC_NONE:
 | |
|           result:=R_MIPS_NONE;
 | |
|         RELOC_ABSOLUTE:
 | |
|           result:=R_MIPS_32;
 | |
|         RELOC_GOTOFF:               {For case jumptables only }
 | |
|           result:=R_MIPS_GPREL32;
 | |
|       else
 | |
|         result:=0;
 | |
|         InternalError(2012110602);
 | |
|       end;
 | |
|     end;
 | |
| 
 | |
| 
 | |
|   function elf_mips_relocname(reltyp:byte):string;
 | |
|     begin
 | |
|       if (reltyp<=high(relocnames)) and
 | |
|         (relocnames[reltyp]<>nil) then
 | |
|         result:=relocnames[reltyp]
 | |
|       else
 | |
|         result:='unknown ('+tostr(reltyp)+')';
 | |
|     end;
 | |
| 
 | |
| 
 | |
|   procedure elf_mips_loadreloc(objrel:TObjRelocation);
 | |
|     begin
 | |
|     end;
 | |
| 
 | |
| 
 | |
|   function elf_mips_loadsection(objinput:TElfObjInput;objdata:TObjData;const shdr:TElfsechdr;shindex:longint):boolean;
 | |
|     var
 | |
|       ri: TElfReginfo;
 | |
|     begin
 | |
|       case shdr.sh_type of
 | |
|         SHT_MIPS_REGINFO:
 | |
|           begin
 | |
|             objinput.ReadBytes(shdr.sh_offset,ri,sizeof(ri));
 | |
|             MaybeSwapElfReginfo(ri);
 | |
|             TElfObjData(objdata).gp_value:=ri.ri_gp_value;
 | |
|             result:=true;
 | |
|           end;
 | |
|         SHT_MIPS_DWARF:
 | |
|           result:=true;
 | |
|       else
 | |
|         writeln('elf_mips_loadsection: ',hexstr(shdr.sh_type,8),' ',objdata.name);
 | |
|         result:=false;
 | |
|       end;
 | |
|     end;
 | |
| 
 | |
| 
 | |
| {*****************************************************************************
 | |
|                                  TElfExeOutputMIPS
 | |
| *****************************************************************************}
 | |
| 
 | |
|   constructor TElfExeOutputMIPS.Create;
 | |
|     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;
 | |
| 
 | |
| 
 | |
|   destructor TElfExeOutputMIPS.Destroy;
 | |
|     begin
 | |
|       local_got_slots.Free;
 | |
|       pic_stub_syms.Free;
 | |
|       pic_stubs.Free;
 | |
|       local_got_relocs.Free;
 | |
|       inherited Destroy;
 | |
|     end;
 | |
| 
 | |
| 
 | |
|   procedure TElfExeOutputMIPS.CreateGOTSection;
 | |
|     begin
 | |
|       nullstub:=internalobjdata.CObjSymbol.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];
 | |
|       { gotpltobjsec is what's pointed to by DT_PLTGOT }
 | |
|       { TODO: this is not correct; under some circumstances ld can generate PLTs for MIPS,
 | |
|         using classic model. We'll need to support it, too. }
 | |
|       gotpltobjsec:=TElfObjSection(gotobjsec);
 | |
| 
 | |
|       internalObjData.SetSection(gotobjsec);
 | |
|       { TODO: must be an absolute symbol; binutils use linker script to define it  }
 | |
|       gotsymbol:=internalObjData.SymbolDefine('_gp',AB_GLOBAL,AT_NONE);
 | |
|       gotsymbol.offset:=$7ff0;
 | |
|       { also define _gp_disp and __gnu_local_gp }
 | |
|       gpdispsym:=internalObjData.SymbolDefine('_gp_disp',AB_GLOBAL,AT_NONE);
 | |
|       gnugpsym:=internalObjData.SymbolDefine('__gnu_local_gp',AB_GLOBAL,AT_NONE);
 | |
|       { reserved entries }
 | |
|       gotobjsec.WriteZeros(sizeof(pint));
 | |
|       putword(gotobjsec,$80000000);
 | |
|     end;
 | |
| 
 | |
| 
 | |
|   procedure TElfExeOutputMIPS.CreatePLT;
 | |
|     begin
 | |
|       pltobjsec:=TElfObjSection.create_ext(internalObjData,'.plt',
 | |
|         SHT_PROGBITS,SHF_ALLOC or SHF_EXECINSTR,4,16);
 | |
|       pltobjsec.SecOptions:=[oso_keep];
 | |
|     end;
 | |
| 
 | |
| 
 | |
|   procedure TElfExeOutputMIPS.WriteTargetDynamicTags;
 | |
|     begin
 | |
|       writeDynTag(DT_MIPS_RLD_VERSION,1);
 | |
|       if not IsSharedLibrary then
 | |
|         {writeDynTag(DT_MIPS_RLD_MAP,rldmapsec)};
 | |
|       writeDynTag(DT_MIPS_FLAGS,RHF_NOTPOT);
 | |
|       if IsSharedLibrary then
 | |
|         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" }
 | |
|       { What the hell is this? BFD writes number of output sections(!!),
 | |
|         the values found in actual files do not match even that,
 | |
|         and don't seem to be connected to reality at all... }
 | |
|       //writeDynTag(DT_MIPS_UNREFEXTNO,0);
 | |
|       {Index of first dynamic symbol in GOT }
 | |
|       writeDynTag(DT_MIPS_GOTSYM,dt_gotsym_value+1);
 | |
|     end;
 | |
| 
 | |
| 
 | |
|   procedure TElfExeOutputMIPS.WritePLTEntry(exesym: TExeSymbol);
 | |
|     begin
 | |
| 
 | |
|     end;
 | |
| 
 | |
| 
 | |
|   function TElfExeOutputMIPS.AllocGOTSlot(objsym:TObjSymbol):boolean;
 | |
|     var
 | |
|       exesym: TExeSymbol;
 | |
|     begin
 | |
|       { MIPS has quite a different way of allocating GOT slots and dynamic relocations }
 | |
|       result:=false;
 | |
|       exesym:=objsym.exesymbol;
 | |
| 
 | |
|       if (exesym=nil) then
 | |
|         InternalError(2013030406);
 | |
|       if exesym.GotOffset>0 then
 | |
|         exit;
 | |
|       make_dynamic_if_undefweak(exesym);
 | |
| 
 | |
|       if (exesym.dynindex>0) and (exesym.ObjSymbol.ObjSection=nil) then
 | |
|         begin
 | |
|           { External symbols must be located at the end of GOT, here just
 | |
|             mark them for dealing later. }
 | |
|           exesym.GotOffset:=high(aword);
 | |
|           exit;
 | |
|         end;
 | |
|       gotobjsec.alloc(sizeof(pint));
 | |
|       exesym.GotOffset:=gotobjsec.size;
 | |
|       result:=true;
 | |
|     end;
 | |
| 
 | |
| 
 | |
|   function put_externals_last(p1,p2:pointer):longint;
 | |
|     var
 | |
|       sym1: TExeSymbol absolute p1;
 | |
|       sym2: TExeSymbol absolute p2;
 | |
|     begin
 | |
|       result:=ord(sym1.gotoffset=high(aword))-ord(sym2.gotoffset=high(aword));
 | |
|     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;
 | |
|       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 }
 | |
|       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
 | |
|         TExeSymbol(dynsymlist[i]).dynindex:=i+1;
 | |
|       { find the symbol to be written as DT_GOTSYM }
 | |
|       for i:=dynsymlist.count-1 downto 0 do
 | |
|         begin
 | |
|           exesym:=TExeSymbol(dynsymlist[i]);
 | |
|           if exesym.gotoffset<>high(aword) then
 | |
|             begin
 | |
|               dt_gotsym_value:=i+1;
 | |
|               break;
 | |
|             end;
 | |
|         end;
 | |
| 
 | |
|       { actually allocate GOT slots for imported symbols }
 | |
|       for i:=dt_gotsym_value to dynsymlist.count-1 do
 | |
|         begin
 | |
|           exesym:=TExeSymbol(dynsymlist[i]);
 | |
|           gotobjsec.alloc(sizeof(pint));
 | |
|           exesym.GotOffset:=gotobjsec.size;
 | |
|         end;
 | |
|       gotsize:=gotobjsec.size;
 | |
|     end;
 | |
| 
 | |
| 
 | |
|   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:=CObjSymbol.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 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);
 | |
|     end;
 | |
| 
 | |
|   procedure TElfExeOutputMIPS.MaybeWriteGOTEntry(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);
 | |
| 
 | |
|       { 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
 | |
|             relocval:=swapendian(relocval);
 | |
|           gotobjsec.write(relocval,sizeof(pint));
 | |
|         end;
 | |
|     end;
 | |
| 
 | |
|   procedure TElfExeOutputMIPS.MaybeWriteTLSIEGotEntry(relocval:aint;objsym:TObjSymbol);
 | |
|     var
 | |
|       gotoff,tmp:aword;
 | |
|     begin
 | |
|       gotoff:=objsym.exesymbol.gotoffset;
 | |
|       if gotoff=0 then
 | |
|         InternalError(2012060903);
 | |
| 
 | |
|       if gotoff=gotobjsec.Data.size+sizeof(pint) then
 | |
|         begin
 | |
|           tmp:=gotobjsec.mempos+gotoff-sizeof(pint);
 | |
|           if (objsym.exesymbol.dynindex>0) then
 | |
|             begin
 | |
|               gotobjsec.writezeros(sizeof(pint));
 | |
|               dynreloclist.Add(TObjRelocation.CreateRaw(tmp,objsym,R_MIPS_TLS_TPREL32));
 | |
|             end
 | |
|           else
 | |
|             begin
 | |
|               putword(gotobjsec,relocval);
 | |
|               if IsSharedLibrary then
 | |
|                 dynreloclist.Add(TObjRelocation.CreateRaw(tmp,nil,R_MIPS_TLS_TPREL32));
 | |
|             end;
 | |
|         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;
 | |
|       lowreloc:TObjRelocation;
 | |
|       reltyp:byte;
 | |
|       externsym:boolean;
 | |
|       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
 | |
|         reltyp:=ElfTarget.encodereloc(ObjReloc)
 | |
|       else
 | |
|         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_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
 | |
|             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;
 | |
|                         objsec.Data.Seek(objreloc.DataOffset);
 | |
|                         objsec.Data.Read(hipart,sizeof(hipart));
 | |
|                         objsec.Data.Seek(lowreloc.DataOffset);
 | |
|                         objsec.Data.Read(lopart,sizeof(lopart));
 | |
|                         break;
 | |
|                       end;
 | |
|                   end;
 | |
|                 if not found then
 | |
|                   InternalError(2013030102);
 | |
|                 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;
 | |
| 
 | |
|         R_MIPS_TLS_GOTTPREL:
 | |
|           inherited AllocGOTSlot(objreloc.symbol);
 | |
|       end;
 | |
|     end;
 | |
| 
 | |
|   type
 | |
|     PRelocData=^TRelocData;
 | |
|     TRelocData=record
 | |
|       next:PRelocData;
 | |
|       objsec:TObjSection;
 | |
|       objrel:TObjRelocation;
 | |
|       addend:aint;
 | |
|     end;
 | |
| 
 | |
|   procedure TElfExeOutputMIPS.DoRelocationFixup(objsec:TObjSection);
 | |
|     var
 | |
|       i,zero:longint;
 | |
|       objreloc: TObjRelocation;
 | |
|       AHL_S,
 | |
|       tmp,
 | |
|       address,
 | |
|       relocval : aint;
 | |
|       relocsec : TObjSection;
 | |
|       data: TDynamicArray;
 | |
|       reltyp: byte;
 | |
|       curloc: aword;
 | |
|       reloclist,hr: PRelocData;
 | |
|       is_gp_disp: boolean;
 | |
|     begin
 | |
|       data:=objsec.data;
 | |
|       reloclist:=nil;
 | |
|       for i:=0 to objsec.ObjRelocations.Count-1 do
 | |
|         begin
 | |
|           objreloc:=TObjRelocation(objsec.ObjRelocations[i]);
 | |
|           case objreloc.typ of
 | |
|             RELOC_NONE:
 | |
|               continue;
 | |
|             RELOC_ZERO:
 | |
|               begin
 | |
|                 data.Seek(objreloc.dataoffset);
 | |
|                 zero:=0;
 | |
|                 data.Write(zero,4);
 | |
|                 continue;
 | |
|               end;
 | |
|           end;
 | |
| 
 | |
|           if (objreloc.flags and rf_raw)=0 then
 | |
|             reltyp:=ElfTarget.encodereloc(objreloc)
 | |
|           else
 | |
|             reltyp:=objreloc.ftype;
 | |
| 
 | |
|           if (oso_rela_relocs in objsec.SecOptions) then
 | |
|             address:=objreloc.orgsize
 | |
|           else
 | |
|             begin
 | |
|               data.Seek(objreloc.dataoffset);
 | |
|               data.Read(address,4);
 | |
|               if source_info.endian<>target_info.endian then
 | |
|                 address:=swapendian(address);
 | |
|             end;
 | |
|           if assigned(objreloc.symbol) then
 | |
|             begin
 | |
|               relocsec:=objreloc.symbol.objsection;
 | |
|               relocval:=objreloc.symbol.address;
 | |
|             end
 | |
|           else if assigned(objreloc.objsection) then
 | |
|             begin
 | |
|               relocsec:=objreloc.objsection;
 | |
|               relocval:=objreloc.objsection.mempos
 | |
|             end
 | |
|           else
 | |
|             internalerror(2012060702);
 | |
| 
 | |
|           { Only debug sections are allowed to have relocs pointing to unused sections }
 | |
|           if assigned(relocsec) and not (relocsec.used and assigned(relocsec.exesection)) and
 | |
|              not (oso_debug in objsec.secoptions) then
 | |
|             begin
 | |
|               writeln(objsec.fullname,' references ',relocsec.fullname);
 | |
|               internalerror(2012060703);
 | |
|             end;
 | |
| 
 | |
|           curloc:=objsec.mempos+objreloc.dataoffset;
 | |
|           if (relocsec=nil) or (relocsec.used) then
 | |
|             case reltyp of
 | |
| 
 | |
|             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
 | |
|                       WriteDynRelocEntry(curloc,R_MIPS_REL32,0,address)
 | |
|                     else
 | |
|                       dynreloclist.add(TObjRelocation.CreateRaw(curloc,objreloc.symbol,R_MIPS_REL32));
 | |
|                   end;
 | |
|                end;
 | |
| 
 | |
|             R_MIPS_26:
 | |
|               begin
 | |
|                 tmp:=(address and $03FFFFFF) shl 2;
 | |
|                 tmp:=((tmp or (curloc and $F0000000))+relocval) shr 2;
 | |
|                 { TODO: Report overflow if upper 4 bits change
 | |
|                         However JAL to undefined weak symbol is not treated as an overflow }
 | |
|                 address:=(address and $FC000000) or (tmp and $3FFFFFF);
 | |
|               end;
 | |
| 
 | |
|             R_MIPS_HI16:
 | |
|               begin
 | |
|                 { This relocation can be handled only after seeing a matching LO16 one,
 | |
|                   moreover BFD supports any number of HI16 to precede a single LO16.
 | |
|                   So just add it to a queue. }
 | |
|                 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;
 | |
|               end;
 | |
| 
 | |
|             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;
 | |
|                     reloclist:=hr^.next;
 | |
|                     // if relocval<>hr^.relocval then  // must be the same symbol
 | |
|                     //   InternalError();
 | |
|                     { _gp_disp and __gnu_local_gp magic }
 | |
|                     if assigned(hr^.objrel.symbol) and
 | |
|                       (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+4;
 | |
|                     AHL_S:=(hr^.addend shl 16)+SmallInt(address)+relocval;
 | |
| 
 | |
|                     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;
 | |
|                 address:=(address and $FFFF0000) or (AHL_S and $FFFF);
 | |
|               end;
 | |
| 
 | |
|             R_MIPS_CALL16,
 | |
|             R_MIPS_GOT16:
 | |
|               begin
 | |
|                 { 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(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
 | |
|                 address:=(address and $FFFF0000) or (relocval and $FFFF);
 | |
|               end;
 | |
| 
 | |
|             R_MIPS_PC16:
 | |
|               begin
 | |
|                 tmp:=((SmallInt(address) shl 2)+relocval-curloc) shr 2;
 | |
|                 if (tmp<>SmallInt(tmp)) then
 | |
|                   ReportRelocOverflow(reltyp,objsec,objreloc);
 | |
|                 address:=(address and $FFFF0000) or (tmp and $FFFF);
 | |
|               end;
 | |
| 
 | |
|             R_MIPS_GPREL32:
 | |
|               address:=address+relocval+TElfObjData(objsec.objdata).gp_value-gotsymbol.address;
 | |
| 
 | |
|             R_MIPS_TLS_GOTTPREL:
 | |
|               begin
 | |
|                 if IsSharedLibrary then
 | |
|                   relocval:=relocval-tlsseg.MemPos
 | |
|                 else
 | |
|                   relocval:=relocval-(tlsseg.MemPos+TP_OFFSET);
 | |
|                 MaybeWriteTLSIEGotEntry(relocval,objreloc.symbol);
 | |
|                 relocval:=-(gotsymbol.offset-(objreloc.symbol.exesymbol.gotoffset-sizeof(pint)));
 | |
|                 // TODO: check overflow
 | |
|                 address:=(address and $FFFF0000) or (relocval and $FFFF);
 | |
|               end;
 | |
| 
 | |
|             R_MIPS_TLS_TPREL_HI16:
 | |
|               begin
 | |
|                 tmp:=SmallInt(address)+relocval-(tlsseg.MemPos+TP_OFFSET);
 | |
|                 tmp:=(tmp+$8000) shr 16;
 | |
|                 address:=(address and $FFFF0000) or (tmp and $FFFF);
 | |
|               end;
 | |
| 
 | |
|             R_MIPS_TLS_TPREL_LO16:
 | |
|               begin
 | |
|                 tmp:=SmallInt(address)+relocval-(tlsseg.MemPos+TP_OFFSET);
 | |
|                 address:=(address and $FFFF0000) or (tmp and $FFFF);
 | |
|               end;
 | |
| 
 | |
|             R_MIPS_JALR: {optimization hint}
 | |
|               begin
 | |
|                 { 4 is subtracted because branch is relative to delay slot, not instruction itself }
 | |
|                 tmp:=(relocval-curloc-4) shr 2;
 | |
|                 if (tmp=SmallInt(tmp)) then
 | |
|                   begin
 | |
|                     if (address=$0320f809) then        { JALR $t9 -> BAL addr }
 | |
|                       address:=$04110000 or (tmp and $FFFF)
 | |
|                     else if (address=$03200008) then   { JR $t9   -> B addr }
 | |
|                       address:=$10000000 or (tmp and $FFFF);
 | |
|                   end;
 | |
|               end;
 | |
|           else
 | |
|             begin
 | |
|               writeln(objsec.fullname,'+',objreloc.dataoffset,' ',objreloc.ftype);
 | |
|               internalerror(200604014);
 | |
|             end;
 | |
|           end
 | |
|         else           { not relocsec.Used }
 | |
|           address:=0;  { Relocation in debug section points to unused section, which is eliminated by linker }
 | |
| 
 | |
|         data.Seek(objreloc.dataoffset);
 | |
|         if source_info.endian<>target_info.endian then
 | |
|           address:=swapendian(address);
 | |
|         data.Write(address,4);
 | |
|       end;
 | |
|     end;
 | |
| 
 | |
| {*****************************************************************************
 | |
|                                     Initialize
 | |
| *****************************************************************************}
 | |
| 
 | |
|   const
 | |
|     elf_target_mips: TElfTarget =
 | |
|       (
 | |
|         max_page_size:     $10000;
 | |
|         exe_image_base:    $400000;
 | |
|         machine_code:      EM_MIPS;
 | |
|         relocs_use_addend: false;
 | |
|         dyn_reloc_codes: (
 | |
|           0,
 | |
|           0,
 | |
|           0,
 | |
|           0,
 | |
|           0
 | |
|         );
 | |
|         relocname:         @elf_mips_relocName;
 | |
|         encodereloc:       @elf_mips_encodeReloc;
 | |
|         loadreloc:         @elf_mips_loadReloc;
 | |
|         loadsection:       @elf_mips_loadSection;
 | |
|         encodeflags:       nil;
 | |
|       );
 | |
| 
 | |
| initialization
 | |
|   ElfTarget:=elf_target_mips;
 | |
|   ElfExeOutputClass:=TElfExeOutputMIPS;
 | |
| 
 | |
| end.
 | |
| 
 |