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