brintos

brintos / llvm-project-archived public Read only

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