aboutsummaryrefslogtreecommitdiff
path: root/llvm/lib/ExecutionEngine/Orc/TargetProcess/JITLoaderGDB.cpp
diff options
context:
space:
mode:
authorStefan Gränitz <stefan.graenitz@gmail.com>2021-03-02 12:37:48 +0100
committerStefan Gränitz <stefan.graenitz@gmail.com>2021-03-02 15:07:35 +0100
commitef2389235c5dec03be93f8c9585cd9416767ef4c (patch)
tree3e3439a9525825670011836aa935da1825a61f9c /llvm/lib/ExecutionEngine/Orc/TargetProcess/JITLoaderGDB.cpp
parent171849c2881bc11c82173588596cca8f539ef81d (diff)
downloadllvm-ef2389235c5dec03be93f8c9585cd9416767ef4c.zip
llvm-ef2389235c5dec03be93f8c9585cd9416767ef4c.tar.gz
llvm-ef2389235c5dec03be93f8c9585cd9416767ef4c.tar.bz2
[Orc] Add JITLink debug support plugin for ELF x86-64
Add a new ObjectLinkingLayer plugin `DebugObjectManagerPlugin` and infrastructure to handle creation of `DebugObject`s as well as their registration in OrcTargetProcess. The current implementation only covers ELF on x86-64, but the infrastructure is not limited to that. The journey starts with a new `LinkGraph` / `JITLinkContext` pair being created for a `MaterializationResponsibility` in ORC's `ObjectLinkingLayer`. It sends a `notifyMaterializing()` notification, which is forwarded to all registered plugins. The `DebugObjectManagerPlugin` aims to create a `DebugObject` form the provided target triple and object buffer. (Future implementations might create `DebugObject`s from a `LinkGraph` in other ways.) On success it will track it as the pending `DebugObject` for the `MaterializationResponsibility`. This patch only implements the `ELFDebugObject` for `x86-64` targets. It follows the RuntimeDyld approach for debug object setup: it captures a copy of the input object, parses all section headers and prepares to patch their load-address fields with their final addresses in target memory. It instructs the plugin to report the section load-addresses once they are available. The plugin overrides `modifyPassConfig()` and installs a JITLink post-allocation pass to capture them. Once JITLink emitted the finalized executable, the plugin emits and registers the `DebugObject`. For emission it requests a new `JITLinkMemoryManager::Allocation` with a single read-only segment, copies the object with patched section load-addresses over to working memory and triggers finalization to target memory. For registration, it notifies the `DebugObjectRegistrar` provided in the constructor and stores the previously pending`DebugObject` as registered for the corresponding MaterializationResponsibility. The `DebugObjectRegistrar` registers the `DebugObject` with the target process. `llvm-jitlink` uses the `TPCDebugObjectRegistrar`, which calls `llvm_orc_registerJITLoaderGDBWrapper()` in the target process via `TargetProcessControl` to emit a `jit_code_entry` compatible with the GDB JIT interface [1]. So far the implementation only supports registration and no removal. It appears to me that it wouldn't raise any new design questions, so I left this as an addition for the near future. [1] https://sourceware.org/gdb/current/onlinedocs/gdb/JIT-Interface.html Reviewed By: lhames Differential Revision: https://reviews.llvm.org/D97335
Diffstat (limited to 'llvm/lib/ExecutionEngine/Orc/TargetProcess/JITLoaderGDB.cpp')
-rw-r--r--llvm/lib/ExecutionEngine/Orc/TargetProcess/JITLoaderGDB.cpp110
1 files changed, 110 insertions, 0 deletions
diff --git a/llvm/lib/ExecutionEngine/Orc/TargetProcess/JITLoaderGDB.cpp b/llvm/lib/ExecutionEngine/Orc/TargetProcess/JITLoaderGDB.cpp
new file mode 100644
index 0000000..30f833e
--- /dev/null
+++ b/llvm/lib/ExecutionEngine/Orc/TargetProcess/JITLoaderGDB.cpp
@@ -0,0 +1,110 @@
+//===- JITLoaderGDB.h - Register objects via GDB JIT interface -*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ExecutionEngine/Orc/TargetProcess/JITLoaderGDB.h"
+
+#include "llvm/ExecutionEngine/JITSymbol.h"
+#include "llvm/Support/BinaryStreamReader.h"
+#include "llvm/Support/ManagedStatic.h"
+
+#include <cstdint>
+#include <mutex>
+#include <utility>
+
+#define DEBUG_TYPE "orc"
+
+// First version as landed in August 2009
+static constexpr uint32_t JitDescriptorVersion = 1;
+
+// Keep in sync with gdb/gdb/jit.h
+extern "C" {
+
+typedef enum {
+ JIT_NOACTION = 0,
+ JIT_REGISTER_FN,
+ JIT_UNREGISTER_FN
+} jit_actions_t;
+
+struct jit_code_entry {
+ struct jit_code_entry *next_entry;
+ struct jit_code_entry *prev_entry;
+ const char *symfile_addr;
+ uint64_t symfile_size;
+};
+
+struct jit_descriptor {
+ uint32_t version;
+ // This should be jit_actions_t, but we want to be specific about the
+ // bit-width.
+ uint32_t action_flag;
+ struct jit_code_entry *relevant_entry;
+ struct jit_code_entry *first_entry;
+};
+
+// We put information about the JITed function in this global, which the
+// debugger reads. Make sure to specify the version statically, because the
+// debugger checks the version before we can set it during runtime.
+struct jit_descriptor __jit_debug_descriptor = {JitDescriptorVersion, 0,
+ nullptr, nullptr};
+
+// Debuggers that implement the GDB JIT interface put a special breakpoint in
+// this function.
+LLVM_ATTRIBUTE_NOINLINE void __jit_debug_register_code() {
+ // The noinline and the asm prevent calls to this function from being
+ // optimized out.
+#if !defined(_MSC_VER)
+ asm volatile("" ::: "memory");
+#endif
+}
+}
+
+using namespace llvm;
+
+// Serialize rendezvous with the debugger as well as access to shared data.
+ManagedStatic<std::mutex> JITDebugLock;
+
+static std::pair<const char *, uint64_t> readDebugObjectInfo(uint8_t *ArgData,
+ uint64_t ArgSize) {
+ BinaryStreamReader ArgReader(ArrayRef<uint8_t>(ArgData, ArgSize),
+ support::endianness::big);
+ uint64_t Addr, Size;
+ cantFail(ArgReader.readInteger(Addr));
+ cantFail(ArgReader.readInteger(Size));
+
+ return std::make_pair(jitTargetAddressToPointer<const char *>(Addr), Size);
+}
+
+extern "C" orc::tpctypes::CWrapperFunctionResult
+llvm_orc_registerJITLoaderGDBWrapper(uint8_t *Data, uint64_t Size) {
+ if (Size != sizeof(uint64_t) + sizeof(uint64_t))
+ return orc::tpctypes::WrapperFunctionResult::from(
+ "Invalid arguments to llvm_orc_registerJITLoaderGDBWrapper")
+ .release();
+
+ jit_code_entry *E = new jit_code_entry;
+ std::tie(E->symfile_addr, E->symfile_size) = readDebugObjectInfo(Data, Size);
+ E->prev_entry = nullptr;
+
+ std::lock_guard<std::mutex> Lock(*JITDebugLock);
+
+ // Insert this entry at the head of the list.
+ jit_code_entry *NextEntry = __jit_debug_descriptor.first_entry;
+ E->next_entry = NextEntry;
+ if (NextEntry) {
+ NextEntry->prev_entry = E;
+ }
+
+ __jit_debug_descriptor.first_entry = E;
+ __jit_debug_descriptor.relevant_entry = E;
+
+ // Run into the rendezvous breakpoint.
+ __jit_debug_descriptor.action_flag = JIT_REGISTER_FN;
+ __jit_debug_register_code();
+
+ return orc::tpctypes::WrapperFunctionResult().release();
+}