[RISCV] Fix evaluating %pcrel_lo against global and weak symbols

Summary:
Previously, we would erroneously turn %pcrel_lo(label), where label has
a %pcrel_hi against a weak symbol, into %pcrel_lo(label + offset), as
evaluatePCRelLo would believe the target independent logic was going to
fold it. Moreover, even if that were fixed, shouldForceRelocation lacks
an MCAsmLayout and thus cannot evaluate the %pcrel_hi fixup to a value
and check the symbol, so we would then erroneously constant-fold the
%pcrel_lo whilst leaving the %pcrel_hi intact. After D72197, this same
sequence also occurs for symbols with global binding, which is triggered
in real-world code.

Instead, as discussed in D71978, we introduce a new FKF_IsTarget flag to
avoid these kinds of issues. All the resolution logic happens in one
place, with no coordination required between RISCAsmBackend and
RISCVMCExpr to ensure they implement the same logic twice. Although the
implementation of %pcrel_hi can be left as target independent, we make
it target dependent to ensure that they are handled identically to
%pcrel_lo, otherwise we risk one of them being constant folded but the
other being preserved. This also allows us to properly support fixup
pairs where the instructions are in different fragments.

Reviewers: asb, lenary, efriedma

Reviewed By: efriedma

Subscribers: arichardson, hiraditya, rbar, johnrusso, simoncook, sabuasal, niosHD, kito-cheng, shiva0217, MaskRay, zzheng, edward-jones, rogfer01, MartinMosbeck, brucehoult, the_o, rkruppe, PkmX, jocewei, psnobl, benna, Jim, s.egerton, pzheng, sameer.abuasal, apazos, luismarques, llvm-commits

Tags: #llvm

Differential Revision: https://reviews.llvm.org/D73211
This commit is contained in:
James Clarke 2020-01-23 02:05:46 +00:00
parent 48490e3247
commit 3f5976c97d
12 changed files with 179 additions and 126 deletions

View File

@ -108,6 +108,14 @@ public:
return false;
}
virtual bool evaluateTargetFixup(const MCAssembler &Asm,
const MCAsmLayout &Layout,
const MCFixup &Fixup, const MCFragment *DF,
const MCValue &Target, uint64_t &Value,
bool &WasForced) {
llvm_unreachable("Need to implement hook if target has custom fixups");
}
/// Apply the \p Value for given \p Fixup into the provided data fragment, at
/// the offset specified by the fixup and following the fixup kind as
/// appropriate. Errors (such as an out of range fixup value) should be

View File

@ -19,7 +19,10 @@ struct MCFixupKindInfo {
FKF_IsPCRel = (1 << 0),
/// Should this fixup kind force a 4-byte aligned effective PC value?
FKF_IsAlignedDownTo32Bits = (1 << 1)
FKF_IsAlignedDownTo32Bits = (1 << 1),
/// Should this fixup be evaluated in a target dependent manner?
FKF_IsTarget = (1 << 2)
};
/// A target specific name for the fixup kind. The names will be unique for

View File

@ -217,6 +217,13 @@ bool MCAssembler::evaluateFixup(const MCAsmLayout &Layout,
}
assert(getBackendPtr() && "Expected assembler backend");
bool IsTarget = getBackendPtr()->getFixupKindInfo(Fixup.getKind()).Flags &
MCFixupKindInfo::FKF_IsTarget;
if (IsTarget)
return getBackend().evaluateTargetFixup(*this, Layout, Fixup, DF, Target,
Value, WasForced);
bool IsPCRel = getBackendPtr()->getFixupKindInfo(Fixup.getKind()).Flags &
MCFixupKindInfo::FKF_IsPCRel;

View File

