[llvm] [JITLink][RISCV] Implement .eh_frame handling (PR #66067)

Job Noorman via llvm-commits llvm-commits at lists.llvm.org
Wed Sep 13 01:26:14 PDT 2023


https://github.com/mtvec updated https://github.com/llvm/llvm-project/pull/66067:

>From 8fec2d718bc212ec2f6d05e16dfa5815c440c274 Mon Sep 17 00:00:00 2001
From: Job Noorman <jnoorman at igalia.com>
Date: Tue, 12 Sep 2023 12:01:54 +0200
Subject: [PATCH] [JITLink][RISCV] Implement .eh_frame handling

This patch enables .eh_frame handling on RISC-V by using the common
`DWARFRecordSectionSplitter`, `EHFrameEdgeFixer`, and
`EHFrameNullTerminator` passes.

This mostly works out of the box but a minor change was needed for
`EHFrameEdgeFixer`: on RISC-V, ADD/SUB relocations are used to calculate
the length of a sequence of instructions when relaxation is enabled.
Since both relocations are at the same offset, this caused an error to
be raised by `EHFrameEdgeFixer`. I have solved this issue by simply
ignoring relocations at the same offset on RISC-V. I believe this is
fine since the DWARF fields where they are used (PC-range and
`DW_CFA_advance_loc`) don't need any special handling.

Besides this, two new edge kinds needed to be implemented for RISC-V:
`Delta64` and `NegDelta32`
---
 .../llvm/ExecutionEngine/JITLink/riscv.h      |  21 ++++
 .../JITLink/EHFrameSupport.cpp                |   9 +-
 .../lib/ExecutionEngine/JITLink/ELF_riscv.cpp |  21 ++++
 llvm/lib/ExecutionEngine/JITLink/riscv.cpp    |   4 +
 .../JITLink/RISCV/ELF_ehframe.s               |  77 +++++++++++++
 .../JITLink/RISCV/ELF_ehframe.test            | 104 ++++++++++++++++++
 6 files changed, 235 insertions(+), 1 deletion(-)
 create mode 100644 llvm/test/ExecutionEngine/JITLink/RISCV/ELF_ehframe.s
 create mode 100644 llvm/test/ExecutionEngine/JITLink/RISCV/ELF_ehframe.test

diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/riscv.h b/llvm/include/llvm/ExecutionEngine/JITLink/riscv.h
index cb66289180880ce..b805dd1ed5bdc85 100644
--- a/llvm/include/llvm/ExecutionEngine/JITLink/riscv.h
+++ b/llvm/include/llvm/ExecutionEngine/JITLink/riscv.h
@@ -203,6 +203,27 @@ enum EdgeKind_riscv : Edge::Kind {
   ///   Fixup <- (Target - Fixup + Addend)
   R_RISCV_32_PCREL,
 
+  /// A 64-bit delta.
+  ///
+  /// Delta from the fixup to the target.
+  ///
+  /// Fixup expression:
+  ///   Fixup <- Target - Fixup + Addend : int64
+  ///
+  Delta64,
+
+  /// A 32-bit negative delta.
+  ///
+  /// Delta from the target back to the fixup.
+  ///
+  /// Fixup expression:
+  ///   Fixup <- Fixup - Target + Addend : int32
+  ///
+  /// Errors:
+  ///   - The result of the fixup expression must fit into an int32, otherwise
+  ///     an out-of-range error will be returned.
+  NegDelta32,
+
   /// An auipc/jalr pair eligible for linker relaxation.
   ///
   /// Linker relaxation will replace this with R_RISCV_RVC_JUMP or R_RISCV_JAL
diff --git a/llvm/lib/ExecutionEngine/JITLink/EHFrameSupport.cpp b/llvm/lib/ExecutionEngine/JITLink/EHFrameSupport.cpp
index 86249591a9be053..ea56a6d533f7a7d 100644
--- a/llvm/lib/ExecutionEngine/JITLink/EHFrameSupport.cpp
+++ b/llvm/lib/ExecutionEngine/JITLink/EHFrameSupport.cpp
@@ -129,11 +129,18 @@ Error EHFrameEdgeFixer::processBlock(ParseContext &PC, Block &B) {
   BlockEdgeMap BlockEdges;
   for (auto &E : B.edges())
     if (E.isRelocation()) {
-      if (BlockEdges.count(E.getOffset()))
+      if (BlockEdges.count(E.getOffset())) {
+        // RISC-V may use ADD/SUB relocation pairs for PC-range and
+        // DW_CFA_advance_loc. We don't need to process these fields here so
+        // just ignore this on RISC-V.
+        if (PC.G.getTargetTriple().isRISCV())
+          continue;
+
         return make_error<JITLinkError>(
             "Multiple relocations at offset " +
             formatv("{0:x16}", E.getOffset()) + " in " + EHFrameSectionName +
             " block at address " + formatv("{0:x16}", B.getAddress()));
+      }
 
       BlockEdges[E.getOffset()] = EdgeTarget(E);
     }
diff --git a/llvm/lib/ExecutionEngine/JITLink/ELF_riscv.cpp b/llvm/lib/ExecutionEngine/JITLink/ELF_riscv.cpp
index 410dd7fedad1a40..664cb8a56277265 100644
--- a/llvm/lib/ExecutionEngine/JITLink/ELF_riscv.cpp
+++ b/llvm/lib/ExecutionEngine/JITLink/ELF_riscv.cpp
@@ -11,10 +11,12 @@
 //===----------------------------------------------------------------------===//
 
 #include "llvm/ExecutionEngine/JITLink/ELF_riscv.h"
+#include "EHFrameSupportImpl.h"
 #include "ELFLinkGraphBuilder.h"
 #include "JITLinkGeneric.h"
 #include "PerGraphGOTAndPLTStubsBuilder.h"
 #include "llvm/BinaryFormat/ELF.h"
+#include "llvm/ExecutionEngine/JITLink/DWARFRecordSectionSplitter.h"
 #include "llvm/ExecutionEngine/JITLink/JITLink.h"
 #include "llvm/ExecutionEngine/JITLink/riscv.h"
 #include "llvm/Object/ELF.h"
@@ -453,6 +455,18 @@ class ELFJITLinker_riscv : public JITLinker<ELFJITLinker_riscv> {
       *(little32_t *)FixupPtr = Word32;
       break;
     }
+    case Delta64: {
+      int64_t Value = E.getTarget().getAddress() + E.getAddend() - FixupAddress;
+      *(little64_t *)FixupPtr = Value;
+      break;
+    }
+    case NegDelta32: {
+      int64_t Value = FixupAddress - E.getTarget().getAddress() + E.getAddend();
+      if (LLVM_UNLIKELY(!isInRangeForImm(Value, 32)))
+        return makeTargetOutOfRangeError(G, B, E);
+      *(little32_t *)FixupPtr = Value;
+      break;
+    }
     case AlignRelaxable:
       // Ignore when the relaxation pass did not run
       break;
@@ -959,6 +973,13 @@ void link_ELF_riscv(std::unique_ptr<LinkGraph> G,
   PassConfiguration Config;
   const Triple &TT = G->getTargetTriple();
   if (Ctx->shouldAddDefaultTargetPasses(TT)) {
+    // Add eh-frame passses.
+    Config.PrePrunePasses.push_back(DWARFRecordSectionSplitter(".eh_frame"));
+    Config.PrePrunePasses.push_back(
+        EHFrameEdgeFixer(".eh_frame", TT.isRISCV32() ? 4 : 8, riscv::R_RISCV_32,
+                         riscv::R_RISCV_64, riscv::R_RISCV_32_PCREL,
+                         riscv::Delta64, riscv::NegDelta32));
+    Config.PrePrunePasses.push_back(EHFrameNullTerminator(".eh_frame"));
     if (auto MarkLive = Ctx->getMarkLivePass(TT))
       Config.PrePrunePasses.push_back(std::move(MarkLive));
     else
diff --git a/llvm/lib/ExecutionEngine/JITLink/riscv.cpp b/llvm/lib/ExecutionEngine/JITLink/riscv.cpp
index a78843b1614795e..207f73cb72f9f5d 100644
--- a/llvm/lib/ExecutionEngine/JITLink/riscv.cpp
+++ b/llvm/lib/ExecutionEngine/JITLink/riscv.cpp
@@ -78,6 +78,10 @@ const char *getEdgeKindName(Edge::Kind K) {
     return "R_RISCV_SET32";
   case R_RISCV_32_PCREL:
     return "R_RISCV_32_PCREL";
+  case Delta64:
+    return "Delta64";
+  case NegDelta32:
+    return "NegDelta32";
   case CallRelaxable:
     return "CallRelaxable";
   case AlignRelaxable:
diff --git a/llvm/test/ExecutionEngine/JITLink/RISCV/ELF_ehframe.s b/llvm/test/ExecutionEngine/JITLink/RISCV/ELF_ehframe.s
new file mode 100644
index 000000000000000..dddd0cdb9968f42
--- /dev/null
+++ b/llvm/test/ExecutionEngine/JITLink/RISCV/ELF_ehframe.s
@@ -0,0 +1,77 @@
+# REQUIRES: asserts
+
+# RUN: llvm-mc -triple=riscv32-linux-gnu -mattr=+relax -filetype=obj -o %t.32.o %s
+# RUN: llvm-jitlink -noexec -phony-externals -debug-only=jitlink %t.32.o 2>&1 | \
+# RUN:   FileCheck %s
+
+# RUN: llvm-mc -triple=riscv64-linux-gnu -mattr=+relax -filetype=obj -o %t.64.o %s
+# RUN: llvm-jitlink -noexec -phony-externals -debug-only=jitlink %t.64.o 2>&1 | \
+# RUN:   FileCheck %s
+
+# Check that splitting of eh-frame sections works.
+#
+# CHECK: DWARFRecordSectionSplitter: Processing .eh_frame...
+# CHECK:  Processing block at
+# CHECK:    Processing CFI record at
+# CHECK:      Extracted {{.*}} section = .eh_frame
+# CHECK:    Processing CFI record at
+# CHECK:      Extracted {{.*}} section = .eh_frame
+# CHECK: EHFrameEdgeFixer: Processing .eh_frame in "{{.*}}"...
+# CHECK:   Processing block at
+# CHECK:     Processing CFI record at
+# CHECK:       Record is CIE
+# CHECK:   Processing block at
+# CHECK:     Processing CFI record at
+# CHECK:       Record is FDE
+# CHECK:         Adding edge at {{.*}} to CIE at: {{.*}}
+# CHECK:         Existing edge at {{.*}} to PC begin at {{.*}}
+# CHECK:         Adding keep-alive edge from target at {{.*}} to FDE at {{.*}}
+# CHECK:   Processing block at
+# CHECK:     Processing CFI record at
+# CHECK:       Record is FDE
+# CHECK:         Adding edge at {{.*}} to CIE at: {{.*}}
+# CHECK:         Existing edge at {{.*}} to PC begin at {{.*}}
+# CHECK:         Adding keep-alive edge from target at {{.*}} to FDE at {{.*}}
+
+## This is "int main { throw 1; }" compiled for riscv32. We use the 32-bit
+## version because it is also legal for riscv64.
+	.text
+	.globl	main
+	.p2align	1
+	.type	main, at function
+main:
+	.cfi_startproc
+	addi	sp, sp, -16
+	.cfi_def_cfa_offset 16
+	sw	ra, 12(sp)
+	.cfi_offset ra, -4
+	li	a0, 4
+	call	__cxa_allocate_exception
+	li	a1, 1
+	sw	a1, 0(a0)
+	lga a1, _ZTIi
+	li	a2, 0
+	call	__cxa_throw
+.Lfunc_end0:
+	.size	main, .Lfunc_end0-main
+	.cfi_endproc
+
+	.globl	dup
+	.p2align	1
+	.type	dup, at function
+dup:
+	.cfi_startproc
+	addi	sp, sp, -16
+	.cfi_def_cfa_offset 16
+	sw	ra, 12(sp)
+	.cfi_offset ra, -4
+	li	a0, 4
+	call	__cxa_allocate_exception
+	li	a1, 1
+	sw	a1, 0(a0)
+	lga a1, _ZTIi
+	li	a2, 0
+	call	__cxa_throw
+.Lfunc_end1:
+	.size	dup, .Lfunc_end1-dup
+	.cfi_endproc
diff --git a/llvm/test/ExecutionEngine/JITLink/RISCV/ELF_ehframe.test b/llvm/test/ExecutionEngine/JITLink/RISCV/ELF_ehframe.test
new file mode 100644
index 000000000000000..95666d2e232b70d
--- /dev/null
+++ b/llvm/test/ExecutionEngine/JITLink/RISCV/ELF_ehframe.test
@@ -0,0 +1,104 @@
+# RUN: yaml2obj -DELFCLASS=ELFCLASS32 -o %t.32.o %s
+# RUN: llvm-jitlink -noexec -check %s %t.32.o
+# RUN: yaml2obj -DELFCLASS=ELFCLASS64 -o %t.64.o %s
+# RUN: llvm-jitlink -noexec -check %s %t.64.o
+
+### Compiled from the following code with -mattr=+relax to force relocations for
+### address_range and DW_CFA_advance_loc (both needed for .balign).
+## 	.text
+## 	.globl	main
+## 	.p2align	1
+## 	.type	main, at function
+## main:
+## 	.cfi_startproc
+##     .balign 8
+## 	addi	sp, sp, -16
+## cfa_advance_loc:
+## 	.cfi_def_cfa_offset 16
+##     nop
+## main_end:
+## 	.size	main, main_end-main
+## 	.cfi_endproc
+
+--- !ELF
+FileHeader:
+  Class:           [[ELFCLASS]]
+  Data:            ELFDATA2LSB
+  Type:            ET_REL
+  Machine:         EM_RISCV
+  SectionHeaderStringTable: .strtab
+Sections:
+  - Name:            .text
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC, SHF_EXECINSTR ]
+    AddressAlign:    0x8
+    Content:         13000000130101FF13000000
+  - Name:            .eh_frame
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC ]
+    AddressAlign:    0x8
+    Content:         1000000000000000017A5200017801011B0C02001000000018000000000000000000000000400E10
+  - Name:            .rela.text
+    Type:            SHT_RELA
+    Flags:           [ SHF_INFO_LINK ]
+    Link:            .symtab
+    AddressAlign:    0x8
+    Info:            .text
+    Relocations:
+      - Type:            R_RISCV_ALIGN
+        Addend:          4
+  - Name:            .rela.eh_frame
+    Type:            SHT_RELA
+    Flags:           [ SHF_INFO_LINK ]
+    Link:            .symtab
+    AddressAlign:    0x8
+    Info:            .eh_frame
+    Relocations:
+      - Offset:          0x1C
+        Symbol:          main
+        Type:            R_RISCV_32_PCREL
+      - Offset:          0x20
+        Symbol:          main_end
+        Type:            R_RISCV_ADD32
+      - Offset:          0x20
+        Symbol:          main
+        Type:            R_RISCV_SUB32
+      - Offset:          0x25
+        Symbol:          cfa_advance_loc
+        Type:            R_RISCV_SET6
+      - Offset:          0x25
+        Symbol:          main
+        Type:            R_RISCV_SUB6
+  - Type:            SectionHeaderTable
+    Sections:
+      - Name:            .strtab
+      - Name:            .text
+      - Name:            .rela.text
+      - Name:            .eh_frame
+      - Name:            .rela.eh_frame
+      - Name:            .symtab
+Symbols:
+  - Name:            cfa_advance_loc
+    Section:         .text
+    Value:           0x8
+  - Name:            main_end
+    Section:         .text
+    Value:           0xC
+  - Name:            main
+    Type:            STT_FUNC
+    Section:         .text
+    Binding:         STB_GLOBAL
+    Size:            0xC
+  - Name:            eh_frame
+    Type:            STT_SECTION
+    Binding:         STB_GLOBAL
+    Section:         .eh_frame
+    Size:            0x28
+...
+
+## CIE_pointer
+# jitlink-check: *{4}(eh_frame + 0x1c) = main - (eh_frame + 0x1c)
+## address_range
+# jitlink-check: *{4}(eh_frame + 0x20) = main_end - main
+## DW_CFA_advance_loc
+# jitlink-check: (*{1}(eh_frame + 0x25)) & 0x3f = cfa_advance_loc - main



More information about the llvm-commits mailing list