Stefan Gränitz ef2389235c [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
2021-03-02 15:07:35 +01:00

131 lines
4.0 KiB
C++

//===- llvm-jitlink-executor.cpp - Out-of-proc executor for llvm-jitlink -===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// Simple out-of-process executor for llvm-jitlink.
//
//===----------------------------------------------------------------------===//
#include "llvm/ADT/StringRef.h"
#include "llvm/ExecutionEngine/Orc/Shared/FDRawByteChannel.h"
#include "llvm/ExecutionEngine/Orc/TargetProcess/JITLoaderGDB.h"
#include "llvm/ExecutionEngine/Orc/TargetProcess/OrcRPCTPCServer.h"
#include "llvm/ExecutionEngine/Orc/TargetProcess/RegisterEHFrames.h"
#include "llvm/Support/DynamicLibrary.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/raw_ostream.h"
#include <sstream>
#ifdef LLVM_ON_UNIX
#include <netinet/in.h>
#include <sys/socket.h>
#endif
using namespace llvm;
using namespace llvm::orc;
ExitOnError ExitOnErr;
LLVM_ATTRIBUTE_USED void linkComponents() {
errs() << (void *)&llvm_orc_registerEHFrameSectionWrapper
<< (void *)&llvm_orc_deregisterEHFrameSectionWrapper
<< (void *)&llvm_orc_registerJITLoaderGDBWrapper;
}
void printErrorAndExit(Twine ErrMsg) {
errs() << "error: " << ErrMsg.str() << "\n\n"
<< "Usage:\n"
<< " llvm-jitlink-executor filedescs=<infd>,<outfd> [args...]\n"
<< " llvm-jitlink-executor listen=<host>:<port> [args...]\n";
exit(1);
}
int openListener(std::string Host, int Port) {
#ifndef LLVM_ON_UNIX
// FIXME: Add TCP support for Windows.
printErrorAndExit("listen option not supported");
return 0;
#else
int SockFD = socket(PF_INET, SOCK_STREAM, 0);
struct sockaddr_in ServerAddr, ClientAddr;
socklen_t ClientAddrLen = sizeof(ClientAddr);
memset(&ServerAddr, 0, sizeof(ServerAddr));
ServerAddr.sin_family = PF_INET;
ServerAddr.sin_family = INADDR_ANY;
ServerAddr.sin_port = htons(Port);
{
// lose the "Address already in use" error message
int Yes = 1;
if (setsockopt(SockFD, SOL_SOCKET, SO_REUSEADDR, &Yes, sizeof(int)) == -1) {
errs() << "Error calling setsockopt.\n";
exit(1);
}
}
if (bind(SockFD, (struct sockaddr *)&ServerAddr, sizeof(ServerAddr)) < 0) {
errs() << "Error on binding.\n";
exit(1);
}
listen(SockFD, 1);
return accept(SockFD, (struct sockaddr *)&ClientAddr, &ClientAddrLen);
#endif
}
int main(int argc, char *argv[]) {
ExitOnErr.setBanner(std::string(argv[0]) + ": ");
int InFD = 0;
int OutFD = 0;
if (argc < 2)
printErrorAndExit("insufficient arguments");
else {
StringRef Arg1 = argv[1];
StringRef SpecifierType, Specifier;
std::tie(SpecifierType, Specifier) = Arg1.split('=');
if (SpecifierType == "filedescs") {
StringRef FD1Str, FD2Str;
std::tie(FD1Str, FD2Str) = Specifier.split(',');
if (FD1Str.getAsInteger(10, InFD))
printErrorAndExit(FD1Str + " is not a valid file descriptor");
if (FD2Str.getAsInteger(10, OutFD))
printErrorAndExit(FD2Str + " is not a valid file descriptor");
} else if (SpecifierType == "listen") {
StringRef Host, PortStr;
std::tie(Host, PortStr) = Specifier.split(':');
int Port = 0;
if (PortStr.getAsInteger(10, Port))
printErrorAndExit("port" + PortStr + " is not a valid integer");
InFD = OutFD = openListener(Host.str(), Port);
} else
printErrorAndExit("invalid specifier type \"" + SpecifierType + "\"");
}
ExitOnErr.setBanner(std::string(argv[0]) + ":");
using JITLinkExecutorEndpoint =
shared::MultiThreadedRPCEndpoint<shared::FDRawByteChannel>;
shared::registerStringError<shared::FDRawByteChannel>();
shared::FDRawByteChannel C(InFD, OutFD);
JITLinkExecutorEndpoint EP(C, true);
OrcRPCTPCServer<JITLinkExecutorEndpoint> Server(EP);
Server.setProgramName(std::string("llvm-jitlink-executor"));
ExitOnErr(Server.run());
return 0;
}