Coverage Report

Created: 2023-09-21 18:56

/Users/buildslave/jenkins/workspace/coverage/llvm-project/lldb/source/Plugins/Platform/POSIX/PlatformPOSIX.cpp
Line
Count
Source (jump to first uncovered line)
1
//===-- PlatformPOSIX.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 "PlatformPOSIX.h"
10
11
#include "Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h"
12
#include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
13
#include "lldb/Core/Debugger.h"
14
#include "lldb/Core/Module.h"
15
#include "lldb/Core/ValueObject.h"
16
#include "lldb/Expression/DiagnosticManager.h"
17
#include "lldb/Expression/FunctionCaller.h"
18
#include "lldb/Expression/UserExpression.h"
19
#include "lldb/Expression/UtilityFunction.h"
20
#include "lldb/Host/File.h"
21
#include "lldb/Host/FileCache.h"
22
#include "lldb/Host/FileSystem.h"
23
#include "lldb/Host/Host.h"
24
#include "lldb/Host/HostInfo.h"
25
#include "lldb/Host/ProcessLaunchInfo.h"
26
#include "lldb/Target/DynamicLoader.h"
27
#include "lldb/Target/ExecutionContext.h"
28
#include "lldb/Target/Process.h"
29
#include "lldb/Target/Thread.h"
30
#include "lldb/Utility/DataBufferHeap.h"
31
#include "lldb/Utility/FileSpec.h"
32
#include "lldb/Utility/LLDBLog.h"
33
#include "lldb/Utility/Log.h"
34
#include "lldb/Utility/StreamString.h"
35
#include "llvm/ADT/ScopeExit.h"
36
#include <optional>
37
38
using namespace lldb;
39
using namespace lldb_private;
40
41
/// Default Constructor
42
PlatformPOSIX::PlatformPOSIX(bool is_host)
43
4.29k
    : RemoteAwarePlatform(is_host), // This is the local host platform
44
4.29k
      m_option_group_platform_rsync(new OptionGroupPlatformRSync()),
45
4.29k
      m_option_group_platform_ssh(new OptionGroupPlatformSSH()),
46
4.29k
      m_option_group_platform_caching(new OptionGroupPlatformCaching()) {}
47
48
/// Destructor.
49
///
50
/// The destructor is virtual since this class is designed to be
51
/// inherited from by the plug-in instance.
52
4.24k
PlatformPOSIX::~PlatformPOSIX() = default;
53
54
lldb_private::OptionGroupOptions *PlatformPOSIX::GetConnectionOptions(
55
1
    lldb_private::CommandInterpreter &interpreter) {
56
1
  auto iter = m_options.find(&interpreter), end = m_options.end();
57
1
  if (iter == end) {
58
1
    std::unique_ptr<lldb_private::OptionGroupOptions> options(
59
1
        new OptionGroupOptions());
60
1
    options->Append(m_option_group_platform_rsync.get());
61
1
    options->Append(m_option_group_platform_ssh.get());
62
1
    options->Append(m_option_group_platform_caching.get());
63
1
    m_options[&interpreter] = std::move(options);
64
1
  }
65
66
1
  return m_options.at(&interpreter).get();
67
1
}
68
69
static uint32_t chown_file(Platform *platform, const char *path,
70
                           uint32_t uid = UINT32_MAX,
71
0
                           uint32_t gid = UINT32_MAX) {
72
0
  if (!platform || !path || *path == 0)
73
0
    return UINT32_MAX;
74
75
0
  if (uid == UINT32_MAX && gid == UINT32_MAX)
76
0
    return 0; // pretend I did chown correctly - actually I just didn't care
77
78
0
  StreamString command;
79
0
  command.PutCString("chown ");
80
0
  if (uid != UINT32_MAX)
81
0
    command.Printf("%d", uid);
82
0
  if (gid != UINT32_MAX)
83
0
    command.Printf(":%d", gid);
84
0
  command.Printf("%s", path);
85
0
  int status;
86
0
  platform->RunShellCommand(command.GetData(), FileSpec(), &status, nullptr,
87
0
                            nullptr, std::chrono::seconds(10));
88
0
  return status;
89
0
}
90
91
lldb_private::Status
92
PlatformPOSIX::PutFile(const lldb_private::FileSpec &source,
93
                       const lldb_private::FileSpec &destination, uint32_t uid,
94
1
                       uint32_t gid) {
95
1
  Log *log = GetLog(LLDBLog::Platform);
96
97
1
  if (IsHost()) {
98
0
    if (source == destination)
99
0
      return Status();
100
    // cp src dst
101
    // chown uid:gid dst
102
0
    std::string src_path(source.GetPath());
103
0
    if (src_path.empty())
104
0
      return Status("unable to get file path for source");
105
0
    std::string dst_path(destination.GetPath());
106
0
    if (dst_path.empty())
107
0
      return Status("unable to get file path for destination");
108
0
    StreamString command;
109
0
    command.Printf("cp %s %s", src_path.c_str(), dst_path.c_str());
110
0
    int status;
111
0
    RunShellCommand(command.GetData(), FileSpec(), &status, nullptr, nullptr,
112
0
                    std::chrono::seconds(10));
113
0
    if (status != 0)
114
0
      return Status("unable to perform copy");
115
0
    if (uid == UINT32_MAX && gid == UINT32_MAX)
116
0
      return Status();
117
0
    if (chown_file(this, dst_path.c_str(), uid, gid) != 0)
118
0
      return Status("unable to perform chown");
119
0
    return Status();
120
1
  } else if (m_remote_platform_sp) {
121
1
    if (GetSupportsRSync()) {
122
0
      std::string src_path(source.GetPath());
123
0
      if (src_path.empty())
124
0
        return Status("unable to get file path for source");
125
0
      std::string dst_path(destination.GetPath());
126
0
      if (dst_path.empty())
127
0
        return Status("unable to get file path for destination");
128
0
      StreamString command;
129
0
      if (GetIgnoresRemoteHostname()) {
130
0
        if (!GetRSyncPrefix())
131
0
          command.Printf("rsync %s %s %s", GetRSyncOpts(), src_path.c_str(),
132
0
                         dst_path.c_str());
133
0
        else
134
0
          command.Printf("rsync %s %s %s%s", GetRSyncOpts(), src_path.c_str(),
135
0
                         GetRSyncPrefix(), dst_path.c_str());
136
0
      } else
137
0
        command.Printf("rsync %s %s %s:%s", GetRSyncOpts(), src_path.c_str(),
138
0
                       GetHostname(), dst_path.c_str());
139
0
      LLDB_LOGF(log, "[PutFile] Running command: %s\n", command.GetData());
140
0
      int retcode;
141
0
      Host::RunShellCommand(command.GetData(), FileSpec(), &retcode, nullptr,
142
0
                            nullptr, std::chrono::minutes(1));
143
0
      if (retcode == 0) {
144
        // Don't chown a local file for a remote system
145
        //                if (chown_file(this,dst_path.c_str(),uid,gid) != 0)
146
        //                    return Status("unable to perform chown");
147
0
        return Status();
148
0
      }
149
      // if we are still here rsync has failed - let's try the slow way before
150
      // giving up
151
0
    }
152
1
  }
153
1
  return Platform::PutFile(source, destination, uid, gid);
154
1
}
155
156
lldb_private::Status PlatformPOSIX::GetFile(
157
    const lldb_private::FileSpec &source,      // remote file path
158
    const lldb_private::FileSpec &destination) // local file path
