aboutsummaryrefslogtreecommitdiff
path: root/bfd/coff-aarch64.c
diff options
context:
space:
mode:
authorAlan Modra <amodra@gmail.com>2023-01-17 21:53:00 +1030
committerAlan Modra <amodra@gmail.com>2023-01-19 17:43:53 +1030
commit066bd434118044487e69a9fbc5cacdee60326595 (patch)
treecd718591b3b9a51ab1a4b62a0217975f25f1b7d6 /bfd/coff-aarch64.c
parentf07170eb86314cbb9ef3e10d19381779a4656d19 (diff)
downloadgdb-066bd434118044487e69a9fbc5cacdee60326595.zip
gdb-066bd434118044487e69a9fbc5cacdee60326595.tar.gz
gdb-066bd434118044487e69a9fbc5cacdee60326595.tar.bz2
The fuzzers have found the reloc special functions in coff-aarch64.c
All of them need a bfd_reloc_offset_in_range check before accessing data + reloc_entry->address. This patch adds the missing checks and sanity checks reloc offsets in coff_pe_aarch64_relocate_section too. All of them also need changing to support objdump -W calls to bfd_simple_get_relocated_section_contents. At least, secrel_reloc needs the support, the others might not be present in dwarf debug sections. * coff-aarch64.c (coff_aarch64_rel21_reloc): Range check reloc offset. Support final-linking. (coff_aarch64_po12l_reloc): Likewise. (coff_aarch64_addr32nb_reloc): Likewise. (coff_aarch64_secrel_reloc): Likewise. (coff_pe_aarch64_relocate_section): Range check reloc offset.
Diffstat (limited to 'bfd/coff-aarch64.c')
-rw-r--r--bfd/coff-aarch64.c199
1 files changed, 157 insertions, 42 deletions
diff --git a/bfd/coff-aarch64.c b/bfd/coff-aarch64.c
index 7057396..73fa244 100644
--- a/bfd/coff-aarch64.c
+++ b/bfd/coff-aarch64.c
@@ -39,48 +39,85 @@
#include "libcoff.h"
+/* For these howto special functions,
+ output_bfd == NULL => final link, or objdump -W and other calls to
+ bfd_simple_get_relocated_section_contents
+ output_bfd != NULL && output_bfd != abfd => ld -r
+ output_bfd != NULL && output_bfd == abfd => gas.
+ FIXME: ld -r is punted to bfd_perform_relocation. This won't be
+ correct for cases where the addend needs to be adjusted, eg. for
+ relocations against section symbols, and the field is split because
+ bfd_perform_relocation can't write addends to split relocation fields. */
+
static bfd_reloc_status_type
-coff_aarch64_rel21_reloc (bfd *abfd ATTRIBUTE_UNUSED,
+coff_aarch64_rel21_reloc (bfd *abfd,
arelent *reloc_entry,
- asymbol *symbol ATTRIBUTE_UNUSED,
+ asymbol *symbol,
void *data,
- asection *input_section ATTRIBUTE_UNUSED,
- bfd *output_bfd ATTRIBUTE_UNUSED,
+ asection *input_section,
+ bfd *output_bfd,
char **error_message ATTRIBUTE_UNUSED)
{
- uint32_t op;
- int32_t param;
+ if (output_bfd != NULL && output_bfd != abfd)
+ return bfd_reloc_continue;
- op = bfd_getl32 (data + reloc_entry->address);
- param = reloc_entry->addend;
+ if (!bfd_reloc_offset_in_range (reloc_entry->howto, abfd,
+ input_section, reloc_entry->address))
+ return bfd_reloc_outofrange;
- if (param > 0xfffff || param < -0x100000)
- return bfd_reloc_overflow;
+ uint32_t op = bfd_getl32 (data + reloc_entry->address);
+ bfd_vma relocation = reloc_entry->addend;
+ bfd_reloc_status_type ret = bfd_reloc_ok;
+ if (output_bfd == NULL)
+ {
+ if (bfd_is_und_section (symbol->section))
+ {
+ if ((symbol->flags & BSF_WEAK) == 0)
+ ret = bfd_reloc_undefined;
+ }
+ else if (!bfd_is_com_section (symbol->section))
+ relocation += (symbol->value
+ + symbol->section->output_offset
+ + symbol->section->output_section->vma);
+ bfd_vma addend = ((op >> 3) & 0x1ffffc) | ((op >> 29) & 0x3);
+ addend = (addend ^ 0x100000) - 0x100000;
+ relocation += addend;
+ relocation -= (reloc_entry->address
+ + input_section->output_offset
+ + input_section->output_section->vma);
+ relocation = (bfd_signed_vma) relocation >> reloc_entry->howto->rightshift;
+ }
+ if (relocation + 0x100000 > 0x1fffff)
+ ret = bfd_reloc_overflow;
op &= 0x9f00001f;
- op |= (param & 0x1ffffc) << 3;
- op |= (param & 0x3) << 29;
+ op |= (relocation & 0x1ffffc) << 3;
+ op |= (relocation & 0x3) << 29;
bfd_putl32 (op, data + reloc_entry->address);
- return bfd_reloc_ok;
+ return ret;
}
static bfd_reloc_status_type
-coff_aarch64_po12l_reloc (bfd *abfd ATTRIBUTE_UNUSED,
+coff_aarch64_po12l_reloc (bfd *abfd,
arelent *reloc_entry,
asymbol *symbol ATTRIBUTE_UNUSED,
void *data,
- asection *input_section ATTRIBUTE_UNUSED,
- bfd *output_bfd ATTRIBUTE_UNUSED,
+ asection *input_section,
+ bfd *output_bfd,
char **error_message ATTRIBUTE_UNUSED)
{
- uint32_t op;
- int32_t param;
- uint8_t shift;
+ if (output_bfd != NULL && output_bfd != abfd)
+ return bfd_reloc_continue;
- op = bfd_getl32 (data + reloc_entry->address);
- param = reloc_entry->addend & 0xfff;
+ if (!bfd_reloc_offset_in_range (reloc_entry->howto, abfd,
+ input_section, reloc_entry->address))
+ return bfd_reloc_outofrange;
+
+ uint32_t op = bfd_getl32 (data + reloc_entry->address);
+ bfd_vma relocation = reloc_entry->addend & 0xfff;
+ int shift;
if ((op & 0xff800000) == 0x3d800000)
{
@@ -93,53 +130,120 @@ coff_aarch64_po12l_reloc (bfd *abfd ATTRIBUTE_UNUSED,
shift = op >> 30;
}
- if (param & ((1 << shift) - 1))
- return bfd_reloc_overflow;
+ bfd_reloc_status_type ret = bfd_reloc_ok;
+ if (output_bfd == NULL)
+ {
+ if (bfd_is_und_section (symbol->section))
+ {
+ if ((symbol->flags & BSF_WEAK) == 0)
+ ret = bfd_reloc_undefined;
+ }
+ else if (!bfd_is_com_section (symbol->section))
+ relocation += (symbol->value
+ + symbol->section->output_offset
+ + symbol->section->output_section->vma);
+ bfd_vma addend = (op >> 10) & 0xfff;
+ addend <<= shift;
+ relocation += addend;
+ }
- param >>= shift;
+ if (relocation & ((1 << shift) - 1))
+ ret = bfd_reloc_overflow;
op &= 0xffc003ff;
- op |= param << 10;
+ op |= (relocation >> shift << 10) & 0x3ffc00;
bfd_putl32 (op, data + reloc_entry->address);
- return bfd_reloc_ok;
+ return ret;
}
static bfd_reloc_status_type
-coff_aarch64_addr32nb_reloc (bfd *abfd ATTRIBUTE_UNUSED,
+coff_aarch64_addr32nb_reloc (bfd *abfd,
arelent *reloc_entry,
asymbol *symbol ATTRIBUTE_UNUSED,
void *data,
- asection *input_section ATTRIBUTE_UNUSED,
- bfd *output_bfd ATTRIBUTE_UNUSED,
- char **error_message ATTRIBUTE_UNUSED)
+ asection *input_section,
+ bfd *output_bfd,
+ char **error_message)
{
- uint64_t val;
+ if (output_bfd != NULL && output_bfd != abfd)
+ return bfd_reloc_continue;
- if ((int64_t) reloc_entry->addend > 0x7fffffff
- || (int64_t) reloc_entry->addend < -0x7fffffff)
- return bfd_reloc_overflow;
+ if (!bfd_reloc_offset_in_range (reloc_entry->howto, abfd,
+ input_section, reloc_entry->address))
+ return bfd_reloc_outofrange;
- val = reloc_entry->addend;
+ bfd_vma relocation = reloc_entry->addend;
+ bfd_reloc_status_type ret = bfd_reloc_ok;
+ if (output_bfd == NULL)
+ {
+ if (bfd_is_und_section (symbol->section))
+ {
+ if ((symbol->flags & BSF_WEAK) == 0)
+ ret = bfd_reloc_undefined;
+ }
+ else if (!bfd_is_com_section (symbol->section))
+ relocation += (symbol->value
+ + symbol->section->output_offset
+ + symbol->section->output_section->vma);
+ bfd_vma addend = bfd_getl_signed_32 (data + reloc_entry->address);
+ relocation += addend;
+ if (bfd_get_flavour (output_bfd) == bfd_target_coff_flavour
+ && obj_pe (output_bfd))
+ relocation -= pe_data (output_bfd)->pe_opthdr.ImageBase;
+ else
+ {
+ *error_message = "unsupported";
+ return bfd_reloc_dangerous;
+ }
+ }
+
+ if (relocation + 0x80000000 > 0xffffffff)
+ ret = bfd_reloc_overflow;
- bfd_putl32 ((uint32_t) val, data + reloc_entry->address);
+ bfd_putl32 (relocation, data + reloc_entry->address);
- return bfd_reloc_ok;
+ return ret;
}
static bfd_reloc_status_type
-coff_aarch64_secrel_reloc (bfd *abfd ATTRIBUTE_UNUSED,
+coff_aarch64_secrel_reloc (bfd *abfd,
arelent *reloc_entry,
asymbol *symbol ATTRIBUTE_UNUSED,
void *data,
- asection *input_section ATTRIBUTE_UNUSED,
- bfd *output_bfd ATTRIBUTE_UNUSED,
+ asection *input_section,
+ bfd *output_bfd,
char **error_message ATTRIBUTE_UNUSED)
{
- bfd_putl32 (reloc_entry->addend, data + reloc_entry->address);
+ if (output_bfd != NULL && output_bfd != abfd)
+ return bfd_reloc_continue;
+
+ if (!bfd_reloc_offset_in_range (reloc_entry->howto, abfd,
+ input_section, reloc_entry->address))
+ return bfd_reloc_outofrange;
+
+ bfd_vma relocation = reloc_entry->addend;
+ bfd_reloc_status_type ret = bfd_reloc_ok;
+ if (output_bfd == NULL)
+ {
+ if (bfd_is_und_section (symbol->section))
+ {
+ if ((symbol->flags & BSF_WEAK) == 0)
+ ret = bfd_reloc_undefined;
+ }
+ else if (!bfd_is_com_section (symbol->section))
+ relocation += (symbol->value
+ + symbol->section->output_offset);
+ bfd_vma addend = bfd_getl_signed_32 (data + reloc_entry->address);
+ relocation += addend;
+ }
+ if (relocation > 0xffffffff)
+ ret = bfd_reloc_overflow;
+
+ bfd_putl32 (relocation, data + reloc_entry->address);
- return bfd_reloc_ok;
+ return ret;
}
#define coff_aarch64_NULL NULL
@@ -438,6 +542,17 @@ coff_pe_aarch64_relocate_section (bfd *output_bfd,
|| (unsigned long) symndx >= obj_raw_syment_count (input_bfd))
continue;
+ /* All the relocs handled below operate on 4 bytes. */
+ if (input_section->size < rel->r_vaddr
+ || input_section->size - rel->r_vaddr < 4)
+ {
+ _bfd_error_handler
+ /* xgettext: c-format */
+ (_("%pB: bad reloc address %#" PRIx64 " in section `%pA'"),
+ input_bfd, (uint64_t) rel->r_vaddr, input_section);
+ continue;
+ }
+
switch (rel->r_type)
{
case IMAGE_REL_ARM64_ADDR32NB: