static void DisableASLRIfRequested(int error_fd, const ProcessLaunchInfo &info) {
#if defined(__linux__)
  if (info.GetFlags().Test(lldb::eLaunchFlagDisableASLR)) {
    const unsigned long personality_get_current = 0xffffffff;
    int value = personality(personality_get_current);
    if (value == -1)
      ExitWithError(error_fd, "personality get");

    value = personality(ADDR_NO_RANDOMIZE | value);
    if (value == -1)
      ExitWithError(error_fd, "personality set");
  }
#endif
}
HostProcess
ProcessLauncherPosix::LaunchProcess(const ProcessLaunchInfo &launch_info,
                                    Error &error) {
  lldb::pid_t pid;
  char exe_path[PATH_MAX];

  launch_info.GetExecutableFile().GetPath(exe_path, sizeof(exe_path));

  // TODO(zturner): Move the code from LaunchProcessPosixSpawn to here, and make
  // MacOSX re-use this
  // ProcessLauncher when it wants a posix_spawn launch.
  error = Host::LaunchProcessPosixSpawn(exe_path, launch_info, pid);
  return HostProcess(pid);
}
ProcessSP
PlatformWindows::DebugProcess(ProcessLaunchInfo &launch_info, Debugger &debugger, Target *target, Error &error)
{
    // Windows has special considerations that must be followed when launching or attaching to a process.  The
    // key requirement is that when launching or attaching to a process, you must do it from the same the thread
    // that will go into a permanent loop which will then receive debug events from the process.  In particular,
    // this means we can't use any of LLDB's generic mechanisms to do it for us, because it doesn't have the
    // special knowledge required for setting up the background thread or passing the right flags.  
    //
    // Another problem is that that LLDB's standard model for debugging a process is to first launch it, have
    // it stop at the entry point, and then attach to it.  In Windows this doesn't quite work, you have to 
    // specify as an argument to CreateProcess() that you're going to debug the process.  So we override DebugProcess
    // here to handle this.  Launch operations go directly to the process plugin, and attach operations almost go
    // directly to the process plugin (but we hijack the events first).  In essence, we encapsulate all the logic
    // of Launching and Attaching in the process plugin, and PlatformWindows::DebugProcess is just a pass-through
    // to get to the process plugin.

    if (launch_info.GetProcessID() != LLDB_INVALID_PROCESS_ID)
    {
        // This is a process attach.  Don't need to launch anything.
        ProcessAttachInfo attach_info(launch_info);
        return Attach(attach_info, debugger, target, error);
    }
    else
    {
        ProcessSP process_sp = target->CreateProcess(launch_info.GetListenerForProcess(debugger),
                                                     launch_info.GetProcessPluginName(),
                                                     nullptr);

        // We need to launch and attach to the process.
        launch_info.GetFlags().Set(eLaunchFlagDebug);
        if (process_sp)
            error = process_sp->Launch(launch_info);

        return process_sp;
    }
}
Beispiel #4
0
Error
PlatformLinux::LaunchNativeProcess (
    ProcessLaunchInfo &launch_info,
    lldb_private::NativeProcessProtocol::NativeDelegate &native_delegate,
    NativeProcessProtocolSP &process_sp)
{
#if !defined(__linux__)
    return Error("only implemented on Linux hosts");
#else
    if (!IsHost ())
        return Error("PlatformLinux::%s (): cannot launch a debug process when not the host", __FUNCTION__);

    // Retrieve the exe module.
    lldb::ModuleSP exe_module_sp;

    Error error = ResolveExecutable (
        launch_info.GetExecutableFile (),
        launch_info.GetArchitecture (),
        exe_module_sp,
        NULL);

    if (!error.Success ())
        return error;

    if (!exe_module_sp)
        return Error("exe_module_sp could not be resolved for %s", launch_info.GetExecutableFile ().GetPath ().c_str ());

    // Launch it for debugging
    error = NativeProcessLinux::LaunchProcess (
        exe_module_sp.get (),
        launch_info,
        native_delegate,
        process_sp);

    return error;
#endif
}
Beispiel #5
0
Error
ProcessPOSIX::DoLaunch (Module *module,
                        ProcessLaunchInfo &launch_info)
{
    Error error;
    assert(m_monitor == NULL);

    const char* working_dir = launch_info.GetWorkingDirectory();
    if (working_dir) {
      FileSpec WorkingDir(working_dir, true);
      if (!WorkingDir || WorkingDir.GetFileType() != FileSpec::eFileTypeDirectory)
      {
          error.SetErrorStringWithFormat("No such file or directory: %s", working_dir);
          return error;
      }
    }

    SetPrivateState(eStateLaunching);

    const lldb_private::FileAction *file_action;

    // Default of NULL will mean to use existing open file descriptors
    const char *stdin_path = NULL;
    const char *stdout_path = NULL;
    const char *stderr_path = NULL;

    file_action = launch_info.GetFileActionForFD (STDIN_FILENO);
    stdin_path = GetFilePath(file_action, stdin_path);

    file_action = launch_info.GetFileActionForFD (STDOUT_FILENO);
    stdout_path = GetFilePath(file_action, stdout_path);

    file_action = launch_info.GetFileActionForFD (STDERR_FILENO);
    stderr_path = GetFilePath(file_action, stderr_path);

    m_monitor = new ProcessMonitor (this, 
                                    module,
                                    launch_info.GetArguments().GetConstArgumentVector(), 
                                    launch_info.GetEnvironmentEntries().GetConstArgumentVector(),
                                    stdin_path, 
                                    stdout_path, 
                                    stderr_path,
                                    working_dir,
                                    launch_info,
                                    error);

    m_module = module;

    if (!error.Success())
        return error;

    SetSTDIOFileDescriptor(m_monitor->GetTerminalFD());

    SetID(m_monitor->GetPID());
    return error;
}
Beispiel #6
0
Error
DebuggerThread::DebugLaunch(const ProcessLaunchInfo &launch_info)
{
    WINLOG_IFALL(WINDOWS_LOG_PROCESS,
        "DebuggerThread::DebugLaunch launching '%s'", launch_info.GetExecutableFile().GetPath().c_str());

    Error error;
    DebugLaunchContext *context = new DebugLaunchContext(this, launch_info);
    HostThread slave_thread(ThreadLauncher::LaunchThread("lldb.plugin.process-windows.slave[?]",
                                                         DebuggerThreadLaunchRoutine, context, &error));

    if (!error.Success())
    {
        WINERR_IFALL(WINDOWS_LOG_PROCESS,
            "DebugLaunch couldn't launch debugger thread.  %s", error.AsCString());
    }

    return error;
}
HostProcess
ProcessLauncherPosixFork::LaunchProcess(const ProcessLaunchInfo &launch_info,
                                        Status &error) {
  char exe_path[PATH_MAX];
  launch_info.GetExecutableFile().GetPath(exe_path, sizeof(exe_path));

  // A pipe used by the child process to report errors.
  PipePosix pipe;
  const bool child_processes_inherit = false;
  error = pipe.CreateNew(child_processes_inherit);
  if (error.Fail())
    return HostProcess();

  ::pid_t pid = ::fork();
  if (pid == -1) {
    // Fork failed
    error.SetErrorStringWithFormatv("Fork failed with error message: {0}",
                                    llvm::sys::StrError());
    return HostProcess(LLDB_INVALID_PROCESS_ID);
  }
  if (pid == 0) {
    // child process
    pipe.CloseReadFileDescriptor();
    ChildFunc(pipe.ReleaseWriteFileDescriptor(), launch_info);
  }

  // parent process

  pipe.CloseWriteFileDescriptor();
  char buf[1000];
  int r = read(pipe.GetReadFileDescriptor(), buf, sizeof buf);

  if (r == 0)
    return HostProcess(pid); // No error. We're done.

  error.SetErrorString(buf);

  llvm::sys::RetryAfterSignal(-1, waitpid, pid, nullptr, 0);

  return HostProcess();
}
Beispiel #8
0
Error Host::LaunchProcess(ProcessLaunchInfo &launch_info) {
  std::unique_ptr<ProcessLauncher> delegate_launcher;
#if defined(_WIN32)
  delegate_launcher.reset(new ProcessLauncherWindows());
#elif defined(__linux__)
  delegate_launcher.reset(new ProcessLauncherLinux());
#else
  delegate_launcher.reset(new ProcessLauncherPosix());
#endif
  MonitoringProcessLauncher launcher(std::move(delegate_launcher));

  Error error;
  HostProcess process = launcher.LaunchProcess(launch_info, error);

  // TODO(zturner): It would be better if the entire HostProcess were returned
  // instead of writing
  // it into this structure.
  launch_info.SetProcessID(process.GetProcessId());

  return error;
}
Beispiel #9
0
Error
Host::LaunchProcessPosixSpawn(const char *exe_path, const ProcessLaunchInfo &launch_info, lldb::pid_t &pid)
{
    Error error;
    Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_HOST | LIBLLDB_LOG_PROCESS));

    posix_spawnattr_t attr;
    error.SetError( ::posix_spawnattr_init (&attr), eErrorTypePOSIX);

    if (error.Fail() || log)
        error.PutToLog(log, "::posix_spawnattr_init ( &attr )");
    if (error.Fail())
        return error;

    // Make a quick class that will cleanup the posix spawn attributes in case
    // we return in the middle of this function.
    lldb_utility::CleanUp <posix_spawnattr_t *, int> posix_spawnattr_cleanup(&attr, posix_spawnattr_destroy);

    sigset_t no_signals;
    sigset_t all_signals;
    sigemptyset (&no_signals);
    sigfillset (&all_signals);
    ::posix_spawnattr_setsigmask(&attr, &no_signals);
