From 6d70009f06c1672c24a52d2769d8391ef40d99e8 Mon Sep 17 00:00:00 2001 From: sergei Date: Fri, 19 Oct 2012 17:21:08 +0000 Subject: [PATCH] + ELF linker. Works on x86_64-linux and i386-linux good enough to pass the testsuite, but still requires a lot of work in nearly all aspects. In particular, no attempt to resolve symbols from shared libs is done, everything is just treated as imports. Symbol versioning isn't supported either. x86_64 is the most elaborated, has some degree of indirect function (GNU_IFUNC) and TLS support, so it is even able to link with static libc/pthreads code (tw14265) and produce an executable that can launch (but still fails due to invalid DWARF unwind info). i386 produces working shared libraries if they are compiled with -Cg, without one your mileage may vary. tw14265 does not link yet due to missing COMDAT group support. git-svn-id: trunk@22775 - --- compiler/i386/cpuelf.pas | 329 ++++++- compiler/link.pas | 1 + compiler/ogbase.pas | 13 +- compiler/ogelf.pas | 1671 +++++++++++++++++++++++++++++++++- compiler/sparc/cpuelf.pas | 49 +- compiler/systems/t_linux.pas | 331 +++++++ compiler/x86_64/cpuelf.pas | 487 +++++++++- 7 files changed, 2831 insertions(+), 50 deletions(-) diff --git a/compiler/i386/cpuelf.pas b/compiler/i386/cpuelf.pas index e1ce5100fa..d6e67bba0f 100644 --- a/compiler/i386/cpuelf.pas +++ b/compiler/i386/cpuelf.pas @@ -28,20 +28,30 @@ interface implementation uses + globtype,cclasses, verbose, systems,ogbase,ogelf,assemble; type - TElfObjOutput386=class(TElfObjectOutput) - function encodereloc(objrel:TObjRelocation):byte;override; + TElfTarget386=class(TElfTarget) + class function encodereloc(objrel:TObjRelocation):byte;override; + class procedure loadreloc(objrel:TObjRelocation);override; end; - TElfAssembler386=class(TInternalAssembler) - constructor create(smart:boolean);override; + TElfExeOutput386=class(TElfExeOutput) + private + procedure MaybeWriteGOTEntry(reltyp:byte;relocval:aint;exesym:TExeSymbol); + protected + procedure WriteFirstPLTEntry;override; + procedure WritePLTEntry(exesym:TExeSymbol);override; + procedure WriteIndirectPLTEntry(exesym:TExeSymbol);override; + procedure GOTRelocPass1(objsec:TObjSection;ObjReloc:TObjRelocation);override; + procedure DoRelocationFixup(objsec:TObjSection);override; end; const { Relocation types } + R_386_NONE = 0; R_386_32 = 1; { ordinary absolute relocation } R_386_PC32 = 2; { PC-relative relocation } R_386_GOT32 = 3; { an offset into GOT } @@ -87,12 +97,14 @@ implementation {**************************************************************************** - TElfObjOutput386 + TElfTarget386 ****************************************************************************} - function TElfObjOutput386.encodereloc(objrel:TObjRelocation):byte; + class function TElfTarget386.encodereloc(objrel:TObjRelocation):byte; begin case objrel.typ of + RELOC_NONE : + result:=R_386_NONE; RELOC_RELATIVE : result:=R_386_PC32; RELOC_ABSOLUTE : @@ -109,14 +121,307 @@ implementation end; end; + + class procedure TElfTarget386.loadreloc(objrel:TObjRelocation); + begin + end; + + {**************************************************************************** - TElfAssembler386 + TElfExeOutput386 ****************************************************************************} - constructor TElfAssembler386.create(smart:boolean); + + procedure TElfExeOutput386.WriteFirstPLTEntry; begin - inherited Create(smart); - CObjOutput:=TElfObjOutput386; + if IsSharedLibrary then + // push 4(%ebx); jmp *8(%ebx) + pltobjsec.writeBytes(#$FF#$B3#$04#$00#$00#$00#$FF#$A3#$08#$00#$00#$00) + else + begin + pltobjsec.writeBytes(#$FF#$35); // push got+4 + pltobjsec.writeReloc_internal(gotpltobjsec,sizeof(pint),4,RELOC_ABSOLUTE); + pltobjsec.writeBytes(#$FF#$25); // jmp *got+8 + pltobjsec.writeReloc_internal(gotpltobjsec,2*sizeof(pint),4,RELOC_ABSOLUTE); + end; + pltobjsec.writeBytes(#$90#$90#$90#$90); // nop + end; + + + procedure TElfExeOutput386.WritePLTEntry(exesym:TExeSymbol); + var + got_offset: aword; + tmp:pint; + begin + got_offset:=gotpltobjsec.size; + if IsSharedLibrary then + begin + pltobjsec.writeBytes(#$FF#$A3); // jmp got+x(%ebx) + pltobjsec.write(got_offset,4); + end + else + begin + pltobjsec.writeBytes(#$FF#$25); // jmp *got+x + pltobjsec.writeReloc_internal(gotpltobjsec,got_offset,4,RELOC_ABSOLUTE); + end; + pltobjsec.writeBytes(#$68); // push $index + tmp:=pltrelocsec.size; + pltobjsec.write(tmp,4); + + pltobjsec.writeBytes(#$E9); // jmp .plt + tmp:=-(4+pltobjsec.Size); + pltobjsec.write(tmp,4); + + { write a .got.plt slot pointing back to the 'push' instruction } + gotpltobjsec.writeReloc_internal(pltobjsec,pltobjsec.size-(16-6),sizeof(pint),RELOC_ABSOLUTE); + + { write a .rel.plt entry } + pltrelocsec.writeReloc_internal(gotpltobjsec,got_offset,sizeof(pint),RELOC_ABSOLUTE); + got_offset:=(exesym.dynindex shl 8) or R_386_JUMP_SLOT; + pltrelocsec.write(got_offset,sizeof(pint)); + if relocs_use_addend then + pltrelocsec.writezeros(sizeof(pint)); + end; + + + procedure TElfExeOutput386.WriteIndirectPLTEntry(exesym:TExeSymbol); + begin + // TODO + inherited WriteIndirectPLTEntry(exesym); + end; + + + procedure TElfExeOutput386.GOTRelocPass1(objsec:TObjSection;ObjReloc:TObjRelocation); + var + objsym:TObjSymbol; + sym:TExeSymbol; + reltyp:byte; + begin + if (ObjReloc.flags and rf_raw)=0 then + reltyp:=ElfTarget.encodereloc(ObjReloc) + else + reltyp:=ObjReloc.ftype; + case reltyp of + + R_386_PLT32: + begin + objsym:=objreloc.symbol.exesymbol.ObjSymbol; + objsym.refs:=objsym.refs or symref_plt; + end; + + R_386_GOT32: + begin + sym:=ObjReloc.symbol.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 sym=nil then + begin + sym:=TExeSymbol.Create(ExeSymbolList,objreloc.symbol.name+'*local*'); + sym.objsymbol:=objreloc.symbol; + objreloc.symbol.exesymbol:=sym; + end; + if sym.GotOffset>0 then + exit; + gotobjsec.alloc(sizeof(pint)); + sym.GotOffset:=gotobjsec.size; + { In shared library, every GOT entry needs a RELATIVE dynamic reloc, + imported/exported symbols need GLOB_DAT instead. For executables, + only the latter applies. } + if IsSharedLibrary or (sym.dynindex>0) then + dynrelocsec.alloc(dynrelocsec.shentsize); + end; + + R_386_32: + begin + { TODO: How to handle absolute relocation to *weak* external symbol + from executable? See test/tweaklib2, symbol test2, ld handles it + differently for PIC and non-PIC code. In non-PIC code it drops + dynamic relocation altogether. } + if not IsSharedLibrary then + exit; + if (oso_executable in objsec.SecOptions) or + not (oso_write in objsec.SecOptions) then + hastextrelocs:=True; + dynrelocsec.alloc(dynrelocsec.shentsize); + objreloc.flags:=objreloc.flags or rf_dynamic; + end; + + R_386_PC32: + begin + if not IsSharedLibrary then + exit; + { In shared library PC32 reloc to external symbol cannot be redirected + to PLT entry, because PIC PLT relies on ebx register set properly. } + if assigned(objreloc.symbol) and + ( + (objreloc.symbol.objsection=nil) or + (oso_plt in objreloc.symbol.objsection.SecOptions) + ) then + begin + { Must be a dynamic symbol } + if not (assigned(objreloc.symbol.exesymbol) and + (objreloc.symbol.exesymbol.dynindex<>0)) then + InternalError(2012101201); + if (oso_executable in objsec.SecOptions) or + not (oso_write in objsec.SecOptions) then + hastextrelocs:=True; + dynrelocsec.alloc(dynrelocsec.shentsize); + objreloc.flags:=objreloc.flags or rf_dynamic; + end; + end; + end; + end; + + + procedure TElfExeOutput386.MaybeWriteGOTEntry(reltyp:byte;relocval:aint;exesym:TExeSymbol); + var + gotoff,dynidx,tmp:aword; + begin + gotoff:=exesym.gotoffset; + if gotoff=0 then + InternalError(2012060902); + + { the GOT slot itself, and a dynamic relocation for it } + { TODO: only data symbols must get here } + if gotoff=gotobjsec.Data.size+sizeof(pint) then + begin + dynidx:=exesym.dynindex; + gotobjsec.write(relocval,sizeof(pint)); + + tmp:=gotobjsec.mempos+gotoff-sizeof(pint); + if (dynidx>0) then + WriteDynRelocEntry(tmp,R_386_GLOB_DAT,dynidx,0) + else if IsSharedLibrary then + WriteDynRelocEntry(tmp,R_386_RELATIVE,0,relocval); + end; + end; + + + procedure TElfExeOutput386.DoRelocationFixup(objsec:TObjSection); + var + i,zero:longint; + objreloc: TObjRelocation; + address, + relocval : aint; + relocsec : TObjSection; + data: TDynamicArray; + reltyp: byte; + begin + data:=objsec.data; + 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 relocs_use_addend then + address:=objreloc.orgsize + else + begin + data.Seek(objreloc.dataoffset); + data.Read(address,4); + 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; + + { TODO: if relocsec=nil, relocations must be copied to .rel.dyn section } + if (relocsec=nil) or (relocsec.used) then + case reltyp of + R_386_PC32: + begin + if (objreloc.flags and rf_dynamic)<>0 then + WriteDynRelocEntry(objreloc.dataoffset+objsec.mempos,R_386_PC32,objreloc.symbol.exesymbol.dynindex,0) + else + address:=address+relocval-(objsec.mempos+objreloc.dataoffset); + end; + + R_386_PLT32: + begin + { If target is in current object, treat as RELOC_RELATIVE } + address:=address+relocval-(objsec.mempos+objreloc.dataoffset); + end; + + R_386_32: + begin + 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 + address:=address+relocval; + WriteDynRelocEntry(objreloc.dataoffset+objsec.mempos,R_386_RELATIVE,0,address); + end + else + { Don't modify address in this case, as it serves as addend for RTLD } + WriteDynRelocEntry(objreloc.dataoffset+objsec.mempos,R_386_32,objreloc.symbol.exesymbol.dynindex,0); + end + else + address:=address+relocval; + end; + + R_386_GOTPC: + begin + address:=address+gotsymbol.address-(objsec.mempos+objreloc.dataoffset); + end; + + R_386_GOT32: + begin + MaybeWriteGOTEntry(reltyp,relocval,objreloc.symbol.exesymbol); + + relocval:=gotobjsec.mempos+objreloc.symbol.exesymbol.gotoffset-sizeof(pint)-gotsymbol.address; + address:=address+relocval; + end; + + //R_386_GOTOFF: !!TODO + + else + begin + writeln(objreloc.typ); + 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); + data.Write(address,4); + end; end; @@ -143,7 +448,9 @@ implementation ); initialization - RegisterAssembler(as_i386_elf32_info,TElfAssembler386); + RegisterAssembler(as_i386_elf32_info,TElfAssembler); + ElfExeOutputClass:=TElfExeOutput386; + ElfTarget:=TElfTarget386; end. diff --git a/compiler/link.pas b/compiler/link.pas index c534f394fa..d1c78c9971 100644 --- a/compiler/link.pas +++ b/compiler/link.pas @@ -1388,6 +1388,7 @@ Implementation STABS for empty linker scripts } exeoutput.MergeStabs; exeoutput.MarkEmptySections; + exeoutput.AfterUnusedSectionRemoval; if ErrorCount>0 then goto myexit; diff --git a/compiler/ogbase.pas b/compiler/ogbase.pas index 23c061e5ea..02d65f1dbd 100644 --- a/compiler/ogbase.pas +++ b/compiler/ogbase.pas @@ -154,8 +154,10 @@ interface typ : TAsmsymtype; { Current assemble pass, used to detect duplicate labels } pass : byte; - objsection : TObjSection; + { how the symbol is referenced (target-specific bitfield) + refs : byte; symidx : longint; + objsection : TObjSection; offset, size : aword; { Used for external and common solving during linking } @@ -375,6 +377,9 @@ interface State : TSymbolState; { Used for vmt references optimization } VTable : TExeVTable; + { fields for ELF linking } + gotoffset : aword; + dynindex : aword; end; TExeSection = class(TFPHashObject) @@ -533,6 +538,7 @@ interface procedure RemoveUnreferencedSections; procedure RemoveDisabledSections; procedure RemoveDebugInfo; + procedure AfterUnusedSectionRemoval;virtual; procedure GenerateLibraryImports(ImportLibraryList:TFPHashObjectList);virtual; procedure GenerateDebugLink(const dbgname:string;dbgcrc:cardinal); function WriteExeFile(const fn:string):boolean; @@ -2556,6 +2562,11 @@ implementation end; + procedure TExeOutput.AfterUnusedSectionRemoval; + begin + end; + + function ByAddress(item1,item2:pointer):longint; var sym1:TObjSymbol absolute item1; diff --git a/compiler/ogelf.pas b/compiler/ogelf.pas index bc12bedc31..b03d9ea8da 100644 --- a/compiler/ogelf.pas +++ b/compiler/ogelf.pas @@ -84,12 +84,174 @@ interface procedure section_create_relocsec(p:TObject;arg:pointer); procedure section_write_sechdr(p:TObject;arg:pointer); protected - function encodereloc(objrel:TObjRelocation):byte;virtual;abstract; function writedata(data:TObjData):boolean;override; public constructor Create(AWriter:TObjectWriter);override; end; + TElfAssembler = class(tinternalassembler) + constructor create(smart:boolean);override; + end; + + TElfTarget=class(TObject) + public + class function encodereloc(objrel:TObjRelocation):byte;virtual;abstract; + class procedure loadreloc(objrel:TObjRelocation);virtual;abstract; + end; + + TElfTargetClass=class of TElfTarget; + + PSectionRec=^TSectionRec; + TSectionRec=record + sec: TObjSection; + relocpos: aword; + relocs: longint; + relentsize: longint; + end; + + TElfObjInput=class(TObjInput) + private + FSecTbl: PSectionRec; + FSymTbl: PPointer; + FLoaded: PBoolean; + nsects: longword; + shentsize: longword; + shoffset: aword; + shstrndx: longword; + shstrtab: PChar; + strtab: PChar; + shstrtablen: longword; + strtablen: longword; + symtaboffset: aword; + syms: longword; + localsyms: longword; + function LoadHeader:word; + procedure LoadSection(const hdr;index:longint;objdata:TObjData); + procedure LoadRelocations(const secrec:TSectionRec); + procedure LoadSymbols(objdata:TObjData;count,locals:longword); + procedure LoadDynamic(const hdr;objdata:TObjData); + public + constructor Create;override; + destructor Destroy;override; + function ReadObjData(AReader:TObjectreader;objdata:TObjData):boolean;override; + class function CanReadObjData(AReader:TObjectreader):boolean;override; + end; + + TElfExeSection=class(TExeSection) + public + secshidx : longword; { index of the section header } + shstridx, + shtype, + shflags, + shlink, + shinfo, + shentsize : longword; + procedure AddObjSection(objsec:TObjSection;ignoreprops:boolean=false);override; + end; + + TElfSegment=class + public + ptype: longword; + pflags: longword; + DataPos: aword; + DataSize: aword; + MemPos: aword; + MemSize: aword; + align: longword; + //physaddr: aword; + FSectionList: TFPObjectList; + constructor Create(atype,aflags,aalign:longword); + destructor Destroy; override; + procedure Add(exesec:TExeSection); + end; + + TElfExeOutput=class(TExeOutput) + private + segmentlist: TFPObjectList; + textseg, + dataseg, + noteseg, + phdrseg: TElfSegment; + shstrtabsect: TElfObjSection; + symtab: TElfSymtab; + shoffset: longint; + gotwritten: boolean; + { dynamic linking } + dynamiclink: boolean; + dynsymnames: Plongword; + dynsymtable: TElfSymtab; + interpobjsec: TObjSection; + dynamicsec, + hashobjsec: TElfObjSection; + neededlist: TFPHashList; + gotsize: aword; + dynrelsize: aword; + + function AttachSection(objsec:TObjSection):TElfExeSection; + function CreateSegment(atype,aflags,aalign:longword):TElfSegment; + procedure CreatePLT; + procedure WriteHeader; + procedure WriteDynamicSymbolsHash; + procedure WriteDynamicTags; + procedure FinishDynamicTags; + procedure exesection_write_header(p:TObject;arg:Pointer); + procedure segment_write_header(p:TObject;arg:Pointer); + procedure mempos_segment(seg:TElfSegment); + procedure datapos_segment(seg:TElfSegment); + procedure MapSectionsToSegments; + procedure WriteStaticSymtable; + procedure InitDynlink; + procedure PrepareGOT; + procedure WriteDynTag(aTag:longword;aValue:longword); + procedure WriteDynTag(aTag:longword;aSection:TObjSection;aOffs:aword=0); + protected + hastextrelocs: boolean; + gotsymbol: TObjSymbol; + dynsymlist: TFPObjectList; + gotobjsec: TObjSection; + pltobjsec, + gotpltobjsec, + pltrelocsec, + ipltrelocsec, + dynrelocsec: TElfObjSection; + tlsseg: TElfSegment; + procedure WriteDynRelocEntry(dataofs:aword;typ:byte;symidx:aword;addend:aword); + procedure WriteFirstPLTEntry;virtual;abstract; + procedure WritePLTEntry(exesym:TExeSymbol);virtual; + procedure WriteIndirectPLTEntry(exesym:TExeSymbol);virtual; + procedure GOTRelocPass1(objsec:TObjSection;ObjReloc:TObjRelocation);virtual;abstract; + public + constructor Create;override; + destructor Destroy;override; + procedure Load_Start;override; + procedure Load_DynamicObject(ObjData:TObjData);override; + procedure Order_Start;override; + procedure Order_end;override; + procedure AfterUnusedSectionRemoval;override; + procedure MemPos_Start;override; + procedure MemPos_ExeSection(const aname:string);override; + procedure DataPos_Start;override; + procedure DataPos_ExeSection(const aname:string);override; + function writeData:boolean;override; + procedure GenerateLibraryImports(ImportLibraryList:TFPHashObjectList);override; + end; + + var + ElfExeOutputClass: TExeOutputClass; + ElfTarget: TElfTargetClass; + + const + { Bits of TObjSymbol.refs field } + symref_plt = 1; + +{TODO: should become property of back-end } +{$ifdef x86_64} + const + relocs_use_addend:Boolean=True; +{$else x86_64} + const + relocs_use_addend:Boolean=False; +{$endif x86_64} implementation @@ -97,6 +259,7 @@ implementation uses SysUtils, verbose, + export,expunix, cutils,globals,fmodule; const @@ -461,10 +624,12 @@ implementation {$ifdef x86_64} const - relocs_use_addend:Boolean=True; + ELF_MAXPAGESIZE:longint=$200000; + TEXT_SEGMENT_START:longint=$400000; {$else x86_64} const - relocs_use_addend:Boolean=False; + ELF_MAXPAGESIZE:longint=$1000; + TEXT_SEGMENT_START:longint=$8048000; {$endif x86_64} procedure MayBeSwapHeader(var h : telf32header); @@ -996,8 +1161,8 @@ implementation const symsecnames: array[boolean] of string[8] = ('.symtab','.dynsym'); strsecnames: array[boolean] of string[8] = ('.strtab','.dynstr'); - symsectypes: array[boolean] of longint = (SHT_SYMTAB,SHT_DYNSYM); - symsecattrs: array[boolean] of longint = (0,SHF_ALLOC); + symsectypes: array[boolean] of longword = (SHT_SYMTAB,SHT_DYNSYM); + symsecattrs: array[boolean] of longword = (0,SHF_ALLOC); constructor TElfSymtab.create(aObjData:TObjData;aKind:TElfSymtabKind); @@ -1084,7 +1249,13 @@ implementation begin if kind<>esk_obj then begin - { TODO } + if assigned(objsym.objsection) and assigned(objsym.objsection.ExeSection) then + begin + if (oso_plt in objsym.objsection.SecOptions) then + elfsym.st_value:=0 + else + elfsym.st_shndx:=TElfExeSection(objsym.objsection.ExeSection).secshidx; + end; end else begin @@ -1147,7 +1318,7 @@ implementation end; rel.address:=objreloc.dataoffset; - rel.info:=ELF_R_INFO(relsym,encodereloc(objreloc)); + rel.info:=ELF_R_INFO(relsym,ElfTarget.encodereloc(objreloc)); rel.addend:=objreloc.orgsize; { write reloc } @@ -1351,4 +1522,1490 @@ implementation end; +{**************************************************************************** + TELFAssembler +****************************************************************************} + + constructor TElfAssembler.Create(smart:boolean); + begin + inherited Create(smart); + CObjOutput:=TElfObjectOutput; + end; + + +{**************************************************************************** + TELFObjectInput +****************************************************************************} + + constructor TElfObjInput.Create; + begin + inherited Create; + CObjData:=TElfObjData; + end; + + + destructor TElfObjInput.Destroy; + begin + if Assigned(FSymTbl) then + FreeMem(FSymTbl); + if Assigned(FSecTbl) then + FreeMem(FSecTbl); + if Assigned(strtab) then + FreeMem(strtab); + if Assigned(shstrtab) then + FreeMem(shstrtab); + inherited Destroy; + end; + + + procedure TElfObjInput.LoadRelocations(const secrec:TSectionRec); + var + i: longint; + rel: TElfReloc; + reltyp: byte; + relsym: longint; + objrel: TObjRelocation; + p: TObjSymbol; + begin + FReader.Seek(secrec.relocpos); + if secrec.sec=nil then + InternalError(2012060203); + for i:=0 to secrec.relocs-1 do + begin + FReader.Read(rel,secrec.relentsize); + MaybeSwapElfReloc(rel); + reltyp:=rel.info and $FF; +{$ifdef cpu64bitaddr} + relsym:=rel.info shr 32; +{$else cpu64bitaddr} + relsym:=(rel.info shr 8) and $FFFFFF; +{$endif cpu64bitaddr} + if relsym>=syms then + InternalError(2012060204); + p:=TObjSymbol(FSymTbl[relsym]); + if assigned(p) then + begin + objrel:=TObjRelocation.CreateRaw(rel.address-secrec.sec.mempos,p,reltyp); + if relocs_use_addend then + objrel.orgsize:=rel.addend; + { perform target-specific actions } + ElfTarget.loadreloc(objrel); + secrec.sec.ObjRelocations.add(objrel); + end + else + begin + InputError('Unable to resolve symbol of relocation'); + exit; + end; + end; + end; + + + procedure TElfObjInput.LoadSymbols(objdata:TObjData;count,locals:longword); + var + i: longint; + sym: TElfSymbol; + bind: TAsmSymBind; + typ: TAsmSymType; + objsym: TObjSymbol; + begin + FSymTbl:=AllocMem(count*sizeof(Pointer)); + for i:=1 to count-1 do + begin + FReader.Read(sym,sizeof(TElfSymbol)); + MaybeSwapElfSymbol(sym); + if sym.st_name>=strtablen then + InternalError(2012060205); + + if sym.st_shndx=SHN_ABS then { ignore absolute symbols (should we really do it???) } + Continue + else if sym.st_shndx=SHN_COMMON then + bind:=AB_COMMON + else if (sym.st_shndx>=nsects) or ((sym.st_shndx>0) and (FSecTbl[sym.st_shndx].sec=nil)) then + begin + writeln(objdata.name,' ',i); + InternalError(2012060206) + end + else + case (sym.st_info shr 4) of + STB_LOCAL: + bind:=AB_LOCAL; + STB_GLOBAL: + if sym.st_shndx=SHN_UNDEF then + bind:=AB_EXTERNAL + else + bind:=AB_GLOBAL; + STB_WEAK: + bind:=AB_WEAK_EXTERNAL; + else + InternalError(2012060207); + end; + + case (sym.st_info and $0F) of + STT_NOTYPE: + typ:=AT_NONE; + STT_OBJECT: + typ:=AT_DATA; + STT_FUNC: + typ:=AT_FUNCTION; + STT_SECTION: + typ:=AT_SECTION; + STT_FILE: + continue; + STT_TLS: + typ:=AT_TLS; + STT_GNU_IFUNC: + typ:=AT_GNU_IFUNC; + else + writeln(objdata.name,' ',sym.st_info and $0F); + InternalError(2012060208); + end; + { validity of name and objsection has been checked above } + { !! all AT_SECTION symbols have duplicate (null) name, + therefore TObjSection.CreateSymbol cannot be used here } + objsym:=TObjSymbol.Create(objdata.ObjSymbolList,string(PChar(@strtab[sym.st_name]))); + objsym.bind:=bind; + objsym.typ:=typ; + if bind<>AB_COMMON then + objsym.objsection:=FSecTbl[sym.st_shndx].sec; + objsym.offset:=sym.st_value; + objsym.size:=sym.st_size; + FSymTbl[i]:=objsym; + end; + end; + + + procedure TElfObjInput.LoadSection(const hdr;index:longint;objdata:tobjdata); + var + shdr: TElfsechdr absolute hdr; + sec: TElfObjSection; + secname: string; + begin + case shdr.sh_type of + SHT_NULL: + {ignore}; + + { SHT_STRTAB may appear for .stabstr and other debug sections. + .shstrtab and .strtab are processed separately and don't appear here. } + SHT_PROGBITS,SHT_NOBITS,SHT_NOTE,SHT_STRTAB: + begin + if shdr.sh_name>=shstrtablen then + InternalError(2012060210); + secname:=string(PChar(@shstrtab[shdr.sh_name])); + + sec:=TElfObjSection.create_ext(objdata,secname, + shdr.sh_type,shdr.sh_flags,shdr.sh_addralign,shdr.sh_entsize); + + if (Length(secname)>3) and (secname[2] in ['d','f','n','s']) then + begin + if (Pos('.stub',secname)=1) or + (Pos('.fpc',secname)=1) then + sec.SecOptions:=[oso_keep] + { ELF does not have any flags specific to debug sections, + but reserves names starting with '.debug' for this purpose } + else if (Pos('.debug',secname)=1) or + (secname='.stab') or + (secname='.stabstr') then + sec.SecOptions:=[oso_debug] + else if (secname='.note.GNU-stack') and (shdr.sh_type=SHT_PROGBITS) then + begin + if (shdr.sh_flags and SHF_EXECINSTR)=0 then + objdata.ExecStack:=False; + end; + end; + + if (shdr.sh_type=SHT_NOTE) and (shdr.sh_size<>0) then + sec.SecOptions:=[oso_keep]; + + sec.index:=index; + sec.DataPos:=shdr.sh_offset; + sec.MemPos:=shdr.sh_addr; + sec.Size:=shdr.sh_size; + FSecTbl[index].sec:=sec; + end; + + SHT_REL,SHT_RELA: + begin + if shdr.sh_info>=nsects then + InternalError(2012060211); + if shdr.sh_entsize<>longword((2+ord(shdr.sh_type=SHT_RELA))*sizeof(pint)) then + InternalError(2012060212); + + with FSecTbl[shdr.sh_info] do + begin + relocpos:=shdr.sh_offset; + relocs:=shdr.sh_size div shdr.sh_entsize; + relentsize:=shdr.sh_entsize; + end; + end; + + SHT_GROUP: + if (shdr.sh_size>=2*sizeof(longword)) and + (shdr.sh_entsize=sizeof(longword)) and + ((shdr.sh_size mod shdr.sh_entsize)=0) then + begin + { we need a dummy section to load the corresponding symbol later on } + secname:=string(PChar(@shstrtab[shdr.sh_name])); + sec:=TElfObjSection.create_ext(objdata,secname, + shdr.sh_type,shdr.sh_flags,shdr.sh_addralign,shdr.sh_entsize); + sec.index:=index; + FSecTbl[index].sec:=sec; + end; + else + InternalError(2012072603); + end; + FLoaded[index]:=True; + end; + + + function TElfObjInput.LoadHeader:word; + var + header:TElfHeader; + begin + result:=ET_NONE; + if not FReader.read(header,sizeof(header)) then + begin + InputError('Can''t read ELF header'); + exit; + end; + if (header.magic[0]<>$7f) or (header.magic[1]<>$45) or + (header.magic[2]<>$4c) or (header.magic[3]<>$46) then + begin + InputError('Illegal ELF magic'); + exit; + end; + if (header.file_version<>1) then + begin + InputError('Unknown ELF file version'); + exit; + end; + if (header.file_class<>ELFCLASS) then + begin + InputError('Wrong ELF file class (32/64 bit mismatch)'); + exit; + end; + if (header.data_encoding<>1+ord(target_info.endian=endian_big)) then + begin + InputError('ELF endianness does not match target'); + exit; + end; + MaybeSwapHeader(header); + if (header.e_version<>1) then + begin + InputError('Unknown ELF data version'); + exit; + end; + if (header.e_machine<>ELFMACHINE) then + begin + InputError('ELF file is for different CPU'); + exit; + end; + + nsects:=header.e_shnum; + shentsize:=header.e_shentsize; + shoffset:=header.e_shoff; + shstrndx:=header.e_shstrndx; + result:=header.e_type; + end; + + + procedure TElfObjInput.LoadDynamic(const hdr;objdata:TObjData); + var + shdr: TElfsechdr absolute hdr; + dt: TElfDyn; + i: longint; + begin + if (shdr.sh_entsize<>sizeof(TElfDyn)) then + InternalError(2012071403); + + FReader.Seek(shdr.sh_offset); + for i:=0 to (shdr.sh_size div shdr.sh_entsize)-1 do + begin + FReader.Read(dt,sizeof(TElfDyn)); + MaybeSwapElfDyn(dt); + case dt.d_tag of + DT_NULL: + break; + DT_SONAME: + TElfObjData(objdata).FName:=string(PChar(@strtab[dt.d_ptr])); + DT_NEEDED: + ; + end; + end; + end; + + + function TElfObjInput.ReadObjData(AReader:TObjectreader;objdata:TObjData):boolean; + var + i,strndx,dynndx: longint; + objsec: TElfObjSection; + shdrs: array of TElfsechdr; + isdyn: boolean; + begin + FReader:=AReader; + InputFileName:=AReader.FileName; + result:=false; + + i:=LoadHeader; + if (i=ET_NONE) then { error message already given in this case } + exit; + if (i<>ET_REL) and (i<>ET_DYN) then + begin + InputError('Not a relocatable or dynamic ELF file'); + exit; + end; + isdyn:=(i=ET_DYN); + + if shentsize<>sizeof(TElfsechdr) then + InternalError(2012062701); + + FSecTbl:=AllocMem(nsects*sizeof(TSectionRec)); + FLoaded:=AllocMem(nsects*sizeof(boolean)); + SetLength(shdrs,nsects); + + FReader.Seek(shoffset); + if not FReader.Read(shdrs[0],nsects*sizeof(TElfsechdr)) then + begin + InputError('Can''t read ELF section headers'); + exit; + end; + if source_info.endian<>target_info.endian then + for i:=0 to nsects-1 do + MaybeSwapSecHeader(shdrs[i]); + + { First, load the .shstrtab section } + if shstrndx>=nsects then + InternalError(2012060201); + if shdrs[shstrndx].sh_type<>SHT_STRTAB then + InternalError(2012060202); + shstrtablen:=shdrs[shstrndx].sh_size; + GetMem(shstrtab,shstrtablen); + FReader.seek(shdrs[shstrndx].sh_offset); + FReader.read(shstrtab^,shstrtablen); + FLoaded[shstrndx]:=True; + + { Locate the symtable, it is typically at the end so loop backwards. + Load the strings, postpone symtable itself until done with sections. + Note that is is legal to have no symtable. + For DSO, locate .dynsym instead, this one is near the beginning, but + overall number of sections won't be big. Also locate .dynamic. } + dynndx:=0; + for i:=nsects-1 downto 1 do + begin + if isdyn and (shdrs[i].sh_type=SHT_DYNAMIC) then + dynndx:=i; + if (shdrs[i].sh_type<>symsectypes[isdyn]) then + continue; + if (shdrs[i].sh_entsize<>sizeof(TElfSymbol)) then + InternalError(2012060213); + if shdrs[i].sh_link>=nsects then + InternalError(2012062702); + strndx:=shdrs[i].sh_link; + if shdrs[strndx].sh_type<>SHT_STRTAB then + InternalError(2012062703); + strtablen:=shdrs[strndx].sh_size; + GetMem(strtab,strtablen); + FReader.seek(shdrs[strndx].sh_offset); + FReader.read(strtab^,strtablen); + + symtaboffset:=shdrs[i].sh_offset; + syms:=shdrs[i].sh_size div sizeof(TElfSymbol); + localsyms:=shdrs[i].sh_info; + FLoaded[i]:=True; + FLoaded[strndx]:=True; + break; + end; + + if isdyn then + begin + if dynndx=0 then + InternalError(2012071401); + if (shdrs[dynndx].sh_link<>strndx) then + InternalError(2012071402); + LoadDynamic(shdrs[dynndx],objdata); + + { for DSO, we aren't interested in actual sections, but need to a dummy one + to maintain integrity. } + objsec:=TElfObjSection.create_ext(objdata,'*DSO*',SHT_PROGBITS,0,0,0); + for i:=1 to nsects-1 do + FSecTbl[i].sec:=objsec; + + { load the symtable } + FReader.Seek(symtaboffset+sizeof(TElfSymbol)); + LoadSymbols(objdata,syms,localsyms); + + result:=True; + exit; + end; + + { assume stack is executable until proven otherwise } + objdata.ExecStack:=True; + + { Process section headers } + for i:=1 to nsects-1 do + if not FLoaded[i] then + LoadSection(shdrs[i],i,objdata); + + { load the content } + ReadSectionContent(objdata); + + { load the symtable } + FReader.Seek(symtaboffset+sizeof(TElfSymbol)); + LoadSymbols(objdata,syms,localsyms); + + { finish relocations } + for i:=0 to objdata.ObjSectionList.Count-1 do + begin + objsec:=TElfObjSection(objdata.ObjsectionList[i]); + { skip debug sections } + if (oso_debug in objsec.SecOptions) and + (cs_link_strip in current_settings.globalswitches) and + not(cs_link_separate_dbg_file in current_settings.globalswitches) then + continue; + + if FSecTbl[objsec.index].relocpos>0 then + LoadRelocations(FSecTbl[objsec.index]); + end; + + result:=True; + end; + + + class function TElfObjInput.CanReadObjData(AReader:TObjectreader):boolean; + var + header: TElfHeader; + begin + result:=false; + if AReader.Read(header,sizeof(header)) then + begin; + if (header.magic[0]=$7f) and (header.magic[1]=$45) and + (header.magic[2]=$4c) and (header.magic[3]=$46) then + { TODO: check additional fields } + result:=true; + end; + AReader.Seek(0); + end; + +{***************************************************************************** + TElfExeOutput +*****************************************************************************} + + constructor TElfExeOutput.Create; + begin + inherited Create; + CObjData:=TElfObjData; + CExeSection:=TElfExeSection; +{$ifdef cpu64} + MaxMemPos:=Qword($FFFFFFFFFFFFFFFF); + //MaxMemPos:=$7EFFFFFF; { As specified by SysV AMD64 ABI for small memory model } +{$else cpu64} + MaxMemPos:=$7FFFFFFF; +{$endif cpu64} + SectionMemAlign:=$20; + SectionDataAlign:=$20; + segmentlist:=TFPObjectList.Create(True); + neededlist:=TFPHashList.Create; + end; + + + destructor TElfExeOutput.Destroy; + begin + neededlist.Free; + segmentlist.Free; + dynsymlist.Free; + if assigned(dynsymnames) then + FreeMem(dynsymnames); + inherited Destroy; + end; + + + function TElfExeOutput.AttachSection(objsec:TObjSection):TElfExeSection; + begin + objsec.SecOptions:=[oso_keep]; + result:=TElfExeSection(FindExeSection(objsec.name)); + if result=nil then + result:=TElfExeSection.Create(ExeSectionList,objsec.name); + result.AddObjSection(objsec); + end; + + + function TElfExeOutput.CreateSegment(atype,aflags,aalign:longword):TElfSegment; + begin + result:=TElfSegment.Create(atype,aflags,aalign); + segmentlist.add(result); + end; + + + procedure TElfExeOutput.WriteHeader; + var + header: TElfHeader; + begin + FillChar(header,sizeof(header),0); + header.magic[0]:=$7f; { = #127'ELF' } + header.magic[1]:=$45; + header.magic[2]:=$4c; + header.magic[3]:=$46; + header.file_class:=ELFCLASS; + if target_info.endian=endian_big then + header.data_encoding:=2 + else + header.data_encoding:=1; + + header.file_version:=1; + if IsSharedLibrary then + header.e_type:=ET_DYN + else + header.e_type:=ET_EXEC; + header.e_machine:=ELFMACHINE; +{$ifdef arm} + if (current_settings.fputype=fpu_soft) then + header.e_flags:=$600; +{$endif arm} + header.e_version:=1; + header.e_phoff:=sizeof(TElfHeader); + header.e_shoff:=shoffset; + header.e_shstrndx:=ExeSectionList.IndexOf(shstrtabsect.ExeSection)+1; + + header.e_shnum:=ExeSectionList.Count+1; + header.e_phnum:=segmentlist.count; + header.e_ehsize:=sizeof(telfheader); + if assigned(EntrySym) then + header.e_entry:=EntrySym.Address; + header.e_shentsize:=sizeof(telfsechdr); + header.e_phentsize:=sizeof(telfproghdr); + MaybeSwapHeader(header); + FWriter.Write(header,sizeof(header)); + end; + + + procedure TElfExeOutput.exesection_write_header(p:TObject;arg:Pointer); + var + shdr: TElfsechdr; + exesec: TElfExeSection absolute p; + begin + FillChar(shdr,sizeof(shdr),0); + shdr.sh_name:=exesec.shstridx; + if (ExeWriteMode=ewm_dbgonly) and + (exesec.SecOptions*[oso_debug,oso_debug_copy]=[]) then + shdr.sh_type:=SHT_NOBITS + else + shdr.sh_type:=exesec.shtype; + shdr.sh_flags:=exesec.shflags; + if (oso_load in exesec.SecOptions) then + shdr.sh_addr:=exesec.MemPos; + shdr.sh_offset:=exesec.DataPos; + shdr.sh_size:=exesec.Size; + shdr.sh_link:=exesec.shlink; + shdr.sh_info:=exesec.shinfo; + shdr.sh_addralign:=exesec.SecAlign; + shdr.sh_entsize:=exesec.shentsize; + MaybeSwapSecHeader(shdr); + FWriter.Write(shdr,sizeof(shdr)); + end; + + + procedure TElfExeOutput.segment_write_header(p:TObject;arg:Pointer); + var + phdr: TElfproghdr; + seg: TElfSegment absolute p; + begin + FillChar(phdr,sizeof(phdr),0); + phdr.p_type:=seg.ptype; + phdr.p_flags:=seg.pflags; + phdr.p_align:=seg.align; + phdr.p_offset:=seg.DataPos; + phdr.p_filesz:=seg.DataSize; + phdr.p_memsz:=seg.MemSize; + phdr.p_vaddr:=seg.MemPos; + phdr.p_paddr:=seg.MemPos; + + MaybeSwapHeader(phdr); + FWriter.Write(phdr,sizeof(phdr)); + end; + + + procedure TElfExeOutput.WriteStaticSymtable; + var + i: longint; + sec: TElfExeSection; + exesym: TExeSymbol; + begin + for i:=0 to ExeSectionList.Count-1 do + begin + sec:=TElfExeSection(ExeSectionList[i]); + { Must not write symbols for internal sections like .symtab } + if (sec.shtype in [SHT_SYMTAB,SHT_STRTAB,SHT_REL,SHT_RELA]) then + continue; + sec.secsymidx:=symtab.symidx; + symtab.writeInternalSymbol(sec.mempos,0,STT_SECTION,sec.secshidx); + end; + { local symbols first } + for i:=0 to ExeSymbolList.Count-1 do + begin + exesym:=TExeSymbol(ExeSymbolList[i]); + if (exesym.objsymbol.bind=AB_LOCAL) and (exesym.objsymbol.typ<>AT_LABEL) then + symtab.WriteSymbol(exesym.objsymbol); + end; + { Global Symbols } + for i:=0 to ExeSymbolList.Count-1 do + begin + exesym:=TExeSymbol(ExeSymbolList[i]); + if (exesym.objsymbol.bind<>AB_LOCAL) then + symtab.WriteSymbol(exesym.objsymbol); + end; + { update exe section properties } + symtab.ExeSection.size:=symtab.size; + TElfExeSection(symtab.ExeSection).shinfo:=symtab.shinfo; + TElfExeSection(symtab.ExeSection).shlink:=ExeSectionList.IndexOf(symtab.fstrsec.ExeSection)+1; + symtab.fstrsec.ExeSection.Size:=symtab.fstrsec.size; + end; + + + procedure TElfExeOutput.MapSectionsToSegments; + var + seg: TElfSegment; + exesec: TExeSection; + i: longint; + begin + if (not IsSharedLibrary) and assigned(interpobjsec) then + begin + phdrseg:=CreateSegment(PT_PHDR,PF_R or PF_X,sizeof(pint)); + seg:=CreateSegment(PT_INTERP,PF_R,1); + seg.Add(interpobjsec.ExeSection); + end; + + textseg:=CreateSegment(PT_LOAD,PF_X or PF_R,ELF_MAXPAGESIZE); + dataseg:=CreateSegment(PT_LOAD,PF_R or PF_W,ELF_MAXPAGESIZE); + for i:=0 to ExeSectionList.Count-1 do + begin + exesec:=TExeSection(ExeSectionList[i]); + if (oso_load in exesec.SecOptions) then + begin + if (TElfExeSection(exesec).shflags and SHF_TLS)<>0 then + begin + if tlsseg=nil then + tlsseg:=CreateSegment(PT_TLS,PF_R,sizeof(pint)); + tlsseg.add(exesec); + end; + + { TODO: at least on Linux, ld seems to drop .note.ABI-tag for static executables. + (Logic is as follows: there is no .note.ABI-tag section in ld script, so it + is processed as orphan section. As such, it is placed after .interp. + For static executables .interp is dropped, and it looks like there's nowhere to + place .note.ABI-tag in this case) + Always including it doesn't harm though (except increasing file size). } + if TElfExeSection(exesec).shtype=SHT_NOTE then + begin + if noteseg=nil then + noteseg:=CreateSegment(PT_NOTE,PF_R,4); + noteseg.Add(exesec); + Include(exesec.SecOptions,oso_debug_copy); + end; + if (oso_executable in exesec.SecOptions) or + not (oso_write in exesec.SecOptions) then + textseg.add(exesec) + else + dataseg.add(exesec); + end; + end; + + if dynamiclink then + begin + seg:=CreateSegment(PT_DYNAMIC,PF_R or PF_W,sizeof(pint)); + seg.add(dynamicsec.ExeSection); + end; + + { stack flags } + CreateSegment(PT_GNU_STACK,PF_R or PF_W or (PF_X*ord(ExecStack)),sizeof(pint)); + end; + + + procedure TElfExeOutput.PrepareGOT; + var + i,j,k: longint; + objsec: TElfObjSection; + exesec: TExeSection; + begin + for i:=0 to ExeSectionList.Count-1 do + begin + exesec:=TExeSection(ExeSectionList[i]); + for j:=0 to exesec.ObjSectionlist.count-1 do + begin + objsec:=TElfObjSection(exesec.ObjSectionlist[j]); + { ignore linker-generated and debug sections } + if (objsec.objdata=internalobjdata) or (oso_debug in objsec.SecOptions) then + continue; + if not objsec.Used then + internalerror(2012060901); + for k:=0 to objsec.ObjRelocations.Count-1 do + GOTRelocPass1(objsec,TObjRelocation(objsec.ObjRelocations[k])); + end; + end; + { remember sizes for sanity checking } + gotsize:=gotobjsec.size; + if assigned(dynrelocsec) then + dynrelsize:=dynrelocsec.size + else + dynrelsize:=0; + end; + + + procedure TElfExeOutput.Load_Start; + begin + inherited Load_Start; + dynsymlist:=TFPObjectList.Create(False); + + gotpltobjsec:=TElfObjSection.create_ext(internalObjData,'.got.plt', + SHT_PROGBITS,SHF_ALLOC or SHF_WRITE,sizeof(pint),sizeof(pint)); + + gotobjsec:=TElfObjSection.create_ext(internalObjData,'.got', + SHT_PROGBITS,SHF_ALLOC or SHF_WRITE,sizeof(pint),sizeof(pint)); + gotobjsec.SecOptions:=[oso_keep]; + + { GOT symbol and reserved .got.plt entries } + internalObjData.SetSection(gotpltobjsec); + gotsymbol:=internalObjData.SymbolDefine('_GLOBAL_OFFSET_TABLE_',AB_GLOBAL,AT_DATA); + gotpltobjsec.writeZeros(3*sizeof(pint)); + end; + + + procedure TElfExeOutput.Load_DynamicObject(objdata:TObjData); + begin + Comment(v_debug,'Dynamic object: '+objdata.name); + if neededlist.Find(objdata.name)=nil then + neededlist.Add(objdata.name,objdata); + end; + + + procedure TElfExeOutput.Order_Start; + begin + inherited Order_Start; + dynamiclink:=IsSharedLibrary or (dynsymlist.count>0) or + ( + (UnresolvedExeSymbols.Count>0) and + not (cs_link_staticflag in current_settings.globalswitches) + ); + if dynamiclink then + InitDynlink; + if dynamiclink or (IndirectObjSymbols.Count>0) then + CreatePLT; + end; + + + procedure TElfExeOutput.Order_end; + + procedure set_oso_keep(const s:string); + var + exesec:TExeSection; + objsec:TObjSection; + i:longint; + begin + exesec:=TExeSection(ExeSectionList.Find(s)); + if assigned(exesec) then + begin + for i:=0 to exesec.ObjSectionList.Count-1 do + begin + objsec:=TObjSection(exesec.ObjSectionList[i]); + { ignore sections used for symbol definition } + if oso_data in objsec.SecOptions then + objsec.SecOptions:=[oso_keep]; + end; + end; + end; + + begin + inherited Order_end; + set_oso_keep('.init'); + set_oso_keep('.fini'); + set_oso_keep('.jcr'); + set_oso_keep('.ctors'); + set_oso_keep('.dtors'); + set_oso_keep('.preinit_array'); + set_oso_keep('.init_array'); + set_oso_keep('.fini_array'); + set_oso_keep('.eh_frame'); + + { let .dynamic reference other dynamic sections so they aren't marked + for removal as unused } + if dynamiclink then + WriteDynamicTags; + end; + + + procedure TElfExeOutput.AfterUnusedSectionRemoval; + var + i:longint; + exesym:TExeSymbol; + objsym:TObjSymbol; + objsec: TObjSection; + begin + { Drop unresolved symbols that aren't referenced, assign dynamic + indices to remaining ones. } + for i:=0 to UnresolvedExeSymbols.Count-1 do + begin + exesym:=TExeSymbol(UnresolvedExeSymbols[i]); + if exesym.state=symstate_dynamic then + begin + if exesym.dynindex<>0 then + InternalError(2012062301); + exesym.dynindex:=dynsymlist.add(exesym)+1; + exesym.state:=symstate_defined; + end + else + UnresolvedExeSymbols[i]:=nil; + end; + UnresolvedExeSymbols.Pack; + + { Scan relocations to determine size of GOT, dynamic reloc section, etc. } + PrepareGOT; + + { Write required PLT entries } + for i:=0 to UnresolvedExeSymbols.Count-1 do + begin + exesym:=TExeSymbol(UnresolvedExeSymbols[i]); + + { if there's no PLT references to symbol, then PLT entry isn't needed } + { !! Does not work correctly yet !! } +// if (exesym.objsymbol.refs and symref_plt)=0 then +// continue; + + { This symbol has a valid address to which relocations are resolved, + but it remains (weak)external when written to dynamic symtable. } + objsym:=internalobjdata.CreateSymbol(exesym.name); + objsym.typ:=AT_FUNCTION; + objsym.bind:=exesym.ObjSymbol.bind; { AB_EXTERNAL or AB_WEAK_EXTERNAL } + objsym.offset:=pltobjsec.size; + objsym.objsection:=pltobjsec; + exesym.ObjSymbol:=objsym; + + WritePLTEntry(exesym); + end; + + { Handle indirect symbols } + for i:=0 to IndirectObjSymbols.Count-1 do + begin + objsym:=TObjSymbol(IndirectObjSymbols[i]); + objsec:=objsym.ExeSymbol.ObjSymbol.objsection; + objsym.bind:=AB_EXTERNAL; { cheat FixupSymbols } + if (oso_plt in objsec.SecOptions) then + continue; + WriteIndirectPLTEntry(objsym.ExeSymbol); + end; + + FixupSymbols; + + if dynamiclink then + WriteDynamicSymbolsHash; + end; + + + procedure TElfExeOutput.MemPos_Start; + var + i,j: longint; + seg: TElfSegment; + exesec: TElfExeSection; + objsec: TObjSection; + tempmempos: qword; + begin + { If using -Xg, .shstrtab needs oso_debug flag, otherwise it won't be written + to .dbg file. RemoveDebugInfo will then destroy ExeSection named .shstrtab, + objsection remains intact but its contents must be rewritten. } + if Assigned(shstrtabsect) then + begin + shstrtabsect.ReleaseData; + shstrtabsect.Size:=0; + shstrtabsect.SecOptions:=[oso_data]; + end + else + shstrtabsect:=TElfObjSection.create_ext(internalObjData,'.shstrtab',SHT_STRTAB,0,1,0); + if (cs_link_separate_dbg_file in current_settings.globalswitches) then + shstrtabsect.SecOptions:=[oso_debug]; + AttachSection(shstrtabsect); + shstrtabsect.writezeros(1); + + if (not gotwritten) then + begin + { Create the static symtable (.symtab and .strtab) } + if (cs_link_separate_dbg_file in current_settings.globalswitches) or + not(cs_link_strip in current_settings.globalswitches) then + begin + symtab:=TElfSymtab.Create(internalObjData,esk_exe); + symtab.SecOptions:=[oso_debug]; + symtab.fstrsec.SecOptions:=[oso_debug]; + AttachSection(symtab); + AttachSection(symtab.fstrsec); + end; + + { Re-enable sections which end up to contain some data + (.got, .rel[a].dyn, .rel[a].plt (includes .rel[a].iplt) and .hash } + if gotobjsec.size<>0 then + Exclude(gotobjsec.ExeSection.SecOptions,oso_disabled); + if assigned(dynrelocsec) and (dynrelocsec.size<>0) then + Exclude(dynrelocsec.ExeSection.SecOptions,oso_disabled); + if assigned(pltrelocsec) and (pltrelocsec.size>0) then + Exclude(pltrelocsec.ExeSection.SecOptions,oso_disabled); + if assigned(ipltrelocsec) and (ipltrelocsec.size>0) then + Exclude(ipltrelocsec.ExeSection.SecOptions,oso_disabled); + if assigned(hashobjsec) then + Exclude(hashobjsec.ExeSection.SecOptions,oso_disabled); + end; + + RemoveDisabledSections; + + SegmentList.Clear; + textseg:=nil; + dataseg:=nil; + phdrseg:=nil; + noteseg:=nil; + MapSectionsToSegments; + + { Assign section indices and fill .shstrtab + List of sections cannot be modified after this point. } + for i:=0 to ExeSectionList.Count-1 do + begin + exesec:=TElfExeSection(ExeSectionList[i]); + exesec.shstridx:=shstrtabsect.writestr(exesec.Name); + exesec.secshidx:=i+1; + end; + + if dynamiclink then + begin + if (not gotwritten) then + FinishDynamicTags; + + { fixup sh_link/sh_info members of various dynamic sections } + TElfExeSection(hashobjsec.ExeSection).shlink:=TElfExeSection(dynsymtable.ExeSection).secshidx; + i:=TElfExeSection(dynsymtable.fstrsec.ExeSection).secshidx; + TElfExeSection(dynamicsec.ExeSection).shlink:=i; + TElfExeSection(dynsymtable.ExeSection).shlink:=i; + + if assigned(pltrelocsec) then + begin + TElfExeSection(pltrelocsec.ExeSection).shlink:=TElfExeSection(dynsymtable.ExeSection).secshidx; + TElfExeSection(pltrelocsec.ExeSection).shinfo:=TElfExeSection(pltobjsec.ExeSection).secshidx; + end; + + if assigned(dynrelocsec) and assigned(dynrelocsec.ExeSection) then + TElfExeSection(dynrelocsec.ExeSection).shlink:=TElfExeSection(dynsymtable.ExeSection).secshidx; + end + else if assigned(ipltrelocsec) then + TElfExeSection(ipltrelocsec.ExeSection).shinfo:=TElfExeSection(pltobjsec.ExeSection).secshidx; + + { The actual layout } + if IsSharedLibrary then + CurrMemPos:=0 + else + CurrMemPos:=TEXT_SEGMENT_START; + textseg.MemPos:=CurrMemPos; + if assigned(phdrseg) then + begin + phdrseg.Mempos:=CurrMemPos+sizeof(TElfHeader); + phdrseg.Memsize:=sizeof(TElfproghdr)*segmentlist.count; + end; + CurrMemPos:=CurrMemPos+sizeof(TElfHeader)+segmentlist.count*sizeof(TElfproghdr); + MemPos_Segment(textseg); + CurrMemPos:=Align(CurrMemPos,SectionDataAlign); {! Data,not MemAlign} + CurrMemPos:=CurrMemPos+ELF_MAXPAGESIZE; + dataseg.MemPos:=CurrMemPos; + MemPos_Segment(dataseg); + { Mempos of unmapped sections is forced to zero, but we have to set positions + of its objsections and update sizes } + for i:=0 to ExeSectionList.Count-1 do + begin + exesec:=TElfExeSection(ExeSectionList[i]); + if not (oso_load in exesec.SecOptions) then + begin + tempmempos:=0; + exesec.MemPos:=tempmempos; + for j:=0 to exesec.ObjSectionList.Count-1 do + begin + objsec:=TObjSection(exesec.ObjSectionList[j]); + tempmempos:=objsec.setmempos(tempmempos); + end; + exesec.Size:=tempmempos; + end; + end; + + { Update MemPos and MemSize of non-load segments, + in particular, TLS sizes are needed to resolve relocations } + for i:=0 to segmentlist.count-1 do + begin + seg:=TElfSegment(segmentlist[i]); + if (seg.ptype=PT_LOAD) or (seg.FSectionList.Count=0) then + continue; + seg.MemPos:=TExeSection(seg.FSectionList.First).MemPos; + for j:=0 to seg.FSectionList.Count-1 do + begin + exesec:=TElfExeSection(seg.FSectionList[j]); + seg.MemSize:=exesec.MemPos+exesec.Size-seg.MemPos; + end; + end; + + if (not gotwritten) then + begin + { reset size of .got and .rel[a].dyn, they will be refilled while fixing up relocations } + if assigned(gotobjsec) then + gotobjsec.size:=0; + if assigned(dynrelocsec) then + begin + dynrelocsec.size:=0; + { write actual .dynsym content (needs valid symbol addresses) } + dynsymtable.size:=sizeof(TElfsymbol); + for i:=0 to dynsymlist.count-1 do + dynsymtable.writeSymbol(TExeSymbol(dynsymlist[i]).objsymbol,dynsymnames[i]); + end; + end; + end; + + + procedure TElfExeOutput.MemPos_Segment(seg:TElfSegment); + var + i: longint; + exesec: TElfExeSection; + begin + for i:=0 to seg.FSectionList.Count-1 do + begin + exesec:=TElfExeSection(seg.FSectionList[i]); + inherited MemPos_ExeSection(exesec); + { .tbss should not contribute to address space } + if (exesec.shtype=SHT_NOBITS) and ((exesec.shflags and SHF_TLS)<>0) then + CurrMemPos:=exesec.MemPos; + end; + { calculate size of the segment } + seg.MemSize:=CurrMemPos-seg.MemPos; + end; + + + procedure TElfExeOutput.MemPos_ExeSection(const aname:string); + begin + // Ignore. All layout is done in mempos_start + end; + + + procedure TElfExeOutput.DataPos_Start; + var + i,j: longint; + exesec: TExeSection; + seg: TElfSegment; + begin + gotwritten:=true; + { sanity checks } + if assigned(gotobjsec) and (gotsize<>gotobjsec.size) then + InternalError(2012092501); + if assigned(dynrelocsec) and (dynrelsize<>dynrelocsec.size) then + InternalError(2012092502); + + if (ExeWriteMode=ewm_dbgonly) or + ( + (ExeWriteMode=ewm_exefull) and + not(cs_link_strip in current_settings.globalswitches) + ) then + WriteStaticSymtable; + + { first handle primary segments } + textseg.DataPos:=0; + CurrDataPos:=sizeof(TElfHeader)+sizeof(TElfproghdr)*segmentlist.count; + if assigned(phdrseg) then + begin + phdrseg.DataPos:=sizeof(TElfHeader); + phdrseg.DataSize:=sizeof(TElfproghdr)*segmentlist.count; + end; + DataPos_Segment(textseg); + CurrDataPos:=align(CurrDataPos,SectionDataAlign); + dataseg.DataPos:=CurrDataPos; + DataPos_Segment(dataseg); + { then unmapped sections } + for i:=0 to ExeSectionList.Count-1 do + begin + exesec:=TExeSection(ExeSectionList[i]); + if not (oso_load in exesec.SecOptions) then + inherited DataPos_ExeSection(exesec); + end; + { finally, update size/position of non-load segments } + for i:=0 to segmentlist.count-1 do + begin + seg:=TElfSegment(segmentlist[i]); + if (seg.ptype=PT_LOAD) or (seg.FSectionList.Count=0) then + continue; + seg.DataPos:=TExeSection(seg.FSectionList.First).DataPos; + for j:=0 to seg.FSectionList.Count-1 do + begin + exesec:=TExeSection(seg.FSectionList[j]); + if oso_data in exesec.SecOptions then + seg.DataSize:=exesec.DataPos+exesec.Size-seg.DataPos; + end; + end; + { place section headers after the data } + shoffset:=CurrDataPos; + CurrDataPos:=CurrDataPos+ExeSectionList.Count*sizeof(TElfsechdr); + end; + + + procedure TElfExeOutput.DataPos_Segment(seg:TElfSegment); + var + i: longint; + exesec: TElfExeSection; + begin + for i:=0 to seg.FSectionList.Count-1 do + begin + exesec:=TElfExeSection(seg.FSectionList[i]); + { ELF needs DataPos set to 'would-be' value for sections that + don't have data, and for non-debug sections in .dbg file, too. + This slightly differs from generic approach. } + if not (oso_data in exesec.SecOptions) or + ( + (ExeWriteMode=ewm_dbgonly) and + (exesec.SecOptions*[oso_debug,oso_debug_copy]=[]) + ) then + begin + CurrDataPos:=align(CurrDataPos,SectionDataAlign); + exesec.DataPos:=CurrDataPos; + end + else + inherited DataPos_ExeSection(exesec); + + end; + { calculate size of the segment } + seg.DataSize:=CurrDataPos-seg.DataPos; + end; + + + procedure TElfExeOutput.DataPos_ExeSection(const aname:string); + begin + // Ignore. Work is done entirely in datapos_start. + end; + + + procedure TElfExeOutput.InitDynlink; + begin + if not IsSharedLibrary then + begin + interpobjsec:=internalObjData.createsection('.interp',1,[oso_data,oso_load,oso_keep]); + { TODO: supply target-specific default } +{$ifdef x86_64} + interpobjsec.writestr('/lib64/ld-linux-x86-64.so.2'); +{$else} + interpobjsec.writestr('/lib/ld-linux.so.2'); +{$endif x86_64} + end; + + hashobjsec:=TElfObjSection.create_ext(internalObjData,'.hash', + SHT_HASH,SHF_ALLOC,sizeof(pint),4); + hashobjsec.secoptions:=[oso_keep]; + + dynsymtable:=TElfSymtab.create(internalObjData,esk_dyn); + + dynamicsec:=TElfObjSection.create_ext(internalObjData,'.dynamic', + SHT_DYNAMIC,SHF_ALLOC or SHF_WRITE,sizeof(pint),sizeof(TElfDyn)); + dynamicsec.SecOptions:=[oso_keep]; + + dynrelocsec:=TElfObjSection.create_reloc(internalObjData,'.dyn',true); + dynrelocsec.SecOptions:=[oso_keep]; + end; + + + const + hashbuckets: array[0..15] of longint=( + 1, 3, 17, 37, 67, 97, 131, 197, 263, 521, 1031, 2053, 4099, 8209, + 16411, 32771); + +{$push}{$r-,q-} + function elfhash(const name:string):longword; + var + g: longword; + i: longint; + begin + result:=0; + for i:=1 to length(name) do + begin + result:=(result shl 4)+ord(name[i]); + g:=result and $F0000000; + if g>0 then + result:=result xor (g shr 24); + result:=result and (not g); + end; + end; +{$pop} + + procedure TElfExeOutput.WriteDynamicSymbolsHash; + var + nchains,nbuckets: longint; + i,j: longint; + hashdata: plongint; + sym: TExeSymbol; + begin + dynsymnames:=AllocMem(dynsymlist.count*sizeof(longword)); + nchains:=dynsymlist.Count+1; + { determine suitable bucket count } + i:=high(hashbuckets); + while (i>=0) and (nchains0 do + j:=2+nbuckets+hashdata[j]; + hashdata[j]:=i+1; + end; + if source_info.endian<>target_info.endian then + for i:=0 to nchains+nbuckets+1 do + hashdata[i]:=swapendian(hashdata[i]); + hashobjsec.write(hashdata^,(2+nchains+nbuckets)*sizeof(longint)); + freemem(hashdata); + end; + + + procedure TElfExeOutput.WriteDynRelocEntry(dataofs:aword;typ:byte;symidx:aword;addend:aword); + var + rel:telfreloc; + begin + rel.address:=dataofs; + rel.info:=ELF_R_INFO(symidx,typ); + rel.addend:=addend; + MaybeSwapElfReloc(rel); + dynrelocsec.write(rel,dynrelocsec.shentsize); + end; + + + procedure TElfExeOutput.WriteDynTag(aTag:longword;aValue:longword); + var + d: TElfDyn; + begin + d.d_tag:=aTag; + d.d_val:=aValue; + MaybeSwapElfDyn(d); + dynamicsec.write(d,sizeof(TElfDyn)); + end; + + + procedure TElfExeOutput.WriteDynTag(aTag:longword;aSection:TObjSection;aOffs:aword); + var + d: TElfDyn; + begin + d.d_tag:=aTag; + if source_info.endian<>target_info.endian then + d.d_tag:=swapendian(d.d_tag); + dynamicsec.write(d.d_tag,sizeof(d.d_tag)); + { TODO: ignores endianness! } + dynamicsec.writeReloc_internal(aSection,aOffs,sizeof(d.d_ptr),RELOC_ABSOLUTE); + end; + + + procedure TElfExeOutput.WriteDynamicTags; + var + s: aword; + i: longint; + sym: TExeSymbol; + hs:string; + begin + for i:=0 to neededlist.Count-1 do + begin + s:=dynsymtable.fstrsec.writestr(neededlist.NameOfIndex(i)); + WriteDynTag(DT_NEEDED,s); + end; + + if IsSharedLibrary then + begin + s:=dynsymtable.fstrsec.writestr(ExtractFileName(current_module.sharedlibfilename)); + WriteDynTag(DT_SONAME,s); + { TODO: names hardcoded here } + sym:=TExeSymbol(ExeSymbolList.Find('FPC_SHARED_LIB_START')); + if assigned(sym) then + WriteDynTag(DT_INIT,sym.objsymbol.objsection,sym.objsymbol.offset); + sym:=TExeSymbol(ExeSymbolList.Find('FPC_LIB_EXIT')); + if assigned(sym) then + WriteDynTag(DT_FINI,sym.objsymbol.objsection,sym.objsymbol.offset); + end; + + { TODO: we need a dedicated parameter to pass runpath, instead of this hack + (-Xr is a different thing, it passes "-rpath-link"). } + if (ParaLinkOptions<>'') then + begin + hs:=ParaLinkOptions; + while (hs<>'') do + begin + if (GetToken(hs,' ')='-rpath') then + begin + s:=dynsymtable.fstrsec.writestr(GetToken(hs,' ')); + WriteDynTag(DT_RPATH,s); + end; + end; + end; + + writeDynTag(DT_HASH,hashobjsec); + writeDynTag(DT_STRTAB,dynsymtable.fstrsec); + writeDynTag(DT_SYMTAB,dynsymtable); + + writeDynTag(DT_SYMENT,sizeof(TElfSymbol)); + + if Assigned(gotpltobjsec) then + writeDynTag(DT_PLTGOT,gotpltobjsec); + end; + + + const + pltreltags: array[boolean] of longword=(DT_REL,DT_RELA); + relsztags: array[boolean] of longword=(DT_RELSZ,DT_RELASZ); + relenttags: array[boolean] of longword=(DT_RELENT,DT_RELAENT); + + procedure TElfExeOutput.FinishDynamicTags; + begin + if assigned(dynsymtable) then + writeDynTag(DT_STRSZ,dynsymtable.fstrsec.size); + + if hastextrelocs then + writeDynTag(DT_TEXTREL,0); + + if Assigned(pltrelocsec) and (pltrelocsec.size>0) then + begin + writeDynTag(DT_PLTRELSZ,pltrelocsec.Size); + writeDynTag(DT_PLTREL,pltreltags[relocs_use_addend]); + writeDynTag(DT_JMPREL,pltrelocsec); + end; + + if Assigned(dynrelocsec) and (dynrelocsec.size>0) then + begin + writeDynTag(pltreltags[relocs_use_addend],dynrelocsec); + writeDynTag(relsztags[relocs_use_addend],dynrelocsec.Size); + writeDynTag(relenttags[relocs_use_addend],dynrelocsec.shentsize); + end; + writeDynTag(DT_NULL,0); + end; + + + procedure TElfExeOutput.CreatePLT; + var + reloc: TObjRelocation; + begin + pltobjsec:=TElfObjSection.create_ext(internalObjData,'.plt', + SHT_PROGBITS,SHF_ALLOC or SHF_EXECINSTR,4,16); + pltobjsec.SecOptions:=[oso_keep,oso_plt]; + + pltrelocsec:=TElfObjSection.create_reloc(internalObjData,'.plt',true); + pltrelocsec.SecOptions:=[oso_keep]; + + ipltrelocsec:=TElfObjSection.create_reloc(internalObjData,'.iplt',true); + ipltrelocsec.SecOptions:=[oso_keep]; + + { reference .dynamic from .got.plt, this isn't necessary if linking statically } + { TODO: maybe move writing initial .got.plt entries here completely + (needs testing --- GOT symbol may get lost if .got.plt is empty)} + if dynamiclink then + begin + reloc:=TObjRelocation.CreateSection(0,dynamicsec,RELOC_ABSOLUTE); + reloc.size:=sizeof(pint); + gotpltobjsec.ObjRelocations.Add(reloc); + end; + + { Initial PLT entry, CPU-specific } + WriteFirstPLTEntry; + end; + + + procedure TElfExeOutput.WritePLTEntry(exesym:TExeSymbol); + begin + // must be implemented by CPU-specific descendant + InternalError(2012092102); + end; + + + procedure TElfExeOutput.WriteIndirectPLTEntry(exesym:TExeSymbol); + begin + // must be implemented by CPU-specific descendant + InternalError(2012092101); + end; + + + function TElfExeOutput.WriteData:boolean; + begin + WriteHeader; + segmentlist.ForEachCall(@segment_write_header,nil); + WriteExeSectionContent; + FWriter.WriteZeros(sizeof(TElfsechdr)); + ExeSectionList.ForEachCall(@exesection_write_header,nil); + result:=true; + end; + + + procedure TElfExeOutput.GenerateLibraryImports(ImportLibraryList:TFPHashObjectList); + var + i:longint; + exportlist: TCmdStrList; + sym: TExeSymbol; + begin + { add exported symbols to dynamic list } + exportlist:=texportlibunix(exportlib).exportedsymnames; + if not exportlist.empty then + repeat + sym:=TExeSymbol(ExeSymbolList.Find(exportlist.getfirst)); + if assigned(sym) then + begin + if assigned(sym.objsymbol.objsection) then + sym.objsymbol.objsection.SecOptions:=[oso_keep]; + sym.dynindex:=dynsymlist.add(sym)+1 + end + else + InternalError(2012071801); + until exportlist.empty; + + if not (cs_link_staticflag in current_settings.globalswitches) then + for i:=0 to UnresolvedExeSymbols.Count-1 do + TExeSymbol(UnresolvedExeSymbols[i]).state:=symstate_defined; + end; + + +{**************************************************************************** + TElfExeSection +****************************************************************************} + + procedure TElfExeSection.AddObjSection(objsec:TObjSection;ignoreprops:boolean); + begin + inherited AddObjSection(objsec,ignoreprops); + if ignoreprops then + exit; + if (shtype=SHT_NULL) then + begin + shtype:=TElfObjSection(objsec).shtype; + shflags:=TElfObjSection(objsec).shflags; + shentsize:=TElfObjSection(objsec).shentsize; + end; + end; + +{**************************************************************************** + TElfSegment +****************************************************************************} + + constructor TElfSegment.Create(atype,aflags,aalign:longword); + begin + ptype:=atype; + pflags:=aflags; + align:=aalign; + FSectionList:=TFPObjectList.Create(false); + end; + + destructor TElfSegment.Destroy; + begin + FSectionList.Free; + inherited Destroy; + end; + + procedure TElfSegment.Add(exesec:TExeSection); + begin + FSectionList.Add(exesec); + end; + + end. diff --git a/compiler/sparc/cpuelf.pas b/compiler/sparc/cpuelf.pas index f76d2e0872..15cbc8ad7a 100644 --- a/compiler/sparc/cpuelf.pas +++ b/compiler/sparc/cpuelf.pas @@ -32,31 +32,50 @@ implementation systems,ogbase,ogelf,assemble; type - TElfObjOutputSparc=class(TElfObjectOutput) - function encodereloc(objrel:TObjRelocation):byte;override; - end; - - TElfAssemblerSparc=class(TInternalAssembler) - constructor create(smart:boolean);override; + TElfTargetSparc=class(TElfTarget) + class function encodereloc(objrel:TObjRelocation):byte;override; + class procedure loadreloc(objrel:TObjRelocation);override; end; const { Relocation types } + R_SPARC_NONE = 0; + R_SPARC_8 = 1; + R_SPARC_16 = 2; R_SPARC_32 = 3; + R_SPARC_DISP8 = 4; + R_SPARC_DISP16 = 5; + R_SPARC_DISP32 = 6; R_SPARC_WDISP30 = 7; + R_SPARC_WDISP22 = 8; R_SPARC_HI22 = 9; + R_SPARC_22 = 10; + R_SPARC_13 = 11; R_SPARC_LO10 = 12; + R_SPARC_GOT10 = 13; + R_SPARC_GOT13 = 14; + R_SPARC_GOT22 = 15; + R_SPARC_PC10 = 16; + R_SPARC_PC22 = 17; + R_SPARC_WPLT30 = 18; + R_SPARC_COPY = 19; + R_SPARC_GLOB_DAT = 20; + R_SPARC_JMP_SLOT = 21; + R_SPARC_RELATIVE = 22; + R_SPARC_UA32 = 23; R_SPARC_GNU_VTINHERIT = 250; R_SPARC_GNU_VTENTRY = 251; {**************************************************************************** - TElfObjOutputSparc + TElfTargetSparc ****************************************************************************} - function TElfObjOutputSparc.encodereloc(objrel:TObjRelocation):byte; + class function TElfTargetSparc.encodereloc(objrel:TObjRelocation):byte; begin case objrel.typ of + RELOC_NONE : + result:=R_SPARC_NONE; RELOC_ABSOLUTE : result:=R_SPARC_32; { TODO } @@ -67,15 +86,10 @@ implementation end; -{**************************************************************************** - TElfAssemblerSparc -****************************************************************************} + class procedure TElfTargetSparc.loadreloc(objrel:TObjRelocation); + begin + end; - constructor TElfAssemblerSparc.create(smart:boolean); - begin - inherited Create(smart); - CObjOutput:=TElfObjOutputSparc; - end; {***************************************************************************** @@ -99,7 +113,8 @@ implementation ); initialization - RegisterAssembler(as_sparc_elf32_info,TElfAssemblerSparc); + RegisterAssembler(as_sparc_elf32_info,TElfAssembler); + ElfTarget:=TElfTargetSparc; end. diff --git a/compiler/systems/t_linux.pas b/compiler/systems/t_linux.pas index 076ba1f9b5..d4693e457c 100644 --- a/compiler/systems/t_linux.pas +++ b/compiler/systems/t_linux.pas @@ -58,6 +58,18 @@ interface procedure LoadPredefinedLibraryOrder; override; end; + TInternalLinkerLinux=class(TInternalLinker) + private + libctype: TLibcType; + reorder: boolean; + linklibc: boolean; + prtobj: string[20]; + dynlinker: string[100]; + public + constructor Create;override; + procedure DefaultLinkScript;override; + procedure InitSysInitUnitName;override; + end; implementation @@ -70,6 +82,7 @@ implementation aasmbase,aasmtai,aasmcpu,cpubase, cgbase,cgobj,cgutils,ogbase,ncgutil, comprsrc, + ogelf, rescmn, i_linux ; @@ -1128,6 +1141,324 @@ begin MakeSharedLibrary:=success; { otherwise a recursive call to link method } end; +{***************************************************************************** + TINTERNALLINKERLINUX +*****************************************************************************} + +constructor TInternalLinkerLinux.Create; +begin + inherited Create; + SetupLibrarySearchPath; + SetupDynlinker(dynlinker,libctype); + + CExeOutput:=ElfExeOutputClass; + CObjInput:=TElfObjInput; + +end; + +procedure TInternalLinkerLinux.InitSysInitUnitName; +begin + linklibc:=ModulesLinkToLibc; + reorder:=linklibc and ReOrderEntries; + sysinitunit:=defsinames[current_module.islibrary]; + prtobj:=defprtnames[current_module.islibrary]; + + if cs_profile in current_settings.moduleswitches then + begin + prtobj:=gprtnames[libctype]; + sysinitunit:=gsinames[libctype]; + linklibc:=true; + end + else if linklibc then + begin + prtobj:=cprtnames[libctype]; + sysinitunit:=csinames[libctype]; + end; +end; + + +procedure TInternalLinkerLinux.DefaultLinkScript; +var + s,s1,s2:TCmdStr; + found1,found2:boolean; + linkToSharedLibs:boolean; + + procedure AddLibraryStatement(const s:TCmdStr); + var + i:longint; + s1,s2:TCmdStr; + begin + i:=pos(target_info.sharedClibext+'.',s); + if (i>0) then + s1:=target_info.sharedClibprefix+S + else + s1:=target_info.sharedClibprefix+S+target_info.sharedClibext; + { TODO: to be compatible with ld search algorithm, each found file + must be tested for target compatibility, incompatible ones should be skipped. } + { TODO: shall we search library without suffix if one with suffix is not found? } + if (not(cs_link_staticflag in current_settings.globalswitches)) and + FindLibraryFile(s1,'','',s2) then + LinkScript.Concat('READSTATICLIBRARY '+maybequoted(s2)) + { TODO: static libraries never have numeric suffix in their names } + else if FindLibraryFile(s,target_info.staticClibprefix,target_info.staticClibext,s2) then + LinkScript.Concat('READSTATICLIBRARY '+maybequoted(s2)) + else + Comment(V_Error,'Import library not found for '+S); + end; + +begin + if cs_profile in current_settings.moduleswitches then + begin + if not(libctype in [glibc2,glibc21]) then + AddSharedLibrary('gmon'); + AddSharedLibrary('c'); + end; + + { add objectfiles, start with prt0 always } + if not (target_info.system in systems_internal_sysinit) and (prtobj<>'') then + LinkScript.Concat('READOBJECT '+ maybequoted(FindObjectFile(prtobj,'',false))); + + { try to add crti and crtbegin if linking to C } + if linklibc and (libctype<>uclibc) then + begin + { crti.o must come first } + if librarysearchpath.FindFile('crti.o',false,s) then + LinkScript.Concat('READOBJECT '+maybequoted(s)); + { then the crtbegin* } + if cs_create_pic in current_settings.moduleswitches then + begin + if librarysearchpath.FindFile('crtbeginS.o',false,s) then + LinkScript.Concat('READOBJECT '+maybequoted(s)); + end + else + if (cs_link_staticflag in current_settings.globalswitches) and + librarysearchpath.FindFile('crtbeginT.o',false,s) then + LinkScript.Concat('READOBJECT '+maybequoted(s)) + else if librarysearchpath.FindFile('crtbegin.o',false,s) then + LinkScript.Concat('READOBJECT '+maybequoted(s)); + end; + + ScriptAddSourceStatements(false); + { we must reorder here because the result could empty sharedlibfiles } + if reorder then + ExpandAndApplyOrder(SharedLibFiles); + + { See tw9089*.pp: if more than one pure-Pascal shared libs are loaded, + and none have rtld in their DT_NEEDED, then rtld cannot finalize correctly. } + if IsSharedLibrary then + LinkScript.Concat('READSTATICLIBRARY '+maybequoted(dynlinker)); + + linkToSharedLibs:=(not SharedLibFiles.Empty); + + { Symbols declared as "external 'libx.so'" are added to ImportLibraryList, library + prefix/extension *not* stripped. TImportLibLinux copies these to SharedLibFiles, + stripping prefixes and extensions. + However extension won't be stripped if library is specified with numeric suffix + (like "libpango-1.0.so.0") + Libraries specified with $LINKLIB directive are directly added to SharedLibFiles + and won't be present in ImportLibraryList. } + while not SharedLibFiles.Empty do + begin + S:=SharedLibFiles.GetFirst; + if (S<>'c') or reorder then + AddLibraryStatement(S); + end; + + if (cs_link_staticflag in current_settings.globalswitches) or + (linklibc and not reorder) then + begin + if (cs_link_staticflag in current_settings.globalswitches) then + begin + AddLibraryStatement('gcc'); + AddLibraryStatement('gcc_eh'); + end; + if linklibc and not reorder then + AddLibraryStatement('c'); + end; + + { objects which must be at the end } + if linklibc and (libctype<>uclibc) then + begin + if cs_create_pic in current_settings.moduleswitches then + found1:=librarysearchpath.FindFile('crtendS.o',false,s1) + else + found1:=librarysearchpath.FindFile('crtend.o',false,s1); + found2:=librarysearchpath.FindFile('crtn.o',false,s2); + if found1 then + LinkScript.Concat('READOBJECT '+maybequoted(s1)); + if found2 then + LinkScript.Concat('READOBJECT '+maybequoted(s2)); + end; + + if (not IsSharedLibrary) then + if (linkToSharedLibs and not linklibc) then + LinkScript.Concat('ENTRYNAME _dynamic_start') + else + LinkScript.Concat('ENTRYNAME _start') + else + LinkScript.Concat('ISSHAREDLIBRARY'); + + with LinkScript do + begin + Concat('HEADER'); + Concat('EXESECTION .interp'); + Concat(' OBJSECTION .interp'); + Concat('ENDEXESECTION'); + Concat('EXESECTION .note.ABI-tag'); + Concat(' OBJSECTION .note.ABI-tag'); + Concat('ENDEXESECTION'); + Concat('EXESECTION .note.gnu.build-id'); + Concat(' OBJSECTION .note.gnu.build-id'); + Concat('ENDEXESECTION'); + Concat('EXESECTION .hash'); + Concat(' OBJSECTION .hash'); + Concat('ENDEXESECTION'); + Concat('EXESECTION .dynsym'); + Concat(' OBJSECTION .dynsym'); + Concat('ENDEXESECTION'); + Concat('EXESECTION .dynstr'); + Concat(' OBJSECTION .dynstr'); + Concat('ENDEXESECTION'); +{$ifdef x86_64} + Concat('EXESECTION .rela.dyn'); + Concat(' OBJSECTION .rela.dyn'); +{$else} + Concat('EXESECTION .rel.dyn'); + Concat(' OBJSECTION .rel.dyn'); +{$endif} + + Concat('ENDEXESECTION'); +{$ifdef x86_64} + Concat('EXESECTION .rela.plt'); + Concat(' OBJSECTION .rela.plt'); + Concat(' PROVIDE __rela_iplt_start'); + Concat(' OBJSECTION .rela.iplt'); + Concat(' PROVIDE __rela_iplt_end'); +{$else} + Concat('EXESECTION .rel.plt'); + Concat(' OBJSECTION .rel.plt'); +{$endif} + Concat('ENDEXESECTION'); + Concat('EXESECTION .init'); + Concat(' OBJSECTION .init'); + Concat('ENDEXESECTION'); + Concat('EXESECTION .plt'); + Concat(' OBJSECTION .plt'); + Concat('ENDEXESECTION'); + Concat('EXESECTION .text'); + Concat(' OBJSECTION .text*'); + Concat('ENDEXESECTION'); + + { This is not in standard ld scripts, it is handled by 'orphan section' functionality } + Concat('EXESECTION __libc_thread_freeres_fn'); + Concat(' PROVIDE __start__libc_thread_freeres_fn'); + Concat(' OBJSECTION __libc_thread_freeres_fn'); + Concat(' PROVIDE __stop__libc_thread_freeres_fn'); + Concat('ENDEXESECTION'); + + Concat('EXESECTION __libc_freeres_fn'); + Concat(' PROVIDE __start__libc_freeres_fn'); + Concat(' OBJSECTION __libc_freeres_fn'); + Concat(' PROVIDE __stop__libc_freeres_fn'); + Concat('ENDEXESECTION'); + + Concat('EXESECTION .fini'); + Concat(' OBJSECTION .fini'); + Concat(' PROVIDE __etext'); + Concat(' PROVIDE _etext'); + Concat(' PROVIDE etext'); + Concat('ENDEXESECTION'); + Concat('EXESECTION .rodata'); + Concat(' OBJSECTION .rodata*'); + Concat('ENDEXESECTION'); + + Concat('EXESECTION .eh_frame'); + Concat(' OBJSECTION .eh_frame'); + Concat('ENDEXESECTION'); + Concat('EXESECTION .gcc_except_table'); + Concat(' OBJSECTION .gcc_except_table'); + Concat(' OBJSECTION .gcc_except_table.*'); + Concat('ENDEXESECTION'); + Concat('EXESECTION .tdata'); + Concat(' OBJSECTION .tdata'); + Concat(' OBJSECTION .tdata.*'); + Concat('ENDEXESECTION'); + Concat('EXESECTION .tbss'); + Concat(' OBJSECTION .tbss'); + Concat(' OBJSECTION .tbss.*'); + Concat('ENDEXESECTION'); + Concat('EXESECTION .preinit_array'); + Concat(' PROVIDE __preinit_array_start'); + Concat(' OBJSECTION .preinit_array'); + Concat(' PROVIDE __preinit_array_end'); + Concat('ENDEXESECTION'); + Concat('EXESECTION .init_array'); + Concat(' PROVIDE __init_array_start'); + { why the hell .ctors are both here and exesection .ctors below?? } + // KEEP ( *(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*))) + Concat(' OBJSECTION .init_array'); + // KEEP ( *(EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors)) + Concat('PROVIDE __init_array_end'); + Concat('ENDEXESECTION'); + Concat('EXESECTION .fini_array'); + Concat(' PROVIDE __fini_array_start'); + // KEEP ( *(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*))) + Concat(' OBJSECTION .fini_array'); + // KEEP ( *(EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors)) + Concat(' PROVIDE __fini_array_end'); + Concat('ENDEXESECTION'); + + Concat('EXESECTION .ctors'); + Concat(' OBJSECTION .ctors*'); + Concat('ENDEXESECTION'); + Concat('EXESECTION .dtors'); + Concat(' OBJSECTION .dtors*'); + Concat('ENDEXESECTION'); + Concat('EXESECTION .jcr'); + Concat(' OBJSECTION .jcr'); + Concat('ENDEXESECTION'); + Concat('EXESECTION .dynamic'); + Concat(' OBJSECTION .dynamic'); + Concat('ENDEXESECTION'); + Concat('EXESECTION .got'); + Concat(' OBJSECTION .got'); + Concat('ENDEXESECTION'); + Concat('EXESECTION .got.plt'); + Concat(' OBJSECTION .got.plt'); + Concat('ENDEXESECTION'); + Concat('EXESECTION .data'); + Concat(' OBJSECTION .data*'); + Concat(' OBJSECTION .fpc*'); + Concat(' OBJSECTION fpc.resources'); + Concat(' PROVIDE _edata'); + Concat(' PROVIDE edata'); + Concat('ENDEXESECTION'); + Concat('EXESECTION .bss'); + Concat(' OBJSECTION .bss*'); + Concat(' OBJSECTION fpc.reshandles'); + Concat(' PROVIDE end'); + Concat(' SYMBOL _end'); + Concat('ENDEXESECTION'); + + { This is not in standard ld scripts, it is handled by 'orphan section' functionality } + Concat('EXESECTION __libc_freeres_ptrs'); + Concat(' PROVIDE __start__libc_freeres_ptrs'); + Concat(' OBJSECTION __libc_freeres_ptrs'); + Concat(' PROVIDE __stop__libc_freeres_ptrs'); + Concat('ENDEXESECTION'); + + ScriptAddGenericSections('.debug_aranges,.debug_pubnames,.debug_info,'+ + '.debug_abbrev,.debug_line,.debug_frame,.debug_str,.debug_loc,'+ + '.debug_macinfo,.debug_weaknames,.debug_funcnames,.debug_typenames,.debug_varnames,.debug_ranges'); + Concat('EXESECTION .stab'); + Concat(' OBJSECTION .stab'); + Concat('ENDEXESECTION'); + Concat('EXESECTION .stabstr'); + Concat(' OBJSECTION .stabstr'); + Concat('ENDEXESECTION'); + end; +end; + {***************************************************************************** Initialize *****************************************************************************} diff --git a/compiler/x86_64/cpuelf.pas b/compiler/x86_64/cpuelf.pas index 51bac89f08..13d6a90b5b 100644 --- a/compiler/x86_64/cpuelf.pas +++ b/compiler/x86_64/cpuelf.pas @@ -28,16 +28,28 @@ interface implementation uses + globtype,cutils,cclasses, verbose, - systems,ogbase,ogelf,assemble; + systems,aasmbase,ogbase,ogelf,assemble; type - TElfObjOutputx86_64=class(TElfObjectOutput) - function encodereloc(objrel:TObjRelocation):byte;override; + TElfTargetx86_64=class(TElfTarget) + class function encodereloc(objrel:TObjRelocation):byte;override; + class procedure loadreloc(objrel:TObjRelocation);override; end; - TElfAssemblerx86_64=class(TInternalAssembler) - constructor create(smart:boolean);override; + TElfExeOutputx86_64=class(TElfExeOutput) + private + function RelocName(reltyp:byte):string; + procedure MaybeWriteGOTEntry(reltyp:byte;relocval:aint;exesym:TExeSymbol); + procedure ReportNonDSOReloc(reltyp:byte;objsec:TObjSection;ObjReloc:TObjRelocation); + procedure ReportRelocOverflow(reltyp:byte;objsec:TObjSection;ObjReloc:TObjRelocation); + protected + procedure WriteFirstPLTEntry;override; + procedure WritePLTEntry(exesym:TExeSymbol);override; + procedure WriteIndirectPLTEntry(exesym:TExeSymbol);override; + procedure GOTRelocPass1(objsec:TObjSection;ObjReloc:TObjRelocation);override; + procedure DoRelocationFixup(objsec:TObjSection);override; end; const @@ -86,14 +98,64 @@ implementation R_X86_64_GNU_VTINHERIT = 250; { GNU extension to record C++ vtable hierarchy } R_X86_64_GNU_VTENTRY = 251; { GNU extension to record C++ vtable member usage } + type + TRelocProp=record + name: PChar; + size: byte; + end; + + const + relocprops: array[0..37] of TRelocProp=( + (name: 'R_X86_64_NONE'; size:0), + (name: 'R_X86_64_64'; size:8), + (name: 'R_X86_64_PC32'; size:4), + (name: 'R_X86_64_GOT32'; size:4), + (name: 'R_X86_64_PLT32'; size:4), + (name: 'R_X86_64_COPY'; size:0), + (name: 'R_X86_64_GLOB_DAT'; size:8), + (name: 'R_X86_64_JUMP_SLOT';size:8), + (name: 'R_X86_64_RELATIVE'; size:8), + (name: 'R_X86_64_GOTPCREL'; size:4), + (name: 'R_X86_64_32'; size:4), + (name: 'R_X86_64_32S'; size:4), + (name: 'R_X86_64_16'; size:2), + (name: 'R_X86_64_PC16'; size:2), + (name: 'R_X86_64_8'; size:1), + (name: 'R_X86_64_PC8'; size:1), + (name: 'R_X86_64_DTPMOD64'; size:8), + (name: 'R_X86_64_DTPOFF64'; size:8), + (name: 'R_X86_64_TPOFF64'; size:8), + (name: 'R_X86_64_TLSGD'; size:4), + (name: 'R_X86_64_TLSLD'; size:4), + (name: 'R_X86_64_DTPOFF32'; size:4), + (name: 'R_X86_64_GOTTPOFF'; size:4), + (name: 'R_X86_64_TPOFF32'; size:4), + (name: 'R_X86_64_PC64'; size:8), + (name: 'R_X86_64_GOTOFF64'; size:8), + (name: 'R_X86_64_GOTPC32'; size:4), + (name: 'R_X86_64_GOT64'; size:8), + (name: 'R_X86_64_GOTPCREL64'; size:8), + (name: 'R_X86_64_GOTPC64'; size:8), + (name: 'R_X86_64_GOTPLT64'; size:8), + (name: 'R_X86_64_PLTOFF64'; size:8), + (name: 'R_X86_64_SIZE32'; size:4), + (name: 'R_X86_64_SIZE64'; size:8), + (name: 'R_X86_64_GOTPC32_TLSDESC'; size:4), + (name: 'R_X86_64_TLSDESC_CALL'; size:0), + (name: 'R_X86_64_TLSDESC'; size:8), + (name: 'R_X86_64_IRELATIVE'; size:8) + ); + {**************************************************************************** - TELFObjectOutputx86_64 + TELFTargetx86_64 ****************************************************************************} - function TElfObjOutputx86_64.encodereloc(objrel:TObjRelocation):byte; + class function TElfTargetx86_64.encodereloc(objrel:TObjRelocation):byte; begin case objrel.typ of + RELOC_NONE : + result:=R_X86_64_NONE; { Note: 8 and 16-bit relocations are known to be non-conformant with AMD64 ABI, so they aren't handled. } RELOC_RELATIVE : @@ -122,17 +184,412 @@ implementation end; end; -{**************************************************************************** - TELFAssemblerx86_64 -****************************************************************************} - constructor TElfAssemblerx86_64.create(smart:boolean); + class procedure TElfTargetx86_64.loadreloc(objrel:TObjRelocation); begin - inherited Create(smart); - CObjOutput:=TElfObjOutputx86_64; end; +{**************************************************************************** + TELFExeOutputx86_64 +****************************************************************************} + + function TElfExeOutputx86_64.RelocName(reltyp:byte):string; + begin + if reltyp<=high(relocprops) then + result:=relocprops[reltyp].name + else + result:='unknown ('+tostr(reltyp)+')'; + end; + + + procedure TElfExeOutputx86_64.ReportNonDSOReloc(reltyp:byte;objsec:TObjSection;ObjReloc:TObjRelocation); + begin + { TODO: include objsec properties into message } + Comment(v_error,'Relocation '+RelocName(reltyp)+' against '''+objreloc.TargetName+''' cannot be used when linking a shared object; recompile with -Cg'); + end; + + + procedure TElfExeOutputx86_64.ReportRelocOverflow(reltyp:byte;objsec:TObjSection;ObjReloc:TObjRelocation); + begin + { TODO: include objsec properties into message } + Comment(v_error,'Relocation truncated to fit: '+RelocName(reltyp)+' against '''+objreloc.TargetName+''''); + end; + + + procedure TElfExeOutputx86_64.GOTRelocPass1(objsec:TObjSection;ObjReloc:TObjRelocation); + var + objsym:TObjSymbol; + sym:TExeSymbol; + reltyp:byte; + begin + if (ObjReloc.flags and rf_raw)=0 then + reltyp:=ElfTarget.encodereloc(ObjReloc) + else + reltyp:=ObjReloc.ftype; + + case reltyp of + R_X86_64_PLT32, + R_X86_64_GOTPLT64: + begin + objsym:=ObjReloc.symbol.exesymbol.ObjSymbol; + objsym.refs:=objsym.refs or symref_plt; + end; + end; + + case reltyp of + R_X86_64_GOT32, + R_X86_64_GOT64, + R_X86_64_GOTTPOFF, + R_X86_64_GOTPCREL, + R_X86_64_GOTPCREL64: + begin + sym:=ObjReloc.symbol.exesymbol; + { TLS IE to locally defined symbol, convert into LE so GOT entry isn't needed + (Is TLS IE allowed in shared libs at all? Yes it is, when lib is accessing + a threadvar in main program or in other *statically* loaded lib; TLS IE access to + own threadvars may render library not loadable dynamically) } +(* + if (reltyp=R_X86_64_GOTTPOFF) and not + (IsSharedLibrary or (sym.dynindex>0)) then + begin + if not IsValidIEtoLE(objsec,ObjReloc) then + Comment(v_error,'Cannot transform TLS IE to LE'); + TranslateIEtoLE(objsec,ObjReloc); + ObjReloc.ftype:=R_X86_64_TPOFF32; + exit; + end; +*) + { 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 sym=nil then + begin + sym:=TExeSymbol.Create(ExeSymbolList,objreloc.symbol.name+'*local*'); + sym.objsymbol:=objreloc.symbol; + objreloc.symbol.exesymbol:=sym; + end; + if sym.GotOffset>0 then + exit; + gotobjsec.alloc(sizeof(pint)); + sym.GotOffset:=gotobjsec.size; + { In shared library, every GOT entry needs a RELATIVE dynamic reloc, + imported/exported symbols need GLOB_DAT instead. For executables, + only the latter applies. } + if IsSharedLibrary or (sym.dynindex>0) then + dynrelocsec.alloc(dynrelocsec.shentsize); + end; + + //R_X86_64_TLSGD, + //R_X86_64_TLSLD: { TODO: allocate two GOT slots } + + { R_X86_64_32S cannot be used in DSOs at all } + R_X86_64_32S: + if IsSharedLibrary then + ReportNonDSOReloc(reltyp,objsec,objreloc); + + { R_X86_64_32 is processed by rtld, but binutils accept it in data sections only. + Relocating the against local symbols is tricky: changing into RELATIVE is not possible, + so it is changed into relocation against section symbol. This requires adding + the appropriate section symbol to dynamic symtable. BFD also has some obscure logic + behind, e.g. it uses .text section for symbols from .data section. + + For now, leave this situation unhandled, as 32-bit relocations aren't commonly + used in 64-bit code. } + + R_X86_64_32: + if IsSharedLibrary then + begin + if (oso_executable in objsec.SecOptions) or + not (oso_write in objsec.SecOptions) then + ReportNonDSOReloc(reltyp,objsec,objreloc) + else + InternalError(2012092601); + end; + + R_X86_64_64: + begin + if IsSharedLibrary then + begin + if (oso_executable in objsec.SecOptions) or + not (oso_write in objsec.SecOptions) then + hastextrelocs:=True; + dynrelocsec.alloc(dynrelocsec.shentsize); + objreloc.flags:=objreloc.flags or rf_dynamic; + end; + end; + end; + end; + + + procedure TElfExeOutputx86_64.MaybeWriteGOTEntry(reltyp:byte;relocval:aint;exesym:TExeSymbol); + var + gotoff,dynidx,tmp:aword; + begin + gotoff:=exesym.gotoffset; + if gotoff=0 then + InternalError(2012060902); + + { the GOT slot itself, and a dynamic relocation for it } + { TODO: only data symbols must get here } + if gotoff=gotobjsec.Data.size+sizeof(pint) then + begin + dynidx:=exesym.dynindex; + gotobjsec.write(relocval,sizeof(pint)); + + tmp:=gotobjsec.mempos+gotoff-sizeof(pint); + if (dynidx>0) then + begin + if (reltyp=R_X86_64_GOTTPOFF) then + if IsSharedLibrary then + WriteDynRelocEntry(tmp,R_X86_64_TPOFF64,dynidx,0) // probably incorrect + else + else + WriteDynRelocEntry(tmp,R_X86_64_GLOB_DAT,dynidx,0); + end + else if IsSharedLibrary then + WriteDynRelocEntry(tmp,R_X86_64_RELATIVE,0,relocval); + end; + end; + + procedure TElfExeOutputx86_64.DoRelocationFixup(objsec:TObjSection); + var + i,zero:longint; + objreloc: TObjRelocation; + address, + relocval : aint; + relocsec : TObjSection; + data: TDynamicArray; + reltyp,relsize: byte; + begin + data:=objsec.data; + 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 reltyp<=high(relocprops) then + relsize:=relocprops[reltyp].size + else + InternalError(2012092103); + + if relocs_use_addend then + address:=objreloc.orgsize + else + begin + data.Seek(objreloc.dataoffset); + data.Read(address,relsize); + 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; + + { TODO: if relocsec=nil, relocations must be copied to .rela.dyn section } + if (relocsec=nil) or (relocsec.used) then + case reltyp of + R_X86_64_PC32, + R_X86_64_PC64: + begin + address:=address+relocval-(objsec.mempos+objreloc.dataoffset); + end; + + R_X86_64_PLT32: + begin + { If target is in current object, treat as RELOC_RELATIVE } + address:=address+relocval-(objsec.mempos+objreloc.dataoffset); + end; + + R_X86_64_TPOFF32, + R_X86_64_TPOFF64: + address:=relocval-(tlsseg.MemPos+tlsseg.MemSize); + + R_X86_64_GOTTPOFF, + R_X86_64_GOTPCREL, + R_X86_64_GOTPCREL64: + begin + if (reltyp=R_X86_64_GOTTPOFF) then + relocval:=relocval-(tlsseg.MemPos+tlsseg.MemSize); + + MaybeWriteGOTEntry(reltyp,relocval,objreloc.symbol.exesymbol); + + { resolves to PC-relative offset to GOT slot } + relocval:=gotobjsec.mempos+objreloc.symbol.exesymbol.gotoffset-sizeof(pint); + address:=address+relocval-(objsec.mempos+objreloc.dataoffset); + end; + + R_X86_64_32S, + R_X86_64_32: + inc(address,relocval); + + R_X86_64_64: + begin + inc(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(objreloc.dataoffset+objsec.mempos,R_X86_64_RELATIVE,0,address) + else + WriteDynRelocEntry(objreloc.dataoffset+objsec.mempos,R_X86_64_64,objreloc.symbol.exesymbol.dynindex,0); + end; + end; + + R_X86_64_GOTPC32, + R_X86_64_GOTPC64: + begin + address:=address+gotsymbol.address-(objsec.mempos+objreloc.dataoffset); + end; + + R_X86_64_GOT32, + R_X86_64_GOT64: + begin + MaybeWriteGOTEntry(reltyp,relocval,objreloc.symbol.exesymbol); + + relocval:=gotobjsec.mempos+objreloc.symbol.exesymbol.gotoffset-sizeof(pint)-gotsymbol.address; + address:=address+relocval; + end; + + else + begin + writeln(objreloc.typ); + internalerror(200604014); + end; + end + else { not relocsec.Used } + address:=0; { Relocation in debug section points to unused section, which is eliminated by linker } + + case relsize of + 8: ; + 4: + begin + case reltyp of + R_X86_64_32: + if qword(address)>qword($FFFFFFFF) then + ReportRelocOverflow(reltyp,objsec,objreloc); + else + if (address>high(longint)) or (address