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