#if defined (__linux__)  || defined (__FreeBSD__)
    ::posix_spawnattr_setsigdefault(&attr, &no_signals);
#else
    ::posix_spawnattr_setsigdefault(&attr, &all_signals);
#endif

    short flags = GetPosixspawnFlags(launch_info);

    error.SetError( ::posix_spawnattr_setflags (&attr, flags), eErrorTypePOSIX);
    if (error.Fail() || log)
        error.PutToLog(log, "::posix_spawnattr_setflags ( &attr, flags=0x%8.8x )", flags);
    if (error.Fail())
        return error;

    // posix_spawnattr_setbinpref_np appears to be an Apple extension per:
    // http://www.unix.com/man-page/OSX/3/posix_spawnattr_setbinpref_np/
#if defined (__APPLE__) && !defined (__arm__)
    
    // Don't set the binpref if a shell was provided.  After all, that's only going to affect what version of the shell
    // is launched, not what fork of the binary is launched.  We insert "arch --arch <ARCH> as part of the shell invocation
    // to do that job on OSX.
    
    if (launch_info.GetShell() == nullptr)
    {
        // We don't need to do this for ARM, and we really shouldn't now that we
        // have multiple CPU subtypes and no posix_spawnattr call that allows us
        // to set which CPU subtype to launch...
        const ArchSpec &arch_spec = launch_info.GetArchitecture();
        cpu_type_t cpu = arch_spec.GetMachOCPUType();
        cpu_type_t sub = arch_spec.GetMachOCPUSubType();
        if (cpu != 0 &&
            cpu != static_cast<cpu_type_t>(UINT32_MAX) &&
            cpu != static_cast<cpu_type_t>(LLDB_INVALID_CPUTYPE) &&
            !(cpu == 0x01000007 && sub == 8)) // If haswell is specified, don't try to set the CPU type or we will fail 
        {
            size_t ocount = 0;
            error.SetError( ::posix_spawnattr_setbinpref_np (&attr, 1, &cpu, &ocount), eErrorTypePOSIX);
            if (error.Fail() || log)
                error.PutToLog(log, "::posix_spawnattr_setbinpref_np ( &attr, 1, cpu_type = 0x%8.8x, count => %llu )", cpu, (uint64_t)ocount);

            if (error.Fail() || ocount != 1)
                return error;
        }
    }

#endif

    const char *tmp_argv[2];
    char * const *argv = (char * const*)launch_info.GetArguments().GetConstArgumentVector();
    char * const *envp = (char * const*)launch_info.GetEnvironmentEntries().GetConstArgumentVector();
    if (argv == NULL)
    {
        // posix_spawn gets very unhappy if it doesn't have at least the program
        // name in argv[0]. One of the side affects I have noticed is the environment
        // variables don't make it into the child process if "argv == NULL"!!!
        tmp_argv[0] = exe_path;
        tmp_argv[1] = NULL;
        argv = (char * const*)tmp_argv;
    }

#if !defined (__APPLE__)
    // manage the working directory
    char current_dir[PATH_MAX];
    current_dir[0] = '\0';
