[lld] lld/AArch64: handle more relocation addends (PR #87328)

Ramkumar Ramachandra via llvm-commits llvm-commits at lists.llvm.org
Fri Apr 5 13:06:54 PDT 2024


https://github.com/artagnon updated https://github.com/llvm/llvm-project/pull/87328

>From 9d2097f9d60d2a9fc501685c3a5c75214f586cd4 Mon Sep 17 00:00:00 2001
From: Ramkumar Ramachandra <ram.ramachandra at arm.com>
Date: Thu, 28 Mar 2024 18:02:29 +0000
Subject: [PATCH] lld/AArch64: handle more relocation addends

The function getImplicitAddend() is incomplete, as it is possible to
cook up object files with other relocation addends. Although using
llvm-mc or the clang integrated assembler does not produce such object
files, a proprietary assembler known as armasm can:

  https://developer.arm.com/documentation/101754/0622/armasm-Legacy-Assembler-Reference

armasm is in a frozen state, but it is still actively used in a lot of
legacy codebases as the directives, macros and operators are very
different from the clang integrated assembler. This makes porting a lot
of legacy code from armasm syntax impractical for a lot of projects.
Some internal testing of projects using open-source clang and lld fell
over at link time when legacy armasm objects were included in the link.

The goal of this patch is to enable people with legacy armasm objects to
be able to use lld as the linker. Sadly armasm uses SHT_REL format
relocations for AArch64 rather than SHT_RELA, which causes lld to reject
the objects. As a frozen project we know the small number of relocations
that the assembler officially supports and won't include (outside the
equivalent of the .reloc directive which I think we can rule out of
scope as that is not commonly used).

The benefit to lld is that it will ease migration from a proprietary to
an open-source toolchain. The drawback is the implementation of a small
number of SHT_REL relocations. Although this patch doesn't aim to
comprehensively cover all possible relocation addends, it does extend
lld to work with the relocation addends that armasm produces, using the
canonical aaelf64 document as a reference:

  https://github.com/ARM-software/abi-aa/blob/main/aaelf64/aaelf64.rst
---
 lld/ELF/Arch/AArch64.cpp                      | 42 +++++++++++++++----
 .../ELF/aarch64-reloc-implicit-addend.test    |  8 +++-
 2 files changed, 41 insertions(+), 9 deletions(-)

diff --git a/lld/ELF/Arch/AArch64.cpp b/lld/ELF/Arch/AArch64.cpp
index 017c17c2b03d83..2bf6e2c6c85195 100644
--- a/lld/ELF/Arch/AArch64.cpp
+++ b/lld/ELF/Arch/AArch64.cpp
@@ -69,6 +69,13 @@ struct AArch64Relaxer {
 };
 } // namespace
 
+// Return the bits [Start, End] from Val shifted Start bits.
+// For instance, getBits(0xF0, 4, 8) returns 0xF.
+static uint64_t getBits(uint64_t val, int start, int end) {
+  uint64_t mask = ((uint64_t)1 << (end + 1 - start)) - 1;
+  return (val >> start) & mask;
+}
+
 AArch64::AArch64() {
   copyRel = R_AARCH64_COPY;
   relativeRel = R_AARCH64_RELATIVE;
@@ -219,6 +226,10 @@ int64_t AArch64::getImplicitAddend(const uint8_t *buf, RelType type) const {
   case R_AARCH64_GLOB_DAT:
   case R_AARCH64_JUMP_SLOT:
     return 0;
+  case R_AARCH64_ABS16:
+  case R_AARCH64_PREL16:
+    return SignExtend64<16>(read16(buf));
+  case R_AARCH64_ABS32:
   case R_AARCH64_PREL32:
     return SignExtend64<32>(read32(buf));
   case R_AARCH64_ABS64:
@@ -227,6 +238,30 @@ int64_t AArch64::getImplicitAddend(const uint8_t *buf, RelType type) const {
   case R_AARCH64_IRELATIVE:
   case R_AARCH64_TLS_TPREL64:
     return read64(buf);
+  case R_AARCH64_MOVW_UABS_G0:
+  case R_AARCH64_MOVW_UABS_G0_NC:
+    return getBits(SignExtend64<16>(read16(buf)), 0, 15);
+  case R_AARCH64_MOVW_UABS_G1:
+  case R_AARCH64_MOVW_UABS_G1_NC:
+    return getBits(SignExtend64<32>(read32(buf)), 16, 31);
+  case R_AARCH64_MOVW_UABS_G2:
+  case R_AARCH64_MOVW_UABS_G2_NC:
+    return getBits(read64(buf), 32, 47);
+  case R_AARCH64_MOVW_UABS_G3:
+    return getBits(read64(buf), 48, 63);
+  case R_AARCH64_TSTBR14:
+    return getBits(SignExtend64<32>(read32(buf)), 2, 15);
+  case R_AARCH64_CONDBR19:
+  case R_AARCH64_LD_PREL_LO19:
+    return getBits(SignExtend64<32>(read32(buf)), 2, 20);
+  case R_AARCH64_ADD_ABS_LO12_NC:
+    return getBits(SignExtend64<16>(read16(buf)), 0, 11);
+  case R_AARCH64_ADR_PREL_PG_HI21:
+  case R_AARCH64_ADR_PREL_PG_HI21_NC:
+    return getBits(SignExtend64<32>(read32(buf)), 12, 32);
+  case R_AARCH64_JUMP26:
+  case R_AARCH64_CALL26:
+    return getBits(SignExtend64<32>(read32(buf)), 2, 27);
   default:
     internalLinkerError(getErrorLocation(buf),
                         "cannot read addend for relocation " + toString(type));
@@ -330,13 +365,6 @@ static void write32AArch64Addr(uint8_t *l, uint64_t imm) {
   write32le(l, (read32le(l) & ~mask) | immLo | immHi);
 }
 
-// Return the bits [Start, End] from Val shifted Start bits.
-// For instance, getBits(0xF0, 4, 8) returns 0xF.
-static uint64_t getBits(uint64_t val, int start, int end) {
-  uint64_t mask = ((uint64_t)1 << (end + 1 - start)) - 1;
-  return (val >> start) & mask;
-}
-
 static void or32le(uint8_t *p, int32_t v) { write32le(p, read32le(p) | v); }
 
 // Update the immediate field in a AARCH64 ldr, str, and add instruction.
diff --git a/lld/test/ELF/aarch64-reloc-implicit-addend.test b/lld/test/ELF/aarch64-reloc-implicit-addend.test
index 15f42c4d87b577..c71ca1e3be56fc 100644
--- a/lld/test/ELF/aarch64-reloc-implicit-addend.test
+++ b/lld/test/ELF/aarch64-reloc-implicit-addend.test
@@ -1,8 +1,12 @@
 ## Test certain REL relocation types generated by legacy armasm.
 # RUN: yaml2obj %s -o %t.o
-# RUN: not ld.lld %t.o -o /dev/null 2>&1 | FileCheck %s
+# RUN: ld.lld %t.o -o %t.out
+# RUN: llvm-objdump -t %t.out | FileCheck %s
 
-# CHECK-COUNT-17: internal linker error: cannot read addend
+# CHECK:      SYMBOL TABLE:
+# CHECK-NEXT: 0000000000200130 l       .branch	0000000000000000 .branch
+# CHECK-NEXT: 0000000000200118 l       .prel	0000000000000000 .prel
+# CHECK-NEXT: 000000000000002a g       *ABS*	0000000000000000 abs
 
 ---
 !ELF



More information about the llvm-commits mailing list