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> 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 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. 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 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 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