252 lines · cpp
1//===-- PlatformQemuUser.cpp ----------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "Plugins/Platform/QemuUser/PlatformQemuUser.h"10#include "Plugins/Process/gdb-remote/ProcessGDBRemote.h"11#include "lldb/Core/PluginManager.h"12#include "lldb/Host/FileSystem.h"13#include "lldb/Host/ProcessLaunchInfo.h"14#include "lldb/Interpreter/OptionValueProperties.h"15#include "lldb/Target/Process.h"16#include "lldb/Target/Target.h"17#include "lldb/Utility/LLDBLog.h"18#include "lldb/Utility/Listener.h"19#include "lldb/Utility/Log.h"20 21using namespace lldb;22using namespace lldb_private;23 24LLDB_PLUGIN_DEFINE(PlatformQemuUser)25 26namespace {27#define LLDB_PROPERTIES_platformqemuuser28#include "PlatformQemuUserProperties.inc"29 30enum {31#define LLDB_PROPERTIES_platformqemuuser32#include "PlatformQemuUserPropertiesEnum.inc"33};34 35class PluginProperties : public Properties {36public:37 PluginProperties() {38 m_collection_sp = std::make_shared<OptionValueProperties>(39 PlatformQemuUser::GetPluginNameStatic());40 m_collection_sp->Initialize(g_platformqemuuser_properties);41 }42 43 llvm::StringRef GetArchitecture() {44 return GetPropertyAtIndexAs<llvm::StringRef>(ePropertyArchitecture, "");45 }46 47 FileSpec GetEmulatorPath() {48 return GetPropertyAtIndexAs<FileSpec>(ePropertyEmulatorPath, {});49 }50 51 Args GetEmulatorArgs() {52 Args result;53 m_collection_sp->GetPropertyAtIndexAsArgs(ePropertyEmulatorArgs, result);54 return result;55 }56 57 Environment GetEmulatorEnvVars() {58 Args args;59 m_collection_sp->GetPropertyAtIndexAsArgs(ePropertyEmulatorEnvVars, args);60 return Environment(args);61 }62 63 Environment GetTargetEnvVars() {64 Args args;65 m_collection_sp->GetPropertyAtIndexAsArgs(ePropertyTargetEnvVars, args);66 return Environment(args);67 }68};69 70} // namespace71 72static PluginProperties &GetGlobalProperties() {73 static PluginProperties g_settings;74 return g_settings;75}76 77llvm::StringRef PlatformQemuUser::GetPluginDescriptionStatic() {78 return "Platform for debugging binaries under user mode qemu";79}80 81void PlatformQemuUser::Initialize() {82 PluginManager::RegisterPlugin(83 GetPluginNameStatic(), GetPluginDescriptionStatic(),84 PlatformQemuUser::CreateInstance, PlatformQemuUser::DebuggerInitialize);85}86 87void PlatformQemuUser::Terminate() {88 PluginManager::UnregisterPlugin(PlatformQemuUser::CreateInstance);89}90 91void PlatformQemuUser::DebuggerInitialize(Debugger &debugger) {92 if (!PluginManager::GetSettingForPlatformPlugin(debugger,93 GetPluginNameStatic())) {94 PluginManager::CreateSettingForPlatformPlugin(95 debugger, GetGlobalProperties().GetValueProperties(),96 "Properties for the qemu-user platform plugin.",97 /*is_global_property=*/true);98 }99}100 101PlatformSP PlatformQemuUser::CreateInstance(bool force, const ArchSpec *arch) {102 if (force)103 return PlatformSP(new PlatformQemuUser());104 return nullptr;105}106 107std::vector<ArchSpec>108PlatformQemuUser::GetSupportedArchitectures(const ArchSpec &process_host_arch) {109 llvm::Triple triple = HostInfo::GetArchitecture().GetTriple();110 triple.setEnvironment(llvm::Triple::UnknownEnvironment);111 triple.setArchName(GetGlobalProperties().GetArchitecture());112 if (triple.getArch() != llvm::Triple::UnknownArch)113 return {ArchSpec(triple)};114 return {};115}116 117static auto get_arg_range(const Args &args) {118 return llvm::make_range(args.GetArgumentArrayRef().begin(),119 args.GetArgumentArrayRef().end());120}121 122// Returns the emulator environment which result in the desired environment123// being presented to the emulated process. We want to be careful about124// preserving the host environment, as it may contain entries (LD_LIBRARY_PATH,125// for example) needed for the operation of the emulator itself.126static Environment ComputeLaunchEnvironment(Environment target,127 Environment host) {128 std::vector<std::string> set_env;129 for (const auto &KV : target) {130 // If the host value differs from the target (or is unset), then set it131 // through QEMU_SET_ENV. Identical entries will be forwarded automatically.132 auto host_it = host.find(KV.first());133 if (host_it == host.end() || host_it->second != KV.second)134 set_env.push_back(Environment::compose(KV));135 }136 llvm::sort(set_env);137 138 std::vector<llvm::StringRef> unset_env;139 for (const auto &KV : host) {140 // If the target is missing some host entries, then unset them through141 // QEMU_UNSET_ENV.142 if (target.count(KV.first()) == 0)143 unset_env.push_back(KV.first());144 }145 llvm::sort(unset_env);146 147 // The actual QEMU_(UN)SET_ENV variables should not be forwarded to the148 // target.149 if (!set_env.empty()) {150 host["QEMU_SET_ENV"] = llvm::join(set_env, ",");151 unset_env.push_back("QEMU_SET_ENV");152 }153 if (!unset_env.empty()) {154 unset_env.push_back("QEMU_UNSET_ENV");155 host["QEMU_UNSET_ENV"] = llvm::join(unset_env, ",");156 }157 return host;158}159 160lldb::ProcessSP PlatformQemuUser::DebugProcess(ProcessLaunchInfo &launch_info,161 Debugger &debugger,162 Target &target, Status &error) {163 Log *log = GetLog(LLDBLog::Platform);164 165 // If platform.plugin.qemu-user.emulator-path is set, use it.166 FileSpec qemu = GetGlobalProperties().GetEmulatorPath();167 // If platform.plugin.qemu-user.emulator-path is not set, build the168 // executable name from platform.plugin.qemu-user.architecture.169 if (!qemu) {170 llvm::StringRef arch = GetGlobalProperties().GetArchitecture();171 // If platform.plugin.qemu-user.architecture is not set, build the172 // executable name from the target Triple's ArchName173 if (arch.empty())174 arch = target.GetArchitecture().GetTriple().getArchName();175 qemu.SetPath(("qemu-" + arch).str());176 }177 FileSystem::Instance().ResolveExecutableLocation(qemu);178 179 llvm::SmallString<0> socket_model, socket_path;180 HostInfo::GetProcessTempDir().GetPath(socket_model);181 llvm::sys::path::append(socket_model, "qemu-%%%%%%%%.socket");182 do {183 llvm::sys::fs::createUniquePath(socket_model, socket_path, false);184 } while (FileSystem::Instance().Exists(socket_path));185 186 Args args({qemu.GetPath(), "-g", socket_path});187 if (!launch_info.GetArg0().empty()) {188 args.AppendArgument("-0");189 args.AppendArgument(launch_info.GetArg0());190 }191 args.AppendArguments(GetGlobalProperties().GetEmulatorArgs());192 args.AppendArgument("--");193 args.AppendArgument(launch_info.GetExecutableFile().GetPath());194 for (size_t i = 1; i < launch_info.GetArguments().size(); ++i)195 args.AppendArgument(launch_info.GetArguments()[i].ref());196 197 LLDB_LOG(log, "{0} -> {1}", get_arg_range(launch_info.GetArguments()),198 get_arg_range(args));199 200 launch_info.SetArguments(args, true);201 202 Environment emulator_env = Host::GetEnvironment();203 if (const std::string &sysroot = GetSDKRootDirectory(); !sysroot.empty())204 emulator_env["QEMU_LD_PREFIX"] = sysroot;205 for (const auto &KV : GetGlobalProperties().GetEmulatorEnvVars())206 emulator_env[KV.first()] = KV.second;207 launch_info.GetEnvironment() = ComputeLaunchEnvironment(208 std::move(launch_info.GetEnvironment()), std::move(emulator_env));209 210 launch_info.SetLaunchInSeparateProcessGroup(true);211 launch_info.GetFlags().Clear(eLaunchFlagDebug);212 launch_info.SetMonitorProcessCallback(ProcessLaunchInfo::NoOpMonitorCallback);213 214 // This is automatically done for host platform in215 // Target::FinalizeFileActions, but we're not a host platform.216 llvm::Error Err = launch_info.SetUpPtyRedirection();217 LLDB_LOG_ERROR(log, std::move(Err), "SetUpPtyRedirection failed: {0}");218 219 error = Host::LaunchProcess(launch_info);220 if (error.Fail())221 return nullptr;222 223 ProcessSP process_sp = target.CreateProcess(224 launch_info.GetListener(),225 process_gdb_remote::ProcessGDBRemote::GetPluginNameStatic(), nullptr,226 true);227 if (!process_sp) {228 error = Status::FromErrorString("Failed to create GDB process");229 return nullptr;230 }231 232 process_sp->HijackProcessEvents(launch_info.GetHijackListener());233 234 error = process_sp->ConnectRemote(("unix-connect://" + socket_path).str());235 if (error.Fail())236 return nullptr;237 238 if (launch_info.GetPTY().GetPrimaryFileDescriptor() !=239 PseudoTerminal::invalid_fd)240 process_sp->SetSTDIOFileDescriptor(241 launch_info.GetPTY().ReleasePrimaryFileDescriptor());242 243 return process_sp;244}245 246Environment PlatformQemuUser::GetEnvironment() {247 Environment env = Host::GetEnvironment();248 for (const auto &KV : GetGlobalProperties().GetTargetEnvVars())249 env[KV.first()] = KV.second;250 return env;251}252