PowerPC, sanity check r_offset in relocate_section
authorAlan Modra <amodra@gmail.com>
Thu, 9 Sep 2021 01:29:24 +0000 (10:59 +0930)
committerAlan Modra <amodra@gmail.com>
Fri, 10 Sep 2021 08:34:18 +0000 (18:04 +0930)
        * elf32-ppc.c (offset_in_range): New function.
(ppc_elf_vle_split16): Sanity check r_offset before accessing
section contents.  Return status.
        (ppc_elf_relocate_section): Sanity check r_offset before
        accessing section contents.  Don't segfault on NULL howto.

bfd/elf32-ppc.c

index dd45da9d6a3bd4567c48e75c70e16207432a16e3..d6b798a1c477e8283bf6fce7c4a1f07ee411b5ab 100644 (file)
@@ -2856,6 +2856,15 @@ is_plt_seq_reloc (enum elf_ppc_reloc_type r_type)
          || r_type == R_PPC_PLTSEQ);
 }
 
+/* Like bfd_reloc_offset_in_range but without a howto.  Return true
+   iff a field of SIZE bytes at OFFSET is within SEC limits.  */
+
+static bool
+offset_in_range (asection *sec, bfd_vma offset, size_t size)
+{
+  return offset <= sec->size && size <= sec->size - offset;
+}
+
 static void
 bad_shared_reloc (bfd *abfd, enum elf_ppc_reloc_type r_type)
 {
@@ -3884,7 +3893,7 @@ ppc_elf_merge_private_bfd_data (bfd *ibfd, struct bfd_link_info *info)
   return true;
 }
 
