aarch64: Add new relocations and limit COFF AArch64 relocation offsets

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.
This commit is contained in:
Zac Walker
2024-01-31 20:15:48 +01:00
committed by Christophe Lyon
parent 046a94c18c
commit f87eaf8ff3
2 changed files with 43 additions and 1 deletions

View 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;

View 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");