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