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: 37 PluginProperties() { 38 m_collection_sp = std::make_shared<OptionValueProperties>( 39 PlatformQemuUser::GetPluginNameStatic()); 40 m_collection_sp->Initialize(g_platformqemuuser_properties); 41 } 42 43 llvm::StringRef GetArchitecture() { 44 return GetPropertyAtIndexAs<llvm::StringRef>(ePropertyArchitecture, ""); 45 } 46 47 FileSpec GetEmulatorPath() { 48 return GetPropertyAtIndexAs<FileSpec>(ePropertyEmulatorPath, {}); 49 } 50 51 Args GetEmulatorArgs() { 52 Args result; 53 m_collection_sp->GetPropertyAtIndexAsArgs(ePropertyEmulatorArgs, result); 54 return result; 55 } 56 57 Environment GetEmulatorEnvVars() { 58 Args args; 59 m_collection_sp->GetPropertyAtIndexAsArgs(ePropertyEmulatorEnvVars, args); 60 return Environment(args); 61 } 62 63 Environment GetTargetEnvVars() { 64 Args args; 65 m_collection_sp->GetPropertyAtIndexAsArgs(ePropertyTargetEnvVars, args); 66 return Environment(args); 67 } 68 }; 69 70 } // namespace 71 72 static PluginProperties &GetGlobalProperties() { 73 static PluginProperties g_settings; 74 return g_settings; 75 } 76 77 llvm::StringRef PlatformQemuUser::GetPluginDescriptionStatic() { 78 return "Platform for debugging binaries under user mode qemu"; 79 } 80 81 void PlatformQemuUser::Initialize() { 82 PluginManager::RegisterPlugin( 83 GetPluginNameStatic(), GetPluginDescriptionStatic(), 84 PlatformQemuUser::CreateInstance, PlatformQemuUser::DebuggerInitialize); 85 } 86 87 void PlatformQemuUser::Terminate() { 88 PluginManager::UnregisterPlugin(PlatformQemuUser::CreateInstance); 89 } 90 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); 98 } 99 } 100 101 PlatformSP PlatformQemuUser::CreateInstance(bool force, const ArchSpec *arch) { 102 if (force) 103 return PlatformSP(new PlatformQemuUser()); 104 return nullptr; 105 } 106 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)}; 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. 126 static Environment ComputeLaunchEnvironment(Environment target, 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) { 163 Log *log = GetLog(LLDBLog::Platform); 164 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. 169 if (!qemu) { 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 173 if (arch.empty()) 174 arch = target.GetArchitecture().GetTriple().getArchName(); 175 qemu.SetPath(("qemu-" + arch).str()); 176 } 177 FileSystem::Instance().ResolveExecutableLocation(qemu); 178 179 llvm::SmallString<0> socket_model, socket_path; 180 HostInfo::GetProcessTempDir().GetPath(socket_model); 181 llvm::sys::path::append(socket_model, "qemu-%%%%%%%%.socket"); 182 do { 183 llvm::sys::fs::createUniquePath(socket_model, socket_path, false); 184 } while (FileSystem::Instance().Exists(socket_path)); 185 186 Args args({qemu.GetPath(), "-g", socket_path}); 187 if (!launch_info.GetArg0().empty()) { 188 args.AppendArgument("-0"); 189 args.AppendArgument(launch_info.GetArg0()); 190 } 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()); 196 197 LLDB_LOG(log, "{0} -> {1}", get_arg_range(launch_info.GetArguments()), 198 get_arg_range(args)); 199 200 launch_info.SetArguments(args, true); 201 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)); 209 210 launch_info.SetLaunchInSeparateProcessGroup(true); 211 launch_info.GetFlags().Clear(eLaunchFlagDebug); 212 launch_info.SetMonitorProcessCallback(ProcessLaunchInfo::NoOpMonitorCallback); 213 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}"); 218 219 error = Host::LaunchProcess(launch_info); 220 if (error.Fail()) 221 return nullptr; 222 223 ProcessSP process_sp = target.CreateProcess( 224 launch_info.GetListener(), 225 process_gdb_remote::ProcessGDBRemote::GetPluginNameStatic(), nullptr, 226 true); 227 if (!process_sp) { 228 error.SetErrorString("Failed to create GDB process"); 229 return nullptr; 230 } 231 232 process_sp->HijackProcessEvents(launch_info.GetHijackListener()); 233 234 error = process_sp->ConnectRemote(("unix-connect://" + socket_path).str()); 235 if (error.Fail()) 236 return nullptr; 237 238 if (launch_info.GetPTY().GetPrimaryFileDescriptor() != 239 PseudoTerminal::invalid_fd) 240 process_sp->SetSTDIOFileDescriptor( 241 launch_info.GetPTY().ReleasePrimaryFileDescriptor()); 242 243 return process_sp; 244 } 245 246 Environment PlatformQemuUser::GetEnvironment() { 247 Environment env = Host::GetEnvironment(); 248 for (const auto &KV : GetGlobalProperties().GetTargetEnvVars()) 249 env[KV.first()] = KV.second; 250 return env; 251 } 252