LLDB  mainline
PlatformQemuUser.cpp
Go to the documentation of this file.
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 
12 #include "lldb/Host/FileSystem.h"
15 #include "lldb/Target/Process.h"
16 #include "lldb/Target/Target.h"
17 #include "lldb/Utility/Listener.h"
18 #include "lldb/Utility/Log.h"
19 
20 using namespace lldb;
21 using namespace lldb_private;
22 
24 
25 #define LLDB_PROPERTIES_platformqemuuser
26 #include "PlatformQemuUserProperties.inc"
27 
28 enum {
29 #define LLDB_PROPERTIES_platformqemuuser
30 #include "PlatformQemuUserPropertiesEnum.inc"
31 };
32 
33 class PluginProperties : public Properties {
34 public:
36  m_collection_sp = std::make_shared<OptionValueProperties>(
37  ConstString(PlatformQemuUser::GetPluginNameStatic()));
38  m_collection_sp->Initialize(g_platformqemuuser_properties);
39  }
40 
41  llvm::StringRef GetArchitecture() {
42  return m_collection_sp->GetPropertyAtIndexAsString(
43  nullptr, ePropertyArchitecture, "");
44  }
45 
47  return m_collection_sp->GetPropertyAtIndexAsFileSpec(nullptr,
48  ePropertyEmulatorPath);
49  }
50 
52  Args result;
53  m_collection_sp->GetPropertyAtIndexAsArgs(nullptr, ePropertyEmulatorArgs,
54  result);
55  return result;
56  }
57 
59  Args args;
60  m_collection_sp->GetPropertyAtIndexAsArgs(nullptr, ePropertyEmulatorEnvVars,
61  args);
62  return Environment(args);
63  }
64 
66  Args args;
67  m_collection_sp->GetPropertyAtIndexAsArgs(nullptr, ePropertyTargetEnvVars,
68  args);
69  return Environment(args);
70  }
71 };
72 
73 static PluginProperties &GetGlobalProperties() {
74  static PluginProperties g_settings;
75  return g_settings;
76 }
77 
78 llvm::StringRef PlatformQemuUser::GetPluginDescriptionStatic() {
79  return "Platform for debugging binaries under user mode qemu";
80 }
81 
82 void PlatformQemuUser::Initialize() {
83  PluginManager::RegisterPlugin(
84  GetPluginNameStatic(), GetPluginDescriptionStatic(),
85  PlatformQemuUser::CreateInstance, PlatformQemuUser::DebuggerInitialize);
86 }
87 
88 void PlatformQemuUser::Terminate() {
89  PluginManager::UnregisterPlugin(PlatformQemuUser::CreateInstance);
90 }
91 
92 void PlatformQemuUser::DebuggerInitialize(Debugger &debugger) {
93  if (!PluginManager::GetSettingForPlatformPlugin(
94  debugger, ConstString(GetPluginNameStatic()))) {
95  PluginManager::CreateSettingForPlatformPlugin(
96  debugger, GetGlobalProperties().GetValueProperties(),
97  ConstString("Properties for the qemu-user platform plugin."),
98  /*is_global_property=*/true);
99  }
100 }
101 
102 PlatformSP PlatformQemuUser::CreateInstance(bool force, const ArchSpec *arch) {
103  if (force)
104  return PlatformSP(new PlatformQemuUser());
105  return nullptr;
106 }
107 
108 std::vector<ArchSpec> PlatformQemuUser::GetSupportedArchitectures() {
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 
117 static 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.
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 
160 lldb::ProcessSP PlatformQemuUser::DebugProcess(ProcessLaunchInfo &launch_info,
161  Debugger &debugger,
162  Target &target, Status &error) {
164 
165  FileSpec qemu = GetGlobalProperties().GetEmulatorPath();
166  if (!qemu)
167  qemu.SetPath(("qemu-" + GetGlobalProperties().GetArchitecture()).str());
168  FileSystem::Instance().ResolveExecutableLocation(qemu);
169 
170  llvm::SmallString<0> socket_model, socket_path;
171  HostInfo::GetProcessTempDir().GetPath(socket_model);
172  llvm::sys::path::append(socket_model, "qemu-%%%%%%%%.socket");
173  do {
174  llvm::sys::fs::createUniquePath(socket_model, socket_path, false);
175  } while (FileSystem::Instance().Exists(socket_path));
176 
177  Args args({qemu.GetPath(), "-g", socket_path});
178  if (!launch_info.GetArg0().empty()) {
179  args.AppendArgument("-0");
180  args.AppendArgument(launch_info.GetArg0());
181  }
182  args.AppendArguments(GetGlobalProperties().GetEmulatorArgs());
183  args.AppendArgument("--");
184  args.AppendArgument(launch_info.GetExecutableFile().GetPath());
185  for (size_t i = 1; i < launch_info.GetArguments().size(); ++i)
186  args.AppendArgument(launch_info.GetArguments()[i].ref());
187 
188  LLDB_LOG(log, "{0} -> {1}", get_arg_range(launch_info.GetArguments()),
189  get_arg_range(args));
190 
191  launch_info.SetArguments(args, true);
192 
193  Environment emulator_env = Host::GetEnvironment();
194  if (ConstString sysroot = GetSDKRootDirectory())
195  emulator_env["QEMU_LD_PREFIX"] = sysroot.GetStringRef().str();
196  for (const auto &KV : GetGlobalProperties().GetEmulatorEnvVars())
197  emulator_env[KV.first()] = KV.second;
199  std::move(launch_info.GetEnvironment()), std::move(emulator_env));
200 
201  launch_info.SetLaunchInSeparateProcessGroup(true);
202  launch_info.GetFlags().Clear(eLaunchFlagDebug);
203  launch_info.SetMonitorProcessCallback(ProcessLaunchInfo::NoOpMonitorCallback,
204  false);
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 
220  ListenerSP listener_sp =
221  Listener::MakeListener("lldb.platform_qemu_user.debugprocess");
222  launch_info.SetHijackListener(listener_sp);
223  Process::ProcessEventHijacker hijacker(*process_sp, listener_sp);
224 
225  error = process_sp->ConnectRemote(("unix-connect://" + socket_path).str());
226  if (error.Fail())
227  return nullptr;
228 
229  if (launch_info.GetPTY().GetPrimaryFileDescriptor() !=
230  PseudoTerminal::invalid_fd)
231  process_sp->SetSTDIOFileDescriptor(
232  launch_info.GetPTY().ReleasePrimaryFileDescriptor());
233 
234  process_sp->WaitForProcessToStop(llvm::None, nullptr, false, listener_sp);
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 }
lldb_private::ProcessInfo::GetArguments
Args & GetArguments()
Definition: ProcessInfo.h:75
PluginProperties::GetArchitecture
llvm::StringRef GetArchitecture()
Definition: PlatformQemuUser.cpp:41
PluginProperties::PluginProperties
PluginProperties()
Definition: PlatformQemuUser.cpp:35
lldb_private::ArchSpec
Definition: ArchSpec.h:33
FileSystem.h
ComputeLaunchEnvironment
static Environment ComputeLaunchEnvironment(Environment target, Environment host)
Definition: PlatformQemuUser.cpp:126
ProcessLaunchInfo.h
PlatformQemuUser.h
PluginProperties::GetTargetEnvVars
Environment GetTargetEnvVars()
Definition: PlatformQemuUser.cpp:65
lldb_private::PseudoTerminal::GetPrimaryFileDescriptor
int GetPrimaryFileDescriptor() const
The primary file descriptor accessor.
Definition: PseudoTerminal.cpp:170
lldb_private::Flags::Clear
ValueType Clear(ValueType mask=~static_cast< ValueType >(0))
Clear one or more flags.
Definition: Flags.h:61
lldb_private::Args::AppendArgument
void AppendArgument(llvm::StringRef arg_str, char quote_char='\0')
Appends a new argument to the end of the list argument list.
Definition: Args.cpp:318
LIBLLDB_LOG_PLATFORM
#define LIBLLDB_LOG_PLATFORM
Definition: Logging.h:39
GetGlobalProperties
static PluginProperties & GetGlobalProperties()
Definition: PlatformQemuUser.cpp:73
lldb_private::ProcessLaunchInfo::GetFlags
Flags & GetFlags()
Definition: ProcessLaunchInfo.h:64
lldb_private::Args
Definition: Args.h:33
lldb_private::FileSpec::SetPath
void SetPath(llvm::StringRef p)
Temporary helper for FileSystem change.
Definition: FileSpec.h:270
Listener.h
lldb_private::Target
Definition: Target.h:452
lldb_private::ProcessLaunchInfo::SetHijackListener
void SetHijackListener(const lldb::ListenerSP &listener_sp)
Definition: ProcessLaunchInfo.h:136
lldb_private::Target::CreateProcess
const lldb::ProcessSP & CreateProcess(lldb::ListenerSP listener_sp, llvm::StringRef plugin_name, const FileSpec *crash_file, bool can_connect)
Definition: Target.cpp:199
Process.h
Target.h
lldb_private::Properties
Definition: UserSettingsController.h:33
PluginProperties::GetEmulatorArgs
Args GetEmulatorArgs()
Definition: PlatformQemuUser.cpp:51
lldb_private::FileSpec
Definition: FileSpec.h:56
error
static llvm::raw_ostream & error(Stream &strm)
Definition: CommandReturnObject.cpp:17
ProcessGDBRemote.h
lldb_private::ProcessLaunchInfo
Definition: ProcessLaunchInfo.h:31
Log.h
PluginProperties::GetEmulatorEnvVars
Environment GetEmulatorEnvVars()
Definition: PlatformQemuUser.cpp:58
lldb_private::ConstString
Definition: ConstString.h:40
lldb_private::ProcessLaunchInfo::SetUpPtyRedirection
llvm::Error SetUpPtyRedirection()
Definition: ProcessLaunchInfo.cpp:213
lldb_private::ProcessLaunchInfo::GetListener
lldb::ListenerSP GetListener() const
Definition: ProcessLaunchInfo.h:128
lldb_private::Debugger
Definition: Debugger.h:70
lldb_private::ProcessLaunchInfo::SetLaunchInSeparateProcessGroup
void SetLaunchInSeparateProcessGroup(bool separate)
Definition: ProcessLaunchInfo.cpp:151
lldb_private::ProcessLaunchInfo::GetPTY
PseudoTerminal & GetPTY()
Definition: ProcessLaunchInfo.h:125
OptionValueProperties.h
PluginProperties::GetEmulatorPath
FileSpec GetEmulatorPath()
Definition: PlatformQemuUser.cpp:46
lldb_private::ProcessLaunchInfo::SetMonitorProcessCallback
void SetMonitorProcessCallback(const Host::MonitorChildProcessCallback &callback, bool monitor_signals)
Definition: ProcessLaunchInfo.cpp:179
lldb_private::Status
Definition: Status.h:44
lldb_private::PseudoTerminal::ReleasePrimaryFileDescriptor
int ReleasePrimaryFileDescriptor()
Release the primary file descriptor.
Definition: PseudoTerminal.cpp:184
lldb_private::ProcessInfo::GetEnvironment
Environment & GetEnvironment()
Definition: ProcessInfo.h:87
lldb_private::PlatformQemuUser
Definition: PlatformQemuUser.h:15
lldb_private::Environment
Definition: Environment.h:18
PluginManager.h
LLDB_LOG
#define LLDB_LOG(log,...)
The LLDB_LOG* macros defined below are the way to emit log messages.
Definition: Log.h:240
lldb_private::Process::ProcessEventHijacker
Definition: Process.h:2169
lldb_private
A class that represents a running process on the host machine.
Definition: SBCommandInterpreterRunOptions.h:16
lldb_private::Args::GetArgumentArrayRef
llvm::ArrayRef< const char * > GetArgumentArrayRef() const
Gets the argument as an ArrayRef.
Definition: Args.h:171
Error
llvm::Error Error
Definition: UdtRecordCompleter.cpp:29
lldb_private::ProcessInfo::GetExecutableFile
FileSpec & GetExecutableFile()
Definition: ProcessInfo.h:42
get_arg_range
static auto get_arg_range(const Args &args)
Definition: PlatformQemuUser.cpp:117
lldb_private::ProcessInfo::GetArg0
llvm::StringRef GetArg0() const
Definition: ProcessInfo.cpp:77
LLDB_PLUGIN_DEFINE
#define LLDB_PLUGIN_DEFINE(PluginName)
Definition: PluginManager.h:31
lldb_private::Log
Definition: Log.h:49
lldb_private::GetLogIfAnyCategoriesSet
Log * GetLogIfAnyCategoriesSet(uint32_t mask)
Definition: Logging.cpp:62
lldb_private::FileSpec::GetPath
size_t GetPath(char *path, size_t max_path_length, bool denormalize=true) const
Extract the full path to the file.
Definition: FileSpec.cpp:346
lldb
Definition: SBAddress.h:15
LLDB_LOG_ERROR
#define LLDB_LOG_ERROR(log, error,...)
Definition: Log.h:263
lldb_private::ProcessInfo::SetArguments
void SetArguments(const Args &args, bool first_arg_is_executable)
Definition: ProcessInfo.cpp:97