Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
openSUSE:Factory:RISCV
llvm17
llvm-riscv-uleb128.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File llvm-riscv-uleb128.patch of Package llvm17
From e65384525e8dcb1d14aa5539d77f23c59e173d5d Mon Sep 17 00:00:00 2001 From: Fangrui Song <i@maskray.me> Date: Thu, 9 Nov 2023 09:27:32 -0800 Subject: [PATCH] [RISCV] Support R_RISCV_SET_ULEB128/R_RISCV_SUB_ULEB128 for .uleb128 directives For a label difference like `.uleb128 A-B`, MC folds A-B even if A and B are separated by a RISC-V linker-relaxable instruction. This incorrect behavior is currently abused by DWARF v5 .debug_loclists/.debug_rnglists (DW_LLE_offset_pair/DW_RLE_offset_pair entry kinds) implemented in Clang/LLVM (see https://github.com/ClangBuiltLinux/linux/issues/1719 for an instance). https://github.com/riscv-non-isa/riscv-elf-psabi-doc/commit/96d6e190e9fc04a8517f9ff7fb9aed3e9876cbd6 defined R_RISCV_SET_ULEB128/R_RISCV_SUB_ULEB128. This patch generates such a pair of relocations to represent A-B that should not be folded. GNU assembler computes the directive size by ignoring shrinkable section content, therefore after linking the value of A-B cannot use more bytes than the reserved number (`final size of uleb128 value at offset ... exceeds available space`). We make the same assumption. ``` w1: call foo w2: .space 120 w3: .uleb128 w2-w1 # 1 byte, 0x08 .uleb128 w3-w1 # 2 bytes, 0x80 0x01 ``` We do not conservatively reserve 10 bytes (maximum size of an uleb128 for uint64_t) as that would pessimize DWARF v5 DW_LLE_offset_pair/DW_RLE_offset_pair, nullifying the benefits of introducing R_RISCV_SET_ULEB128/R_RISCV_SUB_ULEB128 relocations. The supported expressions are limited. For example, * non-subtraction `.uleb128 A` is not allowed * `.uleb128 A-B`: report an error unless A and B are both defined and in the same section The new cl::opt `-riscv-uleb128-reloc` can be used to suppress the relocations. Reviewed By: asb Differential Revision: https://reviews.llvm.org/D157657 --- .../llvm/BinaryFormat/ELFRelocs/RISCV.def | 2 + llvm/include/llvm/MC/MCAsmBackend.h | 8 ++ llvm/include/llvm/MC/MCFixup.h | 1 + llvm/include/llvm/MC/MCFragment.h | 11 ++- llvm/lib/MC/MCAsmBackend.cpp | 1 + llvm/lib/MC/MCAssembler.cpp | 33 ++++++-- .../RISCV/MCTargetDesc/RISCVAsmBackend.cpp | 26 ++++++ .../RISCV/MCTargetDesc/RISCVAsmBackend.h | 2 + llvm/test/MC/ELF/RISCV/gen-dwarf.s | 3 +- llvm/test/MC/RISCV/leb128.s | 81 +++++++++++++++++++ 10 files changed, 154 insertions(+), 14 deletions(-) create mode 100644 llvm/test/MC/RISCV/leb128.s diff --git a/llvm/include/llvm/BinaryFormat/ELFRelocs/RISCV.def b/llvm/include/llvm/BinaryFormat/ELFRelocs/RISCV.def index 9a126df01531..c7fd6490041c 100644 --- a/llvm/include/llvm/BinaryFormat/ELFRelocs/RISCV.def +++ b/llvm/include/llvm/BinaryFormat/ELFRelocs/RISCV.def @@ -55,3 +55,5 @@ ELF_RELOC(R_RISCV_SET32, 56) ELF_RELOC(R_RISCV_32_PCREL, 57) ELF_RELOC(R_RISCV_IRELATIVE, 58) ELF_RELOC(R_RISCV_PLT32, 59) +ELF_RELOC(R_RISCV_SET_ULEB128, 60) +ELF_RELOC(R_RISCV_SUB_ULEB128, 61) diff --git a/llvm/include/llvm/MC/MCAsmBackend.h b/llvm/include/llvm/MC/MCAsmBackend.h index 5e08fb41679b..1fd83f9a7c62 100644 --- a/llvm/include/llvm/MC/MCAsmBackend.h +++ b/llvm/include/llvm/MC/MCAsmBackend.h @@ -21,6 +21,7 @@ class MCAlignFragment; class MCDwarfCallFrameFragment; class MCDwarfLineAddrFragment; class MCFragment; +class MCLEBFragment; class MCRelaxableFragment; class MCSymbol; class MCAsmLayout; @@ -194,6 +195,13 @@ public: return false; } + // Defined by linker relaxation targets to possibly emit LEB128 relocations + // and set Value at the relocated location. + virtual bool relaxLEB128(MCLEBFragment &LF, MCAsmLayout &Layout, + int64_t &Value) const { + return false; + } + /// @} /// Returns the minimum size of a nop in bytes on this target. The assembler diff --git a/llvm/include/llvm/MC/MCFixup.h b/llvm/include/llvm/MC/MCFixup.h index 069ca058310f..7f48a90cb1ec 100644 --- a/llvm/include/llvm/MC/MCFixup.h +++ b/llvm/include/llvm/MC/MCFixup.h @@ -25,6 +25,7 @@ enum MCFixupKind { FK_Data_4, ///< A four-byte fixup. FK_Data_8, ///< A eight-byte fixup. FK_Data_6b, ///< A six-bits fixup. + FK_Data_leb128, ///< A leb128 fixup. FK_PCRel_1, ///< A one-byte pc relative fixup. FK_PCRel_2, ///< A two-byte pc relative fixup. FK_PCRel_4, ///< A four-byte pc relative fixup. diff --git a/llvm/include/llvm/MC/MCFragment.h b/llvm/include/llvm/MC/MCFragment.h index 7be4792a4521..efe44b0b6917 100644 --- a/llvm/include/llvm/MC/MCFragment.h +++ b/llvm/include/llvm/MC/MCFragment.h @@ -428,7 +428,7 @@ public: } }; -class MCLEBFragment : public MCFragment { +class MCLEBFragment final : public MCEncodedFragmentWithFixups<10, 1> { /// True if this is a sleb128, false if uleb128. bool IsSigned; @@ -438,18 +438,17 @@ class MCLEBFragment : public MCFragment { SmallString<8> Contents; public: - MCLEBFragment(const MCExpr &Value_, bool IsSigned_, MCSection *Sec = nullptr) - : MCFragment(FT_LEB, false, Sec), IsSigned(IsSigned_), Value(&Value_) { + MCLEBFragment(const MCExpr &Value, bool IsSigned, MCSection *Sec = nullptr) + : MCEncodedFragmentWithFixups<10, 1>(FT_LEB, false, Sec), + IsSigned(IsSigned), Value(&Value) { Contents.push_back(0); } const MCExpr &getValue() const { return *Value; } + void setValue(const MCExpr *Expr) { Value = Expr; } bool isSigned() const { return IsSigned; } - SmallString<8> &getContents() { return Contents; } - const SmallString<8> &getContents() const { return Contents; } - /// @} static bool classof(const MCFragment *F) { diff --git a/llvm/lib/MC/MCAsmBackend.cpp b/llvm/lib/MC/MCAsmBackend.cpp index 64bbc63719c7..2eef7d363fe7 100644 --- a/llvm/lib/MC/MCAsmBackend.cpp +++ b/llvm/lib/MC/MCAsmBackend.cpp @@ -89,6 +89,7 @@ const MCFixupKindInfo &MCAsmBackend::getFixupKindInfo(MCFixupKind Kind) const { {"FK_Data_4", 0, 32, 0}, {"FK_Data_8", 0, 64, 0}, {"FK_Data_6b", 0, 6, 0}, + {"FK_Data_leb128", 0, 0, 0}, {"FK_PCRel_1", 0, 8, MCFixupKindInfo::FKF_IsPCRel}, {"FK_PCRel_2", 0, 16, MCFixupKindInfo::FKF_IsPCRel}, {"FK_PCRel_4", 0, 32, MCFixupKindInfo::FKF_IsPCRel}, diff --git a/llvm/lib/MC/MCAssembler.cpp b/llvm/lib/MC/MCAssembler.cpp index 55ed1a285cd7..981e0af77083 100644 --- a/llvm/lib/MC/MCAssembler.cpp +++ b/llvm/lib/MC/MCAssembler.cpp @@ -918,6 +918,12 @@ void MCAssembler::layout(MCAsmLayout &Layout) { Contents = DF.getContents(); break; } + case MCFragment::FT_LEB: { + auto &LF = cast<MCLEBFragment>(Frag); + Fixups = LF.getFixups(); + Contents = LF.getContents(); + break; + } case MCFragment::FT_PseudoProbe: { MCPseudoProbeAddrFragment &PF = cast<MCPseudoProbeAddrFragment>(Frag); Fixups = PF.getFixups(); @@ -1006,12 +1012,25 @@ bool MCAssembler::relaxInstruction(MCAsmLayout &Layout, } bool MCAssembler::relaxLEB(MCAsmLayout &Layout, MCLEBFragment &LF) { - uint64_t OldSize = LF.getContents().size(); + const unsigned OldSize = static_cast<unsigned>(LF.getContents().size()); + unsigned PadTo = OldSize; int64_t Value; - bool Abs = LF.getValue().evaluateKnownAbsolute(Value, Layout); - if (!Abs) - report_fatal_error("sleb128 and uleb128 expressions must be absolute"); - SmallString<8> &Data = LF.getContents(); + SmallVectorImpl<char> &Data = LF.getContents(); + LF.getFixups().clear(); + // Use evaluateKnownAbsolute for Mach-O as a hack: .subsections_via_symbols + // requires that .uleb128 A-B is foldable where A and B reside in different + // fragments. This is used by __gcc_except_table. + bool Abs = getSubsectionsViaSymbols() + ? LF.getValue().evaluateKnownAbsolute(Value, Layout) + : LF.getValue().evaluateAsAbsolute(Value, Layout); + if (!Abs) { + if (!getBackend().relaxLEB128(LF, Layout, Value)) { + report_fatal_error("sleb128 and uleb128 expressions must be absolute"); + LF.setValue(MCConstantExpr::create(0, Context)); + } + uint8_t Tmp[10]; // maximum size: ceil(64/7) + PadTo = std::max(PadTo, encodeULEB128(uint64_t(Value), Tmp)); + } Data.clear(); raw_svector_ostream OSE(Data); // The compiler can generate EH table assembly that is impossible to assemble @@ -1019,9 +1038,9 @@ bool MCAssembler::relaxLEB(MCAsmLayout &Layout, MCLEBFragment &LF) { // to a later alignment fragment. To accommodate such tables, relaxation can // only increase an LEB fragment size here, not decrease it. See PR35809. if (LF.isSigned()) - encodeSLEB128(Value, OSE, OldSize); + encodeSLEB128(Value, OSE, PadTo); else - encodeULEB128(Value, OSE, OldSize); + encodeULEB128(Value, OSE, PadTo); return OldSize != LF.getContents().size(); } diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp index 1b890fbe041a..e1e5d3340918 100644 --- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp +++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp @@ -19,6 +19,7 @@ #include "llvm/MC/MCObjectWriter.h" #include "llvm/MC/MCSymbol.h" #include "llvm/MC/MCValue.h" +#include "llvm/Support/CommandLine.h" #include "llvm/Support/Endian.h" #include "llvm/Support/EndianStream.h" #include "llvm/Support/ErrorHandling.h" @@ -27,6 +28,13 @@ using namespace llvm; +// Temporary workaround for old linkers that do not support ULEB128 relocations, +// which are abused by DWARF v5 DW_LLE_offset_pair/DW_RLE_offset_pair +// implemented in Clang/LLVM. +static cl::opt<bool> ULEB128Reloc( + "riscv-uleb128-reloc", cl::init(true), cl::Hidden, + cl::desc("Emit R_RISCV_SET_ULEB128/E_RISCV_SUB_ULEB128 if appropriate")); + std::optional<MCFixupKind> RISCVAsmBackend::getFixupKind(StringRef Name) const { if (STI.getTargetTriple().isOSBinFormatELF()) { unsigned Type; @@ -126,6 +134,7 @@ bool RISCVAsmBackend::shouldForceRelocation(const MCAssembler &Asm, case FK_Data_2: case FK_Data_4: case FK_Data_8: + case FK_Data_leb128: if (Target.isAbsolute()) return false; break; @@ -330,6 +339,18 @@ bool RISCVAsmBackend::relaxDwarfCFA(MCDwarfCallFrameFragment &DF, return true; } +bool RISCVAsmBackend::relaxLEB128(MCLEBFragment &LF, MCAsmLayout &Layout, + int64_t &Value) const { + if (LF.isSigned()) + return false; + const MCExpr &Expr = LF.getValue(); + if (ULEB128Reloc) { + LF.getFixups().push_back( + MCFixup::create(0, &Expr, FK_Data_leb128, Expr.getLoc())); + } + return Expr.evaluateKnownAbsolute(Value, Layout); +} + // Given a compressed control flow instruction this function returns // the expanded instruction. unsigned RISCVAsmBackend::getRelaxedOpcode(unsigned Op) const { @@ -416,6 +437,7 @@ static uint64_t adjustFixupValue(const MCFixup &Fixup, uint64_t Value, case FK_Data_4: case FK_Data_8: case FK_Data_6b: + case FK_Data_leb128: return Value; case RISCV::fixup_riscv_set_6b: return Value & 0x03; @@ -596,6 +618,10 @@ bool RISCVAsmBackend::handleAddSubRelocations(const MCAsmLayout &Layout, TA = ELF::R_RISCV_ADD64; TB = ELF::R_RISCV_SUB64; break; + case llvm::FK_Data_leb128: + TA = ELF::R_RISCV_SET_ULEB128; + TB = ELF::R_RISCV_SUB_ULEB128; + break; default: llvm_unreachable("unsupported fixup size"); } diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.h b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.h index 0ea1f32e8296..baea6b922f01 100644 --- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.h +++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.h @@ -99,6 +99,8 @@ public: bool &WasRelaxed) const override; bool relaxDwarfCFA(MCDwarfCallFrameFragment &DF, MCAsmLayout &Layout, bool &WasRelaxed) const override; + bool relaxLEB128(MCLEBFragment &LF, MCAsmLayout &Layout, + int64_t &Value) const override; bool writeNopData(raw_ostream &OS, uint64_t Count, const MCSubtargetInfo *STI) const override; diff --git a/llvm/test/MC/ELF/RISCV/gen-dwarf.s b/llvm/test/MC/ELF/RISCV/gen-dwarf.s index 2235559d5f35..342ed1cc0e7e 100644 --- a/llvm/test/MC/ELF/RISCV/gen-dwarf.s +++ b/llvm/test/MC/ELF/RISCV/gen-dwarf.s @@ -48,9 +48,10 @@ # RELOC-NEXT: 0x34 R_RISCV_32_PCREL <null> 0x0 # RELOC-NEXT: } -## TODO A section needs two relocations. # RELOC: Section ([[#]]) .rela.debug_rnglists { # RELOC-NEXT: 0xD R_RISCV_64 .text.foo 0x0 +# RELOC-NEXT: 0x15 R_RISCV_SET_ULEB128 <null> 0x0 +# RELOC-NEXT: 0x15 R_RISCV_SUB_ULEB128 .text.foo 0x0 # RELOC-NEXT: 0x17 R_RISCV_64 .text.bar 0x0 # RELOC-NEXT: } diff --git a/llvm/test/MC/RISCV/leb128.s b/llvm/test/MC/RISCV/leb128.s new file mode 100644 index 000000000000..429eac697182 --- /dev/null +++ b/llvm/test/MC/RISCV/leb128.s @@ -0,0 +1,81 @@ +# RUN: llvm-mc -filetype=obj -triple=riscv32 -mattr=-relax %s -o %t +# RUN: llvm-readobj -r -x .alloc_w %t| FileCheck %s +# RUN: llvm-mc -filetype=obj -triple=riscv32 -mattr=+relax %s -o %t.relax +# RUN: llvm-readobj -r -x .alloc_w %t.relax | FileCheck %s --check-prefixes=CHECK,RELAX + +# RUN: llvm-mc -filetype=obj -triple=riscv64 -mattr=-relax %s -o %t +# RUN: llvm-readobj -r -x .alloc_w %t | FileCheck %s +# RUN: llvm-mc -filetype=obj -triple=riscv64 -mattr=+relax %s -o %t.relax +# RUN: llvm-readobj -r -x .alloc_w %t.relax | FileCheck %s --check-prefixes=CHECK,RELAX + +## Test temporary workaround for suppressting relocations for actually-non-foldable +## DWARF v5 DW_LLE_offset_pair/DW_RLE_offset_pair. +# RUN: llvm-mc -filetype=obj -triple=riscv64 -mattr=-relax -riscv-uleb128-reloc=0 %s -o %t0 +# RUN: llvm-readobj -r -x .alloc_w %t0 | FileCheck %s --check-prefix=CHECK0 +# RUN: llvm-mc -filetype=obj -triple=riscv64 -mattr=+relax -riscv-uleb128-reloc=0 %s -o %t0.relax +# RUN: llvm-readobj -r -x .alloc_w %t0.relax | FileCheck %s --check-prefixes=CHECK0,RELAX0 + +# RUN: not llvm-mc -filetype=obj -triple=riscv64 -mattr=-relax --defsym ERR=1 %s -o /dev/null 2>&1 | \ +# RUN: FileCheck %s --check-prefix=ERR +# RUN: not llvm-mc -filetype=obj -triple=riscv64 -mattr=+relax --defsym ERR=1 %s -o /dev/null 2>&1 | \ +# RUN: FileCheck %s --check-prefix=ERR + +# CHECK0: Relocations [ +# CHECK0-NEXT: .rela.alloc_w { +# CHECK0-NEXT: 0x2 R_RISCV_CALL_PLT foo 0x0 +# RELAX0-NEXT: 0x2 R_RISCV_RELAX - 0x0 +# CHECK0-NEXT: } +# CHECK0-NEXT: ] + +# CHECK: Relocations [ +# CHECK-NEXT: .rela.alloc_w { +# CHECK-NEXT: 0x0 R_RISCV_SET_ULEB128 w1 0x0 +# CHECK-NEXT: 0x0 R_RISCV_SUB_ULEB128 w 0x0 +# RELAX-NEXT: 0x1 R_RISCV_SET_ULEB128 w2 0x0 +# RELAX-NEXT: 0x1 R_RISCV_SUB_ULEB128 w1 0x0 +# CHECK-NEXT: 0x2 R_RISCV_CALL_PLT foo 0x0 +# RELAX-NEXT: 0x2 R_RISCV_RELAX - 0x0 +# RELAX-NEXT: 0xA R_RISCV_SET_ULEB128 w2 0x0 +# RELAX-NEXT: 0xA R_RISCV_SUB_ULEB128 w1 0x0 +# RELAX-NEXT: 0xB R_RISCV_SET_ULEB128 w2 0x78 +# RELAX-NEXT: 0xB R_RISCV_SUB_ULEB128 w1 0x0 +# RELAX-NEXT: 0xD R_RISCV_SET_ULEB128 w1 0x0 +# RELAX-NEXT: 0xD R_RISCV_SUB_ULEB128 w2 0x0 +# CHECK-NEXT: } +# CHECK-NEXT: ] + +## R_RISCV_SET_ULEB128 relocated locations contain values not accounting for linker relaxation. +# CHECK: Hex dump of section '.alloc_w': +# CHECK-NEXT: 0x00000000 02089700 0000e780 00000880 01f8ffff ................ +# CHECK-NEXT: 0x00000010 ffffffff ffff01 ....... + +.section .alloc_w,"ax",@progbits; w: +.uleb128 w1-w # w1 is later defined in the same section +.uleb128 w2-w1 # w1 and w2 are separated by a linker relaxable instruction +w1: + call foo +w2: +.uleb128 w2-w1 # 0x08 +.uleb128 w2-w1+120 # 0x0180 +.uleb128 -(w2-w1) # 0x01fffffffffffffffff8 + +.ifdef ERR +# ERR: :[[#@LINE+1]]:16: error: .uleb128 expression is not absolute +.uleb128 extern-w # extern is undefined +# ERR: :[[#@LINE+1]]:11: error: .uleb128 expression is not absolute +.uleb128 w-extern +# ERR: :[[#@LINE+1]]:11: error: .uleb128 expression is not absolute +.uleb128 x-w # x is later defined in another section + +.section .alloc_x,"aw",@progbits; x: +# ERR: :[[#@LINE+1]]:11: error: .uleb128 expression is not absolute +.uleb128 y-x +.section .alloc_y,"aw",@progbits; y: +# ERR: :[[#@LINE+1]]:11: error: .uleb128 expression is not absolute +.uleb128 x-y + +# ERR: :[[#@LINE+1]]:10: error: .uleb128 expression is not absolute +.uleb128 extern +# ERR: :[[#@LINE+1]]:10: error: .uleb128 expression is not absolute +.uleb128 y +.endif -- 2.42.1
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor