diff --git a/lld/ELF/Arch/AArch64.cpp b/lld/ELF/Arch/AArch64.cpp index 99fc750486e4b..9571e0e9566fc 100644 --- a/lld/ELF/Arch/AArch64.cpp +++ b/lld/ELF/Arch/AArch64.cpp @@ -202,11 +202,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_LD64_GOT_LO12_NC: + case R_AARCH64_AUTH_GOT_ADD_LO12_NC: + return RE_AARCH64_AUTH_GOT; case R_AARCH64_LD64_GOTPAGE_LO15: return RE_AARCH64_GOT_PAGE; case R_AARCH64_ADR_GOT_PAGE: case R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21: return RE_AARCH64_GOT_PAGE_PC; + case R_AARCH64_AUTH_ADR_GOT_PAGE: + return RE_AARCH64_AUTH_GOT_PAGE_PC; case R_AARCH64_GOTPCREL32: case R_AARCH64_GOT_LD_PREL19: return R_GOT_PC; @@ -258,6 +263,7 @@ int64_t AArch64::getImplicitAddend(const uint8_t *buf, RelType type) const { return read64(ctx, 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: @@ -528,9 +534,11 @@ void AArch64::relocate(uint8_t *loc, const Relocation &rel, write32(ctx, loc, val); break; case R_AARCH64_ADD_ABS_LO12_NC: + case R_AARCH64_AUTH_GOT_ADD_LO12_NC: write32Imm12(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: @@ -580,6 +588,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_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: diff --git a/lld/ELF/InputSection.cpp b/lld/ELF/InputSection.cpp index 7e76bae19fc6a..26dc5c606f57f 100644 --- a/lld/ELF/InputSection.cpp +++ b/lld/ELF/InputSection.cpp @@ -783,6 +783,7 @@ uint64_t InputSectionBase::getRelocTargetVA(Ctx &ctx, const Relocation &r, case RE_ARM_SBREL: return r.sym->getVA(ctx, a) - getARMStaticBase(*r.sym); case R_GOT: + case RE_AARCH64_AUTH_GOT: case R_RELAX_TLS_GD_TO_IE_ABS: return r.sym->getGotVA(ctx) + a; case RE_LOONGARCH_GOT: @@ -810,6 +811,7 @@ uint64_t InputSectionBase::getRelocTargetVA(Ctx &ctx, const Relocation &r, case R_RELAX_TLS_GD_TO_IE_GOT_OFF: return r.sym->getGotOffset(ctx) + a; case RE_AARCH64_GOT_PAGE_PC: + case RE_AARCH64_AUTH_GOT_PAGE_PC: case RE_AARCH64_RELAX_TLS_GD_TO_IE_PAGE_PC: return getAArch64Page(r.sym->getGotVA(ctx) + a) - getAArch64Page(p); case RE_AARCH64_GOT_PAGE: diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp index 41c04b2903456..07cbdb7806fde 100644 --- a/lld/ELF/Relocations.cpp +++ b/lld/ELF/Relocations.cpp @@ -197,8 +197,9 @@ static bool needsPlt(RelExpr expr) { } bool lld::elf::needsGot(RelExpr expr) { - return oneof( expr); } @@ -910,6 +911,25 @@ void elf::addGotEntry(Ctx &ctx, Symbol &sym) { ctx.target->symbolicRel); } +static void addGotAuthEntry(Ctx &ctx, Symbol &sym) { + ctx.in.got->addEntry(sym); + ctx.in.got->addAuthEntry(sym); + uint64_t off = sym.getGotOffset(ctx); + + // If preemptible, emit a GLOB_DAT relocation. + if (sym.isPreemptible) { + ctx.mainPart->relaDyn->addReloc({R_AARCH64_AUTH_GLOB_DAT, ctx.in.got.get(), + off, DynamicReloc::AgainstSymbol, sym, 0, + R_ABS}); + return; + } + + // Signed GOT requires dynamic relocation. + ctx.in.got->getPartition(ctx).relaDyn->addReloc( + {R_AARCH64_AUTH_RELATIVE, ctx.in.got.get(), off, + DynamicReloc::AddendOnlyWithTargetVA, sym, 0, R_ABS}); +} + static void addTpOffsetGotEntry(Ctx &ctx, Symbol &sym) { ctx.in.got->addEntry(sym); uint64_t off = sym.getGotOffset(ctx); @@ -956,11 +976,12 @@ bool RelocationScanner::isStaticLinkTimeConstant(RelExpr e, RelType type, // These expressions always compute a constant if (oneof(e)) return true; @@ -1075,7 +1096,10 @@ void RelocationScanner::processAux(RelExpr expr, RelType type, uint64_t offset, } else if (!sym.isTls() || ctx.arg.emachine != EM_LOONGARCH) { // Many LoongArch TLS relocs reuse the RE_LOONGARCH_GOT type, in which // case the NEEDS_GOT flag shouldn't get set. - sym.setFlags(NEEDS_GOT); + if (expr == RE_AARCH64_AUTH_GOT || expr == RE_AARCH64_AUTH_GOT_PAGE_PC) + sym.setFlags(NEEDS_GOT | NEEDS_GOT_AUTH); + else + sym.setFlags(NEEDS_GOT | NEEDS_GOT_NONAUTH); } } else if (needsPlt(expr)) { sym.setFlags(NEEDS_PLT); @@ -1750,8 +1774,11 @@ static bool handleNonPreemptibleIfunc(Ctx &ctx, Symbol &sym, uint16_t flags) { // don't try to call the PLT as if it were an ifunc resolver. d.type = STT_FUNC; - if (flags & NEEDS_GOT) + if (flags & NEEDS_GOT) { + assert(!(flags & NEEDS_GOT_AUTH) && + "R_AARCH64_AUTH_IRELATIVE is not supported yet"); addGotEntry(ctx, sym); + } } else if (flags & NEEDS_GOT) { // Redirect GOT accesses to point to the Igot. sym.gotInIgot = true; @@ -1772,8 +1799,19 @@ void elf::postScanRelocations(Ctx &ctx) { return; sym.allocateAux(ctx); - if (flags & NEEDS_GOT) - addGotEntry(ctx, sym); + if (flags & NEEDS_GOT) { + if ((flags & NEEDS_GOT_AUTH) && (flags & NEEDS_GOT_NONAUTH)) { + auto diag = Err(ctx); + diag << "both AUTH and non-AUTH GOT entries for '" << sym.getName() + << "' requested, but only one type of GOT entry per symbol is " + "supported"; + return; + } + if (flags & NEEDS_GOT_AUTH) + addGotAuthEntry(ctx, sym); + else + addGotEntry(ctx, sym); + } if (flags & NEEDS_PLT) addPltEntry(ctx, *ctx.in.plt, *ctx.in.gotPlt, *ctx.in.relaPlt, ctx.target->pltRel, sym); diff --git a/lld/ELF/Relocations.h b/lld/ELF/Relocations.h index 7ca203257ea87..d993ab77adc3c 100644 --- a/lld/ELF/Relocations.h +++ b/lld/ELF/Relocations.h @@ -92,7 +92,9 @@ enum RelExpr { // of a relocation type, there are some relocations whose semantics are // unique to a target. Such relocation are marked with RE_. RE_AARCH64_GOT_PAGE_PC, + RE_AARCH64_AUTH_GOT_PAGE_PC, RE_AARCH64_GOT_PAGE, + RE_AARCH64_AUTH_GOT, RE_AARCH64_PAGE_PC, RE_AARCH64_RELAX_TLS_GD_TO_IE_PAGE_PC, RE_AARCH64_TLSDESC_PAGE, diff --git a/lld/ELF/Symbols.h b/lld/ELF/Symbols.h index fac957a724885..a59faf1037cb2 100644 --- a/lld/ELF/Symbols.h +++ b/lld/ELF/Symbols.h @@ -51,6 +51,8 @@ enum { NEEDS_TLSGD_TO_IE = 1 << 6, NEEDS_GOT_DTPREL = 1 << 7, NEEDS_TLSIE = 1 << 8, + NEEDS_GOT_AUTH = 1 << 9, + NEEDS_GOT_NONAUTH = 1 << 10, }; // The base class for real symbol classes. diff --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp index c8a05e4b9c3cc..baa7a083404fe 100644 --- a/lld/ELF/SyntheticSections.cpp +++ b/lld/ELF/SyntheticSections.cpp @@ -670,6 +670,10 @@ void GotSection::addEntry(const Symbol &sym) { ctx.symAux.back().gotIdx = numEntries++; } +void GotSection::addAuthEntry(const Symbol &sym) { + authEntries.push_back({(numEntries - 1) * ctx.arg.wordsize, sym.isFunc()}); +} + bool GotSection::addTlsDescEntry(const Symbol &sym) { assert(sym.auxIdx == ctx.symAux.size() - 1); ctx.symAux.back().tlsDescIdx = numEntries; @@ -732,6 +736,21 @@ void GotSection::writeTo(uint8_t *buf) { return; ctx.target->writeGotHeader(buf); ctx.target->relocateAlloc(*this, buf); + for (const AuthEntryInfo &authEntry : authEntries) { + // https://github.com/ARM-software/abi-aa/blob/2024Q3/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/2024Q3/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(ctx, 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 132513cbd3b79..9fcee3b481af0 100644 --- a/lld/ELF/SyntheticSections.h +++ b/lld/ELF/SyntheticSections.h @@ -112,6 +112,7 @@ class GotSection final : public SyntheticSection { void addConstant(const Relocation &r); void addEntry(const Symbol &sym); + void addAuthEntry(const Symbol &sym); bool addTlsDescEntry(const Symbol &sym); bool addDynTlsEntry(const Symbol &sym); bool addTlsIndex(); @@ -131,6 +132,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-got-relocations-pauth.s b/lld/test/ELF/aarch64-got-relocations-pauth.s new file mode 100644 index 0000000000000..4456fe3ad7714 --- /dev/null +++ b/lld/test/ELF/aarch64-got-relocations-pauth.s @@ -0,0 +1,90 @@ +# REQUIRES: aarch64 + +# RUN: rm -rf %t && split-file %s %t && cd %t + +# RUN: llvm-mc -filetype=obj -triple=aarch64 %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 ok.s -o ok.o + +# RUN: ld.lld ok.o a.so -pie -o ok1 +# RUN: llvm-readelf -r -S -x .got ok1 | FileCheck %s --check-prefix=OK1 + +# RUN: ld.lld ok.o a.o -pie -o ok2 +# RUN: llvm-readelf -r -S -x .got -s ok2 | FileCheck %s --check-prefix=OK2 + +# OK1: Offset Info Type Symbol's Value Symbol's Name + Addend +# OK1-NEXT: 0000000000020380 0000000100000412 R_AARCH64_AUTH_GLOB_DAT 0000000000000000 bar + 0 +# OK1-NEXT: 0000000000020388 0000000200000412 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) +# OK2: Offset Info Type Symbol's Value Symbol's Name + Addend +# OK2-NEXT: 0000000000020320 0000000000000411 R_AARCH64_AUTH_RELATIVE 10260 +# OK2-NEXT: 0000000000020328 0000000000000411 R_AARCH64_AUTH_RELATIVE 10260 + +# OK1: Hex dump of section '.got': +# OK1-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 + +# OK2: Symbol table '.symtab' contains {{.*}} entries: +# OK2: Num: Value Size Type Bind Vis Ndx Name +# OK2: 0000000000010260 0 FUNC GLOBAL DEFAULT 6 bar +# OK2: 0000000000010260 0 NOTYPE GLOBAL DEFAULT 6 zed + +# OK2: Hex dump of section '.got': +# OK2-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 ok1 | FileCheck %s --check-prefix=OK1-ASM + +# OK1-ASM: <_start>: +# OK1-ASM-NEXT: adrp x0, 0x20000 +# OK1-ASM-NEXT: ldr x0, [x0, #0x380] +# OK1-ASM-NEXT: adrp x1, 0x20000 +# OK1-ASM-NEXT: add x1, x1, #0x380 +# OK1-ASM-NEXT: adrp x0, 0x20000 +# OK1-ASM-NEXT: ldr x0, [x0, #0x388] +# OK1-ASM-NEXT: adrp x1, 0x20000 +# OK1-ASM-NEXT: add x1, x1, #0x388 + +# RUN: llvm-objdump -d ok2 | FileCheck %s --check-prefix=OK2-ASM + +# OK2-ASM: <_start>: +# OK2-ASM-NEXT: adrp x0, 0x20000 +# OK2-ASM-NEXT: ldr x0, [x0, #0x320] +# OK2-ASM-NEXT: adrp x1, 0x20000 +# OK2-ASM-NEXT: add x1, x1, #0x320 +# OK2-ASM-NEXT: adrp x0, 0x20000 +# OK2-ASM-NEXT: ldr x0, [x0, #0x328] +# OK2-ASM-NEXT: adrp x1, 0x20000 +# OK2-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 err.s -o err.o +# RUN: not ld.lld err.o a.so -pie 2>&1 | FileCheck %s --check-prefix=ERR --implicit-check-not=error: +# 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]