#endif

    FileSpec working_dir{launch_info.GetWorkingDirectory()};
    if (working_dir)
    {
#if defined (__APPLE__)
        // Set the working directory on this thread only
        if (__pthread_chdir(working_dir.GetCString()) < 0) {
            if (errno == ENOENT) {
                error.SetErrorStringWithFormat("No such file or directory: %s",
                        working_dir.GetCString());
            } else if (errno == ENOTDIR) {
                error.SetErrorStringWithFormat("Path doesn't name a directory: %s",
                        working_dir.GetCString());
            } else {
                error.SetErrorStringWithFormat("An unknown error occurred when changing directory for process execution.");
            }
            return error;
        }
#else
        if (::getcwd(current_dir, sizeof(current_dir)) == NULL)
        {
            error.SetError(errno, eErrorTypePOSIX);
            error.LogIfError(log, "unable to save the current directory");
            return error;
        }

        if (::chdir(working_dir.GetCString()) == -1)
        {
            error.SetError(errno, eErrorTypePOSIX);
            error.LogIfError(log, "unable to change working directory to %s",
                    working_dir.GetCString());
            return error;
        }
#endif
    }

    ::pid_t result_pid = LLDB_INVALID_PROCESS_ID;
    const size_t num_file_actions = launch_info.GetNumFileActions ();
    if (num_file_actions > 0)
    {
        posix_spawn_file_actions_t file_actions;
        error.SetError( ::posix_spawn_file_actions_init (&file_actions), eErrorTypePOSIX);
        if (error.Fail() || log)
            error.PutToLog(log, "::posix_spawn_file_actions_init ( &file_actions )");
        if (error.Fail())
            return error;

        // Make a quick class that will cleanup the posix spawn attributes in case
        // we return in the middle of this function.
        lldb_utility::CleanUp <posix_spawn_file_actions_t *, int> posix_spawn_file_actions_cleanup (&file_actions, posix_spawn_file_actions_destroy);

        for (size_t i=0; i<num_file_actions; ++i)
        {
            const FileAction *launch_file_action = launch_info.GetFileActionAtIndex(i);
            if (launch_file_action)
            {
                if (!AddPosixSpawnFileAction(&file_actions, launch_file_action, log, error))
                    return error;
            }
        }

        error.SetError(::posix_spawnp(&result_pid, exe_path, &file_actions, &attr, argv, envp), eErrorTypePOSIX);

        if (error.Fail() || log)
        {
            error.PutToLog(log, "::posix_spawnp ( pid => %i, path = '%s', file_actions = %p, attr = %p, argv = %p, envp = %p )", result_pid,
                           exe_path, static_cast<void *>(&file_actions), static_cast<void *>(&attr), reinterpret_cast<const void *>(argv),
                           reinterpret_cast<const void *>(envp));
            if (log)
            {
                for (int ii=0; argv[ii]; ++ii)
                    log->Printf("argv[%i] = '%s'", ii, argv[ii]);
            }
        }

    }
    else
    {
        error.SetError(::posix_spawnp(&result_pid, exe_path, NULL, &attr, argv, envp), eErrorTypePOSIX);

        if (error.Fail() || log)
        {
            error.PutToLog(log, "::posix_spawnp ( pid => %i, path = '%s', file_actions = NULL, attr = %p, argv = %p, envp = %p )",
                           result_pid, exe_path, static_cast<void *>(&attr), reinterpret_cast<const void *>(argv),
                           reinterpret_cast<const void *>(envp));
            if (log)
            {
                for (int ii=0; argv[ii]; ++ii)
                    log->Printf("argv[%i] = '%s'", ii, argv[ii]);
            }
        }
    }
    pid = result_pid;

    if (working_dir)
    {
#if defined (__APPLE__)
        // No more thread specific current working directory
        __pthread_fchdir (-1);
#else
        if (::chdir(current_dir) == -1 && error.Success())
        {
            error.SetError(errno, eErrorTypePOSIX);
            error.LogIfError(log, "unable to change current directory back to %s",
                    current_dir);
        }
#endif
    }

    return error;
}
static void LLVM_ATTRIBUTE_NORETURN ChildFunc(int error_fd,
                                              const ProcessLaunchInfo &info) {
  if (info.GetFlags().Test(eLaunchFlagLaunchInSeparateProcessGroup)) {
    if (setpgid(0, 0) != 0)
      ExitWithError(error_fd, "setpgid");
  }

  for (size_t i = 0; i < info.GetNumFileActions(); ++i) {
    const FileAction &action = *info.GetFileActionAtIndex(i);
    switch (action.GetAction()) {
    case FileAction::eFileActionClose:
      if (close(action.GetFD()) != 0)
        ExitWithError(error_fd, "close");
      break;
    case FileAction::eFileActionDuplicate:
      if (dup2(action.GetFD(), action.GetActionArgument()) == -1)
        ExitWithError(error_fd, "dup2");
      break;
    case FileAction::eFileActionOpen:
      DupDescriptor(error_fd, action.GetFileSpec(), action.GetFD(),
                    action.GetActionArgument());
      break;
    case FileAction::eFileActionNone:
      break;
    }
  }

  const char **argv = info.GetArguments().GetConstArgumentVector();

  // Change working directory
  if (info.GetWorkingDirectory() &&
      0 != ::chdir(info.GetWorkingDirectory().GetCString()))
    ExitWithError(error_fd, "chdir");

  DisableASLRIfRequested(error_fd, info);
  Environment env = info.GetEnvironment();
  FixupEnvironment(env);
  Environment::Envp envp = env.getEnvp();

  // Clear the signal mask to prevent the child from being affected by any
  // masking done by the parent.
  sigset_t set;
  if (sigemptyset(&set) != 0 ||
      pthread_sigmask(SIG_SETMASK, &set, nullptr) != 0)
    ExitWithError(error_fd, "pthread_sigmask");

  if (info.GetFlags().Test(eLaunchFlagDebug)) {
    // Do not inherit setgid powers.
    if (setgid(getgid()) != 0)
      ExitWithError(error_fd, "setgid");

    // HACK:
    // Close everything besides stdin, stdout, and stderr that has no file
    // action to avoid leaking. Only do this when debugging, as elsewhere we
    // actually rely on passing open descriptors to child processes.
    for (int fd = 3; fd < sysconf(_SC_OPEN_MAX); ++fd)
      if (!info.GetFileActionForFD(fd) && fd != error_fd)
        close(fd);

    // Start tracing this child that is about to exec.
    if (ptrace(PT_TRACE_ME, 0, nullptr, 0) == -1)
      ExitWithError(error_fd, "ptrace");
  }

  // Execute.  We should never return...
  execve(argv[0], const_cast<char *const *>(argv), envp);

#if defined(__linux__)
  if (errno == ETXTBSY) {
    // On android M and earlier we can get this error because the adb daemon
    // can hold a write handle on the executable even after it has finished
    // uploading it. This state lasts only a short time and happens only when
    // there are many concurrent adb commands being issued, such as when
    // running the test suite. (The file remains open when someone does an "adb
    // shell" command in the fork() child before it has had a chance to exec.)
    // Since this state should clear up quickly, wait a while and then give it
    // one more go.
    usleep(50000);
    execve(argv[0], const_cast<char *const *>(argv), envp);
  }
#endif

  // ...unless exec fails.  In which case we definitely need to end the child
  // here.
  ExitWithError(error_fd, "execve");
}
// For local debugging, NetBSD will override the debug logic to use llgs-launch
// rather than lldb-launch, llgs-attach.  This differs from current lldb-
// launch, debugserver-attach approach on MacOSX.
lldb::ProcessSP
PlatformNetBSD::DebugProcess(ProcessLaunchInfo &launch_info, Debugger &debugger,
                             Target *target, // Can be NULL, if NULL create a new
                                             // target, else use existing one
                             Status &error) {
  Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM));
  LLDB_LOG(log, "target {0}", target);

  // If we're a remote host, use standard behavior from parent class.
  if (!IsHost())
    return PlatformPOSIX::DebugProcess(launch_info, debugger, target, error);

  //
  // For local debugging, we'll insist on having ProcessGDBRemote create the
  // process.
  //

  ProcessSP process_sp;

  // Make sure we stop at the entry point
  launch_info.GetFlags().Set(eLaunchFlagDebug);

  // We always launch the process we are going to debug in a separate process
  // group, since then we can handle ^C interrupts ourselves w/o having to
  // worry about the target getting them as well.
  launch_info.SetLaunchInSeparateProcessGroup(true);

  // Ensure we have a target.
  if (target == nullptr) {
    LLDB_LOG(log, "creating new target");
    TargetSP new_target_sp;
    error = debugger.GetTargetList().CreateTarget(
        debugger, "", "", eLoadDependentsNo, nullptr, new_target_sp);
    if (error.Fail()) {
      LLDB_LOG(log, "failed to create new target: {0}", error);
      return process_sp;
    }

    target = new_target_sp.get();
    if (!target) {
      error.SetErrorString("CreateTarget() returned nullptr");
      LLDB_LOG(log, "error: {0}", error);
      return process_sp;
    }
  }

  // Mark target as currently selected target.
  debugger.GetTargetList().SetSelectedTarget(target);

  // Now create the gdb-remote process.
  LLDB_LOG(log, "having target create process with gdb-remote plugin");
  process_sp =
      target->CreateProcess(launch_info.GetListener(), "gdb-remote", nullptr);

  if (!process_sp) {
    error.SetErrorString("CreateProcess() failed for gdb-remote process");
    LLDB_LOG(log, "error: {0}", error);
    return process_sp;
  }

  LLDB_LOG(log, "successfully created process");
  // Adjust launch for a hijacker.
  ListenerSP listener_sp;
  if (!launch_info.GetHijackListener()) {
    LLDB_LOG(log, "setting up hijacker");
    listener_sp =
        Listener::MakeListener("lldb.PlatformNetBSD.DebugProcess.hijack");
    launch_info.SetHijackListener(listener_sp);
    process_sp->HijackProcessEvents(listener_sp);
  }

  // Log file actions.
  if (log) {
    LLDB_LOG(log, "launching process with the following file actions:");
    StreamString stream;
    size_t i = 0;
    const FileAction *file_action;
    while ((file_action = launch_info.GetFileActionAtIndex(i++)) != nullptr) {
      file_action->Dump(stream);
      LLDB_LOG(log, "{0}", stream.GetData());
      stream.Clear();
    }
  }

  // Do the launch.
  error = process_sp->Launch(launch_info);
  if (error.Success()) {
    // Handle the hijacking of process events.
    if (listener_sp) {
      const StateType state = process_sp->WaitForProcessToStop(
          llvm::None, NULL, false, listener_sp);

      LLDB_LOG(log, "pid {0} state {0}", process_sp->GetID(), state);
    }

    // Hook up process PTY if we have one (which we should for local debugging
    // with llgs).
    int pty_fd = launch_info.GetPTY().ReleaseMasterFileDescriptor();
    if (pty_fd != PseudoTerminal::invalid_fd) {
      process_sp->SetSTDIOFileDescriptor(pty_fd);
      LLDB_LOG(log, "hooked up STDIO pty to process");
    } else
      LLDB_LOG(log, "not using process STDIO pty");
  } else {
    LLDB_LOG(log, "process launch failed: {0}", error);
    // FIXME figure out appropriate cleanup here.  Do we delete the target? Do
    // we delete the process?  Does our caller do that?
  }

  return process_sp;
}
Beispiel #12
0
Error
ProcessPOSIX::DoLaunch (Module *module,
                        ProcessLaunchInfo &launch_info)
{
    Error error;
    assert(m_monitor == NULL);

    const char* working_dir = launch_info.GetWorkingDirectory();
    if (working_dir) {
      FileSpec WorkingDir(working_dir, true);
      if (!WorkingDir || WorkingDir.GetFileType() != FileSpec::eFileTypeDirectory)
      {
          error.SetErrorStringWithFormat("No such file or directory: %s", working_dir);
          return error;
      }
    }

    SetPrivateState(eStateLaunching);

    const lldb_private::FileAction *file_action;

    // Default of NULL will mean to use existing open file descriptors
    const char *stdin_path = NULL;
    const char *stdout_path = NULL;
    const char *stderr_path = NULL;

    const char * dbg_pts_path = launch_info.GetPTY().GetSlaveName(NULL,0);

    file_action = launch_info.GetFileActionForFD (STDIN_FILENO);
    stdin_path = GetFilePath(file_action, stdin_path, dbg_pts_path);

    file_action = launch_info.GetFileActionForFD (STDOUT_FILENO);
    stdout_path = GetFilePath(file_action, stdout_path, dbg_pts_path);

    file_action = launch_info.GetFileActionForFD (STDERR_FILENO);
    stderr_path = GetFilePath(file_action, stderr_path, dbg_pts_path);

    m_monitor = new ProcessMonitor (this, 
                                    module,
                                    launch_info.GetArguments().GetConstArgumentVector(), 
                                    launch_info.GetEnvironmentEntries().GetConstArgumentVector(),
                                    stdin_path, 
                                    stdout_path, 
                                    stderr_path,
                                    working_dir,
                                    launch_info,
                                    error);

    m_module = module;

    if (!error.Success())
        return error;

    int terminal = m_monitor->GetTerminalFD();
    if (terminal >= 0) {
        // The reader thread will close the file descriptor when done, so we pass it a copy.
        int stdio = fcntl(terminal, F_DUPFD_CLOEXEC, 0);
        if (stdio == -1) {
            error.SetErrorToErrno();
            return error;
        }
        SetSTDIOFileDescriptor(stdio);
    }

    SetID(m_monitor->GetPID());
    return error;
}
Error
PlatformRemoteGDBServer::LaunchProcess (ProcessLaunchInfo &launch_info)
{
    Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_PLATFORM));
    Error error;

    if (log)
        log->Printf ("PlatformRemoteGDBServer::%s() called", __FUNCTION__);

    auto num_file_actions = launch_info.GetNumFileActions ();
    for (decltype(num_file_actions) i = 0; i < num_file_actions; ++i)
    {
        const auto file_action = launch_info.GetFileActionAtIndex (i);
        if (file_action->GetAction () != FileAction::eFileActionOpen)
            continue;
        switch(file_action->GetFD())
        {
        case STDIN_FILENO:
            m_gdb_client.SetSTDIN(file_action->GetFileSpec());
            break;
        case STDOUT_FILENO:
            m_gdb_client.SetSTDOUT(file_action->GetFileSpec());
            break;
        case STDERR_FILENO:
            m_gdb_client.SetSTDERR(file_action->GetFileSpec());
            break;
        }
    }

    m_gdb_client.SetDisableASLR (launch_info.GetFlags().Test (eLaunchFlagDisableASLR));
    m_gdb_client.SetDetachOnError (launch_info.GetFlags().Test (eLaunchFlagDetachOnError));
    
    FileSpec working_dir = launch_info.GetWorkingDirectory();
    if (working_dir)
    {
        m_gdb_client.SetWorkingDir(working_dir);
    }
    
    // Send the environment and the program + arguments after we connect
    const char **envp = launch_info.GetEnvironmentEntries().GetConstArgumentVector();

    if (envp)
    {
        const char *env_entry;
        for (int i=0; (env_entry = envp[i]); ++i)
        {
            if (m_gdb_client.SendEnvironmentPacket(env_entry) != 0)
                break;
        }
    }
    
    ArchSpec arch_spec = launch_info.GetArchitecture();
    const char *arch_triple = arch_spec.GetTriple().str().c_str();
    
    m_gdb_client.SendLaunchArchPacket(arch_triple);
    if (log)
        log->Printf ("PlatformRemoteGDBServer::%s() set launch architecture triple to '%s'", __FUNCTION__, arch_triple ? arch_triple : "<NULL>");

    int arg_packet_err;
    {
        // Scope for the scoped timeout object
        process_gdb_remote::GDBRemoteCommunication::ScopedTimeout timeout(m_gdb_client, 5);
        arg_packet_err = m_gdb_client.SendArgumentsPacket (launch_info);
    }

    if (arg_packet_err == 0)
    {
        std::string error_str;
        if (m_gdb_client.GetLaunchSuccess (error_str))
        {
            const auto pid = m_gdb_client.GetCurrentProcessID (false);
            if (pid != LLDB_INVALID_PROCESS_ID)
            {
                launch_info.SetProcessID (pid);
                if (log)
                    log->Printf ("PlatformRemoteGDBServer::%s() pid %" PRIu64 " launched successfully", __FUNCTION__, pid);
            }
            else
            {
                if (log)
                    log->Printf ("PlatformRemoteGDBServer::%s() launch succeeded but we didn't get a valid process id back!", __FUNCTION__);
                error.SetErrorString ("failed to get PID");
            }
        }
        else
        {
            error.SetErrorString (error_str.c_str());
            if (log)
                log->Printf ("PlatformRemoteGDBServer::%s() launch failed: %s", __FUNCTION__, error.AsCString ());
        }
    }
    else
    {
        error.SetErrorStringWithFormat("'A' packet returned an error: %i", arg_packet_err);
    }
    return error;
}
lldb::ProcessSP
PlatformRemoteGDBServer::DebugProcess (ProcessLaunchInfo &launch_info,
                                       Debugger &debugger,
                                       Target *target,       // Can be NULL, if NULL create a new target, else use existing one
                                       Error &error)
{
    lldb::ProcessSP process_sp;
    if (IsRemote())
    {
        if (IsConnected())
        {
            lldb::pid_t debugserver_pid = LLDB_INVALID_PROCESS_ID;
            uint16_t port = LaunchGDBserverAndGetPort(debugserver_pid);

            if (port == 0)
            {
                error.SetErrorStringWithFormat ("unable to launch a GDB server on '%s'", GetHostname ());
            }
            else
            {
                if (target == NULL)
                {
                    TargetSP new_target_sp;
                    
                    error = debugger.GetTargetList().CreateTarget (debugger,
                                                                   NULL,
                                                                   NULL,
                                                                   false,
                                                                   NULL,
                                                                   new_target_sp);
                    target = new_target_sp.get();
                }
                else
                    error.Clear();
                
                if (target && error.Success())
                {
                    debugger.GetTargetList().SetSelectedTarget(target);
                    
                    // The darwin always currently uses the GDB remote debugger plug-in
                    // so even when debugging locally we are debugging remotely!
                    process_sp = target->CreateProcess (launch_info.GetListenerForProcess(debugger), "gdb-remote", NULL);
                    
                    if (process_sp)
                    {
                        std::string connect_url =
                            MakeGdbServerUrl(m_platform_scheme, m_platform_hostname, port);
                        error = process_sp->ConnectRemote (nullptr, connect_url.c_str());
                        // Retry the connect remote one time...
                        if (error.Fail())
                            error = process_sp->ConnectRemote (nullptr, connect_url.c_str());
                        if (error.Success())
                            error = process_sp->Launch(launch_info);
                        else if (debugserver_pid != LLDB_INVALID_PROCESS_ID)
                        {
                            printf ("error: connect remote failed (%s)\n", error.AsCString());
                            KillSpawnedProcess(debugserver_pid);
                        }
                    }
                }
            }
        }
        else
        {
            error.SetErrorString("not connected to remote gdb server");
        }
    }
    return process_sp;

}
Beispiel #15
0
Error
Host::ShellExpandArguments (ProcessLaunchInfo &launch_info)
{
    Error error;
    if (launch_info.GetFlags().Test(eLaunchFlagShellExpandArguments))
    {
        FileSpec expand_tool_spec;
        if (!HostInfo::GetLLDBPath(lldb::ePathTypeSupportExecutableDir, expand_tool_spec))
        {
            error.SetErrorString("could not find argdumper tool");
            return error;
        }
        expand_tool_spec.AppendPathComponent("argdumper.exe");
        if (!expand_tool_spec.Exists())
        {
            error.SetErrorString("could not find argdumper tool");
            return error;
        }
        
        std::string quoted_cmd_string;
        launch_info.GetArguments().GetQuotedCommandString(quoted_cmd_string);
        std::replace(quoted_cmd_string.begin(), quoted_cmd_string.end(), '\\', '/');
        StreamString expand_command;
        
        expand_command.Printf("%s %s",
                              expand_tool_spec.GetPath().c_str(),
                              quoted_cmd_string.c_str());
        
        int status;
        std::string output;
        RunShellCommand(expand_command.GetData(), launch_info.GetWorkingDirectory(), &status, nullptr, &output, 10);
        
        if (status != 0)
        {
            error.SetErrorStringWithFormat("argdumper exited with error %d", status);
            return error;
        }
        
        auto data_sp = StructuredData::ParseJSON(output);
        if (!data_sp)
        {
            error.SetErrorString("invalid JSON");
            return error;
        }
        
        auto dict_sp = data_sp->GetAsDictionary();
        if (!data_sp)
        {
            error.SetErrorString("invalid JSON");
            return error;
        }
        
        auto args_sp = dict_sp->GetObjectForDotSeparatedPath("arguments");
        if (!args_sp)
        {
            error.SetErrorString("invalid JSON");
            return error;
        }
        
        auto args_array_sp = args_sp->GetAsArray();
        if (!args_array_sp)
        {
            error.SetErrorString("invalid JSON");
            return error;
        }
        
        launch_info.GetArguments().Clear();
        
        for (size_t i = 0;
             i < args_array_sp->GetSize();
             i++)
        {
            auto item_sp = args_array_sp->GetItemAtIndex(i);
            if (!item_sp)
                continue;
            auto str_sp = item_sp->GetAsString();
            if (!str_sp)
                continue;
            
            launch_info.GetArguments().AppendArgument(str_sp->GetValue().c_str());
        }
    }
    
    return error;
}
Error
PlatformRemoteGDBServer::LaunchProcess (ProcessLaunchInfo &launch_info)
{
    Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PLATFORM));
    Error error;
    lldb::pid_t pid = LLDB_INVALID_PROCESS_ID;

    if (log)
        log->Printf ("PlatformRemoteGDBServer::%s() called", __FUNCTION__);

    m_gdb_client.SetSTDIN ("/dev/null");
    m_gdb_client.SetSTDOUT ("/dev/null");
    m_gdb_client.SetSTDERR ("/dev/null");
    m_gdb_client.SetDisableASLR (launch_info.GetFlags().Test (eLaunchFlagDisableASLR));
    m_gdb_client.SetDetachOnError (launch_info.GetFlags().Test (eLaunchFlagDetachOnError));
    
    const char *working_dir = launch_info.GetWorkingDirectory();
    if (working_dir && working_dir[0])
    {
        m_gdb_client.SetWorkingDir (working_dir);
    }
    
    // Send the environment and the program + arguments after we connect
    const char **envp = launch_info.GetEnvironmentEntries().GetConstArgumentVector();

    if (envp)
    {
        const char *env_entry;
        for (int i=0; (env_entry = envp[i]); ++i)
        {
            if (m_gdb_client.SendEnvironmentPacket(env_entry) != 0)
                break;
        }
    }
    
    ArchSpec arch_spec = launch_info.GetArchitecture();
    const char *arch_triple = arch_spec.GetTriple().str().c_str();
    
    m_gdb_client.SendLaunchArchPacket(arch_triple);
    if (log)
        log->Printf ("PlatformRemoteGDBServer::%s() set launch architecture triple to '%s'", __FUNCTION__, arch_triple ? arch_triple : "<NULL>");

    const uint32_t old_packet_timeout = m_gdb_client.SetPacketTimeout (5);
    int arg_packet_err = m_gdb_client.SendArgumentsPacket (launch_info);
    m_gdb_client.SetPacketTimeout (old_packet_timeout);
    if (arg_packet_err == 0)
    {
        std::string error_str;
        if (m_gdb_client.GetLaunchSuccess (error_str))
        {
            pid = m_gdb_client.GetCurrentProcessID ();
            if (pid != LLDB_INVALID_PROCESS_ID)
            {
                launch_info.SetProcessID (pid);
                if (log)
                    log->Printf ("PlatformRemoteGDBServer::%s() pid %" PRIu64 " launched successfully", __FUNCTION__, pid);
            }
            else
            {
                if (log)
                    log->Printf ("PlatformRemoteGDBServer::%s() launch succeeded but we didn't get a valid process id back!", __FUNCTION__);
                // FIXME isn't this an error condition? Do we need to set an error here?  Check with Greg.
            }
        }
        else
        {
            error.SetErrorString (error_str.c_str());
            if (log)
                log->Printf ("PlatformRemoteGDBServer::%s() launch failed: %s", __FUNCTION__, error.AsCString ());
        }
    }
    else
    {
        error.SetErrorStringWithFormat("'A' packet returned an error: %i", arg_packet_err);
    }
    return error;
}
Beispiel #17
0
static void LLVM_ATTRIBUTE_NORETURN ChildFunc(int error_fd,
                                              const ProcessLaunchInfo &info) {
  // First, make sure we disable all logging. If we are logging to stdout, our
  // logs can be
  // mistaken for inferior output.
  Log::DisableAllLogChannels(nullptr);

  // Do not inherit setgid powers.
  if (setgid(getgid()) != 0)
    ExitWithError(error_fd, "setgid");

  if (info.GetFlags().Test(eLaunchFlagLaunchInSeparateProcessGroup)) {
    if (setpgid(0, 0) != 0)
      ExitWithError(error_fd, "setpgid");
  }

  for (size_t i = 0; i < info.GetNumFileActions(); ++i) {
    const FileAction &action = *info.GetFileActionAtIndex(i);
    switch (action.GetAction()) {
    case FileAction::eFileActionClose:
      if (close(action.GetFD()) != 0)
        ExitWithError(error_fd, "close");
      break;
    case FileAction::eFileActionDuplicate:
      if (dup2(action.GetFD(), action.GetActionArgument()) == -1)
        ExitWithError(error_fd, "dup2");
      break;
    case FileAction::eFileActionOpen:
      DupDescriptor(error_fd, action.GetFileSpec(), action.GetFD(),
                    action.GetActionArgument());
      break;
    case FileAction::eFileActionNone:
      break;
    }
  }

  const char **argv = info.GetArguments().GetConstArgumentVector();

  // Change working directory
  if (info.GetWorkingDirectory() &&
      0 != ::chdir(info.GetWorkingDirectory().GetCString()))
    ExitWithError(error_fd, "chdir");

  // Disable ASLR if requested.
  if (info.GetFlags().Test(lldb::eLaunchFlagDisableASLR)) {
    const unsigned long personality_get_current = 0xffffffff;
    int value = personality(personality_get_current);
    if (value == -1)
      ExitWithError(error_fd, "personality get");

    value = personality(ADDR_NO_RANDOMIZE | value);
    if (value == -1)
      ExitWithError(error_fd, "personality set");
  }

  Args env = info.GetEnvironmentEntries();
  FixupEnvironment(env);
  const char **envp = env.GetConstArgumentVector();

  // Clear the signal mask to prevent the child from being affected by
  // any masking done by the parent.
  sigset_t set;
  if (sigemptyset(&set) != 0 ||
      pthread_sigmask(SIG_SETMASK, &set, nullptr) != 0)
    ExitWithError(error_fd, "pthread_sigmask");

  if (info.GetFlags().Test(eLaunchFlagDebug)) {
    // HACK:
    // Close everything besides stdin, stdout, and stderr that has no file
    // action to avoid leaking. Only do this when debugging, as elsewhere we
    // actually rely on
    // passing open descriptors to child processes.
    for (int fd = 3; fd < sysconf(_SC_OPEN_MAX); ++fd)
      if (!info.GetFileActionForFD(fd) && fd != error_fd)
        close(fd);

    // Start tracing this child that is about to exec.
    if (ptrace(PTRACE_TRACEME, 0, nullptr, nullptr) == -1)
      ExitWithError(error_fd, "ptrace");
  }

  // Execute.  We should never return...
  execve(argv[0], const_cast<char *const *>(argv),
         const_cast<char *const *>(envp));

  if (errno == ETXTBSY) {
    // On android M and earlier we can get this error because the adb deamon can
    // hold a write
    // handle on the executable even after it has finished uploading it. This
    // state lasts
    // only a short time and happens only when there are many concurrent adb
    // commands being
    // issued, such as when running the test suite. (The file remains open when
    // someone does
    // an "adb shell" command in the fork() child before it has had a chance to
    // exec.) Since
    // this state should clear up quickly, wait a while and then give it one
    // more go.
    usleep(50000);
    execve(argv[0], const_cast<char *const *>(argv),
           const_cast<char *const *>(envp));
  }

  // ...unless exec fails.  In which case we definitely need to end the child
  // here.
  ExitWithError(error_fd, "execve");
}
Beispiel #18
0
Error
GDBRemoteCommunication::StartDebugserverProcess (const char *hostname,
                                                 uint16_t in_port,
                                                 ProcessLaunchInfo &launch_info,
                                                 uint16_t &out_port)
{
    Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS));
    if (log)
        log->Printf ("GDBRemoteCommunication::%s(hostname=%s, in_port=%" PRIu16 ", out_port=%" PRIu16, __FUNCTION__, hostname ? hostname : "<empty>", in_port, out_port);

    out_port = in_port;
    Error error;
    // If we locate debugserver, keep that located version around
    static FileSpec g_debugserver_file_spec;
    
    char debugserver_path[PATH_MAX];
    FileSpec &debugserver_file_spec = launch_info.GetExecutableFile();
    
    // Always check to see if we have an environment override for the path
    // to the debugserver to use and use it if we do.
    const char *env_debugserver_path = getenv("LLDB_DEBUGSERVER_PATH");
    if (env_debugserver_path)
    {
        debugserver_file_spec.SetFile (env_debugserver_path, false);
        if (log)
            log->Printf ("GDBRemoteCommunication::%s() gdb-remote stub exe path set from environment variable: %s", __FUNCTION__, env_debugserver_path);
    }
    else
        debugserver_file_spec = g_debugserver_file_spec;
    bool debugserver_exists = debugserver_file_spec.Exists();
    if (!debugserver_exists)
    {
        // The debugserver binary is in the LLDB.framework/Resources
        // directory.
        if (HostInfo::GetLLDBPath(ePathTypeSupportExecutableDir, debugserver_file_spec))
        {
            debugserver_file_spec.AppendPathComponent (DEBUGSERVER_BASENAME);
            debugserver_exists = debugserver_file_spec.Exists();
            if (debugserver_exists)
            {
                if (log)
                    log->Printf ("GDBRemoteCommunication::%s() found gdb-remote stub exe '%s'", __FUNCTION__, debugserver_file_spec.GetPath ().c_str ());

                g_debugserver_file_spec = debugserver_file_spec;
            }
            else
            {
                if (log)
                    log->Printf ("GDBRemoteCommunication::%s() could not find gdb-remote stub exe '%s'", __FUNCTION__, debugserver_file_spec.GetPath ().c_str ());

                g_debugserver_file_spec.Clear();
                debugserver_file_spec.Clear();
            }
        }
    }
    
    if (debugserver_exists)
    {
        debugserver_file_spec.GetPath (debugserver_path, sizeof(debugserver_path));

        Args &debugserver_args = launch_info.GetArguments();
        debugserver_args.Clear();
        char arg_cstr[PATH_MAX];

        // Start args with "debugserver /file/path -r --"
        debugserver_args.AppendArgument(debugserver_path);

#if !defined(__APPLE__)
        // First argument to lldb-server must be mode in which to run.
        debugserver_args.AppendArgument("gdbserver");
#endif

        // If a host and port is supplied then use it
        char host_and_port[128];
        if (hostname)
        {
            snprintf (host_and_port, sizeof(host_and_port), "%s:%u", hostname, in_port);
            debugserver_args.AppendArgument(host_and_port);
        }
        else
        {
            host_and_port[0] = '\0';
        }

        // use native registers, not the GDB registers
        debugserver_args.AppendArgument("--native-regs");

        if (launch_info.GetLaunchInSeparateProcessGroup())
        {
            debugserver_args.AppendArgument("--setsid");
        }

        llvm::SmallString<PATH_MAX> named_pipe_path;
        Pipe port_pipe;

        bool listen = false;
        if (host_and_port[0])
        {
            // Create a temporary file to get the stdout/stderr and redirect the
            // output of the command into this file. We will later read this file
            // if all goes well and fill the data into "command_output_ptr"

            if (in_port == 0)
            {
                // Binding to port zero, we need to figure out what port it ends up
                // using using a named pipe...
                error = port_pipe.CreateWithUniqueName("debugserver-named-pipe", false, named_pipe_path);
                if (error.Success())
                {
                    debugserver_args.AppendArgument("--named-pipe");
                    debugserver_args.AppendArgument(named_pipe_path.c_str());
                }
                else
                {
                    if (log)
                        log->Printf("GDBRemoteCommunication::%s() "
                                "named pipe creation failed: %s",
                                __FUNCTION__, error.AsCString());
                    // let's try an unnamed pipe
                    error = port_pipe.CreateNew(true);
                    if (error.Fail())
                    {
                        if (log)
                            log->Printf("GDBRemoteCommunication::%s() "
                                    "unnamed pipe creation failed: %s",
                                    __FUNCTION__, error.AsCString());
                        return error;
                    }
                    int write_fd = port_pipe.GetWriteFileDescriptor();
                    debugserver_args.AppendArgument("--pipe");
                    debugserver_args.AppendArgument(std::to_string(write_fd).c_str());
                    launch_info.AppendCloseFileAction(port_pipe.GetReadFileDescriptor());
                }
            }
            else
            {
                listen = true;
            }
        }
        else
        {
            // No host and port given, so lets listen on our end and make the debugserver
            // connect to us..
            error = StartListenThread ("127.0.0.1", 0);
            if (error.Fail())
                return error;

            ConnectionFileDescriptor *connection = (ConnectionFileDescriptor *)GetConnection ();
            // Wait for 10 seconds to resolve the bound port
            out_port = connection->GetListeningPort(10);
            if (out_port > 0)
            {
                char port_cstr[32];
                snprintf(port_cstr, sizeof(port_cstr), "127.0.0.1:%i", out_port);
                // Send the host and port down that debugserver and specify an option
                // so that it connects back to the port we are listening to in this process
                debugserver_args.AppendArgument("--reverse-connect");
                debugserver_args.AppendArgument(port_cstr);
            }
            else
            {
                error.SetErrorString ("failed to bind to port 0 on 127.0.0.1");
                return error;
            }
        }
        
        const char *env_debugserver_log_file = getenv("LLDB_DEBUGSERVER_LOG_FILE");
        if (env_debugserver_log_file)
        {
            ::snprintf (arg_cstr, sizeof(arg_cstr), "--log-file=%s", env_debugserver_log_file);
            debugserver_args.AppendArgument(arg_cstr);
        }
        
        const char *env_debugserver_log_flags = getenv("LLDB_DEBUGSERVER_LOG_FLAGS");
        if (env_debugserver_log_flags)
        {
            ::snprintf (arg_cstr, sizeof(arg_cstr), "--log-flags=%s", env_debugserver_log_flags);
            debugserver_args.AppendArgument(arg_cstr);
        }

        // Add additional args, starting with LLDB_DEBUGSERVER_EXTRA_ARG_1 until an env var doesn't come back.
        uint32_t env_var_index = 1;
        bool has_env_var;
        do
        {
            char env_var_name[64];
            snprintf (env_var_name, sizeof (env_var_name), "LLDB_DEBUGSERVER_EXTRA_ARG_%" PRIu32, env_var_index++);
            const char *extra_arg = getenv(env_var_name);
            has_env_var = extra_arg != nullptr;

            if (has_env_var)
            {
                debugserver_args.AppendArgument (extra_arg);
                if (log)
                    log->Printf ("GDBRemoteCommunication::%s adding env var %s contents to stub command line (%s)", __FUNCTION__, env_var_name, extra_arg);
            }
        } while (has_env_var);

        // Close STDIN, STDOUT and STDERR.
        launch_info.AppendCloseFileAction (STDIN_FILENO);
        launch_info.AppendCloseFileAction (STDOUT_FILENO);
        launch_info.AppendCloseFileAction (STDERR_FILENO);

        // Redirect STDIN, STDOUT and STDERR to "/dev/null".
        launch_info.AppendSuppressFileAction (STDIN_FILENO, true, false);
        launch_info.AppendSuppressFileAction (STDOUT_FILENO, false, true);
        launch_info.AppendSuppressFileAction (STDERR_FILENO, false, true);
        
        error = Host::LaunchProcess(launch_info);
        
        if (error.Success() && launch_info.GetProcessID() != LLDB_INVALID_PROCESS_ID)
        {
            if (named_pipe_path.size() > 0)
            {
                error = port_pipe.OpenAsReader(named_pipe_path, false);
                if (error.Fail())
                    if (log)
                        log->Printf("GDBRemoteCommunication::%s() "
                                "failed to open named pipe %s for reading: %s",
                                __FUNCTION__, named_pipe_path.c_str(), error.AsCString());
            }

            if (port_pipe.CanWrite())
                port_pipe.CloseWriteFileDescriptor();
            if (port_pipe.CanRead())
            {
                char port_cstr[256];
                port_cstr[0] = '\0';
                size_t num_bytes = sizeof(port_cstr);
                // Read port from pipe with 10 second timeout.
                error = port_pipe.ReadWithTimeout(port_cstr, num_bytes,
                        std::chrono::seconds{10}, num_bytes);
                if (error.Success())
                {
                    assert(num_bytes > 0 && port_cstr[num_bytes-1] == '\0');
                    out_port = StringConvert::ToUInt32(port_cstr, 0);
                    if (log)
                        log->Printf("GDBRemoteCommunication::%s() "
                                "debugserver listens %u port",
                                __FUNCTION__, out_port);
                }
                else
                {
                    if (log)
                        log->Printf("GDBRemoteCommunication::%s() "
                                "failed to read a port value from pipe %s: %s",
                                __FUNCTION__, named_pipe_path.c_str(), error.AsCString());

                }
                port_pipe.Close();
            }

            if (named_pipe_path.size() > 0)
            {
                const auto err = port_pipe.Delete(named_pipe_path);
                if (err.Fail())
                {
                    if (log)
                        log->Printf ("GDBRemoteCommunication::%s failed to delete pipe %s: %s",
                                __FUNCTION__, named_pipe_path.c_str(), err.AsCString());
                }
            }

            // Make sure we actually connect with the debugserver...
            JoinListenThread();
        }
    }
    else
    {
        error.SetErrorStringWithFormat ("unable to locate " DEBUGSERVER_BASENAME );
    }
    return error;
}
Status ProcessFreeBSD::DoLaunch(Module *module,
                                ProcessLaunchInfo &launch_info) {
  Status error;
  assert(m_monitor == NULL);

  FileSpec working_dir = launch_info.GetWorkingDirectory();
  if (working_dir) {
    FileSystem::Instance().Resolve(working_dir);
    if (!FileSystem::Instance().IsDirectory(working_dir.GetPath())) {
      error.SetErrorStringWithFormat("No such file or directory: %s",
                                   working_dir.GetCString());
      return error;
    }
  }

  SetPrivateState(eStateLaunching);

  const lldb_private::FileAction *file_action;

  // Default of empty will mean to use existing open file descriptors
  FileSpec stdin_file_spec{};
  FileSpec stdout_file_spec{};
  FileSpec stderr_file_spec{};

  const FileSpec dbg_pts_file_spec{launch_info.GetPTY().GetSlaveName(NULL, 0)};

  file_action = launch_info.GetFileActionForFD(STDIN_FILENO);
  stdin_file_spec =
      GetFileSpec(file_action, stdin_file_spec, dbg_pts_file_spec);

  file_action = launch_info.GetFileActionForFD(STDOUT_FILENO);
  stdout_file_spec =
      GetFileSpec(file_action, stdout_file_spec, dbg_pts_file_spec);

  file_action = launch_info.GetFileActionForFD(STDERR_FILENO);
  stderr_file_spec =
      GetFileSpec(file_action, stderr_file_spec, dbg_pts_file_spec);

  m_monitor = new ProcessMonitor(
      this, module, launch_info.GetArguments().GetConstArgumentVector(),
      launch_info.GetEnvironment(), stdin_file_spec, stdout_file_spec,
      stderr_file_spec, working_dir, launch_info, error);

  m_module = module;

  if (!error.Success())
    return error;

  int terminal = m_monitor->GetTerminalFD();
  if (terminal >= 0) {
// The reader thread will close the file descriptor when done, so we pass it a
// copy.
#ifdef F_DUPFD_CLOEXEC
    int stdio = fcntl(terminal, F_DUPFD_CLOEXEC, 0);
    if (stdio == -1) {
      error.SetErrorToErrno();
      return error;
    }
#else
    // Special case when F_DUPFD_CLOEXEC does not exist (Debian kFreeBSD)
    int stdio = fcntl(terminal, F_DUPFD, 0);
    if (stdio == -1) {
      error.SetErrorToErrno();
      return error;
    }
    stdio = fcntl(terminal, F_SETFD, FD_CLOEXEC);
    if (stdio == -1) {
      error.SetErrorToErrno();
      return error;
    }
#endif
    SetSTDIOFileDescriptor(stdio);
  }

  SetID(m_monitor->GetPID());
  return error;
}
// For local debugging, Linux will override the debug logic to use llgs-launch rather than
// lldb-launch, llgs-attach.  This differs from current lldb-launch, debugserver-attach
// approach on MacOSX.
lldb::ProcessSP
PlatformLinux::DebugProcess (ProcessLaunchInfo &launch_info,
                             Debugger &debugger,
                             Target *target,       // Can be NULL, if NULL create a new target, else use existing one
                             Error &error)
{
    Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PLATFORM));
    if (log)
        log->Printf ("PlatformLinux::%s entered (target %p)", __FUNCTION__, static_cast<void*>(target));

    // If we're a remote host, use standard behavior from parent class.
    if (!IsHost ())
        return PlatformPOSIX::DebugProcess (launch_info, debugger, target, error);

    //
    // For local debugging, we'll insist on having ProcessGDBRemote create the process.
    //

    ProcessSP process_sp;

    // Ensure we're using llgs for local debugging.
    if (!UseLlgsForLocalDebugging ())
    {
        assert (false && "we're trying to debug a local process but platform.plugin.linux.use-llgs-for-local is false, should never get here");
        error.SetErrorString ("attempted to start gdb-remote-based debugging for local process but platform.plugin.linux.use-llgs-for-local is false");
        return process_sp;
    }

    // Make sure we stop at the entry point
    launch_info.GetFlags ().Set (eLaunchFlagDebug);

    // We always launch the process we are going to debug in a separate process
    // group, since then we can handle ^C interrupts ourselves w/o having to worry
    // about the target getting them as well.
    launch_info.SetLaunchInSeparateProcessGroup(true);

    // Ensure we have a target.
    if (target == nullptr)
    {
        if (log)
            log->Printf ("PlatformLinux::%s creating new target", __FUNCTION__);

        TargetSP new_target_sp;
        error = debugger.GetTargetList().CreateTarget (debugger,
                                                       nullptr,
                                                       nullptr,
                                                       false,
                                                       nullptr,
                                                       new_target_sp);
        if (error.Fail ())
        {
            if (log)
                log->Printf ("PlatformLinux::%s failed to create new target: %s", __FUNCTION__, error.AsCString ());
            return process_sp;
        }

        target = new_target_sp.get();
        if (!target)
        {
            error.SetErrorString ("CreateTarget() returned nullptr");
            if (log)
                log->Printf ("PlatformLinux::%s failed: %s", __FUNCTION__, error.AsCString ());
            return process_sp;
        }
    }
    else
    {
        if (log)
            log->Printf ("PlatformLinux::%s using provided target", __FUNCTION__);
    }

    // Mark target as currently selected target.
    debugger.GetTargetList().SetSelectedTarget(target);

    // Now create the gdb-remote process.
    if (log)
        log->Printf ("PlatformLinux::%s having target create process with gdb-remote plugin", __FUNCTION__);
    process_sp = target->CreateProcess (launch_info.GetListenerForProcess(debugger), "gdb-remote", nullptr);

    if (!process_sp)
    {
        error.SetErrorString ("CreateProcess() failed for gdb-remote process");
        if (log)
            log->Printf ("PlatformLinux::%s failed: %s", __FUNCTION__, error.AsCString ());
        return process_sp;
    }
    else
    {
        if (log)
            log->Printf ("PlatformLinux::%s successfully created process", __FUNCTION__);
    }

    // Set the unix signals properly.
    process_sp->SetUnixSignals (Host::GetUnixSignals ());

    // Adjust launch for a hijacker.
    ListenerSP listener_sp;
    if (!launch_info.GetHijackListener ())
    {
        if (log)
            log->Printf ("PlatformLinux::%s setting up hijacker", __FUNCTION__);

        listener_sp.reset (new Listener("lldb.PlatformLinux.DebugProcess.hijack"));
        launch_info.SetHijackListener (listener_sp);
        process_sp->HijackProcessEvents (listener_sp.get ());
    }

    // Log file actions.
    if (log)
    {
        log->Printf ("PlatformLinux::%s launching process with the following file actions:", __FUNCTION__);

        StreamString stream;
        size_t i = 0;
        const FileAction *file_action;
        while ((file_action = launch_info.GetFileActionAtIndex (i++)) != nullptr)
        {
            file_action->Dump (stream);
            log->PutCString (stream.GetString().c_str ());
            stream.Clear();
        }
    }

    // Do the launch.
    error = process_sp->Launch(launch_info);
    if (error.Success ())
    {
        // Handle the hijacking of process events.
        if (listener_sp)
        {
            const StateType state = process_sp->WaitForProcessToStop (NULL, NULL, false, listener_sp.get());
            process_sp->RestoreProcessEvents();

            if (state == eStateStopped)
            {
                if (log)
                    log->Printf ("PlatformLinux::%s pid %" PRIu64 " state %s\n",
                                 __FUNCTION__, process_sp->GetID (), StateAsCString (state));
            }
            else
            {
                if (log)
                    log->Printf ("PlatformLinux::%s pid %" PRIu64 " state is not stopped - %s\n",
                                 __FUNCTION__, process_sp->GetID (), StateAsCString (state));
            }
        }

        // Hook up process PTY if we have one (which we should for local debugging with llgs).
        int pty_fd = launch_info.GetPTY().ReleaseMasterFileDescriptor();
        if (pty_fd != lldb_utility::PseudoTerminal::invalid_fd)
        {
            process_sp->SetSTDIOFileDescriptor(pty_fd);
            if (log)
                log->Printf ("PlatformLinux::%s pid %" PRIu64 " hooked up STDIO pty to process", __FUNCTION__, process_sp->GetID ());
        }
        else
        {
            if (log)
                log->Printf ("PlatformLinux::%s pid %" PRIu64 " not using process STDIO pty", __FUNCTION__, process_sp->GetID ());
        }
    }
    else
    {
        if (log)
            log->Printf ("PlatformLinux::%s process launch failed: %s", __FUNCTION__, error.AsCString ());
        // FIXME figure out appropriate cleanup here.  Do we delete the target? Do we delete the process?  Does our caller do that?
    }

    return process_sp;
}
Beispiel #21
0
Error
Host::LaunchProcess (ProcessLaunchInfo &launch_info)
{
    Error error;
    char exe_path[PATH_MAX];

    PlatformSP host_platform_sp (Platform::GetHostPlatform ());

    const ArchSpec &arch_spec = launch_info.GetArchitecture();

    FileSpec exe_spec(launch_info.GetExecutableFile());

    FileSpec::FileType file_type = exe_spec.GetFileType();
    if (file_type != FileSpec::eFileTypeRegular)
    {
        lldb::ModuleSP exe_module_sp;
        error = host_platform_sp->ResolveExecutable (exe_spec,
                                                     arch_spec,
                                                     exe_module_sp,
                                                     NULL);

        if (error.Fail())
            return error;

        if (exe_module_sp)
            exe_spec = exe_module_sp->GetFileSpec();
    }

    if (exe_spec.Exists())
    {
        exe_spec.GetPath (exe_path, sizeof(exe_path));
    }
    else
    {
        launch_info.GetExecutableFile().GetPath (exe_path, sizeof(exe_path));
        error.SetErrorStringWithFormat ("executable doesn't exist: '%s'", exe_path);
        return error;
    }

    assert(!launch_info.GetFlags().Test (eLaunchFlagLaunchInTTY));

    ::pid_t pid = LLDB_INVALID_PROCESS_ID;

    error = LaunchProcessPosixSpawn(exe_path, launch_info, pid);

    if (pid != LLDB_INVALID_PROCESS_ID)
    {
        // If all went well, then set the process ID into the launch info
        launch_info.SetProcessID(pid);

        Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));

        // Make sure we reap any processes we spawn or we will have zombies.
        if (!launch_info.MonitorProcess())
        {
            const bool monitor_signals = false;
            StartMonitoringChildProcess (Process::SetProcessExitStatus,
                                         NULL,
                                         pid,
                                         monitor_signals);
            if (log)
                log->PutCString ("monitored child process with default Process::SetProcessExitStatus.");
        }
        else
        {
            if (log)
                log->PutCString ("monitored child process with user-specified process monitor.");
        }
    }
    else
    {
        // Invalid process ID, something didn't go well
        if (error.Success())
            error.SetErrorString ("process launch failed for unknown reasons");
    }
    return error;
}
Error
ProcessFreeBSD::DoLaunch (Module *module,
                        ProcessLaunchInfo &launch_info)
{
    Error error;
    assert(m_monitor == NULL);

    FileSpec working_dir = launch_info.GetWorkingDirectory();
    if (working_dir &&
            (!working_dir.ResolvePath() ||
             working_dir.GetFileType() != FileSpec::eFileTypeDirectory))
    {
        error.SetErrorStringWithFormat("No such file or directory: %s",
                working_dir.GetCString());
        return error;
    }

    SetPrivateState(eStateLaunching);

    const lldb_private::FileAction *file_action;

    // Default of empty will mean to use existing open file descriptors
    FileSpec stdin_file_spec{};
    FileSpec stdout_file_spec{};
    FileSpec stderr_file_spec{};

    const FileSpec dbg_pts_file_spec{launch_info.GetPTY().GetSlaveName(NULL,0), false};

    file_action = launch_info.GetFileActionForFD (STDIN_FILENO);
    stdin_file_spec = GetFileSpec(file_action, stdin_file_spec, dbg_pts_file_spec);

    file_action = launch_info.GetFileActionForFD (STDOUT_FILENO);
    stdout_file_spec = GetFileSpec(file_action, stdout_file_spec, dbg_pts_file_spec);

    file_action = launch_info.GetFileActionForFD (STDERR_FILENO);
    stderr_file_spec = GetFileSpec(file_action, stderr_file_spec, dbg_pts_file_spec);

    m_monitor = new ProcessMonitor(this,
                                   module,
                                   launch_info.GetArguments().GetConstArgumentVector(),
                                   launch_info.GetEnvironmentEntries().GetConstArgumentVector(),
                                   stdin_file_spec,
                                   stdout_file_spec,
                                   stderr_file_spec,
                                   working_dir,
                                   launch_info,
                                   error);

    m_module = module;

    if (!error.Success())
        return error;

    int terminal = m_monitor->GetTerminalFD();
    if (terminal >= 0) {
        // The reader thread will close the file descriptor when done, so we pass it a copy.
        int stdio = fcntl(terminal, F_DUPFD_CLOEXEC, 0);
        if (stdio == -1) {
            error.SetErrorToErrno();
            return error;
        }
        SetSTDIOFileDescriptor(stdio);
    }

    SetID(m_monitor->GetPID());
    return error;
}
HostProcess
MonitoringProcessLauncher::LaunchProcess(const ProcessLaunchInfo &launch_info,
                                         Status &error) {
  ProcessLaunchInfo resolved_info(launch_info);

  error.Clear();
  char exe_path[PATH_MAX];

  PlatformSP host_platform_sp(Platform::GetHostPlatform());

  const ArchSpec &arch_spec = resolved_info.GetArchitecture();

  FileSpec exe_spec(resolved_info.GetExecutableFile());

  llvm::sys::fs::file_status stats;
  status(exe_spec.GetPath(), stats);
  if (!is_regular_file(stats)) {
    ModuleSpec module_spec(exe_spec, arch_spec);
    lldb::ModuleSP exe_module_sp;
    error =
        host_platform_sp->ResolveExecutable(module_spec, exe_module_sp, NULL);

    if (error.Fail())
      return HostProcess();

    if (exe_module_sp) {
      exe_spec = exe_module_sp->GetFileSpec();
      status(exe_spec.GetPath(), stats);
    }
  }

  if (exists(stats)) {
    exe_spec.GetPath(exe_path, sizeof(exe_path));
  } else {
    resolved_info.GetExecutableFile().GetPath(exe_path, sizeof(exe_path));
    error.SetErrorStringWithFormat("executable doesn't exist: '%s'", exe_path);
    return HostProcess();
  }

  resolved_info.SetExecutableFile(exe_spec, false);
  assert(!resolved_info.GetFlags().Test(eLaunchFlagLaunchInTTY));

  HostProcess process =
      m_delegate_launcher->LaunchProcess(resolved_info, error);

  if (process.GetProcessId() != LLDB_INVALID_PROCESS_ID) {
    Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));

    Host::MonitorChildProcessCallback callback =
        launch_info.GetMonitorProcessCallback();

    bool monitor_signals = false;
    if (callback) {
      // If the ProcessLaunchInfo specified a callback, use that.
      monitor_signals = launch_info.GetMonitorSignals();
    } else {
      callback = Process::SetProcessExitStatus;
    }

    process.StartMonitoring(callback, monitor_signals);
    if (log)
      log->PutCString("started monitoring child process.");
  } else {
    // Invalid process ID, something didn't go well
    if (error.Success())
      error.SetErrorString("process launch failed for unknown reasons");
  }
  return process;
}