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