Ver código fonte

+ Support dynamic copy relocations.
* For unresolved weak symbols, provide dynamic binding only if they are referenced via GOT or PLT (ld-compatible behavior).
* Made more TElfExeOutput properties/methods usable by descendant classes.
* x86_64/cpuelf.pas: some refactoring, handle DTPOFF relocations, prohibit TPOFF relocations in shared objects.
* i386/cpuelf.pas: set symref_from_text flags for copy relocations to work correctly.

git-svn-id: trunk@23375 -

sergei 12 anos atrás
pai
commit
af4935e346
4 arquivos alterados com 135 adições e 36 exclusões
  1. 14 0
      compiler/i386/cpuelf.pas
  2. 78 30
      compiler/ogelf.pas
  3. 1 0
      compiler/systems/t_linux.pas
  4. 42 6
      compiler/x86_64/cpuelf.pas

+ 14 - 0
compiler/i386/cpuelf.pas

@@ -210,6 +210,20 @@ implementation
             objsym.refs:=objsym.refs or symref_plt;
           end;
 
+        R_386_32:
+          if (oso_executable in objsec.SecOptions) or
+            not (oso_write in objsec.SecOptions) then
+            begin
+              if assigned(objreloc.symbol) and assigned(objreloc.symbol.exesymbol) then
+                begin
+                  objsym:=objreloc.symbol.exesymbol.ObjSymbol;
+                  objsym.refs:=objsym.refs or symref_from_text;
+                end;
+            end;
+      end;
+
+      case reltyp of
+
         R_386_TLS_IE:
           begin
 

+ 78 - 30
compiler/ogelf.pas

@@ -228,7 +228,6 @@ interface
          shoffset: aword;
          gotwritten: boolean;
          { dynamic linking }
-         dynamiclink: boolean;
          dynsymnames: Plongword;
          dynsymtable: TElfSymtab;
          interpobjsec: TObjSection;
@@ -241,7 +240,7 @@ interface
          dynamicsec,
          hashobjsec: TElfObjSection;
          neededlist: TFPHashList;
-         gotsize: aword;
+         dyncopysyms: TFPObjectList;
          dynrelsize: aword;
 
          function AttachSection(objsec:TObjSection):TElfExeSection;
@@ -257,12 +256,13 @@ interface
          procedure MapSectionsToSegments;
          procedure WriteStaticSymtable;
          procedure InitDynlink;
-         procedure PrepareGOT;
        protected
+         dynamiclink: boolean;
          hastextrelocs: boolean;
          gotsymbol: TObjSymbol;
          dynsymlist: TFPObjectList;
          gotobjsec: TObjSection;
+         dynbssobjsec,
          pltobjsec,
          gotpltobjsec,
          pltrelocsec,
@@ -271,7 +271,9 @@ interface
          dynreloclist: TFPObjectList;
          tlsseg: TElfSegment;
          relative_reloc_count: longint;
-         function AllocGOTSlot(objsym: TObjSymbol):boolean;
+         gotsize: aword;
+         procedure PrepareGOT;virtual;
+         function AllocGOTSlot(objsym: TObjSymbol):boolean;virtual;
          procedure CreateGOTSection;virtual;
          procedure make_dynamic_if_undefweak(exesym:TExeSymbol);
          procedure WriteDynRelocEntry(dataofs:aword;typ:byte;symidx:aword;addend:aword);
@@ -1821,12 +1823,6 @@ implementation
             if dynndx=0 then
               InternalError(2012071401);
 
-            { 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);
@@ -1941,6 +1937,7 @@ implementation
 
     destructor TElfExeOutput.Destroy;
       begin
+        dyncopysyms.Free;
         neededlist.Free;
         segmentlist.Free;
         dynsymlist.Free;
@@ -2180,6 +2177,7 @@ implementation
           exit;
         gotobjsec.alloc(sizeof(pint));
         exesym.GotOffset:=gotobjsec.size;
+        make_dynamic_if_undefweak(exesym);
         { In shared library, every GOT entry needs a RELATIVE dynamic reloc,
           imported/exported symbols need GLOB_DAT instead. For executables,
           only the latter applies. }
@@ -2266,6 +2264,15 @@ implementation
               begin
                 exesym.State:=symstate_defined;
                 exesym.dynindex:=dynsymlist.Add(exesym)+1;
+                { The original binding, value and section of external symbol
+                  must be preserved, therefore resolving directly to .so symbol
+                  hurts more than it helps. Copy type and size, and store .so
+                  symbol in objsym.indsymbol for later use. }
+                exesym.ObjSymbol.typ:=objsym.typ;
+                if objsym.typ<>AT_FUNCTION then
+                  exesym.ObjSymbol.size:=objsym.size;
+                exesym.ObjSymbol.indsymbol:=objsym;
+                objsym.ExeSymbol:=exesym;
                 needed:=true;
               end;
           end;
