diff --git a/llvm/docs/PointerAuth.md b/llvm/docs/PointerAuth.md --- a/llvm/docs/PointerAuth.md +++ b/llvm/docs/PointerAuth.md @@ -300,3 +300,43 @@ * [``llvm.ptrauth.resign``](#llvm-ptrauth-resign): ``AUT*+PAC*``. These are represented as a single pseudo-instruction in the backend to guarantee that the intermediate raw pointer value is not spilled and attackable. + +#### Assembly Representation + +At the assembly level, +[Authenticated Relocations](#authenticated-global-relocation) are represented +using the `@AUTH` modifier: + +```asm + .quad _target@AUTH(,[,addr]) +``` + +where: +* `key` is the Armv8.3-A key identifier (`ia`, `ib`, `da`, `db`) +* `discriminator` is the 16-bit unsigned discriminator value +* `addr` signifies that the authenticated pointer is address-discriminated + (that is, that the relocation's target address is to be blended into the + `discriminator` before it is used in the sign operation. + +For example: +```asm + _authenticated_reference_to_sym: + .quad _sym@AUTH(db,0) + _authenticated_reference_to_sym_addr_disc: + .quad _sym@AUTH(ia,12,addr) +``` + +#### ELF Object File Representation + +At the object file level, +[Authenticated Relocations](#authenticated-global-relocation) are represented +using the `R_AARCH64_AUTH_ABS64` relocation kind (with value `0xE100`). + +The signing schema is encoded in the place of relocation to be applied +as follows: + +``` +| 63 | 62 | 61:60 | 59:48 | 47:32 | 31:0 | +| ----------------- | -------- | -------- | -------- | ------------- | ------------------- | +| address diversity | reserved | key | reserved | discriminator | reserved for addend | +``` diff --git a/llvm/include/llvm/BinaryFormat/ELFRelocs/AArch64.def b/llvm/include/llvm/BinaryFormat/ELFRelocs/AArch64.def --- a/llvm/include/llvm/BinaryFormat/ELFRelocs/AArch64.def +++ b/llvm/include/llvm/BinaryFormat/ELFRelocs/AArch64.def @@ -134,6 +134,7 @@ ELF_RELOC(R_AARCH64_TLS_TPREL64, 0x406) ELF_RELOC(R_AARCH64_TLSDESC, 0x407) ELF_RELOC(R_AARCH64_IRELATIVE, 0x408) +ELF_RELOC(R_AARCH64_AUTH_ABS64, 0xe100) // ELF_RELOC(R_AARCH64_P32_NONE, 0) ELF_RELOC(R_AARCH64_P32_ABS32, 0x001) diff --git a/llvm/lib/MC/MCExpr.cpp b/llvm/lib/MC/MCExpr.cpp --- a/llvm/lib/MC/MCExpr.cpp +++ b/llvm/lib/MC/MCExpr.cpp @@ -743,17 +743,24 @@ /// They might look redundant, but this function can be used before layout /// is done (see the object streamer for example) and having the Asm argument /// lets us avoid relaxations early. -static bool -EvaluateSymbolicAdd(const MCAssembler *Asm, const MCAsmLayout *Layout, - const SectionAddrMap *Addrs, bool InSet, const MCValue &LHS, - const MCSymbolRefExpr *RHS_A, const MCSymbolRefExpr *RHS_B, - int64_t RHS_Cst, MCValue &Res) { +static bool EvaluateSymbolicAdd(const MCAssembler *Asm, + const MCAsmLayout *Layout, + const SectionAddrMap *Addrs, bool InSet, + const MCValue &LHS, const MCValue &RHS, + MCValue &Res) { // FIXME: This routine (and other evaluation parts) are *incredibly* sloppy // about dealing with modifiers. This will ultimately bite us, one day. const MCSymbolRefExpr *LHS_A = LHS.getSymA(); const MCSymbolRefExpr *LHS_B = LHS.getSymB(); int64_t LHS_Cst = LHS.getConstant(); + const MCSymbolRefExpr *RHS_A = RHS.getSymA(); + const MCSymbolRefExpr *RHS_B = RHS.getSymB(); + int64_t RHS_Cst = RHS.getConstant(); + + if (LHS.getRefKind() != 0 || RHS.getRefKind() != 0) + return false; + // Fold the result constant immediately. int64_t Result_Cst = LHS_Cst + RHS_Cst; @@ -962,14 +969,19 @@ case MCBinaryExpr::Sub: // Negate RHS and add. // The cast avoids undefined behavior if the constant is INT64_MIN. - return EvaluateSymbolicAdd(Asm, Layout, Addrs, InSet, LHSValue, - RHSValue.getSymB(), RHSValue.getSymA(), - -(uint64_t)RHSValue.getConstant(), Res); + return EvaluateSymbolicAdd( + Asm, Layout, Addrs, InSet, LHSValue, + MCValue::get(RHSValue.getSymB(), RHSValue.getSymA(), + -(uint64_t)RHSValue.getConstant(), + RHSValue.getRefKind()), + Res); case MCBinaryExpr::Add: - return EvaluateSymbolicAdd(Asm, Layout, Addrs, InSet, LHSValue, - RHSValue.getSymA(), RHSValue.getSymB(), - RHSValue.getConstant(), Res); + return EvaluateSymbolicAdd( + Asm, Layout, Addrs, InSet, LHSValue, + MCValue::get(RHSValue.getSymA(), RHSValue.getSymB(), + RHSValue.getConstant(), RHSValue.getRefKind()), + Res); } } diff --git a/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp b/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp --- a/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp +++ b/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp @@ -180,6 +180,8 @@ bool showMatchError(SMLoc Loc, unsigned ErrCode, uint64_t ErrorInfo, OperandVector &Operands); + bool parseAuthExpr(const MCExpr *&Res, SMLoc &EndLoc); + bool parseDirectiveArch(SMLoc L); bool parseDirectiveArchExtension(SMLoc L); bool parseDirectiveCPU(SMLoc L); @@ -324,6 +326,8 @@ unsigned validateTargetOperandClass(MCParsedAsmOperand &Op, unsigned Kind) override; + bool parsePrimaryExpr(const MCExpr *&Res, SMLoc &EndLoc) override; + static bool classifySymbolRef(const MCExpr *Expr, AArch64MCExpr::VariantKind &ELFRefKind, MCSymbolRefExpr::VariantKind &DarwinRefKind, @@ -7451,6 +7455,112 @@ return false; } +bool AArch64AsmParser::parsePrimaryExpr(const MCExpr *&Res, SMLoc &EndLoc) { + // Try @AUTH expressions: they're more complex than the usual symbol variants. + if (!parseAuthExpr(Res, EndLoc)) + return false; + return getParser().parsePrimaryExpr(Res, EndLoc, nullptr); +} + +/// parseAuthExpr +/// ::= _sym@AUTH(ib,123[,addr]) +/// ::= (_sym + 5)@AUTH(ib,123[,addr]) +/// ::= (_sym - 5)@AUTH(ib,123[,addr]) +bool AArch64AsmParser::parseAuthExpr(const MCExpr *&Res, SMLoc &EndLoc) { + MCAsmParser &Parser = getParser(); + MCContext &Ctx = getContext(); + + AsmToken Tok = Parser.getTok(); + + // Look for '_sym@AUTH' ... + if (Tok.is(AsmToken::Identifier) && Tok.getIdentifier().endswith("@AUTH")) { + StringRef SymName = Tok.getIdentifier().drop_back(strlen("@AUTH")); + if (SymName.find('@') != StringRef::npos) + return TokError( + "combination of @AUTH with other modifiers not supported"); + Res = MCSymbolRefExpr::create(Ctx.getOrCreateSymbol(SymName), Ctx); + + Parser.Lex(); // Eat the identifier. + } else { + // ... or look for a more complex symbol reference, such as ... + SmallVector Tokens; + + // ... '"_long sym"@AUTH' ... + if (Tok.is(AsmToken::String)) + Tokens.resize(2); + // ... or '(_sym + 5)@AUTH'. + else if (Tok.is(AsmToken::LParen)) + Tokens.resize(6); + else + return true; + + if (Parser.getLexer().peekTokens(Tokens) != Tokens.size()) + return true; + + // In either case, the expression ends with '@' 'AUTH'. + if (Tokens[Tokens.size() - 2].isNot(AsmToken::At) || + Tokens[Tokens.size() - 1].isNot(AsmToken::Identifier) || + Tokens[Tokens.size() - 1].getIdentifier() != "AUTH") + return true; + + if (Tok.is(AsmToken::String)) { + StringRef SymName; + if (Parser.parseIdentifier(SymName)) + return true; + Res = MCSymbolRefExpr::create(Ctx.getOrCreateSymbol(SymName), Ctx); + } else { + if (Parser.parsePrimaryExpr(Res, EndLoc, nullptr)) + return true; + } + + Parser.Lex(); // '@' + Parser.Lex(); // 'AUTH' + } + + // At this point, we encountered "@AUTH". There is no fallback anymore. + if (parseToken(AsmToken::LParen, "expected '('")) + return true; + + if (Parser.getTok().isNot(AsmToken::Identifier)) + return TokError("expected key name"); + + StringRef KeyStr = Parser.getTok().getIdentifier(); + auto KeyIDOrNone = AArch64StringToPACKeyID(KeyStr); + if (!KeyIDOrNone) + return TokError("invalid key '" + KeyStr + "'"); + Parser.Lex(); + + if (parseToken(AsmToken::Comma, "expected ','")) + return true; + + if (Parser.getTok().isNot(AsmToken::Integer)) + return TokError("expected integer discriminator"); + int64_t Discriminator = Parser.getTok().getIntVal(); + + if (!isUInt<16>(Discriminator)) + return TokError("integer discriminator " + Twine(Discriminator) + + " out of range [0, 0xFFFF]"); + Parser.Lex(); + + bool UseAddressDiversity = false; + if (Parser.getTok().is(AsmToken::Comma)) { + Parser.Lex(); + if (Parser.getTok().isNot(AsmToken::Identifier) || + Parser.getTok().getIdentifier() != "addr") + return TokError("expected 'addr'"); + UseAddressDiversity = true; + Parser.Lex(); + } + + EndLoc = Parser.getTok().getEndLoc(); + if (parseToken(AsmToken::RParen, "expected ')'")) + return true; + + Res = AArch64AuthMCExpr::create(Res, Discriminator, *KeyIDOrNone, + UseAddressDiversity, Ctx); + return false; +} + bool AArch64AsmParser::classifySymbolRef(const MCExpr *Expr, AArch64MCExpr::VariantKind &ELFRefKind, diff --git a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64AsmBackend.cpp b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64AsmBackend.cpp --- a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64AsmBackend.cpp +++ b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64AsmBackend.cpp @@ -393,6 +393,19 @@ MutableArrayRef Data, uint64_t Value, bool IsResolved, const MCSubtargetInfo *STI) const { + if (Fixup.getTargetKind() == FK_Data_8 && TheTriple.isOSBinFormatELF()) { + auto RefKind = static_cast(Target.getRefKind()); + AArch64MCExpr::VariantKind SymLoc = AArch64MCExpr::getSymbolLoc(RefKind); + if (SymLoc == AArch64AuthMCExpr::VK_AUTH || + SymLoc == AArch64AuthMCExpr::VK_AUTHADDR) { + assert(Value == 0); + const auto *Expr = cast(Fixup.getValue()); + Value = (uint64_t(Expr->getDiscriminator()) << 32) | + (uint64_t(Expr->getKey()) << 60) | + (uint64_t(Expr->hasAddressDiversity()) << 63); + } + } + if (!Value) return; // Doesn't change encoding. unsigned Kind = Fixup.getKind(); diff --git a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64ELFObjectWriter.cpp b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64ELFObjectWriter.cpp --- a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64ELFObjectWriter.cpp +++ b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64ELFObjectWriter.cpp @@ -207,8 +207,12 @@ "ILP32 8 byte absolute data " "relocation not supported (LP64 eqv: ABS64)"); return ELF::R_AARCH64_NONE; - } else + } else { + if (RefKind == AArch64MCExpr::VK_AUTH || + RefKind == AArch64MCExpr::VK_AUTHADDR) + return ELF::R_AARCH64_AUTH_ABS64; return ELF::R_AARCH64_ABS64; + } case AArch64::fixup_aarch64_add_imm12: if (RefKind == AArch64MCExpr::VK_DTPREL_HI12) return R_CLS(TLSLD_ADD_DTPREL_HI12); diff --git a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64MCExpr.h b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64MCExpr.h --- a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64MCExpr.h +++ b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64MCExpr.h @@ -14,7 +14,9 @@ #ifndef LLVM_LIB_TARGET_AARCH64_MCTARGETDESC_AARCH64MCEXPR_H #define LLVM_LIB_TARGET_AARCH64_MCTARGETDESC_AARCH64MCEXPR_H +#include "Utils/AArch64BaseInfo.h" #include "llvm/MC/MCExpr.h" +#include "llvm/Support/Casting.h" #include "llvm/Support/ErrorHandling.h" namespace llvm { @@ -34,6 +36,8 @@ VK_TPREL = 0x007, VK_TLSDESC = 0x008, VK_SECREL = 0x009, + VK_AUTH = 0x00a, + VK_AUTHADDR = 0x00b, VK_SymLocBits = 0x00f, // Variants specifying which part of the final address calculation is @@ -116,6 +120,7 @@ const MCExpr *Expr; const VariantKind Kind; +protected: explicit AArch64MCExpr(const MCExpr *Expr, VariantKind Kind) : Expr(Expr), Kind(Kind) {} @@ -171,6 +176,42 @@ return E->getKind() == MCExpr::Target; } }; + +class AArch64AuthMCExpr final : public AArch64MCExpr { + uint16_t Discriminator; + AArch64PACKey::ID Key; + + explicit AArch64AuthMCExpr(const MCExpr *Expr, uint16_t Discriminator, + AArch64PACKey::ID Key, bool HasAddressDiversity) + : AArch64MCExpr(Expr, HasAddressDiversity ? VK_AUTHADDR : VK_AUTH), + Discriminator(Discriminator), Key(Key) {} + +public: + static const AArch64AuthMCExpr * + create(const MCExpr *Expr, uint16_t Discriminator, AArch64PACKey::ID Key, + bool HasAddressDiversity, MCContext &Ctx); + + AArch64PACKey::ID getKey() const { return Key; } + uint16_t getDiscriminator() const { return Discriminator; } + bool hasAddressDiversity() const { return getKind() == VK_AUTHADDR; } + + void printImpl(raw_ostream &OS, const MCAsmInfo *MAI) const override; + + void visitUsedExpr(MCStreamer &Streamer) const override; + + MCFragment *findAssociatedFragment() const override; + + bool evaluateAsRelocatableImpl(MCValue &Res, const MCAsmLayout *Layout, + const MCFixup *Fixup) const override; + + static bool classof(const MCExpr *E) { + return isa(E) && classof(cast(E)); + } + + static bool classof(const AArch64MCExpr *E) { + return E->getKind() == VK_AUTH || E->getKind() == VK_AUTHADDR; + } +}; } // end namespace llvm #endif diff --git a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64MCExpr.cpp b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64MCExpr.cpp --- a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64MCExpr.cpp +++ b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64MCExpr.cpp @@ -154,3 +154,47 @@ fixELFSymbolsInTLSFixupsImpl(getSubExpr(), Asm); } + +const AArch64AuthMCExpr *AArch64AuthMCExpr::create(const MCExpr *Expr, + uint16_t Discriminator, + AArch64PACKey::ID Key, + bool HasAddressDiversity, + MCContext &Ctx) { + return new (Ctx) + AArch64AuthMCExpr(Expr, Discriminator, Key, HasAddressDiversity); +} + +void AArch64AuthMCExpr::printImpl(raw_ostream &OS, const MCAsmInfo *MAI) const { + bool WrapSubExprInParens = !isa(getSubExpr()); + if (WrapSubExprInParens) + OS << '('; + getSubExpr()->print(OS, MAI); + if (WrapSubExprInParens) + OS << ')'; + + OS << "@AUTH(" << AArch64PACKeyIDToString(Key) << ',' << Discriminator; + if (hasAddressDiversity()) + OS << ",addr"; + OS << ')'; +} + +void AArch64AuthMCExpr::visitUsedExpr(MCStreamer &Streamer) const { + Streamer.visitUsedExpr(*getSubExpr()); +} + +MCFragment *AArch64AuthMCExpr::findAssociatedFragment() const { + llvm_unreachable("FIXME: what goes here?"); +} + +bool AArch64AuthMCExpr::evaluateAsRelocatableImpl(MCValue &Res, + const MCAsmLayout *Layout, + const MCFixup *Fixup) const { + if (!getSubExpr()->evaluateAsRelocatable(Res, Layout, Fixup)) + return false; + + if (Res.getSymB()) + report_fatal_error("Auth relocation can't reference two symbols"); + + Res = MCValue::get(Res.getSymA(), nullptr, Res.getConstant(), getKind()); + return true; +} diff --git a/llvm/test/MC/AArch64/elf-reloc-ptrauth.s b/llvm/test/MC/AArch64/elf-reloc-ptrauth.s new file mode 100644 --- /dev/null +++ b/llvm/test/MC/AArch64/elf-reloc-ptrauth.s @@ -0,0 +1,169 @@ +// RUN: llvm-mc -triple=aarch64 %s --defsym=MCONLY=1 | FileCheck %s --check-prefix=ASM + +// RUN: llvm-mc -triple=aarch64 -filetype=obj %s | \ +// RUN: llvm-readelf -S -r -x .test - | FileCheck %s --check-prefix=RELOC + +// RELOC: Relocation section '.rela.test' at offset 0x1e8 contains 8 entries: +// RELOC-NEXT: Offset Info Type Symbol's Value Symbol's Name + Addend +// RELOC-NEXT: 0000000000000000 000000010000e100 R_AARCH64_AUTH_ABS64 0000000000000000 .helper + 0 +// RELOC-NEXT: 0000000000000010 000000060000e100 R_AARCH64_AUTH_ABS64 0000000000000000 _g1 + 0 +// RELOC-NEXT: 0000000000000020 000000070000e100 R_AARCH64_AUTH_ABS64 0000000000000000 _g2 + 0 +// RELOC-NEXT: 0000000000000030 000000080000e100 R_AARCH64_AUTH_ABS64 0000000000000000 _g3 + 0 +// RELOC-NEXT: 0000000000000040 000000090000e100 R_AARCH64_AUTH_ABS64 0000000000000000 _g4 + 7 +// RELOC-NEXT: 0000000000000050 0000000a0000e100 R_AARCH64_AUTH_ABS64 0000000000000000 _g5 - 3 +// RELOC-NEXT: 0000000000000060 000000020000e100 R_AARCH64_AUTH_ABS64 0000000000000000 _g 6 + 0 +// RELOC-NEXT: 0000000000000070 0000000b0000e100 R_AARCH64_AUTH_ABS64 0000000000000000 _g 7 + 7 + +// RELOC: Hex dump of section '.test': +// VVVVVVVV addend, not needed for rela +// VV reserved +// RELOC-NEXT: 00 00000000 2a000000 +// ^^^^ discriminator +// ^^ 0 no addr diversity 0 reserved 00 ia key 0000 reserved +// RELOC-NEXT: 10 00000000 00000010 +// ^^^^ discriminator +// ^^ 0 no addr diversity 0 reserved 01 ib key 0000 reserved +// RELOC-NEXT: 20 00000000 050000a0 +// ^^^^ discriminator +// ^^ 1 addr diversity 0 reserved 10 da key 0000 reserved +// RELOC-NEXT: 30 00000000 ffff00b0 +// ^^^^ discriminator +// ^^ 1 addr diversity 0 reserved 11 db key 0000 reserved +// RELOC-NEXT: 40 00000000 00000000 +// ^^^^ discriminator +// ^^ 0 no addr diversity 0 reserved 00 ia key 0000 reserved +// RELOC-NEXT: 50 00000000 00de0010 +// ^^^^ discriminator +// ^^ 0 no addr diversity 0 reserved 01 ib key 0000 reserved +// RELOC-NEXT: 60 00000000 ff0000b0 +// ^^^^ discriminator +// ^^ 1 addr diversity 0 reserved 11 db key 0000 reserved +// RELOC-NEXT: 70 00000000 10000000 +// ^^^^ discriminator +// ^^ 0 no addr diversity 0 reserved 00 ia key 0000 reserved + +.section .helper +.local "_g 6" +.type _g0, @function +_g0: + ret + +.section .test +.p2align 3 + +// ASM: .xword _g0@AUTH(ia,42) +.quad _g0@AUTH(ia,42) +.quad 0 + +// ASM: .xword _g1@AUTH(ib,0) +.quad _g1@AUTH(ib,0) +.quad 0 + +// ASM: .xword _g2@AUTH(da,5,addr) +.quad _g2@AUTH(da,5,addr) +.quad 0 + +// ASM: .xword _g3@AUTH(db,65535,addr) +.quad _g3@AUTH(db,0xffff,addr) +.quad 0 + +// ASM: .xword (_g4+7)@AUTH(ia,0) +.quad (_g4 + 7)@AUTH(ia,0) +.quad 0 + +// ASM: .xword (_g5-3)@AUTH(ib,56832) +.quad (_g5 - 3)@AUTH(ib,0xde00) +.quad 0 + +// ASM: .xword "_g 6"@AUTH(db,255,addr) +.quad "_g 6"@AUTH(db,0xff,addr) +.quad 0 + +// ASM: .xword ("_g 7"+7)@AUTH(ia,16) +.quad ("_g 7" + 7)@AUTH(ia,16) +.quad 0 + +.ifdef MCONLY + +// ASM: .xword (_g8@AUTH(ia,42))+1 +.quad _g8@AUTH(ia,42) + 1 + +// ASM: .xword 1+(_g9@AUTH(ia,42)) +.quad 1 + _g9@AUTH(ia,42) + +// ASM: .xword (1+(_g10@AUTH(ia,42)))+1 +.quad 1 + _g10@AUTH(ia,42) + 1 + +// ASM: .xword (_g11@AUTH(ia,42))+(_g12@AUTH(ia,42)) +.quad _g11@AUTH(ia,42) + _g12@AUTH(ia,42) + +.endif // MCONLY + +.ifdef ERR +// RUN: not llvm-mc -triple=aarch64 --defsym=ERR=1 %s 2>&1 | \ +// RUN: FileCheck %s --check-prefix=ERR + +// ERR: :[[#@LINE+1]]:15: error: expected '(' +.quad sym@AUTH)ia,42) + +// ERR: :[[#@LINE+1]]:16: error: expected key name +.quad sym@AUTH(42,42) + +// ERR: :[[#@LINE+1]]:16: error: invalid key 'ic' +.quad sym@AUTH(ic,42) + +// ERR: :[[#@LINE+1]]:19: error: expected ',' +.quad sym@AUTH(ia 42) + +// ERR: :[[#@LINE+1]]:19: error: expected integer discriminator +.quad sym@AUTH(ia,xxx) + +// ERR: :[[#@LINE+1]]:19: error: integer discriminator 65536 out of range [0, 0xFFFF] +.quad sym@AUTH(ia,65536) + +// ERR: :[[#@LINE+1]]:22: error: expected 'addr' +.quad sym@AUTH(ia,42,add) + +// ERR: :[[#@LINE+1]]:21: error: expected ')' +.quad sym@AUTH(ia,42( + +// ERR: :[[#@LINE+1]]:7: error: combination of @AUTH with other modifiers not supported +.quad sym@PLT@AUTH(ia,42) + +// ERR: :[[#@LINE+1]]:11: error: invalid variant 'AUTH@GOT' +.quad sym@AUTH@GOT(ia,42) + +// ERR: :[[#@LINE+1]]:18: error: invalid variant 'TLSDESC@AUTH' +.quad "long sym"@TLSDESC@AUTH(ia,42) + +// ERR: :[[#@LINE+1]]:18: error: invalid variant 'AUTH@PLT' +.quad "long sym"@AUTH@PLT(ia,42) + +// ERR: :[[#@LINE+1]]:17: error: invalid variant 'GOT@AUTH' +.quad (sym - 5)@GOT@AUTH(ia,42) + +// ERR: :[[#@LINE+1]]:17: error: invalid variant 'AUTH@TLSDESC' +.quad (sym + 5)@AUTH@TLSDESC(ia,42) + +// ERR: :[[#@LINE+1]]:12: error: invalid variant 'AUTH' +.quad +sym@AUTH(ia,42) + +.endif // ERR + +.ifdef ERROBJ +// RUN: not llvm-mc -triple=aarch64 -filetype=obj --defsym=ERROBJ=1 %s 2>&1 | \ +// RUN: FileCheck %s --check-prefix=ERROBJ + +// ERROBJ: :[[#@LINE+1]]:7: error: expected relocatable expression +.quad sym@AUTH(ia,42) + 1 + +// ERROBJ: :[[#@LINE+1]]:7: error: expected relocatable expression +.quad 1 + sym@AUTH(ia,42) + +// ERROBJ: :[[#@LINE+1]]:7: error: expected relocatable expression +.quad 1 + sym@AUTH(ia,42) + 1 + +// ERROBJ: :[[#@LINE+1]]:7: error: expected relocatable expression +.quad sym@AUTH(ia,42) + sym@AUTH(ia,42) + +.endif // ERROBJ