xref: /freebsd/contrib/llvm-project/lldb/source/Plugins/Platform/QemuUser/PlatformQemuUser.cpp (revision 5f757f3ff9144b609b3c433dfd370cc6bdc191ad)
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-exception
6 //
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 
21 using namespace lldb;
22 using namespace lldb_private;
23 
24 LLDB_PLUGIN_DEFINE(PlatformQemuUser)
25 
26 namespace {
27 #define LLDB_PROPERTIES_platformqemuuser
28 #include "PlatformQemuUserProperties.inc"
29 
30 enum {
31 #define LLDB_PROPERTIES_platformqemuuser
32 #include "PlatformQemuUserPropertiesEnum.inc"
33 };
34 
35 class PluginProperties : public Properties {
36 public:
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 } // namespace
71 
72 static PluginProperties &GetGlobalProperties() {
73   static PluginProperties g_settings;
74   return g_settings;
75 }
76 
77 llvm::StringRef PlatformQemuUser::GetPluginDescriptionStatic() {
78   return "Platform for debugging binaries under user mode qemu";
79 }
80 
81 void PlatformQemuUser::Initialize() {
82   PluginManager::RegisterPlugin(
83       GetPluginNameStatic(), GetPluginDescriptionStatic(),
84       PlatformQemuUser::CreateInstance, PlatformQemuUser::DebuggerInitialize);
85 }
86 
87 void PlatformQemuUser::Terminate() {
88   PluginManager::UnregisterPlugin(PlatformQemuUser::CreateInstance);
89 }
90 
91 void 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 
101 PlatformSP PlatformQemuUser::CreateInstance(bool force, const ArchSpec *arch) {
102   if (force)
103     return PlatformSP(new PlatformQemuUser());
104   return nullptr;
105 }
106 
107 std::vector<ArchSpec>
108 PlatformQemuUser::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 
117 static 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 environment
123 // being presented to the emulated process. We want to be careful about
124 // preserving the host environment, as it may contain entries (LD_LIBRARY_PATH,
125 // for example) needed for the operation of the emulator itself.
126 static 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 it
131     // 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 through
141     // 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 the
148   // 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 
160 lldb::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 the
168   // 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 the
172     // executable name from the target Triple's ArchName
173     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 in
215   // 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.SetErrorString("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 
246 Environment 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