Coverage Report

Created: 2023-09-30 09:22

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