aboutsummaryrefslogtreecommitdiff
path: root/contrib/llvm-project/lldb/source/Plugins/Platform/QemuUser/PlatformQemuUser.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/llvm-project/lldb/source/Plugins/Platform/QemuUser/PlatformQemuUser.cpp')
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Platform/QemuUser/PlatformQemuUser.cpp251
1 files changed, 251 insertions, 0 deletions
diff --git a/contrib/llvm-project/lldb/source/Plugins/Platform/QemuUser/PlatformQemuUser.cpp b/contrib/llvm-project/lldb/source/Plugins/Platform/QemuUser/PlatformQemuUser.cpp
new file mode 100644
index 000000000000..460f5560573d
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Platform/QemuUser/PlatformQemuUser.cpp
@@ -0,0 +1,251 @@
+//===-- PlatformQemuUser.cpp ----------------------------------------------===//
+//
+// 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 "Plugins/Platform/QemuUser/PlatformQemuUser.h"
+#include "Plugins/Process/gdb-remote/ProcessGDBRemote.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Host/ProcessLaunchInfo.h"
+#include "lldb/Interpreter/OptionValueProperties.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/LLDBLog.h"
+#include "lldb/Utility/Listener.h"
+#include "lldb/Utility/Log.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+LLDB_PLUGIN_DEFINE(PlatformQemuUser)
+
+namespace {
+#define LLDB_PROPERTIES_platformqemuuser
+#include "PlatformQemuUserProperties.inc"
+
+enum {
+#define LLDB_PROPERTIES_platformqemuuser
+#include "PlatformQemuUserPropertiesEnum.inc"
+};
+
+class PluginProperties : public Properties {
+public:
+ PluginProperties() {
+ m_collection_sp = std::make_shared<OptionValueProperties>(
+ PlatformQemuUser::GetPluginNameStatic());
+ m_collection_sp->Initialize(g_platformqemuuser_properties);
+ }
+
+ llvm::StringRef GetArchitecture() {
+ return GetPropertyAtIndexAs<llvm::StringRef>(ePropertyArchitecture, "");
+ }
+
+ FileSpec GetEmulatorPath() {
+ return GetPropertyAtIndexAs<FileSpec>(ePropertyEmulatorPath, {});
+ }
+
+ Args GetEmulatorArgs() {
+ Args result;
+ m_collection_sp->GetPropertyAtIndexAsArgs(ePropertyEmulatorArgs, result);
+ return result;
+ }
+
+ Environment GetEmulatorEnvVars() {
+ Args args;
+ m_collection_sp->GetPropertyAtIndexAsArgs(ePropertyEmulatorEnvVars, args);
+ return Environment(args);
+ }
+
+ Environment GetTargetEnvVars() {
+ Args args;
+ m_collection_sp->GetPropertyAtIndexAsArgs(ePropertyTargetEnvVars, args);
+ return Environment(args);
+ }
+};
+
+} // namespace
+
+static PluginProperties &GetGlobalProperties() {
+ static PluginProperties g_settings;
+ return g_settings;
+}
+
+llvm::StringRef PlatformQemuUser::GetPluginDescriptionStatic() {
+ return "Platform for debugging binaries under user mode qemu";
+}
+
+void PlatformQemuUser::Initialize() {
+ PluginManager::RegisterPlugin(
+ GetPluginNameStatic(), GetPluginDescriptionStatic(),
+ PlatformQemuUser::CreateInstance, PlatformQemuUser::DebuggerInitialize);
+}
+
+void PlatformQemuUser::Terminate() {
+ PluginManager::UnregisterPlugin(PlatformQemuUser::CreateInstance);
+}
+
+void PlatformQemuUser::DebuggerInitialize(Debugger &debugger) {
+ if (!PluginManager::GetSettingForPlatformPlugin(debugger,
+ GetPluginNameStatic())) {
+ PluginManager::CreateSettingForPlatformPlugin(
+ debugger, GetGlobalProperties().GetValueProperties(),
+ "Properties for the qemu-user platform plugin.",
+ /*is_global_property=*/true);
+ }
+}
+
+PlatformSP PlatformQemuUser::CreateInstance(bool force, const ArchSpec *arch) {
+ if (force)
+ return PlatformSP(new PlatformQemuUser());
+ return nullptr;
+}
+
+std::vector<ArchSpec>
+PlatformQemuUser::GetSupportedArchitectures(const ArchSpec &process_host_arch) {
+ llvm::Triple triple = HostInfo::GetArchitecture().GetTriple();
+ triple.setEnvironment(llvm::Triple::UnknownEnvironment);
+ triple.setArchName(GetGlobalProperties().GetArchitecture());
+ if (triple.getArch() != llvm::Triple::UnknownArch)
+ return {ArchSpec(triple)};
+ return {};
+}
+
+static auto get_arg_range(const Args &args) {
+ return llvm::make_range(args.GetArgumentArrayRef().begin(),
+ args.GetArgumentArrayRef().end());
+}
+
+// Returns the emulator environment which result in the desired environment
+// being presented to the emulated process. We want to be careful about
+// preserving the host environment, as it may contain entries (LD_LIBRARY_PATH,
+// for example) needed for the operation of the emulator itself.
+static Environment ComputeLaunchEnvironment(Environment target,
+ Environment host) {
+ std::vector<std::string> set_env;
+ for (const auto &KV : target) {
+ // If the host value differs from the target (or is unset), then set it
+ // through QEMU_SET_ENV. Identical entries will be forwarded automatically.
+ auto host_it = host.find(KV.first());
+ if (host_it == host.end() || host_it->second != KV.second)
+ set_env.push_back(Environment::compose(KV));
+ }
+ llvm::sort(set_env);
+
+ std::vector<llvm::StringRef> unset_env;
+ for (const auto &KV : host) {
+ // If the target is missing some host entries, then unset them through
+ // QEMU_UNSET_ENV.
+ if (target.count(KV.first()) == 0)
+ unset_env.push_back(KV.first());
+ }
+ llvm::sort(unset_env);
+
+ // The actual QEMU_(UN)SET_ENV variables should not be forwarded to the
+ // target.
+ if (!set_env.empty()) {
+ host["QEMU_SET_ENV"] = llvm::join(set_env, ",");
+ unset_env.push_back("QEMU_SET_ENV");
+ }
+ if (!unset_env.empty()) {
+ unset_env.push_back("QEMU_UNSET_ENV");
+ host["QEMU_UNSET_ENV"] = llvm::join(unset_env, ",");
+ }
+ return host;
+}
+
+lldb::ProcessSP PlatformQemuUser::DebugProcess(ProcessLaunchInfo &launch_info,
+ Debugger &debugger,
+ Target &target, Status &error) {
+ Log *log = GetLog(LLDBLog::Platform);
+
+ // If platform.plugin.qemu-user.emulator-path is set, use it.
+ FileSpec qemu = GetGlobalProperties().GetEmulatorPath();
+ // If platform.plugin.qemu-user.emulator-path is not set, build the
+ // executable name from platform.plugin.qemu-user.architecture.
+ if (!qemu) {
+ llvm::StringRef arch = GetGlobalProperties().GetArchitecture();
+ // If platform.plugin.qemu-user.architecture is not set, build the
+ // executable name from the target Triple's ArchName
+ if (arch.empty())
+ arch = target.GetArchitecture().GetTriple().getArchName();
+ qemu.SetPath(("qemu-" + arch).str());
+ }
+ FileSystem::Instance().ResolveExecutableLocation(qemu);
+
+ llvm::SmallString<0> socket_model, socket_path;
+ HostInfo::GetProcessTempDir().GetPath(socket_model);
+ llvm::sys::path::append(socket_model, "qemu-%%%%%%%%.socket");
+ do {
+ llvm::sys::fs::createUniquePath(socket_model, socket_path, false);
+ } while (FileSystem::Instance().Exists(socket_path));
+
+ Args args({qemu.GetPath(), "-g", socket_path});
+ if (!launch_info.GetArg0().empty()) {
+ args.AppendArgument("-0");
+ args.AppendArgument(launch_info.GetArg0());
+ }
+ args.AppendArguments(GetGlobalProperties().GetEmulatorArgs());
+ args.AppendArgument("--");
+ args.AppendArgument(launch_info.GetExecutableFile().GetPath());
+ for (size_t i = 1; i < launch_info.GetArguments().size(); ++i)
+ args.AppendArgument(launch_info.GetArguments()[i].ref());
+
+ LLDB_LOG(log, "{0} -> {1}", get_arg_range(launch_info.GetArguments()),
+ get_arg_range(args));
+
+ launch_info.SetArguments(args, true);
+
+ Environment emulator_env = Host::GetEnvironment();
+ if (const std::string &sysroot = GetSDKRootDirectory(); !sysroot.empty())
+ emulator_env["QEMU_LD_PREFIX"] = sysroot;
+ for (const auto &KV : GetGlobalProperties().GetEmulatorEnvVars())
+ emulator_env[KV.first()] = KV.second;
+ launch_info.GetEnvironment() = ComputeLaunchEnvironment(
+ std::move(launch_info.GetEnvironment()), std::move(emulator_env));
+
+ launch_info.SetLaunchInSeparateProcessGroup(true);
+ launch_info.GetFlags().Clear(eLaunchFlagDebug);
+ launch_info.SetMonitorProcessCallback(ProcessLaunchInfo::NoOpMonitorCallback);
+
+ // This is automatically done for host platform in
+ // Target::FinalizeFileActions, but we're not a host platform.
+ llvm::Error Err = launch_info.SetUpPtyRedirection();
+ LLDB_LOG_ERROR(log, std::move(Err), "SetUpPtyRedirection failed: {0}");
+
+ error = Host::LaunchProcess(launch_info);
+ if (error.Fail())
+ return nullptr;
+
+ ProcessSP process_sp = target.CreateProcess(
+ launch_info.GetListener(),
+ process_gdb_remote::ProcessGDBRemote::GetPluginNameStatic(), nullptr,
+ true);
+ if (!process_sp) {
+ error.SetErrorString("Failed to create GDB process");
+ return nullptr;
+ }
+
+ process_sp->HijackProcessEvents(launch_info.GetHijackListener());
+
+ error = process_sp->ConnectRemote(("unix-connect://" + socket_path).str());
+ if (error.Fail())
+ return nullptr;
+
+ if (launch_info.GetPTY().GetPrimaryFileDescriptor() !=
+ PseudoTerminal::invalid_fd)
+ process_sp->SetSTDIOFileDescriptor(
+ launch_info.GetPTY().ReleasePrimaryFileDescriptor());
+
+ return process_sp;
+}
+
+Environment PlatformQemuUser::GetEnvironment() {
+ Environment env = Host::GetEnvironment();
+ for (const auto &KV : GetGlobalProperties().GetTargetEnvVars())
+ env[KV.first()] = KV.second;
+ return env;
+}