159
4
{
160
4
  Log *log = GetLog(LLDBLog::Platform);
161
162
  // Check the args, first.
163
4
  std::string src_path(source.GetPath());
164
4
  if (src_path.empty())
165
0
    return Status("unable to get file path for source");
166
4
  std::string dst_path(destination.GetPath());
167
4
  if (dst_path.empty())
168
0
    return Status("unable to get file path for destination");
169
4
  if (IsHost()) {
170
0
    if (source == destination)
171
0
      return Status("local scenario->source and destination are the same file "
172
0
                    "path: no operation performed");
173
    // cp src dst
174
0
    StreamString cp_command;
175
0
    cp_command.Printf("cp %s %s", src_path.c_str(), dst_path.c_str());
176
0
    int status;
177
0
    RunShellCommand(cp_command.GetData(), FileSpec(), &status, nullptr, nullptr,
178
0
                    std::chrono::seconds(10));
179
0
    if (status != 0)
180
0
      return Status("unable to perform copy");
181
0
    return Status();
182
4
  } else if (m_remote_platform_sp) {
183
0
    if (GetSupportsRSync()) {
184
0
      StreamString command;
185
0
      if (GetIgnoresRemoteHostname()) {
186
0
        if (!GetRSyncPrefix())
187
0
          command.Printf("rsync %s %s %s", GetRSyncOpts(), src_path.c_str(),
188
0
                         dst_path.c_str());
189
0
        else
190
0
          command.Printf("rsync %s %s%s %s", GetRSyncOpts(), GetRSyncPrefix(),
191
0
                         src_path.c_str(), dst_path.c_str());
192
0
      } else
193
0
        command.Printf("rsync %s %s:%s %s", GetRSyncOpts(),
194
0
                       m_remote_platform_sp->GetHostname(), src_path.c_str(),
195
0
                       dst_path.c_str());
196
0
      LLDB_LOGF(log, "[GetFile] Running command: %s\n", command.GetData());
197
0
      int retcode;
198
0
      Host::RunShellCommand(command.GetData(), FileSpec(), &retcode, nullptr,
199
0
                            nullptr, std::chrono::minutes(1));
200
0
      if (retcode == 0)
201
0
        return Status();
202
      // If we are here, rsync has failed - let's try the slow way before
203
      // giving up
204
0
    }
205
    // open src and dst
206
    // read/write, read/write, read/write, ...
207
    // close src
208
    // close dst
209
0
    LLDB_LOGF(log, "[GetFile] Using block by block transfer....\n");
210
0
    Status error;
211
0
    user_id_t fd_src = OpenFile(source, File::eOpenOptionReadOnly,
212
0
                                lldb::eFilePermissionsFileDefault, error);
213
214
0
    if (fd_src == UINT64_MAX)
215
0
      return Status("unable to open source file");
216
217
0
    uint32_t permissions = 0;
218
0
    error = GetFilePermissions(source, permissions);
219
220
0
    if (permissions == 0)
221
0
      permissions = lldb::eFilePermissionsFileDefault;
222
223
0
    user_id_t fd_dst = FileCache::GetInstance().OpenFile(
224
0
        destination, File::eOpenOptionCanCreate | File::eOpenOptionWriteOnly |
225
0
                         File::eOpenOptionTruncate,
226
0
        permissions, error);
227
228
0
    if (fd_dst == UINT64_MAX) {
229
0
      if (error.Success())
230
0
        error.SetErrorString("unable to open destination file");
231
0
    }
232
233
0
    if (error.Success()) {
234
0
      lldb::WritableDataBufferSP buffer_sp(new DataBufferHeap(1024, 0));
235
0
      uint64_t offset = 0;
236
0
      error.Clear();
237
0
      while (error.Success()) {
238
0
        const uint64_t n_read = ReadFile(fd_src, offset, buffer_sp->GetBytes(),
239
0
                                         buffer_sp->GetByteSize(), error);
240
0
        if (error.Fail())
241
0
          break;
242
0
        if (n_read == 0)
243
0
          break;
244
0
        if (FileCache::GetInstance().WriteFile(fd_dst, offset,
245
0
                                               buffer_sp->GetBytes(), n_read,
246
0
                                               error) != n_read) {
247
0
          if (!error.Fail())
248
0
            error.SetErrorString("unable to write to destination file");
249
0
          break;
250
0
        }
251
0
        offset += n_read;
252
0
      }
253
0
    }
254
    // Ignore the close error of src.
255
0
    if (fd_src != UINT64_MAX)
256
0
      CloseFile(fd_src, error);
257
    // And close the dst file descriptot.
258
0
    if (fd_dst != UINT64_MAX &&
259
0
        !FileCache::GetInstance().CloseFile(fd_dst, error)) {
260
0
      if (!error.Fail())
261
0
        error.SetErrorString("unable to close destination file");
262
0
    }
263
0
    return error;
264
0
  }
265
4
  return Platform::GetFile(source, destination);
266
4
}
267
268
13
std::string PlatformPOSIX::GetPlatformSpecificConnectionInformation() {
269
13
  StreamString stream;
270
13
  if (GetSupportsRSync()) {
271
0
    stream.PutCString("rsync");
272
0
    if ((GetRSyncOpts() && *GetRSyncOpts()) ||
273
0
        (GetRSyncPrefix() && *GetRSyncPrefix()) || GetIgnoresRemoteHostname()) {
274
0
      stream.Printf(", options: ");
275
0
      if (GetRSyncOpts() && *GetRSyncOpts())
276
0
        stream.Printf("'%s' ", GetRSyncOpts());
277
0
      stream.Printf(", prefix: ");
278
0
      if (GetRSyncPrefix() && *GetRSyncPrefix())
279
0
        stream.Printf("'%s' ", GetRSyncPrefix());
280
0
      if (GetIgnoresRemoteHostname())
281
0
        stream.Printf("ignore remote-hostname ");
282
0
    }
283
0
  }
284
13
  if (GetSupportsSSH()) {
285
0
    stream.PutCString("ssh");
286
0
    if (GetSSHOpts() && *GetSSHOpts())
287
0
      stream.Printf(", options: '%s' ", GetSSHOpts());
288
0
  }
289
13
  if (GetLocalCacheDirectory() && *GetLocalCacheDirectory())
290
0
    stream.Printf("cache dir: %s", GetLocalCacheDirectory());
291
13
  if (stream.GetSize())
292
0
    return std::string(stream.GetString());
293
13
  else
294
13
    return "";
295
13
}
296
297
1
const lldb::UnixSignalsSP &PlatformPOSIX::GetRemoteUnixSignals() {
298
1
  if (IsRemote() && m_remote_platform_sp)
299
1
    return m_remote_platform_sp->GetRemoteUnixSignals();
300
0
  return Platform::GetRemoteUnixSignals();
301
1
}
302
303
4
Status PlatformPOSIX::ConnectRemote(Args &args) {
304
4
  Status error;
305
4
  if (IsHost()) {
306
0
    error.SetErrorStringWithFormatv(
307
0
        "can't connect to the host platform '{0}', always connected",
308
0
        GetPluginName());
309
4
  } else {
310
4
    if (!m_remote_platform_sp)
311
4
      m_remote_platform_sp =
312
4
          platform_gdb_server::PlatformRemoteGDBServer::CreateInstance(
313
4
              /*force=*/true, nullptr);
314
315
4
    if (m_remote_platform_sp && error.Success())
316
4
      error = m_remote_platform_sp->ConnectRemote(args);
317
0
    else
318
0
      error.SetErrorString("failed to create a 'remote-gdb-server' platform");
319
320
4
    if (error.Fail())
321
0
      m_remote_platform_sp.reset();
322
4
  }
323
324
4
  if (error.Success() && m_remote_platform_sp) {
325
4
    if (m_option_group_platform_rsync.get() &&
326
4
        m_option_group_platform_ssh.get() &&
327
4
        m_option_group_platform_caching.get()) {
328
4
      if (m_option_group_platform_rsync->m_rsync) {
329
0
        SetSupportsRSync(true);
330
0
        SetRSyncOpts(m_option_group_platform_rsync->m_rsync_opts.c_str());
331
0
        SetRSyncPrefix(m_option_group_platform_rsync->m_rsync_prefix.c_str());
332
0
        SetIgnoresRemoteHostname(
333
0
            m_option_group_platform_rsync->m_ignores_remote_hostname);
334
0
      }
335
4
      if (m_option_group_platform_ssh->m_ssh) {
336
0
        SetSupportsSSH(true);
337
0
        SetSSHOpts(m_option_group_platform_ssh->m_ssh_opts.c_str());
338
0
      }
339
4
      SetLocalCacheDirectory(
340
4
          m_option_group_platform_caching->m_cache_dir.c_str());
341
4
    }
342
4
  }
343
344
4
  return error;
345
4
}
346
347
3
Status PlatformPOSIX::DisconnectRemote() {
348
3
  Status error;
349
350
3
  if (IsHost()) {
351
0
    error.SetErrorStringWithFormatv(
352
0
        "can't disconnect from the host platform '{0}', always connected",
353
0
        GetPluginName());
354
3
  } else {
355
3
    if (m_remote_platform_sp)
356
3
      error = m_remote_platform_sp->DisconnectRemote();
357
0
    else
358
0
      error.SetErrorString("the platform is not currently connected");
359
3
  }
360
3
  return error;
361
3
}
362
363
lldb::ProcessSP PlatformPOSIX::Attach(ProcessAttachInfo &attach_info,
364
                                      Debugger &debugger, Target *target,
365
2.12k
                                      Status &error) {
366
2.12k
  lldb::ProcessSP process_sp;
367
2.12k
  Log *log = GetLog(LLDBLog::Platform);
368
369
2.12k
  if (IsHost()) {
370
2.12k
    if (target == nullptr) {
371
0
      TargetSP new_target_sp;
372
373
0
      error = debugger.GetTargetList().CreateTarget(
374
0
          debugger, "", "", eLoadDependentsNo, nullptr, new_target_sp);
375
0
      target = new_target_sp.get();
376
0
      LLDB_LOGF(log, "PlatformPOSIX::%s created new target", __FUNCTION__);
377
2.12k
    } else {
378
2.12k
      error.Clear();
379
2.12k
      LLDB_LOGF(log, "PlatformPOSIX::%s target already existed, setting target",
380
2.12k
                __FUNCTION__);
381
2.12k
    }
382
383
2.12k
    if (target && error.Success()) {
384
2.12k
      if (log) {
385
0
        ModuleSP exe_module_sp = target->GetExecutableModule();
386
0
        LLDB_LOGF(log, "PlatformPOSIX::%s set selected target to %p %s",
387
0
                  __FUNCTION__, (void *)target,
388
0
                  exe_module_sp ? exe_module_sp->GetFileSpec().GetPath().c_str()
389
0
                                : "<null>");
390
0
      }
391
392
2.12k
      process_sp =
393
2.12k
          target->CreateProcess(attach_info.GetListenerForProcess(debugger),
394
2.12k
                                "gdb-remote", nullptr, true);
395
396
2.12k
      if (process_sp) {
397
2.12k
        ListenerSP listener_sp = attach_info.GetHijackListener();
398
2.12k
        if (listener_sp == nullptr) {
399
3
          listener_sp =
400
3
              Listener::MakeListener("lldb.PlatformPOSIX.attach.hijack");
401
3
          attach_info.SetHijackListener(listener_sp);
402
3
        }
403
2.12k
        process_sp->HijackProcessEvents(listener_sp);
404
2.12k
        process_sp->SetShadowListener(attach_info.GetShadowListener());
405
2.12k
        error = process_sp->Attach(attach_info);
406
2.12k
      }
407
2.12k
    }
408
2.12k
  } else {
409
0
    if (m_remote_platform_sp)
410
0
      process_sp =
411
0
          m_remote_platform_sp->Attach(attach_info, debugger, target, error);
412
0
    else
413
0
      error.SetErrorString("the platform is not currently connected");
414
0
  }
415
2.12k
  return process_sp;
416
2.12k
}
417
418
lldb::ProcessSP PlatformPOSIX::DebugProcess(ProcessLaunchInfo &launch_info,
419
                                            Debugger &debugger, Target &target,
420
0
                                            Status &error) {
421
0
  Log *log = GetLog(LLDBLog::Platform);
422
0
  LLDB_LOG(log, "target {0}", &target);
423
424
0
  ProcessSP process_sp;
425
426
0
  if (!IsHost()) {
427
0
    if (m_remote_platform_sp)
428
0
      process_sp = m_remote_platform_sp->DebugProcess(launch_info, debugger,
429
0
                                                      target, error);
430
0
    else
431
0
      error.SetErrorString("the platform is not currently connected");
432
0
    return process_sp;
433
0
  }
434
435
  //
436
  // For local debugging, we'll insist on having ProcessGDBRemote create the
437
  // process.
438
  //
439
440
  // Make sure we stop at the entry point
441
0
  launch_info.GetFlags().Set(eLaunchFlagDebug);
442
443
  // We always launch the process we are going to debug in a separate process
444
  // group, since then we can handle ^C interrupts ourselves w/o having to
445
  // worry about the target getting them as well.
446
0
  launch_info.SetLaunchInSeparateProcessGroup(true);
447
448
  // Now create the gdb-remote process.
449
0
  LLDB_LOG(log, "having target create process with gdb-remote plugin");
450
0
  process_sp = target.CreateProcess(launch_info.GetListener(), "gdb-remote",
451
0
                                    nullptr, true);
452
453
0
  if (!process_sp) {
454
0
    error.SetErrorString("CreateProcess() failed for gdb-remote process");
455
0
    LLDB_LOG(log, "error: {0}", error);
456
0
    return process_sp;
457
0
  }
458
459
0
  LLDB_LOG(log, "successfully created process");
460
461
0
  process_sp->HijackProcessEvents(launch_info.GetHijackListener());
462
0
  process_sp->SetShadowListener(launch_info.GetShadowListener());
463
464
  // Log file actions.
465
0
  if (log) {
466
0
    LLDB_LOG(log, "launching process with the following file actions:");
467
0
    StreamString stream;
468
0
    size_t i = 0;
469
0
    const FileAction *file_action;
470
0
    while ((file_action = launch_info.GetFileActionAtIndex(i++)) != nullptr) {
471
0
      file_action->Dump(stream);
472
0
      LLDB_LOG(log, "{0}", stream.GetData());
473
0
      stream.Clear();
474
0
    }
475
0
  }
476
477
  // Do the launch.
478
0
  error = process_sp->Launch(launch_info);
479
0
  if (error.Success()) {
480
    // Hook up process PTY if we have one (which we should for local debugging
481
    // with llgs).
482
0
    int pty_fd = launch_info.GetPTY().ReleasePrimaryFileDescriptor();
483
0
    if (pty_fd != PseudoTerminal::invalid_fd) {
484
0
      process_sp->SetSTDIOFileDescriptor(pty_fd);
485
0
      LLDB_LOG(log, "hooked up STDIO pty to process");
486
0
    } else
487
0
      LLDB_LOG(log, "not using process STDIO pty");
488
0
  } else {
489
0
    LLDB_LOG(log, "{0}", error);
490
    // FIXME figure out appropriate cleanup here. Do we delete the process?
491
    // Does our caller do that?
492
0
  }
493
494
0
  return process_sp;
495
0
}
496
497
0
void PlatformPOSIX::CalculateTrapHandlerSymbolNames() {
498
0
  m_trap_handlers.push_back(ConstString("_sigtramp"));
499
0
}
500
501
Status PlatformPOSIX::EvaluateLibdlExpression(
502
    lldb_private::Process *process, const char *expr_cstr,
503
5
    llvm::StringRef expr_prefix, lldb::ValueObjectSP &result_valobj_sp) {
504
5
  DynamicLoader *loader = process->GetDynamicLoader();
505
5
  if (loader) {
506
5
    Status error = loader->CanLoadImage();
507
5
    if (error.Fail())
508
0
      return error;
509
5
  }
510
511
5
  ThreadSP thread_sp(process->GetThreadList().GetExpressionExecutionThread());
512
5
  if (!thread_sp)
513
0
    return Status("Selected thread isn't valid");
514
515
5
  StackFrameSP frame_sp(thread_sp->GetStackFrameAtIndex(0));
516
5
  if (!frame_sp)
517
0
    return Status("Frame 0 isn't valid");
518
519
5
  ExecutionContext exe_ctx;
520
5
  frame_sp->CalculateExecutionContext(exe_ctx);
521
5
  EvaluateExpressionOptions expr_options;
522
5
  expr_options.SetUnwindOnError(true);
523
5
  expr_options.SetIgnoreBreakpoints(true);
524
5
  expr_options.SetExecutionPolicy(eExecutionPolicyAlways);
525
5
  expr_options.SetLanguage(eLanguageTypeC_plus_plus);
526
5
  expr_options.SetTrapExceptions(false); // dlopen can't throw exceptions, so
527
                                         // don't do the work to trap them.
528
5
  expr_options.SetTimeout(process->GetUtilityExpressionTimeout());
529
530
5
  Status expr_error;
531
5
  ExpressionResults result =
532
5
      UserExpression::Evaluate(exe_ctx, expr_options, expr_cstr, expr_prefix,
533
5
                               result_valobj_sp, expr_error);
534
5
  if (result != eExpressionCompleted)
535
0
    return expr_error;
536
537
5
  if (result_valobj_sp->GetError().Fail())
538
0
    return result_valobj_sp->GetError();
539
5
  return Status();
540
5
}
541
542
std::unique_ptr<UtilityFunction>
543
PlatformPOSIX::MakeLoadImageUtilityFunction(ExecutionContext &exe_ctx,
544
4
                                            Status &error) {
545
  // Remember to prepend this with the prefix from
546
  // GetLibdlFunctionDeclarations. The returned values are all in
547
  // __lldb_dlopen_result for consistency. The wrapper returns a void * but
548
  // doesn't use it because UtilityFunctions don't work with void returns at
549
  // present.
550
  //
551
  // Use lazy binding so as to not make dlopen()'s success conditional on
552
  // forcing every symbol in the library.
553
  //
554
  // In general, the debugger should allow programs to load & run with
555
  // libraries as far as they can, instead of defaulting to being super-picky
556
  // about unavailable symbols.
557
  //
558
  // The value "1" appears to imply lazy binding (RTLD_LAZY) on both Darwin
559
  // and other POSIX OSes.
560
4
  static const char *dlopen_wrapper_code = R"(
561
4
  const int RTLD_LAZY = 1;
562
4
563
4
  struct __lldb_dlopen_result {
564
4
    void *image_ptr;
565
4
    const char *error_str;
566
4
  };
567
4
  
568
4
  extern "C" void *memcpy(void *, const void *, size_t size);
569
4
  extern "C" size_t strlen(const char *);
570
4
  
571
4
572
4
  void * __lldb_dlopen_wrapper (const char *name, 
573
4
                                const char *path_strings,
574
4
                                char *buffer,
575
4
                                __lldb_dlopen_result *result_ptr)
576
4
  {
577
4
    // This is the case where the name is the full path:
578
4
    if (!path_strings) {
579
4
      result_ptr->image_ptr = dlopen(name, RTLD_LAZY);
580
4
      if (result_ptr->image_ptr)
581
4
        result_ptr->error_str = nullptr;
582
4
      else
583
4
        result_ptr->error_str = dlerror();
584
4
      return nullptr;
585
4
    }
586
4
    
587
4
    // This is the case where we have a list of paths:
588
4
    size_t name_len = strlen(name);
589
4
    while (path_strings && path_strings[0] != '\0') {
590
4
      size_t path_len = strlen(path_strings);
591
4
      memcpy((void *) buffer, (void *) path_strings, path_len);
592
4
      buffer[path_len] = '/';
593
4
      char *target_ptr = buffer+path_len+1; 
594
4
      memcpy((void *) target_ptr, (void *) name, name_len + 1);
595
4
      result_ptr->image_ptr = dlopen(buffer, RTLD_LAZY);
596
4
      if (result_ptr->image_ptr) {
597
4
        result_ptr->error_str = nullptr;
598
4
        break;
599
4
      }
600
4
      result_ptr->error_str = dlerror();
601
4
      path_strings = path_strings + path_len + 1;
602
4
    }
603
4
    return nullptr;
604
4
  }
605
4
  )";
