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