Coverage Report

Created: 2022-01-15 10:30

/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/Listener.h"
18
#include "lldb/Utility/Log.h"
19
20
using namespace lldb;
21
using namespace lldb_private;
22
23
LLDB_PLUGIN_DEFINE(PlatformQemuUser)
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:
35
3.39k
  PluginProperties() {
36
3.39k
    m_collection_sp = std::make_shared<OptionValueProperties>(
37
3.39k
        ConstString(PlatformQemuUser::GetPluginNameStatic()));
38
3.39k
    m_collection_sp->Initialize(g_platformqemuuser_properties);
39
3.39k
  }
40
41
27
  llvm::StringRef GetArchitecture() {
42
27
    return m_collection_sp->GetPropertyAtIndexAsString(
43
27
        nullptr, ePropertyArchitecture, "");
44
27
  }
45
46
9
  FileSpec GetEmulatorPath() {
47
9
    return m_collection_sp->GetPropertyAtIndexAsFileSpec(nullptr,
48
9
                                                         ePropertyEmulatorPath);
49
9
  }
50
51
9
  Args GetEmulatorArgs() {
52
9
    Args result;
53
9
    m_collection_sp->GetPropertyAtIndexAsArgs(nullptr, ePropertyEmulatorArgs,
54
9
                                              result);
55
9
    return result;
56
9
  }
57
58
9
  Environment GetEmulatorEnvVars() {
59
9
    Args args;
60
9
    m_collection_sp->GetPropertyAtIndexAsArgs(nullptr, ePropertyEmulatorEnvVars,
61
9
                                              args);
62
9
    return Environment(args);
63
9
  }
64
65
9
  Environment GetTargetEnvVars() {
66
9
    Args args;
67
9
    m_collection_sp->GetPropertyAtIndexAsArgs(nullptr, ePropertyTargetEnvVars,
68
9
                                              args);
69
9
    return Environment(args);
70
9
  }
71
};
72
73
3.45k
static PluginProperties &GetGlobalProperties() {
74
3.45k
  static PluginProperties g_settings;
75
3.45k
  return g_settings;
76
3.45k
}
77
78
3.43k
llvm::StringRef PlatformQemuUser::GetPluginDescriptionStatic() {
79
3.43k
  return "Platform for debugging binaries under user mode qemu";
80
3.43k
}
81
82
3.43k
void PlatformQemuUser::Initialize() {
83
3.43k
  PluginManager::RegisterPlugin(
84
3.43k
      GetPluginNameStatic(), GetPluginDescriptionStatic(),
85
3.43k
      PlatformQemuUser::CreateInstance, PlatformQemuUser::DebuggerInitialize);
86
3.43k
}
87
88
3.43k
void PlatformQemuUser::Terminate() {
89
3.43k
  PluginManager::UnregisterPlugin(PlatformQemuUser::CreateInstance);
90
3.43k
}
91
92
5.93k
void PlatformQemuUser::DebuggerInitialize(Debugger &debugger) {
93
5.93k
  if (!PluginManager::GetSettingForPlatformPlugin(
94
5.93k
          debugger, ConstString(GetPluginNameStatic()))) {
95
3.39k
    PluginManager::CreateSettingForPlatformPlugin(
96
3.39k
        debugger, GetGlobalProperties().GetValueProperties(),
97
3.39k
        ConstString("Properties for the qemu-user platform plugin."),
98
3.39k
        /*is_global_property=*/true);
99
3.39k
  }
100
5.93k
}
101
102
83
PlatformSP PlatformQemuUser::CreateInstance(bool force, const ArchSpec *arch) {
103
83
  if (force)
104
9
    return PlatformSP(new PlatformQemuUser());
105
74
  return nullptr;
106
83
}
107
108
26
std::vector<ArchSpec> PlatformQemuUser::GetSupportedArchitectures() {
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
122
  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
122
    auto host_it = host.find(KV.first());
133
122
    if (host_it == host.end() || host_it->second != KV.second)
134
2
      set_env.push_back(Environment::compose(KV));
135
122
  }
136
9
  llvm::sort(set_env);
137
138
9
  std::vector<llvm::StringRef> unset_env;
139
159
  for (const auto &KV : host) {
140
    // If the target is missing some host entries, then unset them through
141
    // QEMU_UNSET_ENV.
142
159
    if (target.count(KV.first()) == 0)
143
37
      unset_env.push_back(KV.first());
144
159
  }
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 = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM);
164
165
9
  FileSpec qemu = GetGlobalProperties().GetEmulatorPath();
