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