-
Notifications
You must be signed in to change notification settings - Fork 15.1k
[PowerPC][MC] Diagnose out of range branch fixups #165859
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
|
@llvm/pr-subscribers-backend-powerpc Author: Nikita Popov (nikic) ChangesCurrently, out-of-range branch fixups will be silently miscompiled. GNU as will instead print an "operand out of range" error instead. Check that the branch target fixups fit into the proper range and have low zero bits. The implementation is inspired by SystemZ:
I'm not sure if there is any way to test the absolute cases. It looks like something like (My actual interest here is not actually assembly usage, but LLVM failing to use the correct branch kind for reasons I've not tracked down yet. Currently this just silently truncates the branch target instead of producing an error.) Full diff: https://github.com/llvm/llvm-project/pull/165859.diff 2 Files Affected:
diff --git a/llvm/lib/Target/PowerPC/MCTargetDesc/PPCAsmBackend.cpp b/llvm/lib/Target/PowerPC/MCTargetDesc/PPCAsmBackend.cpp
index 04b886ae74993..7b69e73ea65be 100644
--- a/llvm/lib/Target/PowerPC/MCTargetDesc/PPCAsmBackend.cpp
+++ b/llvm/lib/Target/PowerPC/MCTargetDesc/PPCAsmBackend.cpp
@@ -25,7 +25,26 @@
#include "llvm/Support/ErrorHandling.h"
using namespace llvm;
-static uint64_t adjustFixupValue(unsigned Kind, uint64_t Value) {
+static uint64_t adjustFixupValue(MCContext &Ctx, const MCFixup &Fixup,
+ unsigned Kind, uint64_t Value) {
+ auto checkBrFixup = [&](unsigned Bits) {
+ int64_t SVal = int64_t(Value);
+ if ((Value & 3) != 0) {
+ Ctx.reportError(Fixup.getLoc(), "branch target not a multiple of four (" +
+ Twine(SVal) + ")");
+ return;
+ }
+
+ // Low two bits are not encoded.
+ Bits += 2;
+ if (!isIntN(Bits, Value)) {
+ Ctx.reportError(Fixup.getLoc(), "branch target out of range (" +
+ Twine(SVal) + " not between " +
+ Twine(minIntN(Bits)) + " and " +
+ Twine(maxIntN(Bits)) + ")");
+ }
+ };
+
switch (Kind) {
default:
llvm_unreachable("Unknown fixup kind!");
@@ -37,10 +56,12 @@ static uint64_t adjustFixupValue(unsigned Kind, uint64_t Value) {
return Value;
case PPC::fixup_ppc_brcond14:
case PPC::fixup_ppc_brcond14abs:
+ checkBrFixup(14);
return Value & 0xfffc;
case PPC::fixup_ppc_br24:
case PPC::fixup_ppc_br24abs:
case PPC::fixup_ppc_br24_notoc:
+ checkBrFixup(24);
return Value & 0x3fffffc;
case PPC::fixup_ppc_half16:
return Value & 0xffff;
@@ -202,7 +223,7 @@ void PPCAsmBackend::applyFixup(const MCFragment &F, const MCFixup &Fixup,
MCFixupKind Kind = Fixup.getKind();
if (mc::isRelocation(Kind))
return;
- Value = adjustFixupValue(Kind, Value);
+ Value = adjustFixupValue(getContext(), Fixup, Kind, Value);
if (!Value)
return; // Doesn't change encoding.
diff --git a/llvm/test/MC/PowerPC/fixup-out-of-range.s b/llvm/test/MC/PowerPC/fixup-out-of-range.s
new file mode 100644
index 0000000000000..e9c4f513cceda
--- /dev/null
+++ b/llvm/test/MC/PowerPC/fixup-out-of-range.s
@@ -0,0 +1,43 @@
+# RUN: not llvm-mc -triple powerpc64le-unknown-unknown -filetype=obj %s 2>&1 >/dev/null | FileCheck %s
+
+; CHECK: error: branch target out of range (32772 not between -32768 and 32767)
+brcond14_out_of_range_hi:
+ beq 0, brcond14_target
+ .space 0x8000
+
+brcond14_target:
+ blr
+
+; CHECK: error: branch target out of range (-32772 not between -32768 and 32767)
+brcond14_out_of_range_lo:
+ .space 0x8004
+ beq 0, brcond14_out_of_range_lo
+
+; CHECK: error: branch target not a multiple of four (5)
+brcond14_misaligned:
+ beq 0, brcond14_misaligned_target
+ .byte 0
+
+brcond14_misaligned_target:
+ blr
+
+; CHECK: error: branch target out of range (33554436 not between -33554432 and 33554431)
+br24_out_of_range_hi:
+ b br24_target
+ .space 0x2000000
+
+br24_target:
+ blr
+
+; CHECK: error: branch target out of range (-33554436 not between -33554432 and 33554431)
+br24_out_of_range_lo:
+ .space 0x2000004
+ b br24_out_of_range_lo
+
+; CHECK: error: branch target not a multiple of four (5)
+br24_misaligned:
+ b br24_misaligned_target
+ .byte 0
+
+br24_misaligned_target:
+ blr
|
Currently, out-of-range branch fixups will be silently miscompiled. GNU as will instead print a message like: > Error: operand out of range (0x0000000000010004 is not between 0xffffffffffff8000 and 0x0000000000007ffc) Check that the branch target fixups fit into the proper range and have low zero bits. I'm not sure if there is any way to test the absolute cases. It looks like something like `beqa 0, 0x10000` already gets rejected during parsing. (My actual interest here is not actually assembly usage, but LLVM producing invalid branches for reasons I've not tracked down yet. Currently this just silently truncates the branch target, and I'd rather make it produce an error instead.)
4aea677 to
5f4bf6a
Compare
Could you use an expression, like beqa 0,lab-. ? |
Currently, out-of-range branch fixups will be silently miscompiled. GNU as will instead print an "operand out of range" error instead.
Check that the branch target fixups fit into the proper range and have low zero bits. The implementation is inspired by SystemZ:
llvm-project/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZMCAsmBackend.cpp
Line 31 in 0ed8e66
I'm not sure if there is any way to test the absolute cases. It looks like something like
beqa 0, 0x10000already gets rejected during parsing.(My actual interest here is not actually assembly usage, but LLVM failing to use the correct branch kind for reasons I've not tracked down yet. Currently this just silently truncates the branch target instead of producing an error.)