166
9
  if (!qemu)
167
1
    qemu.SetPath(("qemu-" + GetGlobalProperties().GetArchitecture()).str());
168
9
  FileSystem::Instance().ResolveExecutableLocation(qemu);
169
170
9
  llvm::SmallString<0> socket_model, socket_path;
171
9
  HostInfo::GetProcessTempDir().GetPath(socket_model);
172
9
  llvm::sys::path::append(socket_model, "qemu-%%%%%%%%.socket");
173
9
  do {
174
9
    llvm::sys::fs::createUniquePath(socket_model, socket_path, false);
175
9
  } while (FileSystem::Instance().Exists(socket_path));
176
177
9
  Args args({qemu.GetPath(), "-g", socket_path});
178
9
  if (!launch_info.GetArg0().empty()) {
179
7
    args.AppendArgument("-0");
180
7
    args.AppendArgument(launch_info.GetArg0());
181
7
  }
182
9
  args.AppendArguments(GetGlobalProperties().GetEmulatorArgs());
183
9
  args.AppendArgument("--");
184
9
  args.AppendArgument(launch_info.GetExecutableFile().GetPath());
185
23
  for (size_t i = 1; i < launch_info.GetArguments().size(); 
++i14
)
186
14
    args.AppendArgument(launch_info.GetArguments()[i].ref());
187
188
9
  LLDB_LOG(log, "{0} -> {1}", get_arg_range(launch_info.GetArguments()),
189
9
           get_arg_range(args));
190
191
9
  launch_info.SetArguments(args, true);
192
193
9
  Environment emulator_env = Host::GetEnvironment();
194
9
  if (ConstString sysroot = GetSDKRootDirectory())
195
1
    emulator_env["QEMU_LD_PREFIX"] = sysroot.GetStringRef().str();
196
9
  for (const auto &KV : GetGlobalProperties().GetEmulatorEnvVars())
197
1
    emulator_env[KV.first()] = KV.second;
198
9
  launch_info.GetEnvironment() = ComputeLaunchEnvironment(
199
9
      std::move(launch_info.GetEnvironment()), std::move(emulator_env));
200
201
9
  launch_info.SetLaunchInSeparateProcessGroup(true);
202
9
  launch_info.GetFlags().Clear(eLaunchFlagDebug);
203
9
  launch_info.SetMonitorProcessCallback(ProcessLaunchInfo::NoOpMonitorCallback,
204
9
                                        false);
205
206
  // This is automatically done for host platform in
207
  // Target::FinalizeFileActions, but we're not a host platform.
208
9
  llvm::Error Err = launch_info.SetUpPtyRedirection();
209
9
  LLDB_LOG_ERROR(log, std::move(Err), "SetUpPtyRedirection failed: {0}");
210
211
9
  error = Host::LaunchProcess(launch_info);
212
9
  if (error.Fail())
213
1
    return nullptr;
214
215
8
  ProcessSP process_sp = target.CreateProcess(
216
8
      launch_info.GetListener(),
217
8
      process_gdb_remote::ProcessGDBRemote::GetPluginNameStatic(), nullptr,
218
8
      true);
219
220
8
  ListenerSP listener_sp =
221
8
      Listener::MakeListener("lldb.platform_qemu_user.debugprocess");
222
8
  launch_info.SetHijackListener(listener_sp);
223
8
  Process::ProcessEventHijacker hijacker(*process_sp, listener_sp);
224
225
8
  error = process_sp->ConnectRemote(("unix-connect://" + socket_path).str());
226
8
  if (error.Fail())
227
0
    return nullptr;
228
229
8
  if (launch_info.GetPTY().GetPrimaryFileDescriptor() !=
230
8
      PseudoTerminal::invalid_fd)
231
7
    process_sp->SetSTDIOFileDescriptor(
232
7
        launch_info.GetPTY().ReleasePrimaryFileDescriptor());
233
234
8
  process_sp->WaitForProcessToStop(llvm::None, nullptr, false, listener_sp);
235
8
  return process_sp;
236
8
}
237
238
9
Environment PlatformQemuUser::GetEnvironment() {
239
9
  Environment env = Host::GetEnvironment();
240
9
  for (const auto &KV : GetGlobalProperties().GetTargetEnvVars())
241
2
    env[KV.first()] = KV.second;
242
9
  return env;
243
9
}