606
607
4
  static const char *dlopen_wrapper_name = "__lldb_dlopen_wrapper";
608
4
  Process *process = exe_ctx.GetProcessSP().get();
609
  // Insert the dlopen shim defines into our generic expression:
610
4
  std::string expr(std::string(GetLibdlFunctionDeclarations(process)));
611
4
  expr.append(dlopen_wrapper_code);
612
4
  Status utility_error;
613
4
  DiagnosticManager diagnostics;
614
615
4
  auto utility_fn_or_error = process->GetTarget().CreateUtilityFunction(
616
4
      std::move(expr), dlopen_wrapper_name, eLanguageTypeC_plus_plus, exe_ctx);
617
4
  if (!utility_fn_or_error) {
618
0
    std::string error_str = llvm::toString(utility_fn_or_error.takeError());
619
0
    error.SetErrorStringWithFormat(
620
0
        "dlopen error: could not create utility function: %s",
621
0
        error_str.c_str());
622
0
    return nullptr;
623
0
  }
624
4
  std::unique_ptr<UtilityFunction> dlopen_utility_func_up =
625
4
      std::move(*utility_fn_or_error);
626
627
4
  Value value;
628
4
  ValueList arguments;
629
4
  FunctionCaller *do_dlopen_function = nullptr;
630
631
  // Fetch the clang types we will need:
