Coverage Report

Created: 2022-01-22 13:19

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