PE32+ binaries that use addresses > 1^32 have a problem in that the linker
authorNick Clifton <nickc@redhat.com>
Fri, 11 Apr 2014 15:02:52 +0000 (16:02 +0100)
committerNick Clifton <nickc@redhat.com>
Fri, 11 Apr 2014 15:02:52 +0000 (16:02 +0100)
converts some address expressions into absolute values, but the PE format
only stores absolutes as 32-bits.  This is a partial solution which attempts
to convert such absolute values back to section relative ones instead.  It
fails for symbols like __image_base and ImageBase__, but it is unclear as to
whether these values are ever actually used by applications.

PR ld/16821
* peXXigen.c (abs_finder): New function.
(_bfd_XXi_swap_sym_out): For absolute symbols with values larger
than 1^32 try to convert them into section relative values
instead.

bfd/ChangeLog
bfd/peXXigen.c

index 79d32c7..7621c6f 100644 (file)
@@ -1,5 +1,13 @@
 2014-04-11  Nick Clifton  <nickc@redhat.com>
 
+       PR ld/16821
+       * peXXigen.c (abs_finder): New function.
+       (_bfd_XXi_swap_sym_out): For absolute symbols with values larger
+       than 1^32 try to convert them into section relative values
+       instead.
+
+2014-04-11  Nick Clifton  <nickc@redhat.com>
+
        * bfd-in2.h: Regenerate.
        * libbfd.h: Regenerate.
 
index ea7846f..36d90cc 100644 (file)
@@ -207,6 +207,14 @@ _bfd_XXi_swap_sym_in (bfd * abfd, void * ext1, void * in1)
 #endif
 }
 
+static bfd_boolean
+abs_finder (bfd * abfd ATTRIBUTE_UNUSED, asection * sec, void * data)
+{
+  bfd_vma abs_val = * (bfd_vma *) data;
+
+  return (sec->vma <= abs_val) && ((sec->vma + (1L << 32)) > abs_val);
+}
+
 unsigned int
 _bfd_XXi_swap_sym_out (bfd * abfd, void * inp, void * extp)
 {
@@ -221,6 +229,29 @@ _bfd_XXi_swap_sym_out (bfd * abfd, void * inp, void * extp)
   else
     memcpy (ext->e.e_name, in->_n._n_name, SYMNMLEN);
 
+  /* The PE32 and PE32+ formats only use 4 bytes to hold the value of a
+     symbol.  This is a problem on 64-bit targets where we can generate
+     absolute symbols with values >= 1^32.  We try to work around this
+     problem by finding a section whose base address is sufficient to
+     reduce the absolute value to < 1^32, and then transforming the
+     symbol into a section relative symbol.  This of course is a hack.  */
+  if (sizeof (in->n_value) > 4
+      && in->n_value > ((1L << 32) - 1)
+      && in->n_scnum == -1)
+    {
+      asection * sec;
+
+      sec = bfd_sections_find_if (abfd, abs_finder, & in->n_value);
+      if (sec)
+       {
+         in->n_value -= sec->vma;
+         in->n_scnum = sec->target_index;
+       }
+      /* else: FIXME: The value is outside the range of any section.  This
+        happens for __image_base__ and __ImageBase__ and maybe some other
+        symbols as well.  We should find a way to handle these values.  */
+    }
+
   H_PUT_32 (abfd, in->n_value, ext->e_value);
   H_PUT_16 (abfd, in->n_scnum, ext->e_scnum);