632
4
  TypeSystemClangSP scratch_ts_sp =
633
4
      ScratchTypeSystemClang::GetForTarget(process->GetTarget());
634
4
  if (!scratch_ts_sp)
635
0
    return nullptr;
636
637
4
  CompilerType clang_void_pointer_type =
638
4
      scratch_ts_sp->GetBasicType(eBasicTypeVoid).GetPointerType();
639
4
  CompilerType clang_char_pointer_type =
640
4
      scratch_ts_sp->GetBasicType(eBasicTypeChar).GetPointerType();
641
642
  // We are passing four arguments, the basename, the list of places to look,
643
  // a buffer big enough for all the path + name combos, and
644
  // a pointer to the storage we've made for the result:
645
4
  value.SetValueType(Value::ValueType::Scalar);
646
4
  value.SetCompilerType(clang_void_pointer_type);
647
4
  arguments.PushValue(value);
648
4
  value.SetCompilerType(clang_char_pointer_type);
649
4
  arguments.PushValue(value);
650
4
  arguments.PushValue(value);
651
4
  arguments.PushValue(value);
652
  
653
4
  do_dlopen_function = dlopen_utility_func_up->MakeFunctionCaller(
654
4
      clang_void_pointer_type, arguments, exe_ctx.GetThreadSP(), utility_error);
