aboutsummaryrefslogtreecommitdiff
path: root/bfd/elf64-ppc.c
diff options
context:
space:
mode:
authorAlan Modra <amodra@gmail.com>2021-09-08 13:18:02 +0930
committerAlan Modra <amodra@gmail.com>2021-09-08 22:22:34 +0930
commit733ae98cb8fb0d5653887cfd79aec3cfe5e44846 (patch)
tree98dcd9c74201869ba26ae57e9d6931c3bbd1cf32 /bfd/elf64-ppc.c
parentbeaddc1a805d70131ffbeaee4d8d4ba41b9e5a2e (diff)
downloadgdb-733ae98cb8fb0d5653887cfd79aec3cfe5e44846.zip
gdb-733ae98cb8fb0d5653887cfd79aec3cfe5e44846.tar.gz
gdb-733ae98cb8fb0d5653887cfd79aec3cfe5e44846.tar.bz2
PowerPC64, sanity check r_offset in relocate_section
This hardens the powerpc64 linker code transformations. * elf64-ppc.c (is_8byte_reloc, offset_in_range): New functions. (ppc64_elf_relocate_section): Sanity check r_offset before accessing section contents for various code transformations.
Diffstat (limited to 'bfd/elf64-ppc.c')
-rw-r--r--bfd/elf64-ppc.c168
1 files changed, 119 insertions, 49 deletions
diff --git a/bfd/elf64-ppc.c b/bfd/elf64-ppc.c
index aa997bb..4ebacbd 100644
--- a/bfd/elf64-ppc.c
+++ b/bfd/elf64-ppc.c
@@ -4601,6 +4601,26 @@ is_plt_seq_reloc (enum elf_ppc64_reloc_type r_type)
|| r_type == R_PPC64_PLTSEQ_NOTOC);
}
+/* Of relocs which might appear paired with TLSGD and TLSLD marker
+ relocs, return true for those that operate on a dword. */
+
+static bool
+is_8byte_reloc (enum elf_ppc64_reloc_type r_type)
+{
+ return (r_type == R_PPC64_PLT_PCREL34
+ || r_type == R_PPC64_PLT_PCREL34_NOTOC
+ || r_type == R_PPC64_PLTCALL);
+}
+
+/* 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;
+}
+
/* 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. */
@@ -15222,13 +15242,18 @@ ppc64_elf_relocate_section (bfd *output_bfd,
break;
case R_PPC64_LO_DS_OPT:
- insn = bfd_get_32 (input_bfd, contents + rel->r_offset - d_offset);
- if ((insn & (0x3fu << 26)) != 58u << 26)
- abort ();
- insn += (14u << 26) - (58u << 26);
- bfd_put_32 (input_bfd, insn, contents + rel->r_offset - d_offset);
- r_type = R_PPC64_TOC16_LO;
- rel->r_info = ELF64_R_INFO (r_symndx, r_type);
+ if (offset_in_range (input_section, rel->r_offset - d_offset, 4))
+ {
+ insn = bfd_get_32 (input_bfd,
+ contents + rel->r_offset - d_offset);
+ if ((insn & (0x3fu << 26)) != 58u << 26)
+ abort ();
+ insn += (14u << 26) - (58u << 26);
+ bfd_put_32 (input_bfd, insn,
+ contents + rel->r_offset - d_offset);
+ r_type = R_PPC64_TOC16_LO;
+ rel->r_info = ELF64_R_INFO (r_symndx, r_type);
+ }
break;
case R_PPC64_TOC16:
@@ -15280,7 +15305,8 @@ ppc64_elf_relocate_section (bfd *output_bfd,
case R_PPC64_GOT_TPREL16_HI:
case R_PPC64_GOT_TPREL16_HA:
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))
{
rel->r_offset -= d_offset;
bfd_put_32 (input_bfd, NOP, contents + rel->r_offset);
@@ -15292,7 +15318,8 @@ ppc64_elf_relocate_section (bfd *output_bfd,
case R_PPC64_GOT_TPREL16_DS:
case R_PPC64_GOT_TPREL16_LO_DS:
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))
{
toctprel:
insn = bfd_get_32 (input_bfd,
@@ -15317,7 +15344,8 @@ ppc64_elf_relocate_section (bfd *output_bfd,
case R_PPC64_GOT_TPREL_PCREL34:
if ((tls_mask & TLS_TLS) != 0
- && (tls_mask & TLS_TPREL) == 0)
+ && (tls_mask & TLS_TPREL) == 0
+ && offset_in_range (input_section, rel->r_offset, 8))
{
/* pld ra,sym@got@tprel@pcrel -> paddi ra,r13,sym@tprel */
pinsn = bfd_get_32 (input_bfd, contents + rel->r_offset);
@@ -15336,7 +15364,8 @@ ppc64_elf_relocate_section (bfd *output_bfd,
case R_PPC64_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 & ~3, 4))
{
insn = bfd_get_32 (input_bfd, contents + (rel->r_offset & ~3));
insn = _bfd_elf_ppc_at_tls_transform (insn, 13);
@@ -15387,13 +15416,15 @@ ppc64_elf_relocate_section (bfd *output_bfd,
case R_PPC64_GOT_TLSGD16_HI:
case R_PPC64_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 & ~3, 4))
goto tls_gdld_hi;
break;
case R_PPC64_GOT_TLSLD16_HI:
case R_PPC64_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 & ~3, 4))
{
tls_gdld_hi:
if ((tls_mask & tls_gd) != 0)
@@ -15412,13 +15443,15 @@ ppc64_elf_relocate_section (bfd *output_bfd,
case R_PPC64_GOT_TLSGD16:
case R_PPC64_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 & ~3, 4))
goto tls_ldgd_opt;
break;
case R_PPC64_GOT_TLSLD16:
case R_PPC64_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 & ~3, 4))
{
unsigned int insn1, insn2;
@@ -15488,10 +15521,11 @@ ppc64_elf_relocate_section (bfd *output_bfd,
}
bfd_put_32 (input_bfd, insn1,
contents + rel->r_offset - d_offset);
- if (offset != (bfd_vma) -1)
+ if (offset != (bfd_vma) -1
+ && offset_in_range (input_section, offset, 4))
{
bfd_put_32 (input_bfd, insn2, contents + offset);
- if (offset + 8 <= input_section->size)
+ if (offset_in_range (input_section, offset + 4, 4))
{
insn2 = bfd_get_32 (input_bfd, contents + offset + 4);
if (insn2 == LD_R2_0R1 + STK_TOC (htab))
@@ -15509,7 +15543,8 @@ ppc64_elf_relocate_section (bfd *output_bfd,
break;
case R_PPC64_GOT_TLSGD_PCREL34:
- 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, 8))
{
pinsn = bfd_get_32 (input_bfd, contents + rel->r_offset);
pinsn <<= 32;
@@ -15535,7 +15570,8 @@ ppc64_elf_relocate_section (bfd *output_bfd,
break;
case R_PPC64_GOT_TLSLD_PCREL34:
- 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, 8))
{
pinsn = bfd_get_32 (input_bfd, contents + rel->r_offset);
pinsn <<= 32;
@@ -15555,7 +15591,10 @@ ppc64_elf_relocate_section (bfd *output_bfd,
case R_PPC64_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,
+ is_8byte_reloc (ELF64_R_TYPE (rel[1].r_info))
+ ? 8 : 4))
{
unsigned int insn2;
enum elf_ppc64_reloc_type r_type1 = ELF64_R_TYPE (rel[1].r_info);
@@ -15571,7 +15610,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
break;
}
- if (ELF64_R_TYPE (rel[1].r_info) == R_PPC64_PLTCALL)
+ if (r_type1 == R_PPC64_PLTCALL)
bfd_put_32 (output_bfd, NOP, contents + offset + 4);
if ((tls_mask & TLS_GDIE) != 0)
@@ -15615,7 +15654,10 @@ ppc64_elf_relocate_section (bfd *output_bfd,
case R_PPC64_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,
+ is_8byte_reloc (ELF64_R_TYPE (rel[1].r_info))
+ ? 8 : 4))
{
unsigned int insn2;
enum elf_ppc64_reloc_type r_type1 = ELF64_R_TYPE (rel[1].r_info);
@@ -15631,7 +15673,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
break;
}
- if (ELF64_R_TYPE (rel[1].r_info) == R_PPC64_PLTCALL)
+ if (r_type1 == R_PPC64_PLTCALL)
bfd_put_32 (output_bfd, NOP, contents + offset + 4);
if (r_type1 == R_PPC64_REL24_NOTOC
@@ -15663,7 +15705,8 @@ ppc64_elf_relocate_section (bfd *output_bfd,
&& rel[1].r_info == ELF64_R_INFO (r_symndx, R_PPC64_DTPREL64)
&& rel[1].r_offset == rel->r_offset + 8)
{
- if ((tls_mask & TLS_GD) == 0)
+ if ((tls_mask & TLS_GD) == 0
+ && offset_in_range (input_section, rel->r_offset, 8))
{
rel[1].r_info = ELF64_R_INFO (r_symndx, R_PPC64_NONE);
if ((tls_mask & TLS_GDIE) != 0)
@@ -15678,7 +15721,8 @@ ppc64_elf_relocate_section (bfd *output_bfd,
}
else
{
- if ((tls_mask & TLS_LD) == 0)
+ if ((tls_mask & TLS_LD) == 0
+ && offset_in_range (input_section, rel->r_offset, 8))
{
bfd_put_64 (output_bfd, 1, contents + rel->r_offset);
r_type = R_PPC64_NONE;
@@ -15699,7 +15743,8 @@ ppc64_elf_relocate_section (bfd *output_bfd,
relocation = TOCstart + htab->sec_info[input_section->id].toc_off;
if (!bfd_link_pic (info)
&& !info->traditional_format
- && relocation + 0x80008000 <= 0xffffffff)
+ && relocation + 0x80008000 <= 0xffffffff
+ && offset_in_range (input_section, rel->r_offset, 8))
{
unsigned int insn1, insn2;
@@ -15721,7 +15766,8 @@ ppc64_elf_relocate_section (bfd *output_bfd,
relocation -= (rel->r_offset
+ input_section->output_offset
+ input_section->output_section->vma);
- if (relocation + 0x80008000 <= 0xffffffff)
+ if (relocation + 0x80008000 <= 0xffffffff
+ && offset_in_range (input_section, rel->r_offset, 8))
{
unsigned int insn1, insn2;
@@ -15758,7 +15804,8 @@ ppc64_elf_relocate_section (bfd *output_bfd,
&& rel[1].r_info == ELF64_R_INFO (r_symndx, R_PPC64_REL16_LO)
&& rel[1].r_offset == rel->r_offset + 4
&& rel[1].r_addend == rel->r_addend + 4
- && relocation + 0x80008000 <= 0xffffffff)
+ && relocation + 0x80008000 <= 0xffffffff
+ && offset_in_range (input_section, rel->r_offset - d_offset, 8))
{
unsigned int insn1, insn2;
offset = rel->r_offset - d_offset;
@@ -15793,7 +15840,8 @@ ppc64_elf_relocate_section (bfd *output_bfd,
+ input_section->output_offset
+ input_section->output_section->vma)
&& tocsave_find (htab, NO_INSERT,
- &local_syms, rel, input_bfd))
+ &local_syms, rel, input_bfd)
+ && offset_in_range (input_section, rel->r_offset, 4))
{
insn = bfd_get_32 (input_bfd, contents + rel->r_offset);
if (insn == NOP
@@ -15813,6 +15861,8 @@ ppc64_elf_relocate_section (bfd *output_bfd,
/* Branch not taken prediction relocations. */
case R_PPC64_ADDR14_BRNTAKEN:
case R_PPC64_REL14_BRNTAKEN:
+ if (!offset_in_range (input_section, rel->r_offset, 4))
+ break;
insn |= bfd_get_32 (input_bfd,
contents + rel->r_offset) & ~(0x01 << 21);
/* Fall through. */
@@ -15873,7 +15923,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
/* All of these stubs may modify r2, so there must be a
branch and link followed by a nop. The nop is
replaced by an insn to restore r2. */
- else if (rel->r_offset + 8 <= input_section->size)
+ else if (offset_in_range (input_section, rel->r_offset, 8))
{
unsigned long br;
@@ -16111,7 +16161,8 @@ ppc64_elf_relocate_section (bfd *output_bfd,
&& (r_type == R_PPC64_REL24
|| r_type == R_PPC64_REL24_NOTOC)
&& relocation == 0
- && addend == 0)
+ && addend == 0
+ && offset_in_range (input_section, rel->r_offset, 4))
{
bfd_put_32 (input_bfd, NOP, contents + rel->r_offset);
goto copy_reloc;
@@ -16127,7 +16178,8 @@ ppc64_elf_relocate_section (bfd *output_bfd,
&& sec != NULL
&& sec->output_section != NULL
&& !discarded_section (sec)
- && (h == NULL || SYMBOL_REFERENCES_LOCAL (info, &h->elf)))
+ && (h == NULL || SYMBOL_REFERENCES_LOCAL (info, &h->elf))
+ && offset_in_range (input_section, rel->r_offset & ~3, 4))
{
insn = bfd_get_32 (input_bfd, contents + (rel->r_offset & ~3));
if ((insn & (0x3fu << 26 | 0x3)) == 58u << 26 /* ld */)
@@ -16150,7 +16202,8 @@ ppc64_elf_relocate_section (bfd *output_bfd,
&& sec != NULL
&& sec->output_section != NULL
&& !discarded_section (sec)
- && (h == NULL || SYMBOL_REFERENCES_LOCAL (info, &h->elf)))
+ && (h == NULL || SYMBOL_REFERENCES_LOCAL (info, &h->elf))
+ && offset_in_range (input_section, rel->r_offset & ~3, 4))
{
insn = bfd_get_32 (input_bfd, contents + (rel->r_offset & ~3));
if (r_type == R_PPC64_GOT16_LO_DS
@@ -16181,7 +16234,8 @@ ppc64_elf_relocate_section (bfd *output_bfd,
&& sec != NULL
&& sec->output_section != NULL
&& !discarded_section (sec)
- && (h == NULL || SYMBOL_REFERENCES_LOCAL (info, &h->elf))))
+ && (h == NULL || SYMBOL_REFERENCES_LOCAL (info, &h->elf))
+ && offset_in_range (input_section, rel->r_offset, 8)))
break;
offset = rel->r_offset;
@@ -16205,7 +16259,8 @@ ppc64_elf_relocate_section (bfd *output_bfd,
&& rel + 1 < relend
&& rel[1].r_offset == rel->r_offset
&& rel[1].r_info == ELF64_R_INFO (0, R_PPC64_PCREL_OPT)
- && (h == NULL || SYMBOL_REFERENCES_LOCAL (info, &h->elf)))
+ && (h == NULL || SYMBOL_REFERENCES_LOCAL (info, &h->elf))
+ && offset_in_range (input_section, rel->r_offset, 8))
{
offset = rel->r_offset;
pinsn = bfd_get_32 (input_bfd, contents + offset);
@@ -16220,7 +16275,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
/* zero means next insn. */
off2 = 8;
off2 += offset;
- if (off2 + 4 <= input_section->size)
+ if (offset_in_range (input_section, off2, 4))
{
uint64_t pinsn2;
bfd_signed_vma addend_off;
@@ -16228,7 +16283,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
pinsn2 <<= 32;
if ((pinsn2 & (63ULL << 58)) == 1ULL << 58)
{
- if (off2 + 8 > input_section->size)
+ if (!offset_in_range (input_section, off2, 8))
break;
pinsn2 |= bfd_get_32 (input_bfd,
contents + off2 + 4);
@@ -16659,7 +16714,8 @@ ppc64_elf_relocate_section (bfd *output_bfd,
case R_PPC64_TPREL16_HIGHESTA:
if (h != NULL
&& h->elf.root.type == bfd_link_hash_undefweak
- && h->elf.dynindx == -1)
+ && h->elf.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
@@ -17041,7 +17097,9 @@ ppc64_elf_relocate_section (bfd *output_bfd,
htab->notoc_plt = 1;
/* Fall through. */
case R_PPC64_PLTCALL:
- if (unresolved_reloc)
+ if (unresolved_reloc
+ && offset_in_range (input_section, rel->r_offset,
+ r_type == R_PPC64_PLTCALL ? 8 : 4))
{
/* No plt entry. Make this into a direct call. */
bfd_byte *p = contents + rel->r_offset;
@@ -17069,7 +17127,8 @@ ppc64_elf_relocate_section (bfd *output_bfd,
htab->notoc_plt = 1;
/* Fall through. */
case R_PPC64_PLT_PCREL34:
- if (unresolved_reloc)
+ if (unresolved_reloc
+ && offset_in_range (input_section, rel->r_offset, 8))
{
bfd_byte *p = contents + rel->r_offset;
bfd_put_32 (input_bfd, PNOP >> 32, p);
@@ -17097,9 +17156,12 @@ ppc64_elf_relocate_section (bfd *output_bfd,
{
bfd_byte *p;
nop_it:
- p = contents + (rel->r_offset & ~3);
- bfd_put_32 (input_bfd, NOP, p);
- goto copy_reloc;
+ if (offset_in_range (input_section, rel->r_offset & ~3, 4))
+ {
+ p = contents + (rel->r_offset & ~3);
+ bfd_put_32 (input_bfd, NOP, p);
+ goto copy_reloc;
+ }
}
break;
@@ -17120,7 +17182,8 @@ ppc64_elf_relocate_section (bfd *output_bfd,
case R_PPC64_TOC16_LO:
case R_PPC64_TOC16_LO_DS:
if (htab->do_toc_opt && relocation + addend + 0x8000 < 0x10000
- && !ppc64_elf_tdata (input_bfd)->unexpected_toc_insn)
+ && !ppc64_elf_tdata (input_bfd)->unexpected_toc_insn
+ && offset_in_range (input_section, rel->r_offset & ~3, 4))
{
bfd_byte *p = contents + (rel->r_offset & ~3);
insn = bfd_get_32 (input_bfd, p);
@@ -17140,7 +17203,9 @@ ppc64_elf_relocate_section (bfd *output_bfd,
break;
case R_PPC64_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);
@@ -17150,7 +17215,9 @@ ppc64_elf_relocate_section (bfd *output_bfd,
case R_PPC64_TPREL16_LO:
case R_PPC64_TPREL16_LO_DS:
- 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);
insn = bfd_get_32 (input_bfd, p);
@@ -17234,6 +17301,8 @@ ppc64_elf_relocate_section (bfd *output_bfd,
case R_PPC64_TPREL16_LO_DS:
case R_PPC64_DTPREL16_DS:
case R_PPC64_DTPREL16_LO_DS:
+ if (!offset_in_range (input_section, rel->r_offset & ~3, 4))
+ break;
insn = bfd_get_32 (input_bfd, contents + (rel->r_offset & ~3));
mask = 3;
/* If this reloc is against an lq, lxv, or stxv insn, then
@@ -17287,7 +17356,8 @@ ppc64_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;
@@ -17329,7 +17399,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
case R_PPC64_PLT_PCREL34_NOTOC:
case R_PPC64_D28:
case R_PPC64_PCREL28:
- if (rel->r_offset + 8 > input_section->size)
+ if (!offset_in_range (input_section, rel->r_offset, 8))
r = bfd_reloc_outofrange;
else
{
@@ -17358,7 +17428,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
break;
case R_PPC64_REL16DX_HA:
- if (rel->r_offset + 4 > input_section->size)
+ if (!offset_in_range (input_section, rel->r_offset, 4))
r = bfd_reloc_outofrange;
else
{