]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
aarch64: Add new relocations and limit COFF AArch64 relocation offsets
authorZac Walker <zacwalker@microsoft.com>
Wed, 31 Jan 2024 19:15:48 +0000 (20:15 +0100)
committerChristophe Lyon <christophe.lyon@linaro.org>
Mon, 19 Feb 2024 13:02:00 +0000 (13:02 +0000)
The patch adds support for the IMAGE_REL_ARM64_REL32 coff relocation
type. This is needed for 32-bit relative address.

It also adds a check for relocation offsets over 21 bits. Offsets
inside coff files are stored in instruction code. In the case of ADRP
the actual value is stored, not a downshifted page offset. This means
values over 21 bits would otherwise be truncated.

Finally it adds a mapping for BFD_RELOC_AARCH64_ADR_GOT_PAGE and
BFD_RELOC_AARCH64_LD64_GOT_LO12_NC that were previously skipped.

ChangeLog:

* bfd/coff-aarch64.c (coff_aarch64_reloc_type_lookup): Add
BFD_RELOC_AARCH64_ADR_GOT_PAGE,
BFD_RELOC_AARCH64_LD64_GOT_LO12_NC and IMAGE_REL_ARM64_REL32
relocations.
(coff_pe_aarch64_relocate_section): Likewise.
* gas/write.c (adjust_reloc_syms): COFF AArch64 relocation
offsets need to be limited to 21bits
(defined): Likewise.

bfd/coff-aarch64.c
gas/write.c

index 825963c0378eac3364cef1e4e4a27e268e9fc150..06c22fc58fa26d5c2b4fe2cc5adc75a34d46c9d3 100644 (file)
@@ -352,6 +352,7 @@ coff_aarch64_reloc_type_lookup (bfd * abfd ATTRIBUTE_UNUSED, bfd_reloc_code_real
     return &arm64_reloc_howto_branch26;
   case BFD_RELOC_AARCH64_ADR_HI21_PCREL:
   case BFD_RELOC_AARCH64_ADR_HI21_NC_PCREL:
+  case BFD_RELOC_AARCH64_ADR_GOT_PAGE:
     return &arm64_reloc_howto_page21;
   case BFD_RELOC_AARCH64_TSTBR14:
     return &arm64_reloc_howto_branch14;
@@ -364,6 +365,7 @@ coff_aarch64_reloc_type_lookup (bfd * abfd ATTRIBUTE_UNUSED, bfd_reloc_code_real
   case BFD_RELOC_AARCH64_LDST32_LO12:
   case BFD_RELOC_AARCH64_LDST64_LO12:
   case BFD_RELOC_AARCH64_LDST128_LO12:
+  case BFD_RELOC_AARCH64_LD64_GOT_LO12_NC:
     return &arm64_reloc_howto_pgoff12l;
   case BFD_RELOC_AARCH64_BRANCH19:
     return &arm64_reloc_howto_branch19;
@@ -761,6 +763,35 @@ coff_pe_aarch64_relocate_section (bfd *output_bfd,
            break;
          }
 
+       case IMAGE_REL_ARM64_REL32:
+         {
+           uint64_t cur_vma;
+           int64_t addend, val;
+
+           addend = bfd_getl32 (contents + rel->r_vaddr);
+
+           if (addend & 0x80000000)
+             addend |= 0xffffffff00000000;
+
+           dest_vma += addend;
+           cur_vma = input_section->output_section->vma
+                     + input_section->output_offset
+                     + rel->r_vaddr;
+
+           val = dest_vma - cur_vma;
+
+           if (val > 0xffffffff || val < -0x100000000)
+             (*info->callbacks->reloc_overflow)
+               (info, h ? &h->root : NULL, syms[symndx]._n._n_name,
+               "IMAGE_REL_ARM64_REL32", addend, input_bfd,
+               input_section, rel->r_vaddr - input_section->vma);
+
+           bfd_putl32 (val, contents + rel->r_vaddr);
+           rel->r_type = IMAGE_REL_ARM64_ABSOLUTE;
+
+           break;
+         }
+
        case IMAGE_REL_ARM64_PAGEOFFSET_12L:
          {
            uint32_t opcode, val;
index 98ead0b991c99875e266c2aef149ba466697335c..18cf18fc8301968794bf1a12ec14d16e22270896 100644 (file)
@@ -779,6 +779,7 @@ adjust_reloc_syms (bfd *abfd ATTRIBUTE_UNUSED,
 {
   segment_info_type *seginfo = seg_info (sec);
   fixS *fixp;
+  valueT val;
 
   if (seginfo == NULL)
     return;
@@ -890,10 +891,20 @@ adjust_reloc_syms (bfd *abfd ATTRIBUTE_UNUSED,
        if ((symsec->flags & SEC_THREAD_LOCAL) != 0)
          continue;
 
+       val = S_GET_VALUE (sym);
+
+#if defined(TC_AARCH64) && defined(OBJ_COFF)
+       /* coff aarch64 relocation offsets need to be limited to 21bits.
+          This is because addend may need to be stored in an ADRP instruction.
+          In this case the addend cannot be stored down shifted otherwise rounding errors occur. */
+       if ((val + 0x100000) > 0x1fffff)
+         continue;
+#endif
+
        /* We refetch the segment when calling section_symbol, rather
           than using symsec, because S_GET_VALUE may wind up changing
           the section when it calls resolve_symbol_value.  */
-       fixp->fx_offset += S_GET_VALUE (sym);
+       fixp->fx_offset += val;
        fixp->fx_addsy = section_symbol (S_GET_SEGMENT (sym));
 #ifdef DEBUG5
        fprintf (stderr, "\nadjusted fixup:\n");