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