[SystemZ] Add range checks for PC-relative fixups.

The AsmParser checks the range of a PC-relative operand, but only if it is
immediate.

This patch adds range checks for operands in applyFixup(), at which point the
offset to a label is known.

The diagnostic message for an operand that is out of range is explicit (with
given value and min/max limits). This is now also done for displacement
fixups.

Review: Ulrich Weigand

Differential Revision: https://reviews.llvm.org/D114194
This commit is contained in:
Jonas Paulsson 2021-11-18 22:40:12 +01:00
parent fd722c5959
commit 5bf4f2acb8
4 changed files with 97 additions and 13 deletions

View File

@ -28,25 +28,43 @@ static uint64_t extractBitsForFixup(MCFixupKind Kind, uint64_t Value,
if (Kind < FirstTargetFixupKind)
return Value;
auto checkFixupInRange = [&](int64_t Min, int64_t Max) -> bool {
int64_t SVal = int64_t(Value);
if (SVal < Min || SVal > Max) {
Ctx.reportError(Fixup.getLoc(), "operand out of range (" + Twine(SVal) +
" not between " + Twine(Min) +
" and " + Twine(Max) + ")");
return false;
}
return true;
};
auto handlePCRelFixupValue = [&](unsigned W) -> uint64_t {
if (Value % 2 != 0)
Ctx.reportError(Fixup.getLoc(), "Non-even PC relative offset.");
if (!checkFixupInRange(minIntN(W) * 2, maxIntN(W) * 2))
return 0;
return (int64_t)Value / 2;
};
switch (unsigned(Kind)) {
case SystemZ::FK_390_PC12DBL:
return handlePCRelFixupValue(12);
case SystemZ::FK_390_PC16DBL:
return handlePCRelFixupValue(16);
case SystemZ::FK_390_PC24DBL:
return handlePCRelFixupValue(24);
case SystemZ::FK_390_PC32DBL:
return (int64_t)Value / 2;
return handlePCRelFixupValue(32);
case SystemZ::FK_390_12:
if (!isUInt<12>(Value)) {
Ctx.reportError(Fixup.getLoc(), "displacement exceeds uint12");
if (!checkFixupInRange(0, maxUIntN(12)))
return 0;
}
return Value;
case SystemZ::FK_390_20: {
if (!isInt<20>(Value)) {
Ctx.reportError(Fixup.getLoc(), "displacement exceeds int20");
if (!checkFixupInRange(minIntN(20), maxIntN(20)))
return 0;
}
// The high byte of a 20 bit displacement value comes first.
uint64_t DLo = Value & 0xfff;
uint64_t DHi = (Value >> 12) & 0xff;

View File

@ -197,7 +197,8 @@ getDispOpValue(const MCInst &MI, unsigned OpNum,
// All instructions follow the pattern where the first displacement has a
// 2 bytes offset, and the second one 4 bytes.
unsigned ByteOffs = Fixups.size() == 0 ? 2 : 4;
Fixups.push_back(MCFixup::create(ByteOffs, MO.getExpr(), (MCFixupKind)Kind));
Fixups.push_back(MCFixup::create(ByteOffs, MO.getExpr(), (MCFixupKind)Kind,
MI.getLoc()));
assert(Fixups.size() <= 2 && "More than two memory operands in MI?");
return 0;
}
@ -296,6 +297,7 @@ SystemZMCCodeEmitter::getPCRelEncoding(const MCInst &MI, unsigned OpNum,
SmallVectorImpl<MCFixup> &Fixups,
unsigned Kind, int64_t Offset,
bool AllowTLS) const {
SMLoc Loc = MI.getLoc();
const MCOperand &MO = MI.getOperand(OpNum);
const MCExpr *Expr;
if (MO.isImm())
@ -311,13 +313,13 @@ SystemZMCCodeEmitter::getPCRelEncoding(const MCInst &MI, unsigned OpNum,
Expr = MCBinaryExpr::createAdd(Expr, OffsetExpr, Ctx);
}
}
Fixups.push_back(MCFixup::create(Offset, Expr, (MCFixupKind)Kind));
Fixups.push_back(MCFixup::create(Offset, Expr, (MCFixupKind)Kind, Loc));
// Output the fixup for the TLS marker if present.
if (AllowTLS && OpNum + 1 < MI.getNumOperands()) {
const MCOperand &MOTLS = MI.getOperand(OpNum + 1);
Fixups.push_back(MCFixup::create(0, MOTLS.getExpr(),
(MCFixupKind)SystemZ::FK_390_TLS_CALL));
Fixups.push_back(MCFixup::create(
0, MOTLS.getExpr(), (MCFixupKind)SystemZ::FK_390_TLS_CALL, Loc));
}
return 0;
}

View File

@ -2,10 +2,14 @@
.text
# CHECK: error: displacement exceeds uint12
# CHECK: error: operand out of range (4096 not between 0 and 4095)
# CHECK-NEXT: la %r1, b-a(%r1)
# CHECK-NEXT: ^
la %r1, b-a(%r1)
# CHECK: error: displacement exceeds int20
# CHECK-NEXT: error: operand out of range (524288 not between -524288 and 524287)
# CHECK-NEXT: lay %r1, d-c(%r1)
# CHECK-NEXT: ^
lay %r1, d-c(%r1)
# CHECK-NOT: error

View File

@ -0,0 +1,60 @@
# RUN: not llvm-mc -triple s390x-unknown-unknown -filetype=obj -mcpu=zEC12 \
# RUN: -o /dev/null %s 2>&1 | FileCheck %s
.text
# Test fixup ranges, which are encoded as half-words.
# 12-bit
# CHECK: error: operand out of range (4096 not between -4096 and 4094)
# CHECK-NEXT: bprp 0, .Lab1, 0
# CHECK-NEXT: ^
# CHECK-NEXT: error: operand out of range (-4098 not between -4096 and 4094)
# CHECK-NEXT: bprp 0, .Lab0, 0
# CHECK-NEXT: ^
bprp 0, .Lab1, 0
.Lab0:
bprp 0, .Lab1, 0
.space 4084
.Lab1:
nopr
bprp 0, .Lab0, 0
bprp 0, .Lab0, 0
# 24-bit
# CHECK-NEXT: error: operand out of range (16777220 not between -16777216 and 16777214)
# CHECK-NEXT: bprp 0, 0, .Lab3
# CHECK-NEXT: ^
# CHECK-NEXT: error: operand out of range (-16777222 not between -16777216 and 16777214)
# CHECK-NEXT: bprp 0, 0, .Lab2
# CHECK-NEXT: ^
bprp 0, 0, .Lab3
.Lab2:
bprp 0, 0, .Lab3
.space 16777208
.Lab3:
nopr
bprp 0, 0, .Lab2
bprp 0, 0, .Lab2
# 16-bit
# CHECK-NEXT: error: operand out of range (65540 not between -65536 and 65534)
# CHECK-NEXT: cij %r1, 0, 0, .Lab5
# CHECK-NEXT: ^
# CHECK-NEXT: error: operand out of range (-65542 not between -65536 and 65534)
# CHECK-NEXT: cij %r1, 0, 0, .Lab4
# CHECK-NEXT: ^
cij %r1, 0, 0, .Lab5
.Lab4:
cij %r1, 0, 0, .Lab5
.space 65528
.Lab5:
nopr
cij %r1, 0, 0, .Lab4
cij %r1, 0, 0, .Lab4
# 32-bit
# Unfortunately there is no support for offsets greater than 32 bits, so we have
# to for now assume they are in range.
# CHECK-NOT: error