From 71cdd3663e969b8efc2193ad53a96438c5a0f498 Mon Sep 17 00:00:00 2001 From: Daniil Kovalev Date: Tue, 18 Jun 2024 15:44:27 +0300 Subject: [PATCH 1/4] [PAC][lld][AArch64][ELF] Support signed GOT Support `R_AARCH64_AUTH_ADR_GOT_PAGE`, `R_AARCH64_AUTH_GOT_LO12_NC` and `R_AARCH64_AUTH_GOT_ADD_LO12_NC` GOT-generating relocations. For preemptible symbols, dynamic relocation `R_AARCH64_AUTH_GLOB_DAT` is emitted. Otherwise, we unconditionally emit `R_AARCH64_AUTH_RELATIVE` dynamic relocation since pointers in signed GOT needs to be signed during dynamic link time. --- lld/ELF/Arch/AArch64.cpp | 9 ++ lld/ELF/InputSection.cpp | 2 + lld/ELF/Relocations.cpp | 42 +++++++-- lld/ELF/Relocations.h | 2 + lld/ELF/Symbols.h | 1 + lld/ELF/SyntheticSections.cpp | 19 +++++ lld/ELF/SyntheticSections.h | 5 ++ lld/test/ELF/aarch64-auth-got-relocations.s | 94 +++++++++++++++++++++ 8 files changed, 165 insertions(+), 9 deletions(-) create mode 100644 lld/test/ELF/aarch64-auth-got-relocations.s diff --git a/lld/ELF/Arch/AArch64.cpp b/lld/ELF/Arch/AArch64.cpp index 47e6ea1ff7756..45d21e777f2d8 100644 --- a/lld/ELF/Arch/AArch64.cpp +++ b/lld/ELF/Arch/AArch64.cpp @@ -169,11 +169,16 @@ RelExpr AArch64::getRelExpr(RelType type, const Symbol &s, case R_AARCH64_LD64_GOT_LO12_NC: case R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC: return R_GOT; + case R_AARCH64_AUTH_GOT_LO12_NC: + case R_AARCH64_AUTH_GOT_ADD_LO12_NC: + return R_AARCH64_AUTH_GOT; case R_AARCH64_LD64_GOTPAGE_LO15: return R_AARCH64_GOT_PAGE; case R_AARCH64_ADR_GOT_PAGE: case R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21: return R_AARCH64_GOT_PAGE_PC; + case R_AARCH64_AUTH_ADR_GOT_PAGE: + return R_AARCH64_AUTH_GOT_PAGE_PC; case R_AARCH64_GOTPCREL32: case R_AARCH64_GOT_LD_PREL19: return R_GOT_PC; @@ -225,6 +230,7 @@ int64_t AArch64::getImplicitAddend(const uint8_t *buf, RelType type) const { return read64(buf + 8); case R_AARCH64_NONE: case R_AARCH64_GLOB_DAT: + case R_AARCH64_AUTH_GLOB_DAT: case R_AARCH64_JUMP_SLOT: return 0; case R_AARCH64_ABS16: @@ -430,9 +436,11 @@ void AArch64::relocate(uint8_t *loc, const Relocation &rel, write64(loc, val); break; case R_AARCH64_ADD_ABS_LO12_NC: + case R_AARCH64_AUTH_GOT_ADD_LO12_NC: or32AArch64Imm(loc, val); break; case R_AARCH64_ADR_GOT_PAGE: + case R_AARCH64_AUTH_ADR_GOT_PAGE: case R_AARCH64_ADR_PREL_PG_HI21: case R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21: case R_AARCH64_TLSDESC_ADR_PAGE21: @@ -482,6 +490,7 @@ void AArch64::relocate(uint8_t *loc, const Relocation &rel, break; case R_AARCH64_LDST64_ABS_LO12_NC: case R_AARCH64_LD64_GOT_LO12_NC: + case R_AARCH64_AUTH_GOT_LO12_NC: case R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC: case R_AARCH64_TLSLE_LDST64_TPREL_LO12_NC: case R_AARCH64_TLSDESC_LD64_LO12: diff --git a/lld/ELF/InputSection.cpp b/lld/ELF/InputSection.cpp index be12218d9948c..f3b54e7f72855 100644 --- a/lld/ELF/InputSection.cpp +++ b/lld/ELF/InputSection.cpp @@ -694,6 +694,7 @@ uint64_t InputSectionBase::getRelocTargetVA(const InputFile *file, RelType type, case R_ARM_SBREL: return sym.getVA(a) - getARMStaticBase(sym); case R_GOT: + case R_AARCH64_AUTH_GOT: case R_RELAX_TLS_GD_TO_IE_ABS: return sym.getGotVA() + a; case R_LOONGARCH_GOT: @@ -721,6 +722,7 @@ uint64_t InputSectionBase::getRelocTargetVA(const InputFile *file, RelType type, case R_RELAX_TLS_GD_TO_IE_GOT_OFF: return sym.getGotOffset() + a; case R_AARCH64_GOT_PAGE_PC: + case R_AARCH64_AUTH_GOT_PAGE_PC: case R_AARCH64_RELAX_TLS_GD_TO_IE_PAGE_PC: return getAArch64Page(sym.getGotVA() + a) - getAArch64Page(p); case R_AARCH64_GOT_PAGE: diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp index 1b08339e3996a..5840b8ead5af0 100644 --- a/lld/ELF/Relocations.cpp +++ b/lld/ELF/Relocations.cpp @@ -209,8 +209,9 @@ static bool needsPlt(RelExpr expr) { } bool lld::elf::needsGot(RelExpr expr) { - return oneof( expr); } @@ -924,13 +925,25 @@ void elf::addGotEntry(Symbol &sym) { // If preemptible, emit a GLOB_DAT relocation. if (sym.isPreemptible) { - mainPart->relaDyn->addReloc({target->gotRel, in.got.get(), off, + RelType gotRel = target->gotRel; + if (sym.hasFlag(NEEDS_GOT_AUTH)) { + assert(config->emachine == EM_AARCH64); + gotRel = R_AARCH64_AUTH_GLOB_DAT; + } + mainPart->relaDyn->addReloc({gotRel, in.got.get(), off, DynamicReloc::AgainstSymbol, sym, 0, R_ABS}); return; } // Otherwise, the value is either a link-time constant or the load base - // plus a constant. + // plus a constant. Signed GOT requires dynamic relocation. + if (sym.hasFlag(NEEDS_GOT_AUTH)) { + in.got->getPartition().relaDyn->addReloc( + {R_AARCH64_AUTH_RELATIVE, in.got.get(), off, + DynamicReloc::AddendOnlyWithTargetVA, sym, 0, R_ABS}); + return; + } + if (!config->isPic || isAbsolute(sym)) in.got->addConstant({R_ABS, target->symbolicRel, off, 0, &sym}); else @@ -983,10 +996,11 @@ bool RelocationScanner::isStaticLinkTimeConstant(RelExpr e, RelType type, // These expressions always compute a constant if (oneof(e)) return true; @@ -1099,7 +1113,17 @@ void RelocationScanner::processAux(RelExpr expr, RelType type, uint64_t offset, } else if (!sym.isTls() || config->emachine != EM_LOONGARCH) { // Many LoongArch TLS relocs reuse the R_LOONGARCH_GOT type, in which // case the NEEDS_GOT flag shouldn't get set. - sym.setFlags(NEEDS_GOT); + bool needsGotAuth = + (expr == R_AARCH64_AUTH_GOT || expr == R_AARCH64_AUTH_GOT_PAGE_PC); + if (!sym.hasFlag(NEEDS_GOT)) { + sym.setFlags(NEEDS_GOT); + if (needsGotAuth) + sym.setFlags(NEEDS_GOT_AUTH); + } else if (needsGotAuth != sym.hasFlag(NEEDS_GOT_AUTH)) { + fatal("both AUTH and non-AUTH GOT entries for '" + sym.getName() + + "' requested, but only one type of GOT entry per symbol is " + "supported"); + } } } else if (needsPlt(expr)) { sym.setFlags(NEEDS_PLT); diff --git a/lld/ELF/Relocations.h b/lld/ELF/Relocations.h index e299d23dd7db3..fb1973b19f485 100644 --- a/lld/ELF/Relocations.h +++ b/lld/ELF/Relocations.h @@ -83,7 +83,9 @@ enum RelExpr { // of a relocation type, there are some relocations whose semantics are // unique to a target. Such relocation are marked with R_. R_AARCH64_GOT_PAGE_PC, + R_AARCH64_AUTH_GOT_PAGE_PC, R_AARCH64_GOT_PAGE, + R_AARCH64_AUTH_GOT, R_AARCH64_PAGE_PC, R_AARCH64_RELAX_TLS_GD_TO_IE_PAGE_PC, R_AARCH64_TLSDESC_PAGE, diff --git a/lld/ELF/Symbols.h b/lld/ELF/Symbols.h index c65c5d6cd0dca..c8aa3d937af0e 100644 --- a/lld/ELF/Symbols.h +++ b/lld/ELF/Symbols.h @@ -54,6 +54,7 @@ enum { NEEDS_TLSGD_TO_IE = 1 << 6, NEEDS_GOT_DTPREL = 1 << 7, NEEDS_TLSIE = 1 << 8, + NEEDS_GOT_AUTH = 1 << 9, }; // Some index properties of a symbol are stored separately in this auxiliary diff --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp index cc423d152e912..b4212b4962634 100644 --- a/lld/ELF/SyntheticSections.cpp +++ b/lld/ELF/SyntheticSections.cpp @@ -655,6 +655,10 @@ void GotSection::addConstant(const Relocation &r) { relocations.push_back(r); } void GotSection::addEntry(const Symbol &sym) { assert(sym.auxIdx == symAux.size() - 1); symAux.back().gotIdx = numEntries++; + if (sym.hasFlag(NEEDS_GOT_AUTH)) { + assert(config->emachine == EM_AARCH64); + authEntries.push_back({(numEntries - 1) * config->wordsize, sym.isFunc()}); + } } bool GotSection::addTlsDescEntry(const Symbol &sym) { @@ -718,6 +722,21 @@ void GotSection::writeTo(uint8_t *buf) { return; target->writeGotHeader(buf); target->relocateAlloc(*this, buf); + for (const AuthEntryInfo &authEntry : authEntries) { + // https://github.com/ARM-software/abi-aa/blob/main/pauthabielf64/pauthabielf64.rst#default-signing-schema + // Signed GOT entries use the IA key for symbols of type STT_FUNC and the + // DA key for all other symbol types, with the address of the GOT entry as + // the modifier. The static linker must encode the signing schema into the + // GOT slot. + // + // https://github.com/ARM-software/abi-aa/blob/main/pauthabielf64/pauthabielf64.rst#encoding-the-signing-schema + // If address diversity is set and the discriminator + // is 0 then modifier = Place + uint8_t *dest = buf + authEntry.offset; + uint64_t key = authEntry.isSymbolFunc ? /*IA*/ 0b00 : /*DA*/ 0b10; + uint64_t addrDiversity = 1; + write64(dest, (addrDiversity << 63) | (key << 60)); + } } static uint64_t getMipsPageAddr(uint64_t addr) { diff --git a/lld/ELF/SyntheticSections.h b/lld/ELF/SyntheticSections.h index 34949025a45f7..7cc6a34c5dc8a 100644 --- a/lld/ELF/SyntheticSections.h +++ b/lld/ELF/SyntheticSections.h @@ -131,6 +131,11 @@ class GotSection final : public SyntheticSection { size_t numEntries = 0; uint32_t tlsIndexOff = -1; uint64_t size = 0; + struct AuthEntryInfo { + size_t offset; + bool isSymbolFunc; + }; + SmallVector authEntries; }; // .note.GNU-stack section. diff --git a/lld/test/ELF/aarch64-auth-got-relocations.s b/lld/test/ELF/aarch64-auth-got-relocations.s new file mode 100644 index 0000000000000..f04e3d953388c --- /dev/null +++ b/lld/test/ELF/aarch64-auth-got-relocations.s @@ -0,0 +1,94 @@ +# REQUIRES: aarch64 + +# RUN: rm -rf %t && split-file %s %t && cd %t + +# RUN: llvm-mc -filetype=obj -triple=aarch64-none-linux %p/Inputs/shared.s -o a.o +# RUN: ld.lld -shared a.o -o a.so + +#--- ok.s + +# RUN: llvm-mc -filetype=obj -triple=aarch64-none-linux ok.s -o ok.o + +# RUN: ld.lld ok.o a.so -pie -o external +# RUN: llvm-readelf -r -S -x .got external | FileCheck %s --check-prefix=EXTERNAL + +# RUN: ld.lld ok.o a.o -pie -o local +# RUN: llvm-readelf -r -S -x .got -s local | FileCheck %s --check-prefix=LOCAL + +# EXTERNAL: Offset Info Type Symbol's Value Symbol's Name + Addend +# EXTERNAL-NEXT: 0000000000020380 000000010000e201 R_AARCH64_AUTH_GLOB_DAT 0000000000000000 bar + 0 +# EXTERNAL-NEXT: 0000000000020388 000000020000e201 R_AARCH64_AUTH_GLOB_DAT 0000000000000000 zed + 0 + +## Symbol's values for bar and zed are equal since they contain no content (see Inputs/shared.s) +# LOCAL: Offset Info Type Symbol's Value Symbol's Name + Addend +# LOCAL-NEXT: 0000000000020320 0000000000000411 R_AARCH64_AUTH_RELATIVE 10260 +# LOCAL-NEXT: 0000000000020328 0000000000000411 R_AARCH64_AUTH_RELATIVE 10260 + +# EXTERNAL: Hex dump of section '.got': +# EXTERNAL-NEXT: 0x00020380 00000000 00000080 00000000 000000a0 +# ^^ +# 0b10000000 bit 63 address diversity = true, bits 61..60 key = IA +# ^^ +# 0b10100000 bit 63 address diversity = true, bits 61..60 key = DA + +# LOCAL: Symbol table '.symtab' contains {{.*}} entries: +# LOCAL: Num: Value Size Type Bind Vis Ndx Name +# LOCAL: 0000000000010260 0 FUNC GLOBAL DEFAULT 6 bar +# LOCAL: 0000000000010260 0 NOTYPE GLOBAL DEFAULT 6 zed + +# LOCAL: Hex dump of section '.got': +# LOCAL-NEXT: 0x00020320 00000000 00000080 00000000 000000a0 +# ^^ +# 0b10000000 bit 63 address diversity = true, bits 61..60 key = IA +# ^^ +# 0b10100000 bit 63 address diversity = true, bits 61..60 key = DA + +# RUN: llvm-objdump -d external | FileCheck %s --check-prefix=EXTERNAL-ASM + +# EXTERNAL-ASM: <_start>: +# EXTERNAL-ASM-NEXT: adrp x0, 0x20000 +# EXTERNAL-ASM-NEXT: ldr x0, [x0, #0x380] +# EXTERNAL-ASM-NEXT: adrp x1, 0x20000 +# EXTERNAL-ASM-NEXT: add x1, x1, #0x380 +# EXTERNAL-ASM-NEXT: adrp x0, 0x20000 +# EXTERNAL-ASM-NEXT: ldr x0, [x0, #0x388] +# EXTERNAL-ASM-NEXT: adrp x1, 0x20000 +# EXTERNAL-ASM-NEXT: add x1, x1, #0x388 + +# RUN: llvm-objdump -d local | FileCheck %s --check-prefix=LOCAL-ASM + +# LOCAL-ASM: <_start>: +# LOCAL-ASM-NEXT: adrp x0, 0x20000 +# LOCAL-ASM-NEXT: ldr x0, [x0, #0x320] +# LOCAL-ASM-NEXT: adrp x1, 0x20000 +# LOCAL-ASM-NEXT: add x1, x1, #0x320 +# LOCAL-ASM-NEXT: adrp x0, 0x20000 +# LOCAL-ASM-NEXT: ldr x0, [x0, #0x328] +# LOCAL-ASM-NEXT: adrp x1, 0x20000 +# LOCAL-ASM-NEXT: add x1, x1, #0x328 + +.globl _start +_start: + adrp x0, :got_auth:bar + ldr x0, [x0, :got_auth_lo12:bar] + adrp x1, :got_auth:bar + add x1, x1, :got_auth_lo12:bar + adrp x0, :got_auth:zed + ldr x0, [x0, :got_auth_lo12:zed] + adrp x1, :got_auth:zed + add x1, x1, :got_auth_lo12:zed + +#--- err.s + +# RUN: llvm-mc -filetype=obj -triple=aarch64-none-linux err.s -o err.o + +# RUN: not ld.lld err.o a.so -pie -o /dev/null 2>&1 | FileCheck %s --check-prefix=ERR + +# ERR: error: both AUTH and non-AUTH GOT entries for 'bar' requested, but only one type of GOT entry per symbol is supported + +.globl _start +_start: + adrp x0, :got_auth:bar + ldr x0, [x0, :got_auth_lo12:bar] + adrp x0, :got:bar + ldr x0, [x0, :got_lo12:bar] From dca427bbf8ab6d53fd01b3d1799d4d723992edd2 Mon Sep 17 00:00:00 2001 From: Daniil Kovalev Date: Mon, 8 Jul 2024 10:00:18 +0300 Subject: [PATCH 2/4] Address review comments --- lld/ELF/SyntheticSections.cpp | 4 +--- lld/ELF/SyntheticSections.h | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp index da6a47d3f3a9e..aef1fc8f43aca 100644 --- a/lld/ELF/SyntheticSections.cpp +++ b/lld/ELF/SyntheticSections.cpp @@ -655,10 +655,8 @@ void GotSection::addConstant(const Relocation &r) { relocations.push_back(r); } void GotSection::addEntry(const Symbol &sym) { assert(sym.auxIdx == symAux.size() - 1); symAux.back().gotIdx = numEntries++; - if (sym.hasFlag(NEEDS_GOT_AUTH)) { - assert(config->emachine == EM_AARCH64); + if (sym.hasFlag(NEEDS_GOT_AUTH)) authEntries.push_back({(numEntries - 1) * config->wordsize, sym.isFunc()}); - } } bool GotSection::addTlsDescEntry(const Symbol &sym) { diff --git a/lld/ELF/SyntheticSections.h b/lld/ELF/SyntheticSections.h index 4a180198a6a84..85991c21fbce8 100644 --- a/lld/ELF/SyntheticSections.h +++ b/lld/ELF/SyntheticSections.h @@ -135,7 +135,7 @@ class GotSection final : public SyntheticSection { size_t offset; bool isSymbolFunc; }; - SmallVector authEntries; + SmallVector authEntries; }; // .note.GNU-stack section. From f7e184c1dfcef9b98a5a8be7f4cfbebbcd9748f3 Mon Sep 17 00:00:00 2001 From: Daniil Kovalev Date: Mon, 22 Jul 2024 11:41:28 +0300 Subject: [PATCH 3/4] Fix relocs names according to #96158 --- lld/ELF/Arch/AArch64.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lld/ELF/Arch/AArch64.cpp b/lld/ELF/Arch/AArch64.cpp index 75f794bc50960..8c32948303485 100644 --- a/lld/ELF/Arch/AArch64.cpp +++ b/lld/ELF/Arch/AArch64.cpp @@ -169,7 +169,7 @@ RelExpr AArch64::getRelExpr(RelType type, const Symbol &s, case R_AARCH64_LD64_GOT_LO12_NC: case R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC: return R_GOT; - case R_AARCH64_AUTH_GOT_LO12_NC: + case R_AARCH64_AUTH_LD64_GOT_LO12_NC: case R_AARCH64_AUTH_GOT_ADD_LO12_NC: return R_AARCH64_AUTH_GOT; case R_AARCH64_LD64_GOTPAGE_LO15: @@ -556,7 +556,7 @@ void AArch64::relocate(uint8_t *loc, const Relocation &rel, break; case R_AARCH64_LDST64_ABS_LO12_NC: case R_AARCH64_LD64_GOT_LO12_NC: - case R_AARCH64_AUTH_GOT_LO12_NC: + case R_AARCH64_AUTH_LD64_GOT_LO12_NC: case R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC: case R_AARCH64_TLSLE_LDST64_TPREL_LO12_NC: case R_AARCH64_TLSDESC_LD64_LO12: From 15225ee05843e704a8fc89eb95264b150d1f3e8a Mon Sep 17 00:00:00 2001 From: Daniil Kovalev Date: Fri, 25 Oct 2024 19:56:21 +0300 Subject: [PATCH 4/4] Use `ctx.arg` instead of `config` --- lld/ELF/Relocations.cpp | 7 ++++--- lld/ELF/SyntheticSections.cpp | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp index 7d24187c562a0..54f3e4f1e8c21 100644 --- a/lld/ELF/Relocations.cpp +++ b/lld/ELF/Relocations.cpp @@ -934,13 +934,14 @@ void elf::addGotEntry(Ctx &ctx, Symbol &sym) { // If preemptible, emit a GLOB_DAT relocation. if (sym.isPreemptible) { - RelType gotRel = target->gotRel; + RelType gotRel = ctx.target->gotRel; if (sym.hasFlag(NEEDS_GOT_AUTH)) { assert(ctx.arg.emachine == EM_AARCH64); gotRel = R_AARCH64_AUTH_GLOB_DAT; } - ctx.mainPart->relaDyn->addReloc({gotRel, in.got.get(), off, - DynamicReloc::AgainstSymbol, sym, 0, R_ABS}); + ctx.mainPart->relaDyn->addReloc({gotRel, ctx.in.got.get(), off, + DynamicReloc::AgainstSymbol, sym, 0, + R_ABS}); return; } diff --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp index ba1e924382973..b349cf82c78e4 100644 --- a/lld/ELF/SyntheticSections.cpp +++ b/lld/ELF/SyntheticSections.cpp @@ -665,7 +665,7 @@ void GotSection::addEntry(const Symbol &sym) { assert(sym.auxIdx == ctx.symAux.size() - 1); ctx.symAux.back().gotIdx = numEntries++; if (sym.hasFlag(NEEDS_GOT_AUTH)) - authEntries.push_back({(numEntries - 1) * config->wordsize, sym.isFunc()}); + authEntries.push_back({(numEntries - 1) * ctx.arg.wordsize, sym.isFunc()}); } bool GotSection::addTlsDescEntry(const Symbol &sym) {