-static void
+static bfd_reloc_status_type
 ppc_elf_vle_split16 (bfd *input_bfd,
                     asection *input_section,
                     unsigned long offset,
@@ -3895,6 +3904,8 @@ ppc_elf_vle_split16 (bfd *input_bfd,
 {
   unsigned int insn, opcode;
 
+  if (!offset_in_range (input_section, offset, 4))
+    return bfd_reloc_outofrange;
   insn = bfd_get_32 (input_bfd, loc);
   opcode = insn & E_OPCODE_MASK;
   if (opcode == E_OR2I_INSN
@@ -3951,6 +3962,7 @@ ppc_elf_vle_split16 (bfd *input_bfd,
     }
   insn |= value & 0x7ff;
   bfd_put_32 (input_bfd, insn, loc);
+  return bfd_reloc_ok;
 }
 
 static void
@@ -7132,7 +7144,8 @@ ppc_elf_relocate_section (bfd *output_bfd,
        case R_PPC_GOT_TPREL16:
        case R_PPC_GOT_TPREL16_LO:
          if ((tls_mask & TLS_TLS) != 0
-             && (tls_mask & TLS_TPREL) == 0)
+             && (tls_mask & TLS_TPREL) == 0
+             && offset_in_range (input_section, rel->r_offset - d_offset, 4))
            {
              bfd_vma insn;
 
@@ -7149,7 +7162,8 @@ ppc_elf_relocate_section (bfd *output_bfd,
 
        case R_PPC_TLS:
          if ((tls_mask & TLS_TLS) != 0
-             && (tls_mask & TLS_TPREL) == 0)
+             && (tls_mask & TLS_TPREL) == 0
+             && offset_in_range (input_section, rel->r_offset, 4))
            {
              bfd_vma insn;
 
@@ -7170,13 +7184,15 @@ ppc_elf_relocate_section (bfd *output_bfd,
        case R_PPC_GOT_TLSGD16_HI:
        case R_PPC_GOT_TLSGD16_HA:
          tls_gd = TLS_GDIE;
-         if ((tls_mask & TLS_TLS) != 0 && (tls_mask & TLS_GD) == 0)
+         if ((tls_mask & TLS_TLS) != 0 && (tls_mask & TLS_GD) == 0
+             && offset_in_range (input_section, rel->r_offset - d_offset, 4))
            goto tls_gdld_hi;
          break;
 
        case R_PPC_GOT_TLSLD16_HI:
        case R_PPC_GOT_TLSLD16_HA:
-         if ((tls_mask & TLS_TLS) != 0 && (tls_mask & TLS_LD) == 0)
+         if ((tls_mask & TLS_TLS) != 0 && (tls_mask & TLS_LD) == 0
+             && offset_in_range (input_section, rel->r_offset - d_offset, 4))
            {
            tls_gdld_hi:
              if ((tls_mask & tls_gd) != 0)
@@ -7195,13 +7211,15 @@ ppc_elf_relocate_section (bfd *output_bfd,
        case R_PPC_GOT_TLSGD16:
        case R_PPC_GOT_TLSGD16_LO:
          tls_gd = TLS_GDIE;
-         if ((tls_mask & TLS_TLS) != 0 && (tls_mask & TLS_GD) == 0)
+         if ((tls_mask & TLS_TLS) != 0 && (tls_mask & TLS_GD) == 0
+             && offset_in_range (input_section, rel->r_offset - d_offset, 4))
            goto tls_ldgd_opt;
          break;
 
        case R_PPC_GOT_TLSLD16:
        case R_PPC_GOT_TLSLD16_LO:
-         if ((tls_mask & TLS_TLS) != 0 && (tls_mask & TLS_LD) == 0)
+         if ((tls_mask & TLS_TLS) != 0 && (tls_mask & TLS_LD) == 0
+             && offset_in_range (input_section, rel->r_offset - d_offset, 4))
            {
              unsigned int insn1, insn2;
              bfd_vma offset;
@@ -7229,7 +7247,8 @@ ppc_elf_relocate_section (bfd *output_bfd,
                  /* IE */
                  insn1 &= (0x1f << 21) | (0x1f << 16);
                  insn1 |= 32u << 26;   /* lwz */
-                 if (offset != (bfd_vma) -1)
+                 if (offset != (bfd_vma) -1
+                     && offset_in_range (input_section, offset, 4))
                    {
                      rel[1].r_info = ELF32_R_INFO (STN_UNDEF, R_PPC_NONE);
                      insn2 = 0x7c631214;       /* add 3,3,2 */
@@ -7262,7 +7281,8 @@ ppc_elf_relocate_section (bfd *output_bfd,
                    }
                  r_type = R_PPC_TPREL16_HA;
                  rel->r_info = ELF32_R_INFO (r_symndx, r_type);
-                 if (offset != (bfd_vma) -1)
+                 if (offset != (bfd_vma) -1
+                     && offset_in_range (input_section, offset, 4))
                    {
                      rel[1].r_info = ELF32_R_INFO (r_symndx, R_PPC_TPREL16_LO);
                      rel[1].r_offset = offset + d_offset;
@@ -7284,7 +7304,8 @@ ppc_elf_relocate_section (bfd *output_bfd,
 
        case R_PPC_TLSGD:
          if ((tls_mask & TLS_TLS) != 0 && (tls_mask & TLS_GD) == 0
-             && rel + 1 < relend)
+             && rel + 1 < relend
+             && offset_in_range (input_section, rel->r_offset, 4))
            {
              unsigned int insn2;
              bfd_vma offset = rel->r_offset;
@@ -7319,7 +7340,8 @@ ppc_elf_relocate_section (bfd *output_bfd,
 
        case R_PPC_TLSLD:
          if ((tls_mask & TLS_TLS) != 0 && (tls_mask & TLS_LD) == 0
-             && rel + 1 < relend)
+             && rel + 1 < relend
+             && offset_in_range (input_section, rel->r_offset, 4))
            {
              unsigned int insn2;
 
@@ -7372,48 +7394,50 @@ ppc_elf_relocate_section (bfd *output_bfd,
          /* Branch not taken prediction relocations.  */
        case R_PPC_ADDR14_BRNTAKEN:
        case R_PPC_REL14_BRNTAKEN:
-         {
-           unsigned int insn;
+         if (offset_in_range (input_section, rel->r_offset, 4))
+           {
+             unsigned int insn;
 
-           insn = bfd_get_32 (input_bfd, contents + rel->r_offset);
-           insn &= ~BRANCH_PREDICT_BIT;
-           insn |= branch_bit;
+             insn = bfd_get_32 (input_bfd, contents + rel->r_offset);
+             insn &= ~BRANCH_PREDICT_BIT;
+             insn |= branch_bit;
 
-           from = (rel->r_offset
-                   + input_section->output_offset
-                   + input_section->output_section->vma);
+             from = (rel->r_offset
+                     + input_section->output_offset
+                     + input_section->output_section->vma);
 
-           /* Invert 'y' bit if not the default.  */
-           if ((bfd_signed_vma) (relocation + rel->r_addend - from) < 0)
-             insn ^= BRANCH_PREDICT_BIT;
+             /* Invert 'y' bit if not the default.  */
+             if ((bfd_signed_vma) (relocation + rel->r_addend - from) < 0)
+               insn ^= BRANCH_PREDICT_BIT;
 
-           bfd_put_32 (input_bfd, insn, contents + rel->r_offset);
-         }
+             bfd_put_32 (input_bfd, insn, contents + rel->r_offset);
+           }
          break;
 
        case R_PPC_PLT16_HA:
-         {
-           unsigned int insn;
+         if (offset_in_range (input_section, rel->r_offset - d_offset, 4))
+           {
+             unsigned int insn;
 
-           insn = bfd_get_32 (input_bfd,
-                              contents + rel->r_offset - d_offset);
-           if ((insn & (0x3fu << 26)) == 15u << 26
-               && (insn & (0x1f << 16)) != 0)
-             {
-               if (!bfd_link_pic (info))
-                 {
-                   /* Convert addis to lis.  */
-                   insn &= ~(0x1f << 16);
-                   bfd_put_32 (input_bfd, insn,
-                               contents + rel->r_offset - d_offset);
-                 }
-             }
-           else if (bfd_link_pic (info))
-             info->callbacks->einfo
-               (_("%P: %H: error: %s with unexpected instruction %x\n"),
-                input_bfd, input_section, rel->r_offset,
-                "R_PPC_PLT16_HA", insn);
-         }
+             insn = bfd_get_32 (input_bfd,
+                                contents + rel->r_offset - d_offset);
+             if ((insn & (0x3fu << 26)) == 15u << 26
+                 && (insn & (0x1f << 16)) != 0)
+               {
+                 if (!bfd_link_pic (info))
+                   {
+                     /* Convert addis to lis.  */
+                     insn &= ~(0x1f << 16);
+                     bfd_put_32 (input_bfd, insn,
+                                 contents + rel->r_offset - d_offset);
+                   }
+               }
+             else if (bfd_link_pic (info))
+               info->callbacks->einfo
+                 (_("%P: %H: error: %s with unexpected instruction %x\n"),
+                  input_bfd, input_section, rel->r_offset,
+                  "R_PPC_PLT16_HA", insn);
+           }
          break;
        }
 
@@ -7429,7 +7453,8 @@ ppc_elf_relocate_section (bfd *output_bfd,
             variable defined in a shared library to PIC.  */
          unsigned int insn;
 
-         if (r_type == R_PPC_ADDR16_HA)
+         if (r_type == R_PPC_ADDR16_HA
+             && offset_in_range (input_section, rel->r_offset - d_offset, 4))
            {
              insn = bfd_get_32 (input_bfd,
                                 contents + rel->r_offset - d_offset);
@@ -7492,7 +7517,9 @@ ppc_elf_relocate_section (bfd *output_bfd,
                   input_bfd, input_section, (uint64_t) rel->r_offset,
                   "R_PPC_ADDR16_HA", insn);
            }
-         else if (r_type == R_PPC_ADDR16_LO)
+         else if (r_type == R_PPC_ADDR16_LO
+                  && offset_in_range (input_section,
+                                      rel->r_offset - d_offset, 4))
            {
              insn = bfd_get_32 (input_bfd,
                                 contents + rel->r_offset - d_offset);
@@ -7616,9 +7643,15 @@ ppc_elf_relocate_section (bfd *output_bfd,
       switch (r_type)
        {
        default:
-         /* xgettext:c-format */
-         _bfd_error_handler (_("%pB: %s unsupported"),
-                             input_bfd, howto->name);
+       de_fault:
+         if (howto)
+           /* xgettext:c-format */
+           _bfd_error_handler (_("%pB: %s unsupported"),
+                               input_bfd, howto->name);
+         else
+           /* xgettext:c-format */
+           _bfd_error_handler (_("%pB: reloc %#x unsupported"),
+                               input_bfd, r_type);
 
          bfd_set_error (bfd_error_bad_value);
          ret = false;
@@ -7956,7 +7989,8 @@ ppc_elf_relocate_section (bfd *output_bfd,
        case R_PPC_TPREL16_HA:
          if (h != NULL
              && h->root.type == bfd_link_hash_undefweak
-             && h->dynindx == -1)
+             && h->dynindx == -1
+             && offset_in_range (input_section, rel->r_offset - d_offset, 4))
            {
              /* Make this relocation against an undefined weak symbol
                 resolve to zero.  This is really just a tweak, since
@@ -8224,66 +8258,73 @@ ppc_elf_relocate_section (bfd *output_bfd,
          /* Fall through.  */
 
        case R_PPC_RELAX:
-         {
-           const int *stub;
-           size_t size;
-           size_t insn_offset = rel->r_offset;
-           unsigned int insn;
-
-           if (bfd_link_pic (info))
-             {
-               relocation -= (input_section->output_section->vma
-                              + input_section->output_offset
-                              + rel->r_offset - 4);
-               stub = shared_stub_entry;
-               bfd_put_32 (input_bfd, stub[0], contents + insn_offset - 12);
-               bfd_put_32 (input_bfd, stub[1], contents + insn_offset - 8);
-               bfd_put_32 (input_bfd, stub[2], contents + insn_offset - 4);
-               stub += 3;
-               size = ARRAY_SIZE (shared_stub_entry) - 3;
-             }
-           else
-             {
-               stub = stub_entry;
-               size = ARRAY_SIZE (stub_entry);
-             }
-
-           relocation += addend;
-           if (bfd_link_relocatable (info))
-             relocation = 0;
-
-           /* First insn is HA, second is LO.  */
-           insn = *stub++;
-           insn |= ((relocation + 0x8000) >> 16) & 0xffff;
-           bfd_put_32 (input_bfd, insn, contents + insn_offset);
-           insn_offset += 4;
+         if (bfd_link_pic (info)
+             ? offset_in_range (input_section, rel->r_offset - 12,
+                                ARRAY_SIZE (shared_stub_entry) * 4)
+             : offset_in_range (input_section, rel->r_offset,
+                                ARRAY_SIZE (stub_entry) * 4))
+           {
+             const int *stub;
+             size_t size;
+             size_t insn_offset = rel->r_offset;
+             unsigned int insn;
 
-           insn = *stub++;
-           insn |= relocation & 0xffff;
-           bfd_put_32 (input_bfd, insn, contents + insn_offset);
-           insn_offset += 4;
-           size -= 2;
+             if (bfd_link_pic (info))
+               {
+                 relocation -= (input_section->output_section->vma
+                                + input_section->output_offset
+                                + rel->r_offset - 4);
+                 stub = shared_stub_entry;
+                 bfd_put_32 (input_bfd, stub[0], contents + insn_offset - 12);
+                 bfd_put_32 (input_bfd, stub[1], contents + insn_offset - 8);
+                 bfd_put_32 (input_bfd, stub[2], contents + insn_offset - 4);
+                 stub += 3;
+                 size = ARRAY_SIZE (shared_stub_entry) - 3;
+               }
+             else
+               {
+                 stub = stub_entry;
+                 size = ARRAY_SIZE (stub_entry);
+               }
 
-           while (size != 0)
-             {
-               insn = *stub++;
-               --size;
-               bfd_put_32 (input_bfd, insn, contents + insn_offset);
-               insn_offset += 4;
-             }
+             relocation += addend;
+             if (bfd_link_relocatable (info))
+               relocation = 0;
+
+             /* First insn is HA, second is LO.  */
+             insn = *stub++;
+             insn |= ((relocation + 0x8000) >> 16) & 0xffff;
+             bfd_put_32 (input_bfd, insn, contents + insn_offset);
+             insn_offset += 4;
+
+             insn = *stub++;
+             insn |= relocation & 0xffff;
+             bfd_put_32 (input_bfd, insn, contents + insn_offset);
+             insn_offset += 4;
+             size -= 2;
+
+             while (size != 0)
+               {
+                 insn = *stub++;
+                 --size;
+                 bfd_put_32 (input_bfd, insn, contents + insn_offset);
+                 insn_offset += 4;
+               }
 
-           /* Rewrite the reloc and convert one of the trailing nop
-              relocs to describe this relocation.  */
-           BFD_ASSERT (ELF32_R_TYPE (relend[-1].r_info) == R_PPC_NONE);
-           /* The relocs are at the bottom 2 bytes */
-           wrel->r_offset = rel->r_offset + d_offset;
-           wrel->r_info = ELF32_R_INFO (r_symndx, R_PPC_ADDR16_HA);
-           wrel->r_addend = rel->r_addend;
-           memmove (wrel + 1, wrel, (relend - wrel - 1) * sizeof (*wrel));
-           wrel++, rel++;
-           wrel->r_offset += 4;
-           wrel->r_info = ELF32_R_INFO (r_symndx, R_PPC_ADDR16_LO);
-         }
+             /* Rewrite the reloc and convert one of the trailing nop
+                relocs to describe this relocation.  */
+             BFD_ASSERT (ELF32_R_TYPE (relend[-1].r_info) == R_PPC_NONE);
+             /* The relocs are at the bottom 2 bytes */
+             wrel->r_offset = rel->r_offset + d_offset;
+             wrel->r_info = ELF32_R_INFO (r_symndx, R_PPC_ADDR16_HA);
+             wrel->r_addend = rel->r_addend;
+             memmove (wrel + 1, wrel, (relend - wrel - 1) * sizeof (*wrel));
+             wrel++, rel++;
+             wrel->r_offset += 4;
+             wrel->r_info = ELF32_R_INFO (r_symndx, R_PPC_ADDR16_LO);
+           }
+         else
+           goto de_fault;
          continue;
 
          /* Indirect .sdata relocation.  */
@@ -8487,151 +8528,164 @@ ppc_elf_relocate_section (bfd *output_bfd,
 
        case R_PPC_VLE_LO16A:
          relocation = relocation + addend;
-         ppc_elf_vle_split16 (input_bfd, input_section, rel->r_offset,
-                              contents + rel->r_offset, relocation,
-                              split16a_type, htab->params->vle_reloc_fixup);
-         goto copy_reloc;
+         r = ppc_elf_vle_split16 (input_bfd, input_section, rel->r_offset,
+                                  contents + rel->r_offset, relocation,
+                                  split16a_type,
+                                  htab->params->vle_reloc_fixup);
+         goto report_reloc;
 
        case R_PPC_VLE_LO16D:
          relocation = relocation + addend;
-         ppc_elf_vle_split16 (input_bfd, input_section, rel->r_offset,
-                              contents + rel->r_offset, relocation,
-                              split16d_type, htab->params->vle_reloc_fixup);
-         goto copy_reloc;
+         r = ppc_elf_vle_split16 (input_bfd, input_section, rel->r_offset,
+                                  contents + rel->r_offset, relocation,
+                                  split16d_type,
+                                  htab->params->vle_reloc_fixup);
+         goto report_reloc;
 
        case R_PPC_VLE_HI16A:
          relocation = (relocation + addend) >> 16;
-         ppc_elf_vle_split16 (input_bfd, input_section, rel->r_offset,
-                              contents + rel->r_offset, relocation,
-                              split16a_type, htab->params->vle_reloc_fixup);
-         goto copy_reloc;
+         r = ppc_elf_vle_split16 (input_bfd, input_section, rel->r_offset,
+                                  contents + rel->r_offset, relocation,
+                                  split16a_type,
+                                  htab->params->vle_reloc_fixup);
+         goto report_reloc;
 
        case R_PPC_VLE_HI16D:
          relocation = (relocation + addend) >> 16;
-         ppc_elf_vle_split16 (input_bfd, input_section, rel->r_offset,
-                              contents + rel->r_offset, relocation,
-                              split16d_type, htab->params->vle_reloc_fixup);
-         goto copy_reloc;
+         r = ppc_elf_vle_split16 (input_bfd, input_section, rel->r_offset,
+                                  contents + rel->r_offset, relocation,
+                                  split16d_type,
+                                  htab->params->vle_reloc_fixup);
+         goto report_reloc;
 
        case R_PPC_VLE_HA16A:
          relocation = (relocation + addend + 0x8000) >> 16;
-         ppc_elf_vle_split16 (input_bfd, input_section, rel->r_offset,
-                              contents + rel->r_offset, relocation,
-                              split16a_type, htab->params->vle_reloc_fixup);
-         goto copy_reloc;
+         r = ppc_elf_vle_split16 (input_bfd, input_section, rel->r_offset,
+                                  contents + rel->r_offset, relocation,
+                                  split16a_type,
+                                  htab->params->vle_reloc_fixup);
+         goto report_reloc;
 
        case R_PPC_VLE_HA16D:
          relocation = (relocation + addend + 0x8000) >> 16;
-         ppc_elf_vle_split16 (input_bfd, input_section, rel->r_offset,
-                              contents + rel->r_offset, relocation,
-                              split16d_type, htab->params->vle_reloc_fixup);
-         goto copy_reloc;
+         r = ppc_elf_vle_split16 (input_bfd, input_section, rel->r_offset,
+                                  contents + rel->r_offset, relocation,
+                                  split16d_type,
+                                  htab->params->vle_reloc_fixup);
+         goto report_reloc;
 
          /* Relocate against either _SDA_BASE_, _SDA2_BASE_, or 0.  */
        case R_PPC_EMB_SDA21:
        case R_PPC_VLE_SDA21:
        case R_PPC_EMB_RELSDA:
        case R_PPC_VLE_SDA21_LO:
-         {
-           const char *name;
-           int reg;
-           unsigned int insn;
-           struct elf_link_hash_entry *sda = NULL;
+         if (!offset_in_range (input_section, rel->r_offset, 4))
+           {
+             r = bfd_reloc_outofrange;
+             goto report_reloc;
+           }
+         else
+           {
+             const char *name;
+             int reg;
+             unsigned int insn;
+             struct elf_link_hash_entry *sda = NULL;
 
-           if (sec == NULL || sec->output_section == NULL)
-             {
-               unresolved_reloc = true;
-               break;
-             }
+             if (sec == NULL || sec->output_section == NULL)
+               {
+                 unresolved_reloc = true;
+                 break;
+               }
 
-           name = bfd_section_name (sec->output_section);
-           if (strcmp (name, ".sdata") == 0
-               || strcmp (name, ".sbss") == 0)
-             {
-               reg = 13;
-               sda = htab->sdata[0].sym;
-             }
-           else if (strcmp (name, ".sdata2") == 0
-                    || strcmp (name, ".sbss2") == 0)
-             {
-               reg = 2;
-               sda = htab->sdata[1].sym;
-             }
-           else if (strcmp (name, ".PPC.EMB.sdata0") == 0
-                    || strcmp (name, ".PPC.EMB.sbss0") == 0)
-             {
-               reg = 0;
-             }
-           else
-             {
-               _bfd_error_handler
-                 /* xgettext:c-format */
-                 (_("%pB: the target (%s) of a %s relocation is "
-                    "in the wrong output section (%s)"),
-                  input_bfd,
-                  sym_name,
-                  howto->name,
-                  name);
+             name = bfd_section_name (sec->output_section);
+             if (strcmp (name, ".sdata") == 0
+                 || strcmp (name, ".sbss") == 0)
+               {
+                 reg = 13;
+                 sda = htab->sdata[0].sym;
+               }
+             else if (strcmp (name, ".sdata2") == 0
+                      || strcmp (name, ".sbss2") == 0)
+               {
+                 reg = 2;
+                 sda = htab->sdata[1].sym;
+               }
+             else if (strcmp (name, ".PPC.EMB.sdata0") == 0
+                      || strcmp (name, ".PPC.EMB.sbss0") == 0)
+               {
+                 reg = 0;
+               }
+             else
+               {
+                 _bfd_error_handler
+                   /* xgettext:c-format */
+                   (_("%pB: the target (%s) of a %s relocation is "
+                      "in the wrong output section (%s)"),
+                    input_bfd,
+                    sym_name,
+                    howto->name,
+                    name);
+
+                 bfd_set_error (bfd_error_bad_value);
+                 ret = false;
+                 goto copy_reloc;
+               }
 
-               bfd_set_error (bfd_error_bad_value);
-               ret = false;
-               goto copy_reloc;
-             }
+             if (sda != NULL)
+               {
+                 if (!is_static_defined (sda))
+                   {
+                     unresolved_reloc = true;
+                     break;
+                   }
+                 addend -= SYM_VAL (sda);
+               }
 
-           if (sda != NULL)
-             {
-               if (!is_static_defined (sda))
-                 {
-                   unresolved_reloc = true;
-                   break;
-                 }
-               addend -= SYM_VAL (sda);
-             }
+             if (r_type == R_PPC_EMB_RELSDA)
+               break;
 
-           if (r_type == R_PPC_EMB_RELSDA)
-             break;
+             /* The PowerPC Embedded Application Binary Interface
+                version 1.0 insanely chose to specify R_PPC_EMB_SDA21
+                operating on a 24-bit field at r_offset.  GNU as and
+                GNU ld have always assumed R_PPC_EMB_SDA21 operates on
+                a 32-bit bit insn at r_offset.  Cope with object file
+                producers that possibly comply with the EABI in
+                generating an odd r_offset for big-endian objects.  */
+             if (r_type == R_PPC_EMB_SDA21)
+               rel->r_offset &= ~1;
 
-           /* The PowerPC Embedded Application Binary Interface
-              version 1.0 insanely chose to specify R_PPC_EMB_SDA21
-              operating on a 24-bit field at r_offset.  GNU as and
-              GNU ld have always assumed R_PPC_EMB_SDA21 operates on
-              a 32-bit bit insn at r_offset.  Cope with object file
-              producers that possibly comply with the EABI in
-              generating an odd r_offset for big-endian objects.  */
-           if (r_type == R_PPC_EMB_SDA21)
-             rel->r_offset &= ~1;
-
-           insn = bfd_get_32 (input_bfd, contents + rel->r_offset);
-           if (reg == 0
-               && (r_type == R_PPC_VLE_SDA21
-                   || r_type == R_PPC_VLE_SDA21_LO))
-             {
-               relocation = relocation + addend;
-               addend = 0;
-
-               /* Force e_li insn, keeping RT from original insn.  */
-               insn &= 0x1f << 21;
-               insn |= 28u << 26;
-
-               /* We have an li20 field, bits 17..20, 11..15, 21..31.  */
-               /* Top 4 bits of value to 17..20.  */
-               insn |= (relocation & 0xf0000) >> 5;
-               /* Next 5 bits of the value to 11..15.  */
-               insn |= (relocation & 0xf800) << 5;
-               /* And the final 11 bits of the value to bits 21 to 31.  */
-               insn |= relocation & 0x7ff;
-
-               bfd_put_32 (input_bfd, insn, contents + rel->r_offset);
-
-               if (r_type == R_PPC_VLE_SDA21
-                   && ((relocation + 0x80000) & 0xffffffff) > 0x100000)
-                 goto overflow;
-               goto copy_reloc;
-             }
-           /* Fill in register field.  */
-           insn = (insn & ~RA_REGISTER_MASK) | (reg << RA_REGISTER_SHIFT);
-           bfd_put_32 (input_bfd, insn, contents + rel->r_offset);
-         }
+             insn = bfd_get_32 (input_bfd, contents + rel->r_offset);
+             if (reg == 0
+                 && (r_type == R_PPC_VLE_SDA21
+                     || r_type == R_PPC_VLE_SDA21_LO))
+               {
+                 relocation = relocation + addend;
+                 addend = 0;
+
+                 /* Force e_li insn, keeping RT from original insn.  */
+                 insn &= 0x1f << 21;
+                 insn |= 28u << 26;
+
+                 /* We have an li20 field, bits 17..20, 11..15, 21..31.  */
+                 /* Top 4 bits of value to 17..20.  */
+                 insn |= (relocation & 0xf0000) >> 5;
+                 /* Next 5 bits of the value to 11..15.  */
+                 insn |= (relocation & 0xf800) << 5;
+                 /* And the final 11 bits of the value to bits 21 to 31.  */
+                 insn |= relocation & 0x7ff;
+
+                 bfd_put_32 (input_bfd, insn, contents + rel->r_offset);
+
+                 r = bfd_reloc_ok;
+                 if (r_type == R_PPC_VLE_SDA21
+                     && ((relocation + 0x80000) & 0xffffffff) > 0x100000)
+                   r = bfd_reloc_overflow;
+                 goto report_reloc;
+               }
+             /* Fill in register field.  */
+             insn = (insn & ~RA_REGISTER_MASK) | (reg << RA_REGISTER_SHIFT);
+             bfd_put_32 (input_bfd, insn, contents + rel->r_offset);
+           }
          break;
 
        case R_PPC_VLE_SDAREL_LO16A:
@@ -8640,95 +8694,113 @@ ppc_elf_relocate_section (bfd *output_bfd,
        case R_PPC_VLE_SDAREL_HI16D:
        case R_PPC_VLE_SDAREL_HA16A:
        case R_PPC_VLE_SDAREL_HA16D:
-         {
-           bfd_vma value;
-           const char *name;
-           struct elf_link_hash_entry *sda = NULL;
-
-           if (sec == NULL || sec->output_section == NULL)
-             {
-               unresolved_reloc = true;
-               break;
-             }
-
-           name = bfd_section_name (sec->output_section);
-           if (strcmp (name, ".sdata") == 0
-               || strcmp (name, ".sbss") == 0)
-             sda = htab->sdata[0].sym;
-           else if (strcmp (name, ".sdata2") == 0
-                    || strcmp (name, ".sbss2") == 0)
-             sda = htab->sdata[1].sym;
-           else
-             {
-               _bfd_error_handler
-                 /* xgettext:c-format */
-                 (_("%pB: the target (%s) of a %s relocation is "
-                    "in the wrong output section (%s)"),
-                  input_bfd,
-                  sym_name,
-                  howto->name,
-                  name);
+         if (!offset_in_range (input_section, rel->r_offset, 4))
+           r = bfd_reloc_outofrange;
+         else
+           {
+             bfd_vma value;
+             const char *name;
+             struct elf_link_hash_entry *sda = NULL;
 
-               bfd_set_error (bfd_error_bad_value);
-               ret = false;
-               goto copy_reloc;
-             }
+             if (sec == NULL || sec->output_section == NULL)
+               {
+                 unresolved_reloc = true;
+                 break;
+               }
 
-           if (sda == NULL || !is_static_defined (sda))
-             {
-               unresolved_reloc = true;
-               break;
-             }
-           value = relocation + addend - SYM_VAL (sda);
+             name = bfd_section_name (sec->output_section);
+             if (strcmp (name, ".sdata") == 0
+                 || strcmp (name, ".sbss") == 0)
+               sda = htab->sdata[0].sym;
+             else if (strcmp (name, ".sdata2") == 0
+                      || strcmp (name, ".sbss2") == 0)
+               sda = htab->sdata[1].sym;
+             else
+               {
+                 _bfd_error_handler
+                   /* xgettext:c-format */
+                   (_("%pB: the target (%s) of a %s relocation is "
+                      "in the wrong output section (%s)"),
+                    input_bfd,
+                    sym_name,
+                    howto->name,
+                    name);
+
+                 bfd_set_error (bfd_error_bad_value);
+                 ret = false;
+                 goto copy_reloc;
+               }
 
-           if (r_type == R_PPC_VLE_SDAREL_LO16A)
-             ppc_elf_vle_split16 (input_bfd, input_section, rel->r_offset,
-                                  contents + rel->r_offset, value,
-                                  split16a_type,
-                                  htab->params->vle_reloc_fixup);
-           else if (r_type == R_PPC_VLE_SDAREL_LO16D)
-             ppc_elf_vle_split16 (input_bfd, input_section, rel->r_offset,
-                                  contents + rel->r_offset, value,
-                                  split16d_type,
-                                  htab->params->vle_reloc_fixup);
-           else if (r_type == R_PPC_VLE_SDAREL_HI16A)
-             {
-               value = value >> 16;
-               ppc_elf_vle_split16 (input_bfd, input_section, rel->r_offset,
-                                    contents + rel->r_offset, value,
-                                    split16a_type,
-                                    htab->params->vle_reloc_fixup);
-             }
-           else if (r_type == R_PPC_VLE_SDAREL_HI16D)
-             {
-               value = value >> 16;
-               ppc_elf_vle_split16 (input_bfd, input_section, rel->r_offset,
-                                    contents + rel->r_offset, value,
-                                    split16d_type,
-                                    htab->params->vle_reloc_fixup);
-             }
-           else if (r_type == R_PPC_VLE_SDAREL_HA16A)
-             {
-               value = (value + 0x8000) >> 16;
-               ppc_elf_vle_split16 (input_bfd, input_section, rel->r_offset,
-                                    contents + rel->r_offset, value,
-                                    split16a_type,
-                                    htab->params->vle_reloc_fixup);
-             }
-           else if (r_type == R_PPC_VLE_SDAREL_HA16D)
-             {
-               value = (value + 0x8000) >> 16;
-               ppc_elf_vle_split16 (input_bfd, input_section, rel->r_offset,
-                                    contents + rel->r_offset, value,
-                                    split16d_type,
-                                    htab->params->vle_reloc_fixup);
-             }
-         }
-         goto copy_reloc;
+             if (sda == NULL || !is_static_defined (sda))
+               {
+                 unresolved_reloc = true;
+                 break;
+               }
+             value = relocation + addend - SYM_VAL (sda);
+
+             if (r_type == R_PPC_VLE_SDAREL_LO16A)
+               r = ppc_elf_vle_split16 (input_bfd, input_section,
+                                        rel->r_offset,
+                                        contents + rel->r_offset, value,
+                                        split16a_type,
+                                        htab->params->vle_reloc_fixup);
+             else if (r_type == R_PPC_VLE_SDAREL_LO16D)
+               r = ppc_elf_vle_split16 (input_bfd, input_section,
+                                        rel->r_offset,
+                                        contents + rel->r_offset, value,
+                                        split16d_type,
+                                        htab->params->vle_reloc_fixup);
+             else if (r_type == R_PPC_VLE_SDAREL_HI16A)
+               {
+                 value = value >> 16;
+                 r = ppc_elf_vle_split16 (input_bfd, input_section,
+                                          rel->r_offset,
+                                          contents + rel->r_offset, value,
+                                          split16a_type,
+                                          htab->params->vle_reloc_fixup);
+               }
+             else if (r_type == R_PPC_VLE_SDAREL_HI16D)
+               {
+                 value = value >> 16;
+                 r = ppc_elf_vle_split16 (input_bfd, input_section,
+                                          rel->r_offset,
+                                          contents + rel->r_offset, value,
+                                          split16d_type,
+                                          htab->params->vle_reloc_fixup);
+               }
+             else if (r_type == R_PPC_VLE_SDAREL_HA16A)
+               {
+                 value = (value + 0x8000) >> 16;
+                 r = ppc_elf_vle_split16 (input_bfd, input_section,
+                                          rel->r_offset,
+                                          contents + rel->r_offset, value,
+                                          split16a_type,
+                                          htab->params->vle_reloc_fixup);
+               }
+             else if (r_type == R_PPC_VLE_SDAREL_HA16D)
+               {
+                 value = (value + 0x8000) >> 16;
+                 r = ppc_elf_vle_split16 (input_bfd, input_section,
+                                          rel->r_offset,
+                                          contents + rel->r_offset, value,
+                                          split16d_type,
+                                          htab->params->vle_reloc_fixup);
+               }
+             else
+               abort ();
+           }
+         goto report_reloc;
 
        case R_PPC_VLE_ADDR20:
-         ppc_elf_vle_split20 (output_bfd, contents + rel->r_offset, relocation);
-         goto copy_reloc;
+         if (!offset_in_range (input_section, rel->r_offset, 4))
+           r = bfd_reloc_outofrange;
+         else
+           {
+             ppc_elf_vle_split20 (output_bfd, contents + rel->r_offset,
+                                  relocation);
+             r = bfd_reloc_ok;
+           }
+         goto report_reloc;
 
          /* Relocate against the beginning of the section.  */
        case R_PPC_SECTOFF:
@@ -8780,7 +8852,10 @@ ppc_elf_relocate_section (bfd *output_bfd,
          break;
 
        case R_PPC_TPREL16_HA:
-         if (htab->do_tls_opt && relocation + addend + 0x8000 < 0x10000)
+         if (htab->do_tls_opt
+             && relocation + addend + 0x8000 < 0x10000
+             && offset_in_range (input_section, rel->r_offset & ~3, 4))
+
            {
              bfd_byte *p = contents + (rel->r_offset & ~3);
              bfd_put_32 (input_bfd, NOP, p);
@@ -8788,7 +8863,9 @@ ppc_elf_relocate_section (bfd *output_bfd,
          break;
 
        case R_PPC_TPREL16_LO:
-         if (htab->do_tls_opt && relocation + addend + 0x8000 < 0x10000)
+         if (htab->do_tls_opt
+             && relocation + addend + 0x8000 < 0x10000
+             && offset_in_range (input_section, rel->r_offset & ~3, 4))
            {
              bfd_byte *p = contents + (rel->r_offset & ~3);
              unsigned int insn = bfd_get_32 (input_bfd, p);
@@ -8807,13 +8884,16 @@ ppc_elf_relocate_section (bfd *output_bfd,
        case R_PPC_PLTCALL:
          if (unresolved_reloc)
            {
-             bfd_byte *p = contents + rel->r_offset;
-             unsigned int insn = bfd_get_32 (input_bfd, p);
-             insn &= 1;
-             bfd_put_32 (input_bfd, B | insn, p);
-             unresolved_reloc = save_unresolved_reloc;
-             r_type = R_PPC_REL24;
-             howto = ppc_elf_howto_table[r_type];
+             if (offset_in_range (input_section, rel->r_offset, 4))
+               {
+                 bfd_byte *p = contents + rel->r_offset;
+                 unsigned int insn = bfd_get_32 (input_bfd, p);
+                 insn &= 1;
+                 bfd_put_32 (input_bfd, B | insn, p);
+                 unresolved_reloc = save_unresolved_reloc;
+                 r_type = R_PPC_REL24;
+                 howto = ppc_elf_howto_table[r_type];
+               }
            }
          else if (htab->plt_type != PLT_NEW)
            info->callbacks->einfo
@@ -8827,11 +8907,14 @@ ppc_elf_relocate_section (bfd *output_bfd,
        case R_PPC_PLT16_LO:
          if (unresolved_reloc)
            {
-             bfd_byte *p = contents + (rel->r_offset & ~3);
-             bfd_put_32 (input_bfd, NOP, p);
-             unresolved_reloc = false;
-             r_type = R_PPC_NONE;
-             howto = ppc_elf_howto_table[r_type];
+             if (offset_in_range (input_section, rel->r_offset & ~3, 4))
+               {
+                 bfd_byte *p = contents + (rel->r_offset & ~3);
+                 bfd_put_32 (input_bfd, NOP, p);
+                 unresolved_reloc = false;
+                 r_type = R_PPC_NONE;
+                 howto = ppc_elf_howto_table[r_type];
+               }
            }
          else if (htab->plt_type != PLT_NEW)
            info->callbacks->einfo
@@ -8893,36 +8976,37 @@ ppc_elf_relocate_section (bfd *output_bfd,
        case R_PPC_GOT_DTPREL16_LO:
        case R_PPC_GOT_TPREL16:
        case R_PPC_GOT_TPREL16_LO:
-         {
-           /* The 32-bit ABI lacks proper relocations to deal with
-              certain 64-bit instructions.  Prevent damage to bits
-              that make up part of the insn opcode.  */
-           unsigned int insn, mask, lobit;
-
-           insn = bfd_get_32 (input_bfd,
-                              contents + rel->r_offset - d_offset);
-           mask = 0;
-           if (is_insn_ds_form (insn))
-             mask = 3;
-           else if (is_insn_dq_form (insn))
-             mask = 15;
-           else
-             break;
-           relocation += addend;
-           addend = insn & mask;
-           lobit = mask & relocation;
-           if (lobit != 0)
-             {
-               relocation ^= lobit;
-               info->callbacks->einfo
-                 /* xgettext:c-format */
-                 (_("%H: error: %s against `%s' not a multiple of %u\n"),
-                  input_bfd, input_section, rel->r_offset,
-                  howto->name, sym_name, mask + 1);
-               bfd_set_error (bfd_error_bad_value);
-               ret = false;
-             }
-         }
+         if (offset_in_range (input_section, rel->r_offset - d_offset, 4))
+           {
+             /* The 32-bit ABI lacks proper relocations to deal with
+                certain 64-bit instructions.  Prevent damage to bits
+                that make up part of the insn opcode.  */
+             unsigned int insn, mask, lobit;
+
+             insn = bfd_get_32 (input_bfd,
+                                contents + rel->r_offset - d_offset);
+             mask = 0;
+             if (is_insn_ds_form (insn))
+               mask = 3;
+             else if (is_insn_dq_form (insn))
+               mask = 15;
+             else
+               break;
+             relocation += addend;
+             addend = insn & mask;
+             lobit = mask & relocation;
+             if (lobit != 0)
+               {
+                 relocation ^= lobit;
+                 info->callbacks->einfo
+                   /* xgettext:c-format */
+                   (_("%H: error: %s against `%s' not a multiple of %u\n"),
+                    input_bfd, input_section, rel->r_offset,
+                    howto->name, sym_name, mask + 1);
+                 bfd_set_error (bfd_error_bad_value);
+                 ret = false;
+               }
+           }
          break;
        }
 
@@ -8957,7 +9041,8 @@ ppc_elf_relocate_section (bfd *output_bfd,
         have different reloc types.  */
       if (howto->complain_on_overflow != complain_overflow_dont
          && howto->dst_mask == 0xffff
-         && (input_section->flags & SEC_CODE) != 0)
+         && (input_section->flags & SEC_CODE) != 0
+         && offset_in_range (input_section, rel->r_offset & ~3, 4))
        {
          enum complain_overflow complain = complain_overflow_signed;
 
@@ -8984,7 +9069,7 @@ ppc_elf_relocate_section (bfd *output_bfd,
       if (r_type == R_PPC_REL16DX_HA)
        {
          /* Split field reloc isn't handled by _bfd_final_link_relocate.  */
-         if (rel->r_offset + 4 > input_section->size)
+         if (offset_in_range (input_section, rel->r_offset, 4))
            r = bfd_reloc_outofrange;
          else
            {
@@ -9006,11 +9091,11 @@ ppc_elf_relocate_section (bfd *output_bfd,
        r = _bfd_final_link_relocate (howto, input_bfd, input_section, contents,
                                      rel->r_offset, relocation, addend);
 
+    report_reloc:
       if (r != bfd_reloc_ok)
        {
          if (r == bfd_reloc_overflow)
            {
-           overflow:
              /* On code like "if (foo) foo();" don't report overflow
                 on a branch to zero when foo is undefined.  */
              if (!warned