xref: /freebsd-src/contrib/llvm-project/lldb/source/Plugins/Platform/QemuUser/PlatformQemuUser.cpp (revision 5f757f3ff9144b609b3c433dfd370cc6bdc191ad)
14824e7fdSDimitry Andric //===-- PlatformQemuUser.cpp ----------------------------------------------===//
24824e7fdSDimitry Andric //
34824e7fdSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
44824e7fdSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
54824e7fdSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
64824e7fdSDimitry Andric //
74824e7fdSDimitry Andric //===----------------------------------------------------------------------===//
84824e7fdSDimitry Andric 
94824e7fdSDimitry Andric #include "Plugins/Platform/QemuUser/PlatformQemuUser.h"
104824e7fdSDimitry Andric #include "Plugins/Process/gdb-remote/ProcessGDBRemote.h"
114824e7fdSDimitry Andric #include "lldb/Core/PluginManager.h"
124824e7fdSDimitry Andric #include "lldb/Host/FileSystem.h"
134824e7fdSDimitry Andric #include "lldb/Host/ProcessLaunchInfo.h"
144824e7fdSDimitry Andric #include "lldb/Interpreter/OptionValueProperties.h"
154824e7fdSDimitry Andric #include "lldb/Target/Process.h"
164824e7fdSDimitry Andric #include "lldb/Target/Target.h"
1781ad6265SDimitry Andric #include "lldb/Utility/LLDBLog.h"
184824e7fdSDimitry Andric #include "lldb/Utility/Listener.h"
194824e7fdSDimitry Andric #include "lldb/Utility/Log.h"
204824e7fdSDimitry Andric 
214824e7fdSDimitry Andric using namespace lldb;
224824e7fdSDimitry Andric using namespace lldb_private;
234824e7fdSDimitry Andric 
244824e7fdSDimitry Andric LLDB_PLUGIN_DEFINE(PlatformQemuUser)
254824e7fdSDimitry Andric 
26bdd1243dSDimitry Andric namespace {
274824e7fdSDimitry Andric #define LLDB_PROPERTIES_platformqemuuser
284824e7fdSDimitry Andric #include "PlatformQemuUserProperties.inc"
294824e7fdSDimitry Andric 
304824e7fdSDimitry Andric enum {
314824e7fdSDimitry Andric #define LLDB_PROPERTIES_platformqemuuser
324824e7fdSDimitry Andric #include "PlatformQemuUserPropertiesEnum.inc"
334824e7fdSDimitry Andric };
344824e7fdSDimitry Andric 
354824e7fdSDimitry Andric class PluginProperties : public Properties {
364824e7fdSDimitry Andric public:
PluginProperties()374824e7fdSDimitry Andric   PluginProperties() {
384824e7fdSDimitry Andric     m_collection_sp = std::make_shared<OptionValueProperties>(
39*5f757f3fSDimitry Andric         PlatformQemuUser::GetPluginNameStatic());
404824e7fdSDimitry Andric     m_collection_sp->Initialize(g_platformqemuuser_properties);
414824e7fdSDimitry Andric   }
424824e7fdSDimitry Andric 
GetArchitecture()434824e7fdSDimitry Andric   llvm::StringRef GetArchitecture() {
4406c3fb27SDimitry Andric     return GetPropertyAtIndexAs<llvm::StringRef>(ePropertyArchitecture, "");
454824e7fdSDimitry Andric   }
464824e7fdSDimitry Andric 
GetEmulatorPath()474824e7fdSDimitry Andric   FileSpec GetEmulatorPath() {
4806c3fb27SDimitry Andric     return GetPropertyAtIndexAs<FileSpec>(ePropertyEmulatorPath, {});
494824e7fdSDimitry Andric   }
500eae32dcSDimitry Andric 
GetEmulatorArgs()510eae32dcSDimitry Andric   Args GetEmulatorArgs() {
520eae32dcSDimitry Andric     Args result;
5306c3fb27SDimitry Andric     m_collection_sp->GetPropertyAtIndexAsArgs(ePropertyEmulatorArgs, result);
540eae32dcSDimitry Andric     return result;
550eae32dcSDimitry Andric   }
560eae32dcSDimitry Andric 
GetEmulatorEnvVars()570eae32dcSDimitry Andric   Environment GetEmulatorEnvVars() {
580eae32dcSDimitry Andric     Args args;
5906c3fb27SDimitry Andric     m_collection_sp->GetPropertyAtIndexAsArgs(ePropertyEmulatorEnvVars, args);
600eae32dcSDimitry Andric     return Environment(args);
610eae32dcSDimitry Andric   }
620eae32dcSDimitry Andric 
GetTargetEnvVars()630eae32dcSDimitry Andric   Environment GetTargetEnvVars() {
640eae32dcSDimitry Andric     Args args;
6506c3fb27SDimitry Andric     m_collection_sp->GetPropertyAtIndexAsArgs(ePropertyTargetEnvVars, args);
660eae32dcSDimitry Andric     return Environment(args);
670eae32dcSDimitry Andric   }
684824e7fdSDimitry Andric };
694824e7fdSDimitry Andric 
70bdd1243dSDimitry Andric } // namespace
71bdd1243dSDimitry Andric 
GetGlobalProperties()724824e7fdSDimitry Andric static PluginProperties &GetGlobalProperties() {
734824e7fdSDimitry Andric   static PluginProperties g_settings;
744824e7fdSDimitry Andric   return g_settings;
754824e7fdSDimitry Andric }
764824e7fdSDimitry Andric 
GetPluginDescriptionStatic()774824e7fdSDimitry Andric llvm::StringRef PlatformQemuUser::GetPluginDescriptionStatic() {
784824e7fdSDimitry Andric   return "Platform for debugging binaries under user mode qemu";
794824e7fdSDimitry Andric }
804824e7fdSDimitry Andric 
Initialize()814824e7fdSDimitry Andric void PlatformQemuUser::Initialize() {
824824e7fdSDimitry Andric   PluginManager::RegisterPlugin(
834824e7fdSDimitry Andric       GetPluginNameStatic(), GetPluginDescriptionStatic(),
844824e7fdSDimitry Andric       PlatformQemuUser::CreateInstance, PlatformQemuUser::DebuggerInitialize);
854824e7fdSDimitry Andric }
864824e7fdSDimitry Andric 
Terminate()874824e7fdSDimitry Andric void PlatformQemuUser::Terminate() {
884824e7fdSDimitry Andric   PluginManager::UnregisterPlugin(PlatformQemuUser::CreateInstance);
894824e7fdSDimitry Andric }
904824e7fdSDimitry Andric 
DebuggerInitialize(Debugger & debugger)914824e7fdSDimitry Andric void PlatformQemuUser::DebuggerInitialize(Debugger &debugger) {
92*5f757f3fSDimitry Andric   if (!PluginManager::GetSettingForPlatformPlugin(debugger,
93*5f757f3fSDimitry Andric                                                   GetPluginNameStatic())) {
944824e7fdSDimitry Andric     PluginManager::CreateSettingForPlatformPlugin(
954824e7fdSDimitry Andric         debugger, GetGlobalProperties().GetValueProperties(),
9606c3fb27SDimitry Andric         "Properties for the qemu-user platform plugin.",
974824e7fdSDimitry Andric         /*is_global_property=*/true);
984824e7fdSDimitry Andric   }
994824e7fdSDimitry Andric }
1004824e7fdSDimitry Andric 
CreateInstance(bool force,const ArchSpec * arch)1014824e7fdSDimitry Andric PlatformSP PlatformQemuUser::CreateInstance(bool force, const ArchSpec *arch) {
1024824e7fdSDimitry Andric   if (force)
1034824e7fdSDimitry Andric     return PlatformSP(new PlatformQemuUser());
1044824e7fdSDimitry Andric   return nullptr;
1054824e7fdSDimitry Andric }
1064824e7fdSDimitry Andric 
10781ad6265SDimitry Andric std::vector<ArchSpec>
GetSupportedArchitectures(const ArchSpec & process_host_arch)10881ad6265SDimitry Andric PlatformQemuUser::GetSupportedArchitectures(const ArchSpec &process_host_arch) {
1094824e7fdSDimitry Andric   llvm::Triple triple = HostInfo::GetArchitecture().GetTriple();
1104824e7fdSDimitry Andric   triple.setEnvironment(llvm::Triple::UnknownEnvironment);
1114824e7fdSDimitry Andric   triple.setArchName(GetGlobalProperties().GetArchitecture());
1124824e7fdSDimitry Andric   if (triple.getArch() != llvm::Triple::UnknownArch)
1134824e7fdSDimitry Andric     return {ArchSpec(triple)};
1144824e7fdSDimitry Andric   return {};
1154824e7fdSDimitry Andric }
1164824e7fdSDimitry Andric 
get_arg_range(const Args & args)1174824e7fdSDimitry Andric static auto get_arg_range(const Args &args) {
1184824e7fdSDimitry Andric   return llvm::make_range(args.GetArgumentArrayRef().begin(),
1194824e7fdSDimitry Andric                           args.GetArgumentArrayRef().end());
1204824e7fdSDimitry Andric }
1214824e7fdSDimitry Andric 
1220eae32dcSDimitry Andric // Returns the emulator environment which result in the desired environment
1230eae32dcSDimitry Andric // being presented to the emulated process. We want to be careful about
1240eae32dcSDimitry Andric // preserving the host environment, as it may contain entries (LD_LIBRARY_PATH,
1250eae32dcSDimitry Andric // for example) needed for the operation of the emulator itself.
ComputeLaunchEnvironment(Environment target,Environment host)1260eae32dcSDimitry Andric static Environment ComputeLaunchEnvironment(Environment target,
1270eae32dcSDimitry Andric                                             Environment host) {
1280eae32dcSDimitry Andric   std::vector<std::string> set_env;
1290eae32dcSDimitry Andric   for (const auto &KV : target) {
1300eae32dcSDimitry Andric     // If the host value differs from the target (or is unset), then set it
1310eae32dcSDimitry Andric     // through QEMU_SET_ENV. Identical entries will be forwarded automatically.
1320eae32dcSDimitry Andric     auto host_it = host.find(KV.first());
1330eae32dcSDimitry Andric     if (host_it == host.end() || host_it->second != KV.second)
1340eae32dcSDimitry Andric       set_env.push_back(Environment::compose(KV));
1350eae32dcSDimitry Andric   }
1360eae32dcSDimitry Andric   llvm::sort(set_env);
1370eae32dcSDimitry Andric 
1380eae32dcSDimitry Andric   std::vector<llvm::StringRef> unset_env;
1390eae32dcSDimitry Andric   for (const auto &KV : host) {
1400eae32dcSDimitry Andric     // If the target is missing some host entries, then unset them through
1410eae32dcSDimitry Andric     // QEMU_UNSET_ENV.
1420eae32dcSDimitry Andric     if (target.count(KV.first()) == 0)
1430eae32dcSDimitry Andric       unset_env.push_back(KV.first());
1440eae32dcSDimitry Andric   }
1450eae32dcSDimitry Andric   llvm::sort(unset_env);
1460eae32dcSDimitry Andric 
1470eae32dcSDimitry Andric   // The actual QEMU_(UN)SET_ENV variables should not be forwarded to the
1480eae32dcSDimitry Andric   // target.
1490eae32dcSDimitry Andric   if (!set_env.empty()) {
1500eae32dcSDimitry Andric     host["QEMU_SET_ENV"] = llvm::join(set_env, ",");
1510eae32dcSDimitry Andric     unset_env.push_back("QEMU_SET_ENV");
1520eae32dcSDimitry Andric   }
1530eae32dcSDimitry Andric   if (!unset_env.empty()) {
1540eae32dcSDimitry Andric     unset_env.push_back("QEMU_UNSET_ENV");
1550eae32dcSDimitry Andric     host["QEMU_UNSET_ENV"] = llvm::join(unset_env, ",");
1560eae32dcSDimitry Andric   }
1570eae32dcSDimitry Andric   return host;
1580eae32dcSDimitry Andric }
1590eae32dcSDimitry Andric 
DebugProcess(ProcessLaunchInfo & launch_info,Debugger & debugger,Target & target,Status & error)1604824e7fdSDimitry Andric lldb::ProcessSP PlatformQemuUser::DebugProcess(ProcessLaunchInfo &launch_info,
1614824e7fdSDimitry Andric                                                Debugger &debugger,
1624824e7fdSDimitry Andric                                                Target &target, Status &error) {
16381ad6265SDimitry Andric   Log *log = GetLog(LLDBLog::Platform);
1644824e7fdSDimitry Andric 
165*5f757f3fSDimitry Andric   // If platform.plugin.qemu-user.emulator-path is set, use it.
16604eeddc0SDimitry Andric   FileSpec qemu = GetGlobalProperties().GetEmulatorPath();
167*5f757f3fSDimitry Andric   // If platform.plugin.qemu-user.emulator-path is not set, build the
168*5f757f3fSDimitry Andric   // executable name from platform.plugin.qemu-user.architecture.
169*5f757f3fSDimitry Andric   if (!qemu) {
170*5f757f3fSDimitry Andric     llvm::StringRef arch = GetGlobalProperties().GetArchitecture();
171*5f757f3fSDimitry Andric     // If platform.plugin.qemu-user.architecture is not set, build the
172*5f757f3fSDimitry Andric     // executable name from the target Triple's ArchName
173*5f757f3fSDimitry Andric     if (arch.empty())
174*5f757f3fSDimitry Andric       arch = target.GetArchitecture().GetTriple().getArchName();
175*5f757f3fSDimitry Andric     qemu.SetPath(("qemu-" + arch).str());
176*5f757f3fSDimitry Andric   }
17704eeddc0SDimitry Andric   FileSystem::Instance().ResolveExecutableLocation(qemu);
1784824e7fdSDimitry Andric 
1794824e7fdSDimitry Andric   llvm::SmallString<0> socket_model, socket_path;
1804824e7fdSDimitry Andric   HostInfo::GetProcessTempDir().GetPath(socket_model);
1814824e7fdSDimitry Andric   llvm::sys::path::append(socket_model, "qemu-%%%%%%%%.socket");
1824824e7fdSDimitry Andric   do {
1834824e7fdSDimitry Andric     llvm::sys::fs::createUniquePath(socket_model, socket_path, false);
1844824e7fdSDimitry Andric   } while (FileSystem::Instance().Exists(socket_path));
1854824e7fdSDimitry Andric 
18604eeddc0SDimitry Andric   Args args({qemu.GetPath(), "-g", socket_path});
18704eeddc0SDimitry Andric   if (!launch_info.GetArg0().empty()) {
18804eeddc0SDimitry Andric     args.AppendArgument("-0");
18904eeddc0SDimitry Andric     args.AppendArgument(launch_info.GetArg0());
19004eeddc0SDimitry Andric   }
1910eae32dcSDimitry Andric   args.AppendArguments(GetGlobalProperties().GetEmulatorArgs());
1920eae32dcSDimitry Andric   args.AppendArgument("--");
1930eae32dcSDimitry Andric   args.AppendArgument(launch_info.GetExecutableFile().GetPath());
1944824e7fdSDimitry Andric   for (size_t i = 1; i < launch_info.GetArguments().size(); ++i)
1954824e7fdSDimitry Andric     args.AppendArgument(launch_info.GetArguments()[i].ref());
1964824e7fdSDimitry Andric 
1974824e7fdSDimitry Andric   LLDB_LOG(log, "{0} -> {1}", get_arg_range(launch_info.GetArguments()),
1984824e7fdSDimitry Andric            get_arg_range(args));
1994824e7fdSDimitry Andric 
2004824e7fdSDimitry Andric   launch_info.SetArguments(args, true);
2010eae32dcSDimitry Andric 
2020eae32dcSDimitry Andric   Environment emulator_env = Host::GetEnvironment();
20306c3fb27SDimitry Andric   if (const std::string &sysroot = GetSDKRootDirectory(); !sysroot.empty())
20406c3fb27SDimitry Andric     emulator_env["QEMU_LD_PREFIX"] = sysroot;
2050eae32dcSDimitry Andric   for (const auto &KV : GetGlobalProperties().GetEmulatorEnvVars())
2060eae32dcSDimitry Andric     emulator_env[KV.first()] = KV.second;
2070eae32dcSDimitry Andric   launch_info.GetEnvironment() = ComputeLaunchEnvironment(
2080eae32dcSDimitry Andric       std::move(launch_info.GetEnvironment()), std::move(emulator_env));
2090eae32dcSDimitry Andric 
2104824e7fdSDimitry Andric   launch_info.SetLaunchInSeparateProcessGroup(true);
2114824e7fdSDimitry Andric   launch_info.GetFlags().Clear(eLaunchFlagDebug);
21281ad6265SDimitry Andric   launch_info.SetMonitorProcessCallback(ProcessLaunchInfo::NoOpMonitorCallback);
2134824e7fdSDimitry Andric 
2140eae32dcSDimitry Andric   // This is automatically done for host platform in
2150eae32dcSDimitry Andric   // Target::FinalizeFileActions, but we're not a host platform.
2160eae32dcSDimitry Andric   llvm::Error Err = launch_info.SetUpPtyRedirection();
2170eae32dcSDimitry Andric   LLDB_LOG_ERROR(log, std::move(Err), "SetUpPtyRedirection failed: {0}");
2180eae32dcSDimitry Andric 
2194824e7fdSDimitry Andric   error = Host::LaunchProcess(launch_info);
2204824e7fdSDimitry Andric   if (error.Fail())
2214824e7fdSDimitry Andric     return nullptr;
2224824e7fdSDimitry Andric 
2234824e7fdSDimitry Andric   ProcessSP process_sp = target.CreateProcess(
2244824e7fdSDimitry Andric       launch_info.GetListener(),
2254824e7fdSDimitry Andric       process_gdb_remote::ProcessGDBRemote::GetPluginNameStatic(), nullptr,
2264824e7fdSDimitry Andric       true);
22781ad6265SDimitry Andric   if (!process_sp) {
22881ad6265SDimitry Andric     error.SetErrorString("Failed to create GDB process");
22981ad6265SDimitry Andric     return nullptr;
23081ad6265SDimitry Andric   }
2310eae32dcSDimitry Andric 
23281ad6265SDimitry Andric   process_sp->HijackProcessEvents(launch_info.GetHijackListener());
2334824e7fdSDimitry Andric 
2344824e7fdSDimitry Andric   error = process_sp->ConnectRemote(("unix-connect://" + socket_path).str());
2354824e7fdSDimitry Andric   if (error.Fail())
2364824e7fdSDimitry Andric     return nullptr;
2374824e7fdSDimitry Andric 
2380eae32dcSDimitry Andric   if (launch_info.GetPTY().GetPrimaryFileDescriptor() !=
2390eae32dcSDimitry Andric       PseudoTerminal::invalid_fd)
2400eae32dcSDimitry Andric     process_sp->SetSTDIOFileDescriptor(
2410eae32dcSDimitry Andric         launch_info.GetPTY().ReleasePrimaryFileDescriptor());
2420eae32dcSDimitry Andric 
2434824e7fdSDimitry Andric   return process_sp;
2444824e7fdSDimitry Andric }
2450eae32dcSDimitry Andric 
GetEnvironment()2460eae32dcSDimitry Andric Environment PlatformQemuUser::GetEnvironment() {
2470eae32dcSDimitry Andric   Environment env = Host::GetEnvironment();
2480eae32dcSDimitry Andric   for (const auto &KV : GetGlobalProperties().GetTargetEnvVars())
2490eae32dcSDimitry Andric     env[KV.first()] = KV.second;
2500eae32dcSDimitry Andric   return env;
2510eae32dcSDimitry Andric }
252