short Host::GetPosixspawnFlags (ProcessLaunchInfo &launch_info) { #ifndef __ANDROID__ short flags = POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK; #if defined (__APPLE__) if (launch_info.GetFlags().Test (eLaunchFlagExec)) flags |= POSIX_SPAWN_SETEXEC; // Darwin specific posix_spawn flag if (launch_info.GetFlags().Test (eLaunchFlagDebug)) flags |= POSIX_SPAWN_START_SUSPENDED; // Darwin specific posix_spawn flag if (launch_info.GetFlags().Test (eLaunchFlagDisableASLR)) flags |= _POSIX_SPAWN_DISABLE_ASLR; // Darwin specific posix_spawn flag if (launch_info.GetLaunchInSeparateProcessGroup()) flags |= POSIX_SPAWN_SETPGROUP; #ifdef POSIX_SPAWN_CLOEXEC_DEFAULT #if defined (__APPLE__) && (defined (__x86_64__) || defined (__i386__)) static LazyBool g_use_close_on_exec_flag = eLazyBoolCalculate; if (g_use_close_on_exec_flag == eLazyBoolCalculate) { g_use_close_on_exec_flag = eLazyBoolNo; uint32_t major, minor, update; if (HostInfo::GetOSVersion(major, minor, update)) { // Kernel panic if we use the POSIX_SPAWN_CLOEXEC_DEFAULT on 10.7 or earlier if (major > 10 || (major == 10 && minor > 7)) { // Only enable for 10.8 and later OS versions g_use_close_on_exec_flag = eLazyBoolYes; } } } #else static LazyBool g_use_close_on_exec_flag = eLazyBoolYes; #endif // Close all files exception those with file actions if this is supported. if (g_use_close_on_exec_flag == eLazyBoolYes) flags |= POSIX_SPAWN_CLOEXEC_DEFAULT; #endif #endif // #if defined (__APPLE__) return flags; #else assert(false *&& "Host::GetPosixspawnFlags() not supported on Android"); return 0; #endif }
lldb::ProcessSP PlatformPOSIX::DebugProcess (ProcessLaunchInfo &launch_info, Debugger &debugger, Target *target, // Can be NULL, if NULL create a new target, else use existing one Error &error) { ProcessSP process_sp; if (IsHost()) { // We are going to hand this process off to debugserver which will be in charge of setting the exit status. // We still need to reap it from lldb but if we let the monitor thread also set the exit status, we set up a // race between debugserver & us for who will find out about the debugged process's death. launch_info.GetFlags().Set(eLaunchFlagDontSetExitStatus); process_sp = Platform::DebugProcess (launch_info, debugger, target, error); } else { if (m_remote_platform_sp) process_sp = m_remote_platform_sp->DebugProcess (launch_info, debugger, target, error); else error.SetErrorString ("the platform is not currently connected"); } return process_sp; }
int32_t PlatformNetBSD::GetResumeCountForLaunchInfo(ProcessLaunchInfo &launch_info) { int32_t resume_count = 0; // Always resume past the initial stop when we use eLaunchFlagDebug if (launch_info.GetFlags().Test(eLaunchFlagDebug)) { // Resume past the stop for the final exec into the true inferior. ++resume_count; } // If we're not launching a shell, we're done. const FileSpec &shell = launch_info.GetShell(); if (!shell) return resume_count; std::string shell_string = shell.GetPath(); // We're in a shell, so for sure we have to resume past the shell exec. ++resume_count; // Figure out what shell we're planning on using. const char *shell_name = strrchr(shell_string.c_str(), '/'); if (shell_name == NULL) shell_name = shell_string.c_str(); else shell_name++; if (strcmp(shell_name, "csh") == 0 || strcmp(shell_name, "tcsh") == 0 || strcmp(shell_name, "zsh") == 0 || strcmp(shell_name, "sh") == 0) { // These shells seem to re-exec themselves. Add another resume. ++resume_count; } return resume_count; }
Error PlatformRemoteGDBServer::LaunchProcess (ProcessLaunchInfo &launch_info) { Error error; lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; 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)); 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); 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); } else { error.SetErrorString (error_str.c_str()); } } else { error.SetErrorStringWithFormat("'A' packet returned an error: %i", arg_packet_err); } return error; }
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 }
Error ProcessWindows::DoLaunch(Module *exe_module, ProcessLaunchInfo &launch_info) { // Even though m_session_data is accessed here, it is before a debugger thread has been // kicked off. So there's no race conditions, and it shouldn't be necessary to acquire // the mutex. Error result; if (!launch_info.GetFlags().Test(eLaunchFlagDebug)) { result.SetErrorString("ProcessWindows can only be used to launch processes for debugging."); return result; } m_session_data.reset(new ProcessWindowsData(launch_info)); SetPrivateState(eStateLaunching); DebugDelegateSP delegate(new LocalDebugDelegate(shared_from_this())); m_session_data->m_debugger.reset(new DebuggerThread(delegate)); DebuggerThreadSP debugger = m_session_data->m_debugger; // Kick off the DebugLaunch asynchronously and wait for it to complete. result = debugger->DebugLaunch(launch_info); HostProcess process; if (result.Success()) { // Block this function until we receive the initial stop from the process. if (::WaitForSingleObject(m_session_data->m_initial_stop_event, INFINITE) == WAIT_OBJECT_0) { process = debugger->GetProcess(); if (m_session_data->m_launch_error.Fail()) result = m_session_data->m_launch_error; } else result.SetError(::GetLastError(), eErrorTypeWin32); } if (!result.Success()) return result; // We've hit the initial stop. The private state should already be set to stopped as a result // of encountering the breakpoint exception in ProcessWindows::OnDebugException. launch_info.SetProcessID(process.GetProcessId()); SetID(process.GetProcessId()); return result; }
lldb::ProcessSP Platform::DebugProcess (ProcessLaunchInfo &launch_info, Debugger &debugger, Target *target, // Can be NULL, if NULL create a new target, else use existing one Listener &listener, Error &error) { 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); error = LaunchProcess (launch_info); if (error.Success()) { if (launch_info.GetProcessID() != LLDB_INVALID_PROCESS_ID) { ProcessAttachInfo attach_info (launch_info); process_sp = Attach (attach_info, debugger, target, listener, error); if (process_sp) { launch_info.SetHijackListener(attach_info.GetHijackListener()); // Since we attached to the process, it will think it needs to detach // if the process object just goes away without an explicit call to // Process::Kill() or Process::Detach(), so let it know to kill the // process if this happens. process_sp->SetShouldDetach (false); // If we didn't have any file actions, the pseudo terminal might // have been used where the slave side was given as the file to // open for stdin/out/err after we have already opened the master // so we can read/write stdin/out/err. int pty_fd = launch_info.GetPTY().ReleaseMasterFileDescriptor(); if (pty_fd != lldb_utility::PseudoTerminal::invalid_fd) { process_sp->SetSTDIOFileDescriptor(pty_fd); } } } } return process_sp; }
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 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; }
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"); }
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; }
// 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; }
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"); }
// 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 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; }