diff --git a/llvm/include/llvm/MC/MCObjectStreamer.h b/llvm/include/llvm/MC/MCObjectStreamer.h --- a/llvm/include/llvm/MC/MCObjectStreamer.h +++ b/llvm/include/llvm/MC/MCObjectStreamer.h @@ -171,9 +171,9 @@ void emitTPRel64Value(const MCExpr *Value) override; void emitGPRel32Value(const MCExpr *Value) override; void emitGPRel64Value(const MCExpr *Value) override; - bool emitRelocDirective(const MCExpr &Offset, StringRef Name, - const MCExpr *Expr, SMLoc Loc, - const MCSubtargetInfo &STI) override; + Optional> + emitRelocDirective(const MCExpr &Offset, StringRef Name, const MCExpr *Expr, + SMLoc Loc, const MCSubtargetInfo &STI) override; using MCStreamer::emitFill; void emitFill(const MCExpr &NumBytes, uint64_t FillValue, SMLoc Loc = SMLoc()) override; diff --git a/llvm/include/llvm/MC/MCStreamer.h b/llvm/include/llvm/MC/MCStreamer.h --- a/llvm/include/llvm/MC/MCStreamer.h +++ b/llvm/include/llvm/MC/MCStreamer.h @@ -1014,13 +1014,12 @@ virtual void emitSyntaxDirective(); - /// Emit a .reloc directive. - /// Returns true if the relocation could not be emitted because Name is not - /// known. - virtual bool emitRelocDirective(const MCExpr &Offset, StringRef Name, - const MCExpr *Expr, SMLoc Loc, - const MCSubtargetInfo &STI) { - return true; + /// Record a relocation described by the .reloc directive. Return None if + /// succeeded. Otherwise, return a pair (Name is invalid, error message). + virtual Optional> + emitRelocDirective(const MCExpr &Offset, StringRef Name, const MCExpr *Expr, + SMLoc Loc, const MCSubtargetInfo &STI) { + return None; } virtual void emitAddrsig() {} diff --git a/llvm/lib/MC/MCAsmStreamer.cpp b/llvm/lib/MC/MCAsmStreamer.cpp --- a/llvm/lib/MC/MCAsmStreamer.cpp +++ b/llvm/lib/MC/MCAsmStreamer.cpp @@ -349,9 +349,9 @@ void emitBundleLock(bool AlignToEnd) override; void emitBundleUnlock() override; - bool emitRelocDirective(const MCExpr &Offset, StringRef Name, - const MCExpr *Expr, SMLoc Loc, - const MCSubtargetInfo &STI) override; + Optional> + emitRelocDirective(const MCExpr &Offset, StringRef Name, const MCExpr *Expr, + SMLoc Loc, const MCSubtargetInfo &STI) override; void emitAddrsig() override; void emitAddrsigSym(const MCSymbol *Sym) override; @@ -2072,9 +2072,10 @@ EmitEOL(); } -bool MCAsmStreamer::emitRelocDirective(const MCExpr &Offset, StringRef Name, - const MCExpr *Expr, SMLoc, - const MCSubtargetInfo &STI) { +Optional> +MCAsmStreamer::emitRelocDirective(const MCExpr &Offset, StringRef Name, + const MCExpr *Expr, SMLoc, + const MCSubtargetInfo &STI) { OS << "\t.reloc "; Offset.print(OS, MAI); OS << ", " << Name; @@ -2083,7 +2084,7 @@ Expr->print(OS, MAI); } EmitEOL(); - return false; + return None; } void MCAsmStreamer::emitAddrsig() { diff --git a/llvm/lib/MC/MCObjectStreamer.cpp b/llvm/lib/MC/MCObjectStreamer.cpp --- a/llvm/lib/MC/MCObjectStreamer.cpp +++ b/llvm/lib/MC/MCObjectStreamer.cpp @@ -18,6 +18,7 @@ #include "llvm/MC/MCObjectWriter.h" #include "llvm/MC/MCSection.h" #include "llvm/MC/MCSymbol.h" +#include "llvm/MC/MCValue.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/SourceMgr.h" using namespace llvm; @@ -664,12 +665,13 @@ DF->getContents().resize(DF->getContents().size() + 8, 0); } -bool MCObjectStreamer::emitRelocDirective(const MCExpr &Offset, StringRef Name, - const MCExpr *Expr, SMLoc Loc, - const MCSubtargetInfo &STI) { +Optional> +MCObjectStreamer::emitRelocDirective(const MCExpr &Offset, StringRef Name, + const MCExpr *Expr, SMLoc Loc, + const MCSubtargetInfo &STI) { Optional MaybeKind = Assembler->getBackend().getFixupKind(Name); if (!MaybeKind.hasValue()) - return true; + return std::make_pair(true, std::string("unknown relocation name")); MCFixupKind Kind = *MaybeKind; @@ -680,27 +682,33 @@ MCDataFragment *DF = getOrCreateDataFragment(&STI); flushPendingLabels(DF, DF->getContents().size()); - int64_t OffsetValue; - if (Offset.evaluateAsAbsolute(OffsetValue)) { - if (OffsetValue < 0) - llvm_unreachable(".reloc offset is negative"); - DF->getFixups().push_back(MCFixup::create(OffsetValue, Expr, Kind, Loc)); - return false; + MCValue OffsetVal; + if (!Offset.evaluateAsRelocatable(OffsetVal, nullptr, nullptr)) + return std::make_pair(false, + std::string(".reloc offset is not relocatable")); + if (OffsetVal.isAbsolute()) { + if (OffsetVal.getConstant() < 0) + return std::make_pair(false, std::string(".reloc offset is negative")); + DF->getFixups().push_back( + MCFixup::create(OffsetVal.getConstant(), Expr, Kind, Loc)); + return None; } + if (OffsetVal.getSymB()) + return std::make_pair(false, + std::string(".reloc offset is not representable")); - if (Offset.getKind() != llvm::MCExpr::SymbolRef) - llvm_unreachable(".reloc offset is not absolute nor a label"); - - const MCSymbolRefExpr &SRE = cast(Offset); + const MCSymbolRefExpr &SRE = cast(*OffsetVal.getSymA()); if (SRE.getSymbol().isDefined()) { - DF->getFixups().push_back(MCFixup::create(SRE.getSymbol().getOffset(), - Expr, Kind, Loc)); - return false; + // FIXME SRE.getSymbol() may not be relative to DF. + DF->getFixups().push_back( + MCFixup::create(SRE.getSymbol().getOffset() + OffsetVal.getConstant(), + Expr, Kind, Loc)); + return None; } PendingFixups.emplace_back(&SRE.getSymbol(), DF, - MCFixup::create(-1, Expr, Kind, Loc)); - return false; + MCFixup::create(-1, Expr, Kind, Loc)); + return None; } void MCObjectStreamer::emitFill(const MCExpr &NumBytes, uint64_t FillValue, diff --git a/llvm/lib/MC/MCParser/AsmParser.cpp b/llvm/lib/MC/MCParser/AsmParser.cpp --- a/llvm/lib/MC/MCParser/AsmParser.cpp +++ b/llvm/lib/MC/MCParser/AsmParser.cpp @@ -3011,20 +3011,12 @@ bool AsmParser::parseDirectiveReloc(SMLoc DirectiveLoc) { const MCExpr *Offset; const MCExpr *Expr = nullptr; - int64_t OffsetValue; SMLoc OffsetLoc = Lexer.getTok().getLoc(); if (parseExpression(Offset)) return true; - - if ((Offset->evaluateAsAbsolute(OffsetValue, - getStreamer().getAssemblerPtr()) && - check(OffsetValue < 0, OffsetLoc, "expression is negative")) || - (check(Offset->getKind() != llvm::MCExpr::Constant && - Offset->getKind() != llvm::MCExpr::SymbolRef, - OffsetLoc, "expected non-negative number or a label")) || - (parseToken(AsmToken::Comma, "expected comma") || - check(getTok().isNot(AsmToken::Identifier), "expected relocation name"))) + if (parseToken(AsmToken::Comma, "expected comma") || + check(getTok().isNot(AsmToken::Identifier), "expected relocation name")) return true; SMLoc NameLoc = Lexer.getTok().getLoc(); @@ -3048,8 +3040,10 @@ const MCTargetAsmParser &MCT = getTargetParser(); const MCSubtargetInfo &STI = MCT.getSTI(); - if (getStreamer().emitRelocDirective(*Offset, Name, Expr, DirectiveLoc, STI)) - return Error(NameLoc, "unknown relocation name"); + if (Optional> Err = + getStreamer().emitRelocDirective(*Offset, Name, Expr, DirectiveLoc, + STI)) + return Error(Err->first ? NameLoc : OffsetLoc, Err->second); return false; } diff --git a/llvm/test/MC/ELF/reloc-directive.s b/llvm/test/MC/ELF/reloc-directive.s new file mode 100644 --- /dev/null +++ b/llvm/test/MC/ELF/reloc-directive.s @@ -0,0 +1,61 @@ +## Target specific relocation support is tested in MC/$target/*reloc-directive*.s +# RUN: llvm-mc -triple=x86_64 %s | FileCheck %s --check-prefix=ASM +# RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t +# RUN: llvm-readobj -r %t | FileCheck %s + +# ASM: .Ltmp0: +# ASM-NEXT: .reloc (.Ltmp0+3)-2, R_X86_64_NONE, foo +# ASM-NEXT: .Ltmp1: +# ASM-NEXT: .reloc .Ltmp1-1, R_X86_64_NONE, foo +# ASM-NEXT: .Ltmp2: +# ASM-NEXT: .reloc 2+.Ltmp2, R_X86_64_NONE, foo +# ASM-NEXT: .reloc (1+foo)+3, R_X86_64_NONE, data+1 + +# CHECK: 0x2 R_X86_64_NONE foo 0x0 +# CHECK-NEXT: 0x0 R_X86_64_NONE foo 0x0 +# CHECK-NEXT: 0x3 R_X86_64_NONE foo 0x0 +# CHECK-NEXT: 0x4 R_X86_64_NONE data 0x1 + +.text +.globl foo +foo: + ret + .reloc .+3-2, R_X86_64_NONE, foo + .reloc .-1, R_X86_64_NONE, foo + .reloc 2+., R_X86_64_NONE, foo + .reloc 1+foo+3, R_X86_64_NONE, data+1 + +.data +.globl data +data: + .long 0 + +# RUN: not llvm-mc -filetype=obj -triple=x86_64 --defsym=ERR=1 %s 2>&1 | FileCheck %s --check-prefix=ERR + +.ifdef ERR +.text +.globl a, b +a: ret +b: ret +x: ret +y: ret + +# ERR: {{.*}}.s:[[#@LINE+1]]:10: error: expected comma +.reloc 0 R_X86_64_NONE, a + +# ERR: {{.*}}.s:[[#@LINE+1]]:8: error: .reloc offset is negative +.reloc -1, R_X86_64_NONE, a +# ERR: {{.*}}.s:[[#@LINE+1]]:8: error: .reloc offset is not relocatable +.reloc 2*., R_X86_64_NONE, a +# ERR: {{.*}}.s:[[#@LINE+1]]:8: error: .reloc offset is not relocatable +.reloc a+a, R_X86_64_NONE, a +## GNU as accepts a-a but rejects b-a. +# ERR: {{.*}}.s:[[#@LINE+1]]:8: error: .reloc offset is not representable +.reloc a-a, R_X86_64_NONE, a +## TODO GNU as accepts x-x and y-x. +# ERR: {{.*}}.s:[[#@LINE+1]]:8: error: .reloc offset is not representable +.reloc x-x, R_X86_64_NONE, a + +# ERR: {{.*}}.s:[[#@LINE+1]]:8: error: directional label undefined +.reloc 1f, R_X86_64_NONE, a +.endif diff --git a/llvm/test/MC/Mips/reloc-directive-bad.s b/llvm/test/MC/Mips/reloc-directive-bad.s --- a/llvm/test/MC/Mips/reloc-directive-bad.s +++ b/llvm/test/MC/Mips/reloc-directive-bad.s @@ -2,12 +2,6 @@ # RUN: -target-abi=o32 2>&1 | FileCheck %s .text foo: - .reloc foo+4, R_MIPS_32, .text # CHECK: :[[@LINE]]:9: error: expected non-negative number or a label - .reloc foo+foo, R_MIPS_32, .text # CHECK: :[[@LINE]]:9: error: expected non-negative number or a label .reloc 0, R_MIPS_32, .text+.text # CHECK: :[[@LINE]]:23: error: expression must be relocatable - .reloc 0 R_MIPS_32, .text # CHECK: :[[@LINE]]:11: error: expected comma .reloc 0, 0, R_MIPS_32, .text # CHECK: :[[@LINE]]:12: error: expected relocation name - .reloc -1, R_MIPS_32, .text # CHECK: :[[@LINE]]:9: error: expression is negative - .reloc 1b, R_MIPS_32, .text # CHECK: :[[@LINE]]:9: error: directional label undefined - .reloc 1f, R_MIPS_32, .text # CHECK: :[[@LINE]]:9: error: directional label undefined nop