]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blobdiff - bfd/elf32-i386.c
Fix: Null pointer dereference in elf32-i386.c
[thirdparty/binutils-gdb.git] / bfd / elf32-i386.c
index dee5352a0b605e9c5326e1c87243531cb6951e69..abfba406a81d117ea0fba687d224a732d6597f9b 100644 (file)
@@ -1,5 +1,5 @@
 /* Intel 80386/80486-specific support for 32-bit ELF
-   Copyright (C) 1993-2021 Free Software Foundation, Inc.
+   Copyright (C) 1993-2023 Free Software Foundation, Inc.
 
    This file is part of BFD, the Binary File Descriptor library.
 
 /* 386 uses REL relocations instead of RELA.  */
 #define USE_REL        1
 
-#include "elf/i386.h"
-
 static reloc_howto_type elf_howto_table[]=
 {
-  HOWTO(R_386_NONE, 0, 3, 0, false, 0, complain_overflow_dont,
+  HOWTO(R_386_NONE, 0, 0, 0, false, 0, complain_overflow_dont,
        bfd_elf_generic_reloc, "R_386_NONE",
        true, 0x00000000, 0x00000000, false),
-  HOWTO(R_386_32, 0, 2, 32, false, 0, complain_overflow_bitfield,
+  HOWTO(R_386_32, 0, 4, 32, false, 0, complain_overflow_dont,
        bfd_elf_generic_reloc, "R_386_32",
        true, 0xffffffff, 0xffffffff, false),
-  HOWTO(R_386_PC32, 0, 2, 32, true, 0, complain_overflow_bitfield,
+  HOWTO(R_386_PC32, 0, 4, 32, true, 0, complain_overflow_dont,
        bfd_elf_generic_reloc, "R_386_PC32",
        true, 0xffffffff, 0xffffffff, true),
-  HOWTO(R_386_GOT32, 0, 2, 32, false, 0, complain_overflow_bitfield,
+  HOWTO(R_386_GOT32, 0, 4, 32, false, 0, complain_overflow_dont,
        bfd_elf_generic_reloc, "R_386_GOT32",
        true, 0xffffffff, 0xffffffff, false),
-  HOWTO(R_386_PLT32, 0, 2, 32, true, 0, complain_overflow_bitfield,
+  HOWTO(R_386_PLT32, 0, 4, 32, true, 0, complain_overflow_dont,
        bfd_elf_generic_reloc, "R_386_PLT32",
        true, 0xffffffff, 0xffffffff, true),
-  HOWTO(R_386_COPY, 0, 2, 32, false, 0, complain_overflow_bitfield,
+  HOWTO(R_386_COPY, 0, 4, 32, false, 0, complain_overflow_dont,
        bfd_elf_generic_reloc, "R_386_COPY",
        true, 0xffffffff, 0xffffffff, false),
-  HOWTO(R_386_GLOB_DAT, 0, 2, 32, false, 0, complain_overflow_bitfield,
+  HOWTO(R_386_GLOB_DAT, 0, 4, 32, false, 0, complain_overflow_dont,
        bfd_elf_generic_reloc, "R_386_GLOB_DAT",
        true, 0xffffffff, 0xffffffff, false),
-  HOWTO(R_386_JUMP_SLOT, 0, 2, 32, false, 0, complain_overflow_bitfield,
+  HOWTO(R_386_JUMP_SLOT, 0, 4, 32, false, 0, complain_overflow_dont,
        bfd_elf_generic_reloc, "R_386_JUMP_SLOT",
        true, 0xffffffff, 0xffffffff, false),
-  HOWTO(R_386_RELATIVE, 0, 2, 32, false, 0, complain_overflow_bitfield,
+  HOWTO(R_386_RELATIVE, 0, 4, 32, false, 0, complain_overflow_dont,
        bfd_elf_generic_reloc, "R_386_RELATIVE",
        true, 0xffffffff, 0xffffffff, false),
-  HOWTO(R_386_GOTOFF, 0, 2, 32, false, 0, complain_overflow_bitfield,
+  HOWTO(R_386_GOTOFF, 0, 4, 32, false, 0, complain_overflow_dont,
        bfd_elf_generic_reloc, "R_386_GOTOFF",
        true, 0xffffffff, 0xffffffff, false),
-  HOWTO(R_386_GOTPC, 0, 2, 32, true, 0, complain_overflow_bitfield,
+  HOWTO(R_386_GOTPC, 0, 4, 32, true, 0, complain_overflow_dont,
        bfd_elf_generic_reloc, "R_386_GOTPC",
        true, 0xffffffff, 0xffffffff, true),
 
@@ -72,74 +70,74 @@ static reloc_howto_type elf_howto_table[]=
 #define R_386_ext_offset (R_386_TLS_TPOFF - R_386_standard)
 
   /* These relocs are a GNU extension.  */
-  HOWTO(R_386_TLS_TPOFF, 0, 2, 32, false, 0, complain_overflow_bitfield,
+  HOWTO(R_386_TLS_TPOFF, 0, 4, 32, false, 0, complain_overflow_dont,
        bfd_elf_generic_reloc, "R_386_TLS_TPOFF",
        true, 0xffffffff, 0xffffffff, false),
-  HOWTO(R_386_TLS_IE, 0, 2, 32, false, 0, complain_overflow_bitfield,
+  HOWTO(R_386_TLS_IE, 0, 4, 32, false, 0, complain_overflow_dont,
        bfd_elf_generic_reloc, "R_386_TLS_IE",
        true, 0xffffffff, 0xffffffff, false),
-  HOWTO(R_386_TLS_GOTIE, 0, 2, 32, false, 0, complain_overflow_bitfield,
+  HOWTO(R_386_TLS_GOTIE, 0, 4, 32, false, 0, complain_overflow_dont,
        bfd_elf_generic_reloc, "R_386_TLS_GOTIE",
        true, 0xffffffff, 0xffffffff, false),
-  HOWTO(R_386_TLS_LE, 0, 2, 32, false, 0, complain_overflow_bitfield,
+  HOWTO(R_386_TLS_LE, 0, 4, 32, false, 0, complain_overflow_dont,
        bfd_elf_generic_reloc, "R_386_TLS_LE",
        true, 0xffffffff, 0xffffffff, false),
-  HOWTO(R_386_TLS_GD, 0, 2, 32, false, 0, complain_overflow_bitfield,
+  HOWTO(R_386_TLS_GD, 0, 4, 32, false, 0, complain_overflow_dont,
        bfd_elf_generic_reloc, "R_386_TLS_GD",
        true, 0xffffffff, 0xffffffff, false),
-  HOWTO(R_386_TLS_LDM, 0, 2, 32, false, 0, complain_overflow_bitfield,
+  HOWTO(R_386_TLS_LDM, 0, 4, 32, false, 0, complain_overflow_dont,
        bfd_elf_generic_reloc, "R_386_TLS_LDM",
        true, 0xffffffff, 0xffffffff, false),
-  HOWTO(R_386_16, 0, 1, 16, false, 0, complain_overflow_bitfield,
+  HOWTO(R_386_16, 0, 2, 16, false, 0, complain_overflow_bitfield,
        bfd_elf_generic_reloc, "R_386_16",
        true, 0xffff, 0xffff, false),
-  HOWTO(R_386_PC16, 0, 1, 16, true, 0, complain_overflow_bitfield,
+  HOWTO(R_386_PC16, 0, 2, 16, true, 0, complain_overflow_bitfield,
        bfd_elf_generic_reloc, "R_386_PC16",
        true, 0xffff, 0xffff, true),
-  HOWTO(R_386_8, 0, 0, 8, false, 0, complain_overflow_bitfield,
+  HOWTO(R_386_8, 0, 1, 8, false, 0, complain_overflow_bitfield,
        bfd_elf_generic_reloc, "R_386_8",
        true, 0xff, 0xff, false),
-  HOWTO(R_386_PC8, 0, 0, 8, true, 0, complain_overflow_signed,
+  HOWTO(R_386_PC8, 0, 1, 8, true, 0, complain_overflow_signed,
        bfd_elf_generic_reloc, "R_386_PC8",
        true, 0xff, 0xff, true),
 
 #define R_386_ext (R_386_PC8 + 1 - R_386_ext_offset)
 #define R_386_tls_offset (R_386_TLS_LDO_32 - R_386_ext)
   /* These are common with Solaris TLS implementation.  */
-  HOWTO(R_386_TLS_LDO_32, 0, 2, 32, false, 0, complain_overflow_bitfield,
+  HOWTO(R_386_TLS_LDO_32, 0, 4, 32, false, 0, complain_overflow_dont,
        bfd_elf_generic_reloc, "R_386_TLS_LDO_32",
        true, 0xffffffff, 0xffffffff, false),
-  HOWTO(R_386_TLS_IE_32, 0, 2, 32, false, 0, complain_overflow_bitfield,
+  HOWTO(R_386_TLS_IE_32, 0, 4, 32, false, 0, complain_overflow_dont,
        bfd_elf_generic_reloc, "R_386_TLS_IE_32",
        true, 0xffffffff, 0xffffffff, false),
-  HOWTO(R_386_TLS_LE_32, 0, 2, 32, false, 0, complain_overflow_bitfield,
+  HOWTO(R_386_TLS_LE_32, 0, 4, 32, false, 0, complain_overflow_dont,
        bfd_elf_generic_reloc, "R_386_TLS_LE_32",
        true, 0xffffffff, 0xffffffff, false),
-  HOWTO(R_386_TLS_DTPMOD32, 0, 2, 32, false, 0, complain_overflow_bitfield,
+  HOWTO(R_386_TLS_DTPMOD32, 0, 4, 32, false, 0, complain_overflow_dont,
        bfd_elf_generic_reloc, "R_386_TLS_DTPMOD32",
        true, 0xffffffff, 0xffffffff, false),
-  HOWTO(R_386_TLS_DTPOFF32, 0, 2, 32, false, 0, complain_overflow_bitfield,
+  HOWTO(R_386_TLS_DTPOFF32, 0, 4, 32, false, 0, complain_overflow_dont,
        bfd_elf_generic_reloc, "R_386_TLS_DTPOFF32",
        true, 0xffffffff, 0xffffffff, false),
-  HOWTO(R_386_TLS_TPOFF32, 0, 2, 32, false, 0, complain_overflow_bitfield,
+  HOWTO(R_386_TLS_TPOFF32, 0, 4, 32, false, 0, complain_overflow_dont,
        bfd_elf_generic_reloc, "R_386_TLS_TPOFF32",
        true, 0xffffffff, 0xffffffff, false),
-  HOWTO(R_386_SIZE32, 0, 2, 32, false, 0, complain_overflow_unsigned,
+  HOWTO(R_386_SIZE32, 0, 4, 32, false, 0, complain_overflow_dont,
        bfd_elf_generic_reloc, "R_386_SIZE32",
        true, 0xffffffff, 0xffffffff, false),
-  HOWTO(R_386_TLS_GOTDESC, 0, 2, 32, false, 0, complain_overflow_bitfield,
+  HOWTO(R_386_TLS_GOTDESC, 0, 4, 32, false, 0, complain_overflow_dont,
        bfd_elf_generic_reloc, "R_386_TLS_GOTDESC",
        true, 0xffffffff, 0xffffffff, false),
-  HOWTO(R_386_TLS_DESC_CALL, 0, 3, 0, false, 0, complain_overflow_dont,
+  HOWTO(R_386_TLS_DESC_CALL, 0, 0, 0, false, 0, complain_overflow_dont,
        bfd_elf_generic_reloc, "R_386_TLS_DESC_CALL",
        false, 0, 0, false),
-  HOWTO(R_386_TLS_DESC, 0, 2, 32, false, 0, complain_overflow_bitfield,
+  HOWTO(R_386_TLS_DESC, 0, 4, 32, false, 0, complain_overflow_dont,
        bfd_elf_generic_reloc, "R_386_TLS_DESC",
        true, 0xffffffff, 0xffffffff, false),
-  HOWTO(R_386_IRELATIVE, 0, 2, 32, false, 0, complain_overflow_bitfield,
+  HOWTO(R_386_IRELATIVE, 0, 4, 32, false, 0, complain_overflow_dont,
        bfd_elf_generic_reloc, "R_386_IRELATIVE",
        true, 0xffffffff, 0xffffffff, false),
-  HOWTO(R_386_GOT32X, 0, 2, 32, false, 0, complain_overflow_bitfield,
+  HOWTO(R_386_GOT32X, 0, 4, 32, false, 0, complain_overflow_dont,
        bfd_elf_generic_reloc, "R_386_GOT32X",
        true, 0xffffffff, 0xffffffff, false),
 
@@ -150,7 +148,7 @@ static reloc_howto_type elf_howto_table[]=
 /* GNU extension to record C++ vtable hierarchy.  */
   HOWTO (R_386_GNU_VTINHERIT,  /* type */
         0,                     /* rightshift */
-        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        4,                     /* size */
         0,                     /* bitsize */
         false,                 /* pc_relative */
         0,                     /* bitpos */
@@ -165,7 +163,7 @@ static reloc_howto_type elf_howto_table[]=
 /* GNU extension to record C++ vtable member usage.  */
   HOWTO (R_386_GNU_VTENTRY,    /* type */
         0,                     /* rightshift */
-        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        4,                     /* size */
         0,                     /* bitsize */
         false,                 /* pc_relative */
         0,                     /* bitpos */
@@ -181,10 +179,6 @@ static reloc_howto_type elf_howto_table[]=
 
 };
 
-#define X86_PCREL_TYPE_P(TYPE) ((TYPE) == R_386_PC32)
-
-#define X86_SIZE_TYPE_P(TYPE) ((TYPE) == R_386_SIZE32)
-
 #ifdef DEBUG_GEN_RELOC
 #define TRACE(str) \
   fprintf (stderr, "i386 bfd reloc lookup %d (%s)\n", code, str)
@@ -528,7 +522,7 @@ elf_i386_grok_psinfo (bfd *abfd, Elf_Internal_Note *note)
    Functions named elf_i386_* are called by external routines, other
    functions are only called locally.  elf_i386_* functions appear
    in this file more or less in the order in which they are called
-   from external routines.  eg. elf_i386_check_relocs is called
+   from external routines.  eg. elf_i386_scan_relocs is called
    early in the link process, elf_i386_finish_dynamic_sections is
    one of the last functions.  */
 
@@ -969,7 +963,8 @@ elf_i386_check_tls_transition (asection *sec,
          || !((struct elf_x86_link_hash_entry *) h)->tls_get_addr)
        return false;
       else if (indirect_call)
-       return (ELF32_R_TYPE (rel[1].r_info) == R_386_GOT32X);
+       return (ELF32_R_TYPE (rel[1].r_info) == R_386_GOT32X
+               || ELF32_R_TYPE (rel[1].r_info) == R_386_GOT32);
       else
        return (ELF32_R_TYPE (rel[1].r_info) == R_386_PC32
                || ELF32_R_TYPE (rel[1].r_info) == R_386_PLT32);
@@ -1068,6 +1063,7 @@ elf_i386_tls_transition (struct bfd_link_info *info, bfd *abfd,
   unsigned int from_type = *r_type;
   unsigned int to_type = from_type;
   bool check = true;
+  unsigned int to_le_type, to_ie_type;
 
   /* Skip TLS transition for functions.  */
   if (h != NULL
@@ -1075,6 +1071,18 @@ elf_i386_tls_transition (struct bfd_link_info *info, bfd *abfd,
          || h->type == STT_GNU_IFUNC))
     return true;
 
+  if (get_elf_backend_data (abfd)->target_os == is_solaris)
+    {
+      /* NB: Solaris only supports R_386_TLS_LE and R_386_TLS_IE.  */
+      to_le_type = R_386_TLS_LE;
+      to_ie_type = R_386_TLS_IE;
+    }
+  else
+    {
+      to_le_type = R_386_TLS_LE_32;
+      to_ie_type = R_386_TLS_IE_32;
+    }
+
   switch (from_type)
     {
     case R_386_TLS_GD:
@@ -1086,10 +1094,10 @@ elf_i386_tls_transition (struct bfd_link_info *info, bfd *abfd,
       if (bfd_link_executable (info))
        {
          if (h == NULL)
-           to_type = R_386_TLS_LE_32;
+           to_type = to_le_type;
          else if (from_type != R_386_TLS_IE
                   && from_type != R_386_TLS_GOTIE)
-           to_type = R_386_TLS_IE_32;
+           to_type = to_ie_type;
        }
 
       /* When we are called from elf_i386_relocate_section, there may
@@ -1099,7 +1107,7 @@ elf_i386_tls_transition (struct bfd_link_info *info, bfd *abfd,
          unsigned int new_to_type = to_type;
 
          if (TLS_TRANSITION_IE_TO_LE_P (info, h, tls_type))
-           new_to_type = R_386_TLS_LE_32;
+           new_to_type = to_le_type;
 
          if (to_type == R_386_TLS_GD
              || to_type == R_386_TLS_GOTDESC
@@ -1108,11 +1116,11 @@ elf_i386_tls_transition (struct bfd_link_info *info, bfd *abfd,
              if (tls_type == GOT_TLS_IE_POS)
                new_to_type = R_386_TLS_GOTIE;
              else if (tls_type & GOT_TLS_IE)
-               new_to_type = R_386_TLS_IE_32;
+               new_to_type = to_ie_type;
            }
 
          /* We checked the transition before when we were called from
-            elf_i386_check_relocs.  We only want to check the new
+            elf_i386_scan_relocs.  We only want to check the new
             transition which hasn't been checked before.  */
          check = new_to_type != to_type && from_type == to_type;
          to_type = new_to_type;
@@ -1122,7 +1130,7 @@ elf_i386_tls_transition (struct bfd_link_info *info, bfd *abfd,
 
     case R_386_TLS_LDM:
       if (bfd_link_executable (info))
-       to_type = R_386_TLS_LE_32;
+       to_type = to_le_type;
       break;
 
     default:
@@ -1233,6 +1241,12 @@ elf_i386_convert_load_reloc (bfd *abfd, Elf_Internal_Shdr *symtab_hdr,
     return true;
 
   htab = elf_x86_hash_table (link_info, I386_ELF_DATA);
+  if (htab == NULL || ! is_x86_elf (abfd, htab))
+    {
+      bfd_set_error (bfd_error_wrong_format);
+      return false;
+    }
+
   is_pic = bfd_link_pic (link_info);
 
   r_type = *r_type_p;
@@ -1452,26 +1466,21 @@ elf_i386_convert_load_reloc (bfd *abfd, Elf_Internal_Shdr *symtab_hdr,
   return true;
 }
 
-/* Rename some of the generic section flags to better document how they
-   are used here.  */
-#define check_relocs_failed    sec_flg0
-
 /* Look through the relocs for a section during the first phase, and
-   calculate needed space in the global offset table, procedure linkage
-   table, and dynamic reloc sections.  */
+   calculate needed space in the global offset table, and procedure
+   linkage table.  */
 
 static bool
-elf_i386_check_relocs (bfd *abfd,
-                      struct bfd_link_info *info,
-                      asection *sec,
-                      const Elf_Internal_Rela *relocs)
+elf_i386_scan_relocs (bfd *abfd,
+                     struct bfd_link_info *info,
+                     asection *sec,
+                     const Elf_Internal_Rela *relocs)
 {
   struct elf_x86_link_hash_table *htab;
   Elf_Internal_Shdr *symtab_hdr;
   struct elf_link_hash_entry **sym_hashes;
   const Elf_Internal_Rela *rel;
   const Elf_Internal_Rela *rel_end;
-  asection *sreloc;
   bfd_byte *contents;
   bool converted;
 
@@ -1501,8 +1510,6 @@ elf_i386_check_relocs (bfd *abfd,
 
   converted = false;
 
-  sreloc = NULL;
-
   rel_end = relocs + sec->reloc_count;
   for (rel = relocs; rel < rel_end; rel++)
     {
@@ -1673,25 +1680,12 @@ elf_i386_check_relocs (bfd *abfd,
              {
                bfd_signed_vma *local_got_refcounts;
 
+               if (!elf_x86_allocate_local_got_info (abfd,
+                                                     symtab_hdr->sh_info))
+                     goto error_return;
+
                /* This is a global offset table entry for a local symbol.  */
                local_got_refcounts = elf_local_got_refcounts (abfd);
-               if (local_got_refcounts == NULL)
-                 {
-                   bfd_size_type size;
-
-                   size = symtab_hdr->sh_info;
-                   size *= (sizeof (bfd_signed_vma)
-                            + sizeof (bfd_vma) + sizeof(char));
-                   local_got_refcounts = (bfd_signed_vma *)
-                       bfd_zalloc (abfd, size);
-                   if (local_got_refcounts == NULL)
-                     goto error_return;
-                   elf_local_got_refcounts (abfd) = local_got_refcounts;
-                   elf_x86_local_tlsdesc_gotent (abfd)
-                     = (bfd_vma *) (local_got_refcounts + symtab_hdr->sh_info);
-                   elf_x86_local_got_tls_type (abfd)
-                     = (char *) (local_got_refcounts + 2 * symtab_hdr->sh_info);
-                 }
                local_got_refcounts[r_symndx] = 1;
                old_tls_type = elf_x86_local_got_tls_type (abfd) [r_symndx];
              }
@@ -1798,11 +1792,20 @@ elf_i386_check_relocs (bfd *abfd,
                }
              else
                {
-                 h->pointer_equality_needed = 1;
-                 /* R_386_32 can be resolved at run-time.  */
+                 /* R_386_32 can be resolved at run-time.  Function
+                    pointer reference doesn't need PLT for pointer
+                    equality.  */
                  if (r_type == R_386_32
                      && (sec->flags & SEC_READONLY) == 0)
                    func_pointer_ref = true;
+
+                 /* IFUNC symbol needs pointer equality in PDE so that
+                    function pointer reference will be resolved to its
+                    PLT entry directly.  */
+                 if (!func_pointer_ref
+                     || (bfd_link_pde (info)
+                         && h->type == STT_GNU_IFUNC))
+                   h->pointer_equality_needed = 1;
                }
 
              if (!func_pointer_ref)
@@ -1815,36 +1818,46 @@ elf_i386_check_relocs (bfd *abfd,
                     adjust_dynamic_symbol.  */
                  h->non_got_ref = 1;
 
+                 if (!elf_has_indirect_extern_access (sec->owner))
+                   eh->non_got_ref_without_indirect_extern_access = 1;
+
                  /* We may need a .plt entry if the symbol is a function
                     defined in a shared lib or is a function referenced
                     from the code or read-only section.  */
                  if (!h->def_regular
                      || (sec->flags & (SEC_CODE | SEC_READONLY)) != 0)
                    h->plt.refcount = 1;
+
+                 if (htab->elf.target_os != is_solaris
+                     && h->pointer_equality_needed
+                     && h->type == STT_FUNC
+                     && eh->def_protected
+                     && !SYMBOL_DEFINED_NON_SHARED_P (h)
+                     && h->def_dynamic)
+                   {
+                     /* Disallow non-canonical reference to canonical
+                        protected function.  */
+                     _bfd_error_handler
+                       /* xgettext:c-format */
+                       (_("%pB: non-canonical reference to canonical "
+                          "protected function `%s' in %pB"),
+                        abfd, h->root.root.string,
+                        h->root.u.def.section->owner);
+                     bfd_set_error (bfd_error_bad_value);
+                     goto error_return;
+                   }
                }
            }
 
          size_reloc = false;
        do_size:
          if (!no_dynreloc
-             && NEED_DYNAMIC_RELOCATION_P (info, false, h, sec, r_type,
-                                           R_386_32))
+             && NEED_DYNAMIC_RELOCATION_P (false, info, false, h, sec,
+                                           r_type, R_386_32))
            {
              struct elf_dyn_relocs *p;
              struct elf_dyn_relocs **head;
 
-             /* We must copy these reloc types into the output file.
-                Create a reloc section in dynobj and make room for
-                this reloc.  */
-             if (sreloc == NULL)
-               {
-                 sreloc = _bfd_elf_make_dynamic_reloc_section
-                   (sec, htab->elf.dynobj, 2, abfd, /*rela?*/ false);
-
-                 if (sreloc == NULL)
-                   goto error_return;
-               }
-
              /* If this is a global symbol, we count the number of
                 relocations we need for this symbol.  */
              if (h != NULL)
@@ -1915,13 +1928,14 @@ elf_i386_check_relocs (bfd *abfd,
 
   if (elf_section_data (sec)->this_hdr.contents != contents)
     {
-      if (!converted && !info->keep_memory)
+      if (!converted && !_bfd_link_keep_memory (info))
        free (contents);
       else
        {
          /* Cache the section contents for elf_link_input_bfd if any
             load is converted or --no-keep-memory isn't used.  */
          elf_section_data (sec)->this_hdr.contents = contents;
+         info->cache_size += sec->size;
        }
     }
 
@@ -1938,6 +1952,24 @@ elf_i386_check_relocs (bfd *abfd,
   return false;
 }
 
+static bool
+elf_i386_always_size_sections (bfd *output_bfd,
+                              struct bfd_link_info *info)
+{
+  bfd *abfd;
+
+  /* Scan relocations after rel_from_abs has been set on __ehdr_start.  */
+  for (abfd = info->input_bfds;
+       abfd != (bfd *) NULL;
+       abfd = abfd->link.next)
+    if (bfd_get_flavour (abfd) == bfd_target_elf_flavour
+       && !_bfd_elf_link_iterate_on_relocs (abfd, info,
+                                            elf_i386_scan_relocs))
+      return false;
+
+  return _bfd_x86_elf_always_size_sections (output_bfd, info);
+}
+
 /* Set the correct type for an x86 ELF section.  We do this by the
    section name, which is a hack, but ought to work.  */
 
@@ -2012,9 +2044,10 @@ elf_i386_relocate_section (bfd *output_bfd,
   Elf_Internal_Rela *wrel;
   Elf_Internal_Rela *relend;
   bool is_vxworks_tls;
+  unsigned expected_tls_le;
   unsigned plt_entry_size;
 
-  /* Skip if check_relocs failed.  */
+  /* Skip if check_relocs or scan_relocs failed.  */
   if (input_section->check_relocs_failed)
     return false;
 
@@ -2100,9 +2133,9 @@ elf_i386_relocate_section (bfd *output_bfd,
              bfd_vma addend;
              bfd_byte *where = contents + rel->r_offset;
 
-             switch (howto->size)
+             switch (bfd_get_reloc_size (howto))
                {
-               case 0:
+               case 1:
                  addend = bfd_get_8 (input_bfd, where);
                  if (howto->pc_relative)
                    {
@@ -2110,7 +2143,7 @@ elf_i386_relocate_section (bfd *output_bfd,
                      addend += 1;
                    }
                  break;
-               case 1:
+               case 2:
                  addend = bfd_get_16 (input_bfd, where);
                  if (howto->pc_relative)
                    {
@@ -2118,7 +2151,7 @@ elf_i386_relocate_section (bfd *output_bfd,
                      addend += 2;
                    }
                  break;
-               case 2:
+               case 4:
                  addend = bfd_get_32 (input_bfd, where);
                  if (howto->pc_relative)
                    {
@@ -2141,20 +2174,20 @@ elf_i386_relocate_section (bfd *output_bfd,
                  addend += msec->output_section->vma + msec->output_offset;
                }
 
-             switch (howto->size)
+             switch (bfd_get_reloc_size (howto))
                {
-               case 0:
+               case 1:
                  /* FIXME: overflow checks.  */
                  if (howto->pc_relative)
                    addend -= 1;
                  bfd_put_8 (input_bfd, addend, where);
                  break;
-               case 1:
+               case 2:
                  if (howto->pc_relative)
                    addend -= 2;
                  bfd_put_16 (input_bfd, addend, where);
                  break;
-               case 2:
+               case 4:
                  if (howto->pc_relative)
                    addend -= 4;
                  bfd_put_32 (input_bfd, addend, where);
@@ -2302,7 +2335,7 @@ elf_i386_relocate_section (bfd *output_bfd,
                    {
                      /* This references the local defitionion.  We must
                         initialize this entry in the global offset table.
-                        Since the offset must always be a multiple of 8,
+                        Since the offset must always be a multiple of 4,
                         we use the least significant bit to record
                         whether we have initialized it already.
 
@@ -2449,6 +2482,11 @@ elf_i386_relocate_section (bfd *output_bfd,
              goto do_relocation;
 
            case R_386_GOTOFF:
+             /* NB: We can't use the PLT entry as the function address
+                for PIC since the PIC register may not be set up
+                properly for indirect call. */
+             if (bfd_link_pic (info))
+               goto bad_ifunc_reloc;
              relocation -= (gotplt->output_section->vma
                             + gotplt->output_offset);
              goto do_relocation;
@@ -2489,8 +2527,10 @@ elf_i386_relocate_section (bfd *output_bfd,
                      bfd_put_32 (output_bfd, relocation,
                                  htab->elf.sgot->contents + off);
                      h->got.offset |= 1;
-
-                     if (GENERATE_RELATIVE_RELOC_P (info, h))
+                     /* NB: Don't generate relative relocation here if
+                        it has been generated by DT_RELR.  */
+                     if (!info->enable_dt_relr
+                         && GENERATE_RELATIVE_RELOC_P (info, h))
                        {
                          /* PR ld/21402: If this symbol isn't dynamic
                             in PIC, generate R_386_RELATIVE here.  */
@@ -2520,7 +2560,9 @@ elf_i386_relocate_section (bfd *output_bfd,
                              htab->elf.sgot->contents + off);
                  local_got_offsets[r_symndx] |= 1;
 
-                 if (bfd_link_pic (info))
+                 /* NB: Don't generate relative relocation here if it
+                    has been generated by DT_RELR.  */
+                 if (!info->enable_dt_relr && bfd_link_pic (info))
                    relative_reloc = true;
                }
            }
@@ -2716,12 +2758,14 @@ elf_i386_relocate_section (bfd *output_bfd,
              || is_vxworks_tls)
            break;
 
-         if (GENERATE_DYNAMIC_RELOCATION_P (info, eh, r_type, sec,
-                                            false, resolved_to_zero,
+         if (GENERATE_DYNAMIC_RELOCATION_P (false, info, eh, r_type,
+                                            sec, false,
+                                            resolved_to_zero,
                                             (r_type == R_386_PC32)))
            {
              Elf_Internal_Rela outrel;
              bool skip, relocate;
+             bool generate_dynamic_reloc = true;
              asection *sreloc;
 
              /* When generating a shared object, these relocations
@@ -2743,29 +2787,39 @@ elf_i386_relocate_section (bfd *output_bfd,
 
              if (skip)
                memset (&outrel, 0, sizeof outrel);
-             else if (COPY_INPUT_RELOC_P (info, h, r_type))
+             else if (COPY_INPUT_RELOC_P (false, info, h, r_type))
                outrel.r_info = ELF32_R_INFO (h->dynindx, r_type);
              else
                {
                  /* This symbol is local, or marked to become local.  */
                  relocate = true;
-                 outrel.r_info = ELF32_R_INFO (0, R_386_RELATIVE);
+                 /* NB: Don't generate relative relocation here if it
+                    has been generated by DT_RELR.  */
+                 if (info->enable_dt_relr)
+                   generate_dynamic_reloc = false;
+                 else
+                   {
+                     outrel.r_info = ELF32_R_INFO (0, R_386_RELATIVE);
 
-                 if (htab->params->report_relative_reloc)
-                   _bfd_x86_elf_link_report_relative_reloc
-                     (info, input_section, h, sym, "R_386_RELATIVE",
-                      &outrel);
+                     if (htab->params->report_relative_reloc)
+                       _bfd_x86_elf_link_report_relative_reloc
+                         (info, input_section, h, sym, "R_386_RELATIVE",
+                          &outrel);
+                   }
                }
 
-             sreloc = elf_section_data (input_section)->sreloc;
-
-             if (sreloc == NULL || sreloc->contents == NULL)
+             if (generate_dynamic_reloc)
                {
-                 r = bfd_reloc_notsupported;
-                 goto check_relocation_error;
-               }
+                 sreloc = elf_section_data (input_section)->sreloc;
 
-             elf_append_rel (output_bfd, sreloc, &outrel);
+                 if (sreloc == NULL || sreloc->contents == NULL)
+                   {
+                     r = bfd_reloc_notsupported;
+                     goto check_relocation_error;
+                   }
+
+                 elf_append_rel (output_bfd, sreloc, &outrel);
+               }
 
              /* If this reloc is against an external symbol, we do
                 not want to fiddle with the addend.  Otherwise, we
@@ -2820,9 +2874,17 @@ elf_i386_relocate_section (bfd *output_bfd,
                                         relend, h, r_symndx, true))
            return false;
 
-         if (r_type_tls == R_386_TLS_LE_32)
+         expected_tls_le = htab->elf.target_os == is_solaris
+           ? R_386_TLS_LE : R_386_TLS_LE_32;
+         if (r_type_tls == expected_tls_le)
            {
-             BFD_ASSERT (! unresolved_reloc);
+             /* NB: Solaris only supports R_386_TLS_GD->R_386_TLS_LE.  */
+             BFD_ASSERT (! unresolved_reloc
+                         && (htab->elf.target_os != is_solaris
+                             || (htab->elf.target_os == is_solaris
+                                 && (r_type == R_386_TLS_GD
+                                     || r_type == R_386_TLS_IE
+                                     || r_type == R_386_TLS_GOTIE))));
              if (r_type == R_386_TLS_GD)
                {
                  unsigned int type;
@@ -3319,7 +3381,9 @@ elf_i386_relocate_section (bfd *output_bfd,
                 into:
                        movl %gs:0, %eax
                        leal 0(%esi), %esi  */
-             BFD_ASSERT (r_type == R_386_TLS_LE_32);
+             expected_tls_le = htab->elf.target_os == is_solaris
+               ? R_386_TLS_LE : R_386_TLS_LE_32;
+             BFD_ASSERT (r_type == expected_tls_le);
              if (*(contents + rel->r_offset + 4) == 0xff
                  || *(contents + rel->r_offset + 4) == 0x67)
                memcpy (contents + rel->r_offset - 2,
@@ -3791,6 +3855,7 @@ elf_i386_finish_dynamic_symbol (bfd *output_bfd,
       Elf_Internal_Rela rel;
       asection *relgot = htab->elf.srelgot;
       const char *relative_reloc_name = NULL;
+      bool generate_dynamic_reloc = true;
 
       /* This symbol has an entry in the global offset table.  Set it
         up.  */
@@ -3873,8 +3938,13 @@ elf_i386_finish_dynamic_symbol (bfd *output_bfd,
               && SYMBOL_REFERENCES_LOCAL_P (info, h))
        {
          BFD_ASSERT((h->got.offset & 1) != 0);
-         rel.r_info = ELF32_R_INFO (0, R_386_RELATIVE);
-         relative_reloc_name = "R_386_RELATIVE";
+         if (info->enable_dt_relr)
+           generate_dynamic_reloc = false;
+         else
+           {
+             rel.r_info = ELF32_R_INFO (0, R_386_RELATIVE);
+             relative_reloc_name = "R_386_RELATIVE";
+           }
        }
       else
        {
@@ -3885,12 +3955,15 @@ elf_i386_finish_dynamic_symbol (bfd *output_bfd,
          rel.r_info = ELF32_R_INFO (h->dynindx, R_386_GLOB_DAT);
        }
 
-      if (relative_reloc_name != NULL
-         && htab->params->report_relative_reloc)
-       _bfd_x86_elf_link_report_relative_reloc
-         (info, relgot, h, sym, relative_reloc_name, &rel);
+      if (generate_dynamic_reloc)
+       {
+         if (relative_reloc_name != NULL
+             && htab->params->report_relative_reloc)
+           _bfd_x86_elf_link_report_relative_reloc
+             (info, relgot, h, sym, relative_reloc_name, &rel);
 
-      elf_append_rel (output_bfd, relgot, &rel);
+         elf_append_rel (output_bfd, relgot, &rel);
+       }
     }
 
   if (h->needs_copy)
@@ -4013,6 +4086,14 @@ elf_i386_finish_dynamic_sections (bfd *output_bfd,
 
   if (htab->elf.splt && htab->elf.splt->size > 0)
     {
+      if (bfd_is_abs_section (htab->elf.splt->output_section))
+       {
+         info->callbacks->einfo
+           (_("%F%P: discarded output section: `%pA'\n"),
+            htab->elf.splt);
+         return false;
+       }
+
       /* UnixWare sets the entsize of .plt to 4, although that doesn't
         really seem like the right value.  */
       elf_section_data (htab->elf.splt->output_section)
@@ -4381,7 +4462,6 @@ elf_i386_link_setup_gnu_properties (struct bfd_link_info *info)
 #define elf_backend_got_header_size    12
 #define elf_backend_plt_alignment      4
 #define elf_backend_dtrel_excludes_plt 1
-#define elf_backend_extern_protected_data 1
 #define elf_backend_caches_rawsize     1
 #define elf_backend_want_dynrelro      1
 
@@ -4395,7 +4475,7 @@ elf_i386_link_setup_gnu_properties (struct bfd_link_info *info)
 #define bfd_elf32_get_synthetic_symtab       elf_i386_get_synthetic_symtab
 
 #define elf_backend_relocs_compatible        _bfd_elf_relocs_compatible
-#define elf_backend_check_relocs             elf_i386_check_relocs
+#define elf_backend_always_size_sections      elf_i386_always_size_sections
 #define elf_backend_create_dynamic_sections   _bfd_elf_create_dynamic_sections
 #define elf_backend_fake_sections            elf_i386_fake_sections
 #define elf_backend_finish_dynamic_sections   elf_i386_finish_dynamic_sections