655
4
  if (utility_error.Fail()) {
656
0
    error.SetErrorStringWithFormat(
657
0
        "dlopen error: could not make function caller: %s",
658
0
        utility_error.AsCString());
659
0
    return nullptr;
660
0
  }
661
  
662
4
  do_dlopen_function = dlopen_utility_func_up->GetFunctionCaller();
663
4
  if (!do_dlopen_function) {
664
0
    error.SetErrorString("dlopen error: could not get function caller.");
665
0
    return nullptr;
666
0
  }
667
  
668
  // We made a good utility function, so cache it in the process:
669
4
  return dlopen_utility_func_up;
670
4
}
671
672
uint32_t PlatformPOSIX::DoLoadImage(lldb_private::Process *process,
673
                                    const lldb_private::FileSpec &remote_file,
674
                                    const std::vector<std::string> *paths,
675
                                    lldb_private::Status &error,
676
11
                                    lldb_private::FileSpec *loaded_image) {
677
11
  if (loaded_image)
678
7
    loaded_image->Clear();
679
680
11
  std::string path;
681
11
  path = remote_file.GetPath();
682
  
683
11
  ThreadSP thread_sp = process->GetThreadList().GetExpressionExecutionThread();
684
11
  if (!thread_sp) {
685
0
    error.SetErrorString("dlopen error: no thread available to call dlopen.");
686
0
    return LLDB_INVALID_IMAGE_TOKEN;
687
0
  }
688
  
689
11
  DiagnosticManager diagnostics;
690
  
691
11
  ExecutionContext exe_ctx;
692
11
  thread_sp->CalculateExecutionContext(exe_ctx);
693
694
11
  Status utility_error;
695
11
  UtilityFunction *dlopen_utility_func;
696
11
  ValueList arguments;
697
11
  FunctionCaller *do_dlopen_function = nullptr;
698
699
  // The UtilityFunction is held in the Process.  Platforms don't track the
700
  // lifespan of the Targets that use them, we can't put this in the Platform.
701
11
  dlopen_utility_func = process->GetLoadImageUtilityFunction(
702
11
      this, [&]() -> std::unique_ptr<UtilityFunction> {
703
4
        return MakeLoadImageUtilityFunction(exe_ctx, error);
704
4
      });
705
  // If we couldn't make it, the error will be in error, so we can exit here.
706
11
  if (!dlopen_utility_func)
707
0
    return LLDB_INVALID_IMAGE_TOKEN;
708
    
709
11
  do_dlopen_function = dlopen_utility_func->GetFunctionCaller();
710
11
  if (!do_dlopen_function) {
711
0
    error.SetErrorString("dlopen error: could not get function caller.");
712
0
    return LLDB_INVALID_IMAGE_TOKEN;
713
0
  }
714
11
  arguments = do_dlopen_function->GetArgumentValues();
715
  
716
  // Now insert the path we are searching for and the result structure into the
717
  // target.
718
11
  uint32_t permissions = ePermissionsReadable|ePermissionsWritable;
719
11
  size_t path_len = path.size() + 1;
720
11
  lldb::addr_t path_addr = process->AllocateMemory(path_len, 
721
11
                                                   permissions,
722
11
                                                   utility_error);
723
11
  if (path_addr == LLDB_INVALID_ADDRESS) {
724
0
    error.SetErrorStringWithFormat(
725
0
        "dlopen error: could not allocate memory for path: %s",
726
0
        utility_error.AsCString());
727
0
    return LLDB_INVALID_IMAGE_TOKEN;
728
0
  }
729
730
  // Make sure we deallocate the input string memory:
731
11
  auto path_cleanup = llvm::make_scope_exit([process, path_addr] {
732
    // Deallocate the buffer.
733
11
    process->DeallocateMemory(path_addr);
734
11
  });
735
736
11
  process->WriteMemory(path_addr, path.c_str(), path_len, utility_error);
737
11
  if (utility_error.Fail()) {
738
0
    error.SetErrorStringWithFormat(
739
0
        "dlopen error: could not write path string: %s",
740
0
        utility_error.AsCString());
741
0
    return LLDB_INVALID_IMAGE_TOKEN;
742
0
  }
743
  
744
  // Make space for our return structure.  It is two pointers big: the token
745
  // and the error string.
746
11
  const uint32_t addr_size = process->GetAddressByteSize();
747
11
  lldb::addr_t return_addr = process->CallocateMemory(2*addr_size,
748
11
                                                      permissions,
749
11
                                                      utility_error);
750
11
  if (utility_error.Fail()) {
751
0
    error.SetErrorStringWithFormat(
752
0
        "dlopen error: could not allocate memory for path: %s",
753
0
        utility_error.AsCString());
754
0
    return LLDB_INVALID_IMAGE_TOKEN;
755
0
  }
756
  
757
  // Make sure we deallocate the result structure memory
758
11
  auto return_cleanup = llvm::make_scope_exit([process, return_addr] {
759
    // Deallocate the buffer
760
11
    process->DeallocateMemory(return_addr);
761
11
  });
762
763
  // This will be the address of the storage for paths, if we are using them,
764
  // or nullptr to signal we aren't.
765
11
  lldb::addr_t path_array_addr = 0x0;
766
11
  std::optional<llvm::detail::scope_exit<std::function<void()>>>
767
11
      path_array_cleanup;
768
769
  // This is the address to a buffer large enough to hold the largest path
770
  // conjoined with the library name we're passing in.  This is a convenience 
771
  // to avoid having to call malloc in the dlopen function.
772
11
  lldb::addr_t buffer_addr = 0x0;
773
11
  std::optional<llvm::detail::scope_exit<std::function<void()>>> buffer_cleanup;
774
775
  // Set the values into our args and write them to the target:
776
11
  if (paths != nullptr) {
777
    // First insert the paths into the target.  This is expected to be a 
778
    // continuous buffer with the strings laid out null terminated and
779
    // end to end with an empty string terminating the buffer.
780
    // We also compute the buffer's required size as we go.
781
7
    size_t buffer_size = 0;
782
7
    std::string path_array;
783
18
    for (auto path : *paths) {
784
      // Don't insert empty paths, they will make us abort the path
785
      // search prematurely.
786
18
      if (path.empty())
787
3
        continue;
788
15
      size_t path_size = path.size();
789
15
      path_array.append(path);
790
15
      path_array.push_back('\0');
791
15
      if (path_size > buffer_size)
792
9
        buffer_size = path_size;
793
15
    }
794
7
    path_array.push_back('\0');
795
    
796
7
    path_array_addr = process->AllocateMemory(path_array.size(), 
797
7
                                              permissions,
798
7
                                              utility_error);
799
7
    if (path_array_addr == LLDB_INVALID_ADDRESS) {
800
0
      error.SetErrorStringWithFormat(
801
0
          "dlopen error: could not allocate memory for path array: %s",
802
0
          utility_error.AsCString());
803
0
      return LLDB_INVALID_IMAGE_TOKEN;
804
0
    }
805
    
806
    // Make sure we deallocate the paths array.
807
7
    path_array_cleanup.emplace([process, path_array_addr]() {
808
      // Deallocate the path array.
809
7
      process->DeallocateMemory(path_array_addr);
810
7
    });
811
812
7
    process->WriteMemory(path_array_addr, path_array.data(), 
813
7
                         path_array.size(), utility_error);
814
815
7
    if (utility_error.Fail()) {
816
0
      error.SetErrorStringWithFormat(
817
0
          "dlopen error: could not write path array: %s",
818
0
          utility_error.AsCString());
819
0
      return LLDB_INVALID_IMAGE_TOKEN;
820
0
    }
821
    // Now make spaces in the target for the buffer.  We need to add one for
822
    // the '/' that the utility function will insert and one for the '\0':
823
7
    buffer_size += path.size() + 2;
824
    
825
7
    buffer_addr = process->AllocateMemory(buffer_size, 
826
7
                                          permissions,
827
7
                                          utility_error);
828
7
    if (buffer_addr == LLDB_INVALID_ADDRESS) {
829
0
      error.SetErrorStringWithFormat(
830
0
          "dlopen error: could not allocate memory for buffer: %s",
831
0
          utility_error.AsCString());
832
0
      return LLDB_INVALID_IMAGE_TOKEN;
833
0
    }
834
  
835
    // Make sure we deallocate the buffer memory:
836
7
    buffer_cleanup.emplace([process, buffer_addr]() {
837
      // Deallocate the buffer.
838
7
      process->DeallocateMemory(buffer_addr);
839
7
    });
840
7
  }
841
    
842
11
  arguments.GetValueAtIndex(0)->GetScalar() = path_addr;
843
11
  arguments.GetValueAtIndex(1)->GetScalar() = path_array_addr;
844
11
  arguments.GetValueAtIndex(2)->GetScalar() = buffer_addr;
845
11
  arguments.GetValueAtIndex(3)->GetScalar() = return_addr;
846
847
11
  lldb::addr_t func_args_addr = LLDB_INVALID_ADDRESS;
848
  
849
11
  diagnostics.Clear();
850
11
  if (!do_dlopen_function->WriteFunctionArguments(exe_ctx, 
851
11
                                                 func_args_addr,
852
11
                                                 arguments,
853
11
                                                 diagnostics)) {
854
0
    error.SetErrorStringWithFormat(
855
0
        "dlopen error: could not write function arguments: %s",
856
0
        diagnostics.GetString().c_str());
857
0
    return LLDB_INVALID_IMAGE_TOKEN;
858
0
  }
859
  
860
  // Make sure we clean up the args structure.  We can't reuse it because the
861
  // Platform lives longer than the process and the Platforms don't get a
862
  // signal to clean up cached data when a process goes away.
863
11
  auto args_cleanup =
864
11
      llvm::make_scope_exit([do_dlopen_function, &exe_ctx, func_args_addr] {
865
11
        do_dlopen_function->DeallocateFunctionResults(exe_ctx, func_args_addr);
866
11
      });
867
868
  // Now run the caller:
869
11
  EvaluateExpressionOptions options;
870
11
  options.SetExecutionPolicy(eExecutionPolicyAlways);
871
11
  options.SetLanguage(eLanguageTypeC_plus_plus);
872
11
  options.SetIgnoreBreakpoints(true);
873
11
  options.SetUnwindOnError(true);
874
11
  options.SetTrapExceptions(false); // dlopen can't throw exceptions, so
875
                                    // don't do the work to trap them.
876
11
  options.SetTimeout(process->GetUtilityExpressionTimeout());
877
11
  options.SetIsForUtilityExpr(true);
878
879
11
  Value return_value;
880
  // Fetch the clang types we will need:
881
11
  TypeSystemClangSP scratch_ts_sp =
882
11
      ScratchTypeSystemClang::GetForTarget(process->GetTarget());
883
11
  if (!scratch_ts_sp) {
884
0
    error.SetErrorString("dlopen error: Unable to get TypeSystemClang");
885
0
    return LLDB_INVALID_IMAGE_TOKEN;
886
0
  }
887
888
11
  CompilerType clang_void_pointer_type =
889
11
      scratch_ts_sp->GetBasicType(eBasicTypeVoid).GetPointerType();
890
891
11
  return_value.SetCompilerType(clang_void_pointer_type);
892
  
893
11
  ExpressionResults results = do_dlopen_function->ExecuteFunction(
894
11
      exe_ctx, &func_args_addr, options, diagnostics, return_value);
895
11
  if (results != eExpressionCompleted) {
896
0
    error.SetErrorStringWithFormat(
897
0
        "dlopen error: failed executing dlopen wrapper function: %s",
898
0
        diagnostics.GetString().c_str());
899
0
    return LLDB_INVALID_IMAGE_TOKEN;
900
0
  }
901
  
902
  // Read the dlopen token from the return area:
903
11
  lldb::addr_t token = process->ReadPointerFromMemory(return_addr, 
904
11
                                                      utility_error);
905
11
  if (utility_error.Fail()) {
906
0
    error.SetErrorStringWithFormat(
907
0
        "dlopen error: could not read the return struct: %s",
908
0
        utility_error.AsCString());
909
0
    return LLDB_INVALID_IMAGE_TOKEN;
910
0
  }
911
  
912
  // The dlopen succeeded!
913
11
  if (token != 0x0) {
914
7
    if (loaded_image && 
buffer_addr != 0x05
)
915
5
    {
916
      // Capture the image which was loaded.  We leave it in the buffer on
917
      // exit from the dlopen function, so we can just read it from there:
918
5
      std::string name_string;
919
5
      process->ReadCStringFromMemory(buffer_addr, name_string, utility_error);
920
5
      if (utility_error.Success())
921
5
        loaded_image->SetFile(name_string, llvm::sys::path::Style::posix);
922
5
    }
923
7
    return process->AddImageToken(token);
924
7
  }
925
    
926
  // We got an error, lets read in the error string:
927
4
  std::string dlopen_error_str;
928
4
  lldb::addr_t error_addr 
929
4
    = process->ReadPointerFromMemory(return_addr + addr_size, utility_error);
930
4
  if (utility_error.Fail()) {
931
0
    error.SetErrorStringWithFormat(
932
0
        "dlopen error: could not read error string: %s",
933
0
        utility_error.AsCString());
934
0
    return LLDB_INVALID_IMAGE_TOKEN;
935
0
  }
936
  
937
4
  size_t num_chars = process->ReadCStringFromMemory(error_addr + addr_size, 
938
4
                                                    dlopen_error_str, 
939
4
                                                    utility_error);
940
4
  if (utility_error.Success() && num_chars > 0)
941
4
    error.SetErrorStringWithFormat("dlopen error: %s",
942
4
                                   dlopen_error_str.c_str());
943
0
  else
944
0
    error.SetErrorStringWithFormat("dlopen failed for unknown reasons.");
945
946
4
  return LLDB_INVALID_IMAGE_TOKEN;
947
4
}
948
949
Status PlatformPOSIX::UnloadImage(lldb_private::Process *process,
950
5
                                  uint32_t image_token) {
951
5
  const addr_t image_addr = process->GetImagePtrFromToken(image_token);
952
5
  if (image_addr == LLDB_INVALID_IMAGE_TOKEN)
953
0
    return Status("Invalid image token");
954
955
5
  StreamString expr;
956
5
  expr.Printf("dlclose((void *)0x%" PRIx64 ")", image_addr);
957
5
  llvm::StringRef prefix = GetLibdlFunctionDeclarations(process);
958
5
  lldb::ValueObjectSP result_valobj_sp;
959
5
  Status error = EvaluateLibdlExpression(process, expr.GetData(), prefix,
960
5
                                         result_valobj_sp);
961
5
  if (error.Fail())
962
0
    return error;
963
964
5
  if (result_valobj_sp->GetError().Fail())
965
0
    return result_valobj_sp->GetError();
966
967
5
  Scalar scalar;
968
5
  if (result_valobj_sp->ResolveValue(scalar)) {
969
5
    if (scalar.UInt(1))
970
0
      return Status("expression failed: \"%s\"", expr.GetData());
971
5
    process->ResetImageToken(image_token);
972
5
  }
973
5
  return Status();
974
5
}
975
976
llvm::StringRef
977
9
PlatformPOSIX::GetLibdlFunctionDeclarations(lldb_private::Process *process) {
978
9
  return R"(
979
9
              extern "C" void* dlopen(const char*, int);
980
9
              extern "C" void* dlsym(void*, const char*);
981
9
              extern "C" int   dlclose(void*);
982
9
              extern "C" char* dlerror(void);
983
9
             )";
984
9
}
985
986
0
ConstString PlatformPOSIX::GetFullNameForDylib(ConstString basename) {
987
0
  if (basename.IsEmpty())
988
0
    return basename;
989
990
0
  StreamString stream;
991
0
  stream.Printf("lib%s.so", basename.GetCString());
992
0
  return ConstString(stream.GetString());
993
0
}