@@ -2368,8 +2375,10 @@ implementation
               begin
                 if exesym.dynindex<>0 then
                   InternalError(2012062301);
-                exesym.dynindex:=dynsymlist.add(exesym)+1;
-                exesym.state:=symstate_defined;
+                { Weak-referenced symbols are changed into dynamic ones
+                  only if referenced through GOT or PLT (this is BFD-compatible) }
+                if exesym.state<>symstate_undefweak then
+                  exesym.dynindex:=dynsymlist.add(exesym)+1;
               end
             else
               UnresolvedExeSymbols[i]:=nil;
@@ -2386,21 +2395,44 @@ implementation
             if assigned(exesym.ObjSymbol.objsection) then  // an exported symbol
               continue;
 
-            { 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);
+            if ((exesym.ObjSymbol.refs and symref_plt)<>0) or
+              ((exesym.ObjSymbol.typ=AT_FUNCTION) and (not IsSharedLibrary)) then
+              begin
+                make_dynamic_if_undefweak(exesym);
+
+                { 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.indsymbol:=exesym.ObjSymbol.indsymbol;
+                objsym.offset:=pltobjsec.size;
+                objsym.objsection:=pltobjsec;
+                objsym.exesymbol:=exesym;
+                exesym.ObjSymbol:=objsym;
+
+                WritePLTEntry(exesym);
+              end
+            else if ((exesym.ObjSymbol.refs and symref_from_text)<>0) and
+              (exesym.ObjSymbol.typ<>AT_FUNCTION) and (not IsSharedLibrary) and
+              (exesym.state<>symstate_undefweak) then
+              begin
+                if exesym.ObjSymbol.size=0 then
+                  Comment(v_error,'Dynamic variable '+exesym.name+' has zero size');
+                internalobjdata.setSection(dynbssobjsec);
+                internalobjdata.allocalign(var_align(exesym.ObjSymbol.size));
+                objsym:=internalobjdata.SymbolDefine(exesym.name,AB_GLOBAL,AT_DATA);
+                objsym.size:=exesym.ObjSymbol.size;
+                objsym.indsymbol:=exesym.ObjSymbol.indsymbol;
+                exesym.ObjSymbol:=objsym;
+                objsym.exesymbol:=exesym;
+                dynbssobjsec.alloc(objsym.size);
+                { allocate space for R_xx_COPY relocation for this symbol;
+                  we'll create it later, to be consistent with "-z combreloc" semantics }
+                dyncopysyms.add(objsym);
+                dynrelocsec.alloc(dynrelocsec.shentsize);
+                inc(dynrelsize,dynrelocsec.shentsize);
+              end;
           end;
 
         { Handle indirect symbols }
@@ -2439,7 +2471,8 @@ implementation
           (.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
+        if assigned(dynrelocsec) and
+          ((dynrelocsec.size<>0) or (dyncopysyms.count<>0)) then
           Exclude(dynrelocsec.ExeSection.SecOptions,oso_disabled);
         if assigned(pltrelocsec) and (pltrelocsec.size>0) then
           Exclude(pltrelocsec.ExeSection.SecOptions,oso_disabled);
@@ -2568,9 +2601,10 @@ implementation
 
         if (not gotwritten) then
           begin
-            { reset size of .got and .rel[a].dyn, they will be refilled while fixing up relocations }
+            { Reset size of .got and .rel[a].dyn, they will be refilled while fixing up relocations.
+              For .got, consider already written reserved entries. }
             if assigned(gotobjsec) then
-              gotobjsec.size:=0;
+              gotobjsec.size:=gotobjsec.data.size;
             if assigned(dynrelocsec) then
               begin
                 dynrelocsec.size:=0;
@@ -2615,6 +2649,7 @@ implementation
         exesec: TExeSection;
         seg: TElfSegment;
         objreloc: TObjRelocation;
+        objsym: TObjSymbol;
       begin
         gotwritten:=true;
         { If target does not support sorted relocations, it is expected to write the
@@ -2623,6 +2658,14 @@ implementation
           should remain. }
         if assigned(dynrelocsec) then
           begin
+            { Append R_xx_COPY relocations }
+            for i:=0 to dyncopysyms.count-1 do
+              begin
+                objsym:=TObjSymbol(dyncopysyms[i]);
+                dynreloclist.Add(TObjRelocation.CreateRaw(objsym.address,objsym,ElfTarget.dyn_reloc_codes[dr_copy]));
+              end;
+            dyncopysyms.Clear;
+
             if (dynrelocsec.size+(dynreloclist.count*dynrelocsec.shentsize)<>dynrelsize) then
               InternalError(2012110601);
             { Write out non-RELATIVE dynamic relocations
@@ -2742,6 +2785,10 @@ implementation
         dynrelocsec:=TElfObjSection.create_reloc(internalObjData,'.dyn',true);
         dynrelocsec.SecOptions:=[oso_keep];
 
+        dynbssobjsec:=TElfObjSection.create_ext(internalObjData,'.dynbss',
+          SHT_NOBITS,SHF_ALLOC or SHF_WRITE,sizeof(pint){16??},0);
+        dynbssobjsec.SecOptions:=[oso_keep];
+
         dynreloclist:=TFPObjectList.Create(true);
 
         symversec:=TElfObjSection.create_ext(internalObjData,'.gnu.version',
@@ -2753,6 +2800,7 @@ implementation
         verneedsec:=TElfObjSection.create_ext(internalObjData,'.gnu.version_r',
           SHT_GNU_VERNEED,SHF_ALLOC,sizeof(pint),0);
         verneedsec.SecOptions:=[oso_keep];
+        dyncopysyms:=TFPObjectList.Create(False);
       end;
 
 

+ 1 - 0
compiler/systems/t_linux.pas

@@ -1457,6 +1457,7 @@ begin
       Concat('  PROVIDE edata');
       Concat('ENDEXESECTION');
       Concat('EXESECTION .bss');
+      Concat('  OBJSECTION .dynbss');
       Concat('  OBJSECTION .bss*');
       Concat('  OBJSECTION fpc.reshandles');
       Concat('  PROVIDE end');

+ 42 - 6
compiler/x86_64/cpuelf.pas

@@ -199,6 +199,7 @@ implementation
       objsym:TObjSymbol;
       objreloc:TObjRelocation;
       reltyp:byte;
+      externsym,fromtext:boolean;
     begin
       objreloc:=TObjRelocation(objsec.ObjRelocations[idx]);
       if (ObjReloc.flags and rf_raw)=0 then
@@ -249,6 +250,7 @@ implementation
         //R_X86_64_TLSGD,
         //R_X86_64_TLSLD:  { TODO: allocate two GOT slots }
 
+        R_X86_64_TPOFF32,
         { R_X86_64_32S cannot be used in DSOs at all }
         R_X86_64_32S:
           if IsSharedLibrary then
@@ -275,17 +277,40 @@ implementation
 
         R_X86_64_64:
           begin
+            fromtext:=(oso_executable in objsec.SecOptions) or
+              not (oso_write in objsec.SecOptions);
+            externsym:=assigned(objreloc.symbol) and
+              assigned(objreloc.symbol.exesymbol) and
+              (objreloc.symbol.exesymbol.dynindex<>0);
+
             if IsSharedLibrary then
               begin
-                if (oso_executable in objsec.SecOptions) or
-                  not (oso_write in objsec.SecOptions) then
+                if fromtext then
                   hastextrelocs:=True;
                 dynrelocsec.alloc(dynrelocsec.shentsize);
                 objreloc.flags:=objreloc.flags or rf_dynamic;
-                if (objreloc.symbol=nil) or
-                   (objreloc.symbol.exesymbol=nil) or
-                   (objreloc.symbol.exesymbol.dynindex=0) then
+                if (not externsym) then
                   Inc(relative_reloc_count);
+              end
+            else if externsym then
+              // TODO: R_X86_64_32 and R_X86_64_32S here?
+              begin
+                objsym:=objreloc.symbol.ExeSymbol.ObjSymbol;
+                { If symbol has non-GOT references from readonly sections, then it needs a
+                  copy reloc, which eliminates any dynamic relocations to this symbol from
+                  writable sections as well. OTOH if it is referenced *only* from writable
+                  sections, then it's better not to generate a copy reloc and keep dynamic
+                  relocations. The following code is based on assumption that all readonly
+                  sections are processed before writable ones (which is true for current
+                  segment mapping).
+                  For arbitrary segment mapping, this will probably require a separate pass. }
+                if fromtext then
+                  objsym.refs:=objsym.refs or symref_from_text
+                else if (objsym.refs and symref_from_text)=0 then
+                  begin
+                    dynrelocsec.alloc(dynrelocsec.shentsize);
+                    objreloc.flags:=objreloc.flags or rf_dynamic;
+                  end;
               end;
           end;
       end;
@@ -403,9 +428,20 @@ implementation
                   address:=address+relocval-PC;
                 end;
 
+              //R_X86_64_DTPOFF64 is possible in data??
+              R_X86_64_DTPOFF32:
+                begin
+                  { In executable it behaves as TPOFF32, but data expressions
+                    like ".long foo@dtpoff" resolve to positive offset }
+                  if IsSharedLibrary or not (oso_executable in objsec.SecOptions) then
+                    address:=address+relocval-tlsseg.MemPos
+                  else
+                    address:=address+relocval-(tlsseg.MemPos+tlsseg.MemSize);
+                end;
+
               R_X86_64_TPOFF32,
               R_X86_64_TPOFF64:
-                address:=relocval-(tlsseg.MemPos+tlsseg.MemSize);
+                address:=address+relocval-(tlsseg.MemPos+tlsseg.MemSize);
 
               R_X86_64_GOTTPOFF,
               R_X86_64_GOTPCREL,