1 //===-- PlatformQemuUser.cpp ----------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
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"
22 using namespace lldb_private
;
24 LLDB_PLUGIN_DEFINE(PlatformQemuUser
)
27 #define LLDB_PROPERTIES_platformqemuuser
28 #include "PlatformQemuUserProperties.inc"
31 #define LLDB_PROPERTIES_platformqemuuser
32 #include "PlatformQemuUserPropertiesEnum.inc"
35 class PluginProperties
: public Properties
{
38 m_collection_sp
= std::make_shared
<OptionValueProperties
>(
39 PlatformQemuUser::GetPluginNameStatic());
40 m_collection_sp
->Initialize(g_platformqemuuser_properties
);
43 llvm::StringRef
GetArchitecture() {
44 return GetPropertyAtIndexAs
<llvm::StringRef
>(ePropertyArchitecture
, "");
47 FileSpec
GetEmulatorPath() {
48 return GetPropertyAtIndexAs
<FileSpec
>(ePropertyEmulatorPath
, {});
51 Args
GetEmulatorArgs() {
53 m_collection_sp
->GetPropertyAtIndexAsArgs(ePropertyEmulatorArgs
, result
);
57 Environment
GetEmulatorEnvVars() {
59 m_collection_sp
->GetPropertyAtIndexAsArgs(ePropertyEmulatorEnvVars
, args
);
60 return Environment(args
);
63 Environment
GetTargetEnvVars() {
65 m_collection_sp
->GetPropertyAtIndexAsArgs(ePropertyTargetEnvVars
, args
);
66 return Environment(args
);
72 static PluginProperties
&GetGlobalProperties() {
73 static PluginProperties g_settings
;
77 llvm::StringRef
PlatformQemuUser::GetPluginDescriptionStatic() {
78 return "Platform for debugging binaries under user mode qemu";
81 void PlatformQemuUser::Initialize() {
82 PluginManager::RegisterPlugin(
83 GetPluginNameStatic(), GetPluginDescriptionStatic(),
84 PlatformQemuUser::CreateInstance
, PlatformQemuUser::DebuggerInitialize
);
87 void PlatformQemuUser::Terminate() {
88 PluginManager::UnregisterPlugin(PlatformQemuUser::CreateInstance
);
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);
101 PlatformSP
PlatformQemuUser::CreateInstance(bool force
, const ArchSpec
*arch
) {
103 return PlatformSP(new PlatformQemuUser());
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
)};
117 static auto get_arg_range(const Args
&args
) {
118 return llvm::make_range(args
.GetArgumentArrayRef().begin(),
119 args
.GetArgumentArrayRef().end());
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
,
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
));
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
142 if (target
.count(KV
.first()) == 0)
143 unset_env
.push_back(KV
.first());
145 llvm::sort(unset_env
);
147 // The actual QEMU_(UN)SET_ENV variables should not be forwarded to the
149 if (!set_env
.empty()) {
150 host
["QEMU_SET_ENV"] = llvm::join(set_env
, ",");
151 unset_env
.push_back("QEMU_SET_ENV");
153 if (!unset_env
.empty()) {
154 unset_env
.push_back("QEMU_UNSET_ENV");
155 host
["QEMU_UNSET_ENV"] = llvm::join(unset_env
, ",");
160 lldb::ProcessSP
PlatformQemuUser::DebugProcess(ProcessLaunchInfo
&launch_info
,
162 Target
&target
, Status
&error
) {
163 Log
*log
= GetLog(LLDBLog::Platform
);
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.
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
174 arch
= target
.GetArchitecture().GetTriple().getArchName();
175 qemu
.SetPath(("qemu-" + arch
).str());
177 FileSystem::Instance().ResolveExecutableLocation(qemu
);
179 llvm::SmallString
<0> socket_model
, socket_path
;
180 HostInfo::GetProcessTempDir().GetPath(socket_model
);
181 llvm::sys::path::append(socket_model
, "qemu-%%%%%%%%.socket");
183 llvm::sys::fs::createUniquePath(socket_model
, socket_path
, false);
184 } while (FileSystem::Instance().Exists(socket_path
));
186 Args
args({qemu
.GetPath(), "-g", socket_path
});
187 if (!launch_info
.GetArg0().empty()) {
188 args
.AppendArgument("-0");
189 args
.AppendArgument(launch_info
.GetArg0());
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());
197 LLDB_LOG(log
, "{0} -> {1}", get_arg_range(launch_info
.GetArguments()),
198 get_arg_range(args
));
200 launch_info
.SetArguments(args
, true);
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
));
210 launch_info
.SetLaunchInSeparateProcessGroup(true);
211 launch_info
.GetFlags().Clear(eLaunchFlagDebug
);
212 launch_info
.SetMonitorProcessCallback(ProcessLaunchInfo::NoOpMonitorCallback
);
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}");
219 error
= Host::LaunchProcess(launch_info
);
223 ProcessSP process_sp
= target
.CreateProcess(
224 launch_info
.GetListener(),
225 process_gdb_remote::ProcessGDBRemote::GetPluginNameStatic(), nullptr,
228 error
= Status::FromErrorString("Failed to create GDB process");
232 process_sp
->HijackProcessEvents(launch_info
.GetHijackListener());
234 error
= process_sp
->ConnectRemote(("unix-connect://" + socket_path
).str());
238 if (launch_info
.GetPTY().GetPrimaryFileDescriptor() !=
239 PseudoTerminal::invalid_fd
)
240 process_sp
->SetSTDIOFileDescriptor(
241 launch_info
.GetPTY().ReleasePrimaryFileDescriptor());
246 Environment
PlatformQemuUser::GetEnvironment() {
247 Environment env
= Host::GetEnvironment();
248 for (const auto &KV
: GetGlobalProperties().GetTargetEnvVars())
249 env
[KV
.first()] = KV
.second
;