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
21using namespace lldb;
22using namespace lldb_private;
23
24LLDB_PLUGIN_DEFINE(PlatformQemuUser)
25
26namespace {
27#define LLDB_PROPERTIES_platformqemuuser
28#include "PlatformQemuUserProperties.inc"
29
30enum {
31#define LLDB_PROPERTIES_platformqemuuser
32#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} // namespace
71
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 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.
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 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
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 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
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