@ -9,6 +9,7 @@
#include "RISCVAsmBackend.h"
#include "RISCVMCExpr.h"
#include "llvm/ADT/APInt.h"
#include "llvm/MC/MCAsmLayout.h"
#include "llvm/MC/MCAssembler.h"
#include "llvm/MC/MCContext.h"
#include "llvm/MC/MCDirectives.h"
@ -28,8 +29,6 @@ using namespace llvm;
bool RISCVAsmBackend::shouldForceRelocation(const MCAssembler &Asm,
const MCFixup &Fixup,
const MCValue &Target) {
bool ShouldForce = false;
switch (Fixup.getTargetKind()) {
default:
break;
@ -44,40 +43,9 @@ bool RISCVAsmBackend::shouldForceRelocation(const MCAssembler &Asm,
case RISCV::fixup_riscv_tls_got_hi20:
case RISCV::fixup_riscv_tls_gd_hi20:
return true;
case RISCV::fixup_riscv_pcrel_lo12_i:
case RISCV::fixup_riscv_pcrel_lo12_s:
// For pcrel_lo12, force a relocation if the target of the corresponding
// pcrel_hi20 is not in the same fragment.
const MCFixup *T = cast<RISCVMCExpr>(Fixup.getValue())->getPCRelHiFixup();
if (!T) {
Asm.getContext().reportError(Fixup.getLoc(),
"could not find corresponding %pcrel_hi");
return false;
}
switch (T->getTargetKind()) {
default:
llvm_unreachable("Unexpected fixup kind for pcrel_lo12");
break;
case RISCV::fixup_riscv_got_hi20:
case RISCV::fixup_riscv_tls_got_hi20:
case RISCV::fixup_riscv_tls_gd_hi20:
ShouldForce = true;
break;
case RISCV::fixup_riscv_pcrel_hi20: {
MCFragment *TFragment = T->getValue()->findAssociatedFragment();
MCFragment *FixupFragment = Fixup.getValue()->findAssociatedFragment();
assert(FixupFragment && "We should have a fragment for this fixup");
ShouldForce =
!TFragment || TFragment->getParent() != FixupFragment->getParent();
break;
}
}
break;
}
return ShouldForce || STI.getFeatureBits()[RISCV::FeatureRelax] ||
ForceRelocs;
return STI.getFeatureBits()[RISCV::FeatureRelax] || ForceRelocs;
}
bool RISCVAsmBackend::fixupNeedsRelaxationAdvanced(const MCFixup &Fixup,
@ -284,6 +252,67 @@ static uint64_t adjustFixupValue(const MCFixup &Fixup, uint64_t Value,
}
}
bool RISCVAsmBackend::evaluateTargetFixup(
const MCAssembler &Asm, const MCAsmLayout &Layout, const MCFixup &Fixup,
const MCFragment *DF, const MCValue &Target, uint64_t &Value,
bool &WasForced) {
const MCFixup *AUIPCFixup;
const MCFragment *AUIPCDF;
MCValue AUIPCTarget;
switch (Fixup.getTargetKind()) {
default:
llvm_unreachable("Unexpected fixup kind!");
case RISCV::fixup_riscv_pcrel_hi20:
AUIPCFixup = &Fixup;
AUIPCDF = DF;
AUIPCTarget = Target;
break;
case RISCV::fixup_riscv_pcrel_lo12_i:
case RISCV::fixup_riscv_pcrel_lo12_s: {
AUIPCFixup = cast<RISCVMCExpr>(Fixup.getValue())->getPCRelHiFixup(&AUIPCDF);
if (!AUIPCFixup) {
Asm.getContext().reportError(Fixup.getLoc(),
"could not find corresponding %pcrel_hi");
return true;
}
// MCAssembler::evaluateFixup will emit an error for this case when it sees
// the %pcrel_hi, so don't duplicate it when also seeing the %pcrel_lo.
const MCExpr *AUIPCExpr = AUIPCFixup->getValue();
if (!AUIPCExpr->evaluateAsRelocatable(AUIPCTarget, &Layout, AUIPCFixup))
return true;
break;
}
}
if (!AUIPCTarget.getSymA() || AUIPCTarget.getSymB())
return false;
const MCSymbolRefExpr *A = AUIPCTarget.getSymA();
const MCSymbol &SA = A->getSymbol();
if (A->getKind() != MCSymbolRefExpr::VK_None || SA.isUndefined())
return false;
auto *Writer = Asm.getWriterPtr();
if (!Writer)
return false;
bool IsResolved = Writer->isSymbolRefDifferenceFullyResolvedImpl(
Asm, SA, *AUIPCDF, false, true);
if (!IsResolved)
return false;
Value = Layout.getSymbolOffset(SA) + AUIPCTarget.getConstant();
Value -= Layout.getFragmentOffset(AUIPCDF) + AUIPCFixup->getOffset();
if (shouldForceRelocation(Asm, *AUIPCFixup, AUIPCTarget)) {
WasForced = true;
return false;
}
return true;
}
void RISCVAsmBackend::applyFixup(const MCAssembler &Asm, const MCFixup &Fixup,
const MCValue &Target,
MutableArrayRef<char> Data, uint64_t Value,

View File

@ -65,6 +65,11 @@ public:
const MCAsmLayout &Layout,
MCAlignFragment &AF) override;
bool evaluateTargetFixup(const MCAssembler &Asm, const MCAsmLayout &Layout,
const MCFixup &Fixup, const MCFragment *DF,
const MCValue &Target, uint64_t &Value,
bool &WasForced) override;
void applyFixup(const MCAssembler &Asm, const MCFixup &Fixup,
const MCValue &Target, MutableArrayRef<char> Data,
uint64_t Value, bool IsResolved,
@ -101,9 +106,12 @@ public:
{ "fixup_riscv_hi20", 12, 20, 0 },
{ "fixup_riscv_lo12_i", 20, 12, 0 },
{ "fixup_riscv_lo12_s", 0, 32, 0 },
{ "fixup_riscv_pcrel_hi20", 12, 20, MCFixupKindInfo::FKF_IsPCRel },
{ "fixup_riscv_pcrel_lo12_i", 20, 12, MCFixupKindInfo::FKF_IsPCRel },
{ "fixup_riscv_pcrel_lo12_s", 0, 32, MCFixupKindInfo::FKF_IsPCRel },
{ "fixup_riscv_pcrel_hi20", 12, 20,
MCFixupKindInfo::FKF_IsPCRel | MCFixupKindInfo::FKF_IsTarget },
{ "fixup_riscv_pcrel_lo12_i", 20, 12,
MCFixupKindInfo::FKF_IsPCRel | MCFixupKindInfo::FKF_IsTarget },
{ "fixup_riscv_pcrel_lo12_s", 0, 32,
MCFixupKindInfo::FKF_IsPCRel | MCFixupKindInfo::FKF_IsTarget },
{ "fixup_riscv_got_hi20", 12, 20, MCFixupKindInfo::FKF_IsPCRel },
{ "fixup_riscv_tprel_hi20", 12, 20, 0 },
{ "fixup_riscv_tprel_lo12_i", 20, 12, 0 },

View File

@ -47,7 +47,7 @@ void RISCVMCExpr::printImpl(raw_ostream &OS, const MCAsmInfo *MAI) const {
OS << ')';
}
const MCFixup *RISCVMCExpr::getPCRelHiFixup() const {
const MCFixup *RISCVMCExpr::getPCRelHiFixup(const MCFragment **DFOut) const {
MCValue AUIPCLoc;
if (!getSubExpr()->evaluateAsRelocatable(AUIPCLoc, nullptr, nullptr))
return nullptr;
@ -81,6 +81,8 @@ const MCFixup *RISCVMCExpr::getPCRelHiFixup() const {
case RISCV::fixup_riscv_tls_got_hi20:
case RISCV::fixup_riscv_tls_gd_hi20:
case RISCV::fixup_riscv_pcrel_hi20:
if (DFOut)
*DFOut = DF;
return &F;
}
}
@ -88,74 +90,9 @@ const MCFixup *RISCVMCExpr::getPCRelHiFixup() const {
return nullptr;
}
bool RISCVMCExpr::evaluatePCRelLo(MCValue &Res, const MCAsmLayout *Layout,
const MCFixup *Fixup) const {
// VK_RISCV_PCREL_LO has to be handled specially. The MCExpr inside is
// actually the location of a auipc instruction with a VK_RISCV_PCREL_HI fixup
// pointing to the real target. We need to generate an MCValue in the form of
// (<real target> + <offset from this fixup to the auipc fixup>). The Fixup
// is pcrel relative to the VK_RISCV_PCREL_LO fixup, so we need to add the
// offset to the VK_RISCV_PCREL_HI Fixup from VK_RISCV_PCREL_LO to correct.
// Don't try to evaluate if the fixup will be forced as a relocation (e.g.
// as linker relaxation is enabled). If we evaluated pcrel_lo in this case,
// the modified fixup will be converted into a relocation that no longer
// points to the pcrel_hi as the linker requires.
auto &RAB =
static_cast<RISCVAsmBackend &>(Layout->getAssembler().getBackend());
if (RAB.willForceRelocations())
return false;
MCValue AUIPCLoc;
if (!getSubExpr()->evaluateAsValue(AUIPCLoc, *Layout))
return false;
const MCSymbolRefExpr *AUIPCSRE = AUIPCLoc.getSymA();
// Don't try to evaluate %pcrel_hi/%pcrel_lo pairs that cross fragment
// boundries.
if (!AUIPCSRE ||
findAssociatedFragment() != AUIPCSRE->findAssociatedFragment())
return false;
const MCSymbol *AUIPCSymbol = &AUIPCSRE->getSymbol();
if (!AUIPCSymbol)
return false;
const MCFixup *TargetFixup = getPCRelHiFixup();
if (!TargetFixup)
return false;
if ((unsigned)TargetFixup->getKind() != RISCV::fixup_riscv_pcrel_hi20)
return false;
MCValue Target;
if (!TargetFixup->getValue()->evaluateAsValue(Target, *Layout))
return false;
if (!Target.getSymA() || !Target.getSymA()->getSymbol().isInSection())
return false;
if (&Target.getSymA()->getSymbol().getSection() !=
findAssociatedFragment()->getParent())
return false;
// We must use TargetFixup rather than AUIPCSymbol here. They will almost
// always have the same offset, except for the case when AUIPCSymbol is at
// the end of a fragment and the fixup comes from offset 0 in the next
// fragment.
uint64_t AUIPCOffset = TargetFixup->getOffset();
Res = MCValue::get(Target.getSymA(), nullptr,
Target.getConstant() + (Fixup->getOffset() - AUIPCOffset));
return true;
}
bool RISCVMCExpr::evaluateAsRelocatableImpl(MCValue &Res,
const MCAsmLayout *Layout,
const MCFixup *Fixup) const {
if (Kind == VK_RISCV_PCREL_LO && evaluatePCRelLo(Res, Layout, Fixup))
return true;
if (!getSubExpr()->evaluateAsRelocatable(Res, Layout, Fixup))
return false;

View File

@ -46,9 +46,6 @@ private:
int64_t evaluateAsInt64(int64_t Value) const;
bool evaluatePCRelLo(MCValue &Res, const MCAsmLayout *Layout,
const MCFixup *Fixup) const;
explicit RISCVMCExpr(const MCExpr *Expr, VariantKind Kind)
: Expr(Expr), Kind(Kind) {}
@ -61,11 +58,11 @@ public:
const MCExpr *getSubExpr() const { return Expr; }
/// Get the corresponding PC-relative HI fixup that a VK_RISCV_PCREL_LO
/// points to.
/// points to, and optionally the fragment containing it.
///
/// \returns nullptr if this isn't a VK_RISCV_PCREL_LO pointing to a
/// known PC-relative HI fixup.
const MCFixup *getPCRelHiFixup() const;
const MCFixup *getPCRelHiFixup(const MCFragment **DFOut) const;
void printImpl(raw_ostream &OS, const MCAsmInfo *MAI) const override;
bool evaluateAsRelocatableImpl(MCValue &Res, const MCAsmLayout *Layout,

View File

@ -12,11 +12,12 @@
# RUN: | FileCheck --check-prefix RELAX %s
# Fixups for %pcrel_hi / %pcrel_lo can be evaluated within a section,
# regardless of the fragment containing the target address.
# regardless of the fragment containing the target address, provided symbol
# binding allows it.
function:
.Lpcrel_label1:
auipc a0, %pcrel_hi(other_function)
auipc a0, %pcrel_hi(local_function)
addi a1, a0, %pcrel_lo(.Lpcrel_label1)
# NORELAX: auipc a0, 0
# NORELAX-NOT: R_RISCV
@ -24,7 +25,7 @@ function:
# NORELAX-NOT: R_RISCV
# RELAX: auipc a0, 0
# RELAX: R_RISCV_PCREL_HI20 other_function
# RELAX: R_RISCV_PCREL_HI20 local_function
# RELAX: R_RISCV_RELAX *ABS*
# RELAX: addi a1, a0, 0
# RELAX: R_RISCV_PCREL_LO12_I .Lpcrel_label1
@ -32,7 +33,7 @@ function:
.p2align 2 # Cause a new fragment be emitted here
.Lpcrel_label2:
auipc a0, %pcrel_hi(other_function)
auipc a0, %pcrel_hi(local_function)
addi a1, a0, %pcrel_lo(.Lpcrel_label2)
# NORELAX: auipc a0, 0
# NORELAX-NOT: R_RISCV
@ -40,13 +41,72 @@ function:
# NORELAX-NOT: R_RISCV
# RELAX: auipc a0, 0
# RELAX: R_RISCV_PCREL_HI20 other_function
# RELAX: R_RISCV_PCREL_HI20 local_function
# RELAX: R_RISCV_RELAX *ABS*
# RELAX: addi a1, a0, 0
# RELAX: R_RISCV_PCREL_LO12_I .Lpcrel_label2
# RELAX: R_RISCV_RELAX *ABS*
.type other_function,@function
other_function:
.type local_function,@function
local_function:
ret
# Check we correctly evaluate when fixups are in different fragments
.Lpcrel_label3:
auipc a0, %pcrel_hi(local_function)
.p2align 2 # Cause a new fragment be emitted here
addi a1, a0, %pcrel_lo(.Lpcrel_label3)
# NORELAX: auipc a0, 0
# NORELAX-NOT: R_RISCV
# NORELAX: addi a1, a0, -4
# NORELAX-NOT: R_RISCV
# RELAX: auipc a0, 0
# RELAX: R_RISCV_PCREL_HI20 local_function
# RELAX: R_RISCV_RELAX *ABS*
# RELAX: addi a1, a0, 0
# RELAX: R_RISCV_PCREL_LO12_I .Lpcrel_label3
# RELAX: R_RISCV_RELAX *ABS*
# Check handling of symbol binding.
.Lpcrel_label4:
auipc a0, %pcrel_hi(global_function)
addi a1, a0, %pcrel_lo(.Lpcrel_label4)
# NORELAX: auipc a0, 0
# NORELAX: R_RISCV_PCREL_HI20 global_function
# NORELAX: addi a1, a0, 0
# NORELAX: R_RISCV_PCREL_LO12_I .Lpcrel_label4
# RELAX: auipc a0, 0
# RELAX: R_RISCV_PCREL_HI20 global_function
# RELAX: R_RISCV_RELAX *ABS*
# RELAX: addi a1, a0, 0
# RELAX: R_RISCV_PCREL_LO12_I .Lpcrel_label4
# RELAX: R_RISCV_RELAX *ABS*
.Lpcrel_label5:
auipc a0, %pcrel_hi(weak_function)
addi a1, a0, %pcrel_lo(.Lpcrel_label5)
# NORELAX: auipc a0, 0
# NORELAX: R_RISCV_PCREL_HI20 weak_function
# NORELAX: addi a1, a0, 0
# NORELAX: R_RISCV_PCREL_LO12_I .Lpcrel_label5
# RELAX: auipc a0, 0
# RELAX: R_RISCV_PCREL_HI20 weak_function
# RELAX: R_RISCV_RELAX *ABS*
# RELAX: addi a1, a0, 0
# RELAX: R_RISCV_PCREL_LO12_I .Lpcrel_label5
# RELAX: R_RISCV_RELAX *ABS*
.global global_function
.type global_function,@function
global_function:
ret
.weak weak_function
.type weak_function,@function
weak_function:
ret

View File

@ -3,3 +3,5 @@
1:
addi a0, a0, %pcrel_lo(1b) # CHECK: :[[@LINE]]:3: error: could not find corresponding %pcrel_hi
addi a0, a0, %pcrel_lo(0x123456) # CHECK: :[[@LINE]]:3: error: could not find corresponding %pcrel_hi
addi a0, a0, %pcrel_lo(foo) # CHECK: :[[@LINE]]:3: error: could not find corresponding %pcrel_hi

View File

@ -14,6 +14,8 @@
# CHECK-ALIAS....Match the alias (tests instr. to alias mapping)
# CHECK-EXPAND...Match canonical instr. unconditionally (tests alias expansion)
# Needed for testing valid %pcrel_lo expressions
.Lpcrel_hi0: auipc a0, %pcrel_hi(foo)
# CHECK-INST: addi a0, zero, 0
# CHECK-ALIAS: mv a0, zero
@ -71,16 +73,13 @@ li x12, 0xFFFFFFFF
# CHECK-EXPAND: addi a0, zero, 1110
li a0, %lo(0x123456)
# CHECK-OBJ-NOALIAS: addi a0, zero, 0
# CHECK-OBJ: R_RISCV_PCREL_LO12
li a0, %pcrel_lo(0x123456)
# CHECK-OBJ-NOALIAS: addi a0, zero, 0
# CHECK-OBJ: R_RISCV_LO12
li a0, %lo(foo)
# CHECK-OBJ-NOALIAS: addi a0, zero, 0
# CHECK-OBJ: R_RISCV_PCREL_LO12
li a0, %pcrel_lo(foo)
li a0, %pcrel_lo(.Lpcrel_hi0)
.equ CONST, 0x123456
# CHECK-EXPAND: lui a0, 291

View File

@ -11,6 +11,9 @@
.equ CONST, 30
# Needed for testing valid %pcrel_lo expressions
.Lpcrel_hi0: auipc a0, %pcrel_hi(foo)
# CHECK-ASM-AND-OBJ: lui a0, 2
# CHECK-ASM: encoding: [0x37,0x25,0x00,0x00]
lui a0, 2
@ -161,11 +164,11 @@ lw a0, 97(a2)
# CHECK-OBJ: lbu s5, 0(s6)
# CHECK-OBJ: R_RISCV_LO12
lbu s5, %lo(foo)(s6)
# CHECK-ASM: lhu t3, %pcrel_lo(foo)(t3)
# CHECK-ASM: lhu t3, %pcrel_lo(.Lpcrel_hi0)(t3)
# CHECK-ASM: encoding: [0x03,0x5e,0bAAAA1110,A]
# CHECK-OBJ: lhu t3, 0(t3)
# CHECK-OBJ: R_RISCV_PCREL_LO12
lhu t3, %pcrel_lo(foo)(t3)
lhu t3, %pcrel_lo(.Lpcrel_hi0)(t3)
# CHECK-ASM-AND-OBJ: lb t0, 30(t1)
# CHECK-ASM: encoding: [0x83,0x02,0xe3,0x01]
lb t0, CONST(t1)

View File

@ -17,6 +17,9 @@
# TODO ld
# TODO sd
# Needed for testing valid %pcrel_lo expressions
.Lpcrel_hi0: auipc a0, %pcrel_hi(foo)
# CHECK-INST: addi a0, zero, 0
# CHECK-ALIAS: mv a0, zero
li x10, 0
@ -107,16 +110,13 @@ li t5, 0xFFFFFFFFFFFFFFFF
# CHECK-EXPAND: addi a0, zero, 1110
li a0, %lo(0x123456)
# CHECK-OBJ-NOALIAS: addi a0, zero, 0
# CHECK-OBJ: R_RISCV_PCREL_LO12
li a0, %pcrel_lo(0x123456)
# CHECK-OBJ-NOALIAS: addi a0, zero, 0
# CHECK-OBJ: R_RISCV_LO12
li a0, %lo(foo)
# CHECK-OBJ-NOALIAS: addi a0, zero, 0
# CHECK-OBJ: R_RISCV_PCREL_LO12
li a0, %pcrel_lo(foo)
li a0, %pcrel_lo(.Lpcrel_hi0)
.equ CONST, 0x123456
# CHECK-EXPAND: lui a0, 291