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; } }
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 }
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; }
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(); }
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; }
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; }
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; }
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; }
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"); }
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; }
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; }