Revert "Revert "Make it possible for lldb to launch a remote binary with no local file.""

This reverts commit dd5505a8f2.

I picked the wrong class for the test, should have been GDBRemoteTestBase.
This commit is contained in:
Jim Ingham 2021-11-17 14:49:55 -08:00
parent 02eca53a50
commit 92eaad2dd7
4 changed files with 319 additions and 211 deletions

View File

@ -159,7 +159,12 @@ protected:
// If our listener is nullptr, users aren't allows to launch
ModuleSP exe_module_sp = target->GetExecutableModule();
if (exe_module_sp == nullptr) {
// If the target already has an executable module, then use that. If it
// doesn't then someone must be trying to launch using a path that will
// make sense to the remote stub, but doesn't exist on the local host.
// In that case use the ExecutableFile that was set in the target's
// ProcessLaunchInfo.
if (exe_module_sp == nullptr && !target->GetProcessLaunchInfo().GetExecutableFile()) {
result.AppendError("no file in target, create a debug target using the "
"'target create' command");
return false;
@ -219,11 +224,17 @@ protected:
if (!target_settings_argv0.empty()) {
m_options.launch_info.GetArguments().AppendArgument(
target_settings_argv0);
m_options.launch_info.SetExecutableFile(
exe_module_sp->GetPlatformFileSpec(), false);
if (exe_module_sp)
m_options.launch_info.SetExecutableFile(
exe_module_sp->GetPlatformFileSpec(), false);
else
m_options.launch_info.SetExecutableFile(target->GetProcessLaunchInfo().GetExecutableFile(), false);
} else {
m_options.launch_info.SetExecutableFile(
exe_module_sp->GetPlatformFileSpec(), true);
if (exe_module_sp)
m_options.launch_info.SetExecutableFile(
exe_module_sp->GetPlatformFileSpec(), true);
else
m_options.launch_info.SetExecutableFile(target->GetProcessLaunchInfo().GetExecutableFile(), true);
}
if (launch_args.GetArgumentCount() == 0) {
@ -250,11 +261,20 @@ protected:
llvm::StringRef data = stream.GetString();
if (!data.empty())
result.AppendMessage(data);
const char *archname =
exe_module_sp->GetArchitecture().GetArchitectureName();
result.AppendMessageWithFormat(
"Process %" PRIu64 " launched: '%s' (%s)\n", process_sp->GetID(),
exe_module_sp->GetFileSpec().GetPath().c_str(), archname);
// If we didn't have a local executable, then we wouldn't have had an
// executable module before launch.
if (!exe_module_sp)
exe_module_sp = target->GetExecutableModule();
if (!exe_module_sp) {
result.AppendWarning("Could not get executable module after launch.");
} else {
const char *archname =
exe_module_sp->GetArchitecture().GetArchitectureName();
result.AppendMessageWithFormat(
"Process %" PRIu64 " launched: '%s' (%s)\n", process_sp->GetID(),
exe_module_sp->GetFileSpec().GetPath().c_str(), archname);
}
result.SetStatus(eReturnStatusSuccessFinishResult);
result.SetDidChangeProcessState(true);
} else {

View File

@ -677,143 +677,133 @@ Status ProcessGDBRemote::DoLaunch(lldb_private::Module *exe_module,
// LLDB_LOG_OPTION_PREPEND_PROC_AND_THREAD);
// ::LogSetLogFile ("/dev/stdout");
ObjectFile *object_file = exe_module->GetObjectFile();
if (object_file) {
error = EstablishConnectionIfNeeded(launch_info);
if (error.Success()) {
PseudoTerminal pty;
const bool disable_stdio = (launch_flags & eLaunchFlagDisableSTDIO) != 0;
error = EstablishConnectionIfNeeded(launch_info);
if (error.Success()) {
PseudoTerminal pty;
const bool disable_stdio = (launch_flags & eLaunchFlagDisableSTDIO) != 0;
PlatformSP platform_sp(GetTarget().GetPlatform());
if (disable_stdio) {
// set to /dev/null unless redirected to a file above
if (!stdin_file_spec)
stdin_file_spec.SetFile(FileSystem::DEV_NULL,
FileSpec::Style::native);
if (!stdout_file_spec)
stdout_file_spec.SetFile(FileSystem::DEV_NULL,
FileSpec::Style::native);
if (!stderr_file_spec)
stderr_file_spec.SetFile(FileSystem::DEV_NULL,
FileSpec::Style::native);
} else if (platform_sp && platform_sp->IsHost()) {
// If the debugserver is local and we aren't disabling STDIO, lets use
// a pseudo terminal to instead of relying on the 'O' packets for stdio
// since 'O' packets can really slow down debugging if the inferior
// does a lot of output.
if ((!stdin_file_spec || !stdout_file_spec || !stderr_file_spec) &&
!errorToBool(pty.OpenFirstAvailablePrimary(O_RDWR | O_NOCTTY))) {
FileSpec secondary_name(pty.GetSecondaryName());
PlatformSP platform_sp(GetTarget().GetPlatform());
if (disable_stdio) {
// set to /dev/null unless redirected to a file above
if (!stdin_file_spec)
stdin_file_spec.SetFile(FileSystem::DEV_NULL,
FileSpec::Style::native);
stdin_file_spec = secondary_name;
if (!stdout_file_spec)
stdout_file_spec.SetFile(FileSystem::DEV_NULL,
FileSpec::Style::native);
stdout_file_spec = secondary_name;
if (!stderr_file_spec)
stderr_file_spec.SetFile(FileSystem::DEV_NULL,
FileSpec::Style::native);
} else if (platform_sp && platform_sp->IsHost()) {
// If the debugserver is local and we aren't disabling STDIO, lets use
// a pseudo terminal to instead of relying on the 'O' packets for stdio
// since 'O' packets can really slow down debugging if the inferior
// does a lot of output.
if ((!stdin_file_spec || !stdout_file_spec || !stderr_file_spec) &&
!errorToBool(pty.OpenFirstAvailablePrimary(O_RDWR | O_NOCTTY))) {
FileSpec secondary_name(pty.GetSecondaryName());
if (!stdin_file_spec)
stdin_file_spec = secondary_name;
if (!stdout_file_spec)
stdout_file_spec = secondary_name;
if (!stderr_file_spec)
stderr_file_spec = secondary_name;
}
LLDB_LOGF(
log,
"ProcessGDBRemote::%s adjusted STDIO paths for local platform "
"(IsHost() is true) using secondary: stdin=%s, stdout=%s, "
"stderr=%s",
__FUNCTION__,
stdin_file_spec ? stdin_file_spec.GetCString() : "<null>",
stdout_file_spec ? stdout_file_spec.GetCString() : "<null>",
stderr_file_spec ? stderr_file_spec.GetCString() : "<null>");
stderr_file_spec = secondary_name;
}
LLDB_LOGF(
log,
"ProcessGDBRemote::%s adjusted STDIO paths for local platform "
"(IsHost() is true) using secondary: stdin=%s, stdout=%s, "
"stderr=%s",
__FUNCTION__,
stdin_file_spec ? stdin_file_spec.GetCString() : "<null>",
stdout_file_spec ? stdout_file_spec.GetCString() : "<null>",
stderr_file_spec ? stderr_file_spec.GetCString() : "<null>");
}
LLDB_LOGF(log,
"ProcessGDBRemote::%s final STDIO paths after all "
"adjustments: stdin=%s, stdout=%s, stderr=%s",
__FUNCTION__,
stdin_file_spec ? stdin_file_spec.GetCString() : "<null>",
stdout_file_spec ? stdout_file_spec.GetCString() : "<null>",
stderr_file_spec ? stderr_file_spec.GetCString() : "<null>");
LLDB_LOGF(log,
"ProcessGDBRemote::%s final STDIO paths after all "
"adjustments: stdin=%s, stdout=%s, stderr=%s",
__FUNCTION__,
stdin_file_spec ? stdin_file_spec.GetCString() : "<null>",
stdout_file_spec ? stdout_file_spec.GetCString() : "<null>",
stderr_file_spec ? stderr_file_spec.GetCString() : "<null>");
if (stdin_file_spec)
m_gdb_comm.SetSTDIN(stdin_file_spec);
if (stdout_file_spec)
m_gdb_comm.SetSTDOUT(stdout_file_spec);
if (stderr_file_spec)
m_gdb_comm.SetSTDERR(stderr_file_spec);
if (stdin_file_spec)
m_gdb_comm.SetSTDIN(stdin_file_spec);
if (stdout_file_spec)
m_gdb_comm.SetSTDOUT(stdout_file_spec);
if (stderr_file_spec)
m_gdb_comm.SetSTDERR(stderr_file_spec);
m_gdb_comm.SetDisableASLR(launch_flags & eLaunchFlagDisableASLR);
m_gdb_comm.SetDetachOnError(launch_flags & eLaunchFlagDetachOnError);
m_gdb_comm.SetDisableASLR(launch_flags & eLaunchFlagDisableASLR);
m_gdb_comm.SetDetachOnError(launch_flags & eLaunchFlagDetachOnError);
m_gdb_comm.SendLaunchArchPacket(
GetTarget().GetArchitecture().GetArchitectureName());
m_gdb_comm.SendLaunchArchPacket(
GetTarget().GetArchitecture().GetArchitectureName());
const char *launch_event_data = launch_info.GetLaunchEventData();
if (launch_event_data != nullptr && *launch_event_data != '\0')
m_gdb_comm.SendLaunchEventDataPacket(launch_event_data);
const char *launch_event_data = launch_info.GetLaunchEventData();
if (launch_event_data != nullptr && *launch_event_data != '\0')
m_gdb_comm.SendLaunchEventDataPacket(launch_event_data);
if (working_dir) {
m_gdb_comm.SetWorkingDir(working_dir);
}
if (working_dir) {
m_gdb_comm.SetWorkingDir(working_dir);
}
// Send the environment and the program + arguments after we connect
m_gdb_comm.SendEnvironment(launch_info.GetEnvironment());
// Send the environment and the program + arguments after we connect
m_gdb_comm.SendEnvironment(launch_info.GetEnvironment());
{
// Scope for the scoped timeout object
GDBRemoteCommunication::ScopedTimeout timeout(m_gdb_comm,
std::chrono::seconds(10));
{
// Scope for the scoped timeout object
GDBRemoteCommunication::ScopedTimeout timeout(m_gdb_comm,
std::chrono::seconds(10));
int arg_packet_err = m_gdb_comm.SendArgumentsPacket(launch_info);
if (arg_packet_err == 0) {
std::string error_str;
if (m_gdb_comm.GetLaunchSuccess(error_str)) {
SetID(m_gdb_comm.GetCurrentProcessID());
} else {
error.SetErrorString(error_str.c_str());
}
int arg_packet_err = m_gdb_comm.SendArgumentsPacket(launch_info);
if (arg_packet_err == 0) {
std::string error_str;
if (m_gdb_comm.GetLaunchSuccess(error_str)) {
SetID(m_gdb_comm.GetCurrentProcessID());
} else {
error.SetErrorStringWithFormat("'A' packet returned an error: %i",
arg_packet_err);
error.SetErrorString(error_str.c_str());
}
} else {
error.SetErrorStringWithFormat("'A' packet returned an error: %i",
arg_packet_err);
}
}
if (GetID() == LLDB_INVALID_PROCESS_ID) {
LLDB_LOGF(log, "failed to connect to debugserver: %s",
error.AsCString());
KillDebugserverProcess();
return error;
}
StringExtractorGDBRemote response;
if (m_gdb_comm.GetStopReply(response)) {
SetLastStopPacket(response);
const ArchSpec &process_arch = m_gdb_comm.GetProcessArchitecture();
if (process_arch.IsValid()) {
GetTarget().MergeArchitecture(process_arch);
} else {
const ArchSpec &host_arch = m_gdb_comm.GetHostArchitecture();
if (host_arch.IsValid())
GetTarget().MergeArchitecture(host_arch);
}
if (GetID() == LLDB_INVALID_PROCESS_ID) {
LLDB_LOGF(log, "failed to connect to debugserver: %s",
error.AsCString());
KillDebugserverProcess();
return error;
SetPrivateState(SetThreadStopInfo(response));
if (!disable_stdio) {
if (pty.GetPrimaryFileDescriptor() != PseudoTerminal::invalid_fd)
SetSTDIOFileDescriptor(pty.ReleasePrimaryFileDescriptor());
}
StringExtractorGDBRemote response;
if (m_gdb_comm.GetStopReply(response)) {
SetLastStopPacket(response);
const ArchSpec &process_arch = m_gdb_comm.GetProcessArchitecture();
if (process_arch.IsValid()) {
GetTarget().MergeArchitecture(process_arch);
} else {
const ArchSpec &host_arch = m_gdb_comm.GetHostArchitecture();
if (host_arch.IsValid())
GetTarget().MergeArchitecture(host_arch);
}
SetPrivateState(SetThreadStopInfo(response));
if (!disable_stdio) {
if (pty.GetPrimaryFileDescriptor() != PseudoTerminal::invalid_fd)
SetSTDIOFileDescriptor(pty.ReleasePrimaryFileDescriptor());
}
}
} else {
LLDB_LOGF(log, "failed to connect to debugserver: %s", error.AsCString());
}
} else {
// Set our user ID to an invalid process ID.
SetID(LLDB_INVALID_PROCESS_ID);
error.SetErrorStringWithFormat(
"failed to get object file from '%s' for arch %s",
exe_module->GetFileSpec().GetFilename().AsCString(),
exe_module->GetArchitecture().GetArchitectureName());
LLDB_LOGF(log, "failed to connect to debugserver: %s", error.AsCString());
}
return error;
}

View File

@ -2493,118 +2493,125 @@ Status Process::Launch(ProcessLaunchInfo &launch_info) {
m_process_input_reader.reset();
Module *exe_module = GetTarget().GetExecutableModulePointer();
if (!exe_module) {
error.SetErrorString("executable module does not exist");
return error;
}
char local_exec_file_path[PATH_MAX];
char platform_exec_file_path[PATH_MAX];
exe_module->GetFileSpec().GetPath(local_exec_file_path,
sizeof(local_exec_file_path));
exe_module->GetPlatformFileSpec().GetPath(platform_exec_file_path,
sizeof(platform_exec_file_path));
if (FileSystem::Instance().Exists(exe_module->GetFileSpec())) {
// The "remote executable path" is hooked up to the local Executable
// module. But we should be able to debug a remote process even if the
// executable module only exists on the remote. However, there needs to
// be a way to express this path, without actually having a module.
// The way to do that is to set the ExecutableFile in the LaunchInfo.
// Figure that out here:
FileSpec exe_spec_to_use;
if (!exe_module) {
if (!launch_info.GetExecutableFile()) {
error.SetErrorString("executable module does not exist");
return error;
}
exe_spec_to_use = launch_info.GetExecutableFile();
} else
exe_spec_to_use = exe_module->GetFileSpec();
if (exe_module && FileSystem::Instance().Exists(exe_module->GetFileSpec())) {
// Install anything that might need to be installed prior to launching.
// For host systems, this will do nothing, but if we are connected to a
// remote platform it will install any needed binaries
error = GetTarget().Install(&launch_info);
if (error.Fail())
return error;
}
// Listen and queue events that are broadcasted during the process launch.
ListenerSP listener_sp(Listener::MakeListener("LaunchEventHijack"));
HijackProcessEvents(listener_sp);
auto on_exit = llvm::make_scope_exit([this]() { RestoreProcessEvents(); });
// Listen and queue events that are broadcasted during the process launch.
ListenerSP listener_sp(Listener::MakeListener("LaunchEventHijack"));
HijackProcessEvents(listener_sp);
auto on_exit = llvm::make_scope_exit([this]() { RestoreProcessEvents(); });
if (PrivateStateThreadIsValid())
PausePrivateStateThread();
if (PrivateStateThreadIsValid())
PausePrivateStateThread();
error = WillLaunch(exe_module);
if (error.Success()) {
const bool restarted = false;
SetPublicState(eStateLaunching, restarted);
m_should_detach = false;
error = WillLaunch(exe_module);
if (error.Success()) {
const bool restarted = false;
SetPublicState(eStateLaunching, restarted);
m_should_detach = false;
if (m_public_run_lock.TrySetRunning()) {
// Now launch using these arguments.
error = DoLaunch(exe_module, launch_info);
} else {
// This shouldn't happen
error.SetErrorString("failed to acquire process run lock");
}
if (m_public_run_lock.TrySetRunning()) {
// Now launch using these arguments.
error = DoLaunch(exe_module, launch_info);
} else {
// This shouldn't happen
error.SetErrorString("failed to acquire process run lock");
if (error.Fail()) {
if (GetID() != LLDB_INVALID_PROCESS_ID) {
SetID(LLDB_INVALID_PROCESS_ID);
const char *error_string = error.AsCString();
if (error_string == nullptr)
error_string = "launch failed";
SetExitStatus(-1, error_string);
}
} else {
EventSP event_sp;
if (error.Fail()) {
if (GetID() != LLDB_INVALID_PROCESS_ID) {
SetID(LLDB_INVALID_PROCESS_ID);
const char *error_string = error.AsCString();
if (error_string == nullptr)
error_string = "launch failed";
SetExitStatus(-1, error_string);
}
} else {
EventSP event_sp;
// Now wait for the process to launch and return control to us, and then
// call DidLaunch:
StateType state = WaitForProcessStopPrivate(event_sp, seconds(10));
// Now wait for the process to launch and return control to us, and then
// call DidLaunch:
StateType state = WaitForProcessStopPrivate(event_sp, seconds(10));
if (state == eStateInvalid || !event_sp) {
// We were able to launch the process, but we failed to catch the
// initial stop.
error.SetErrorString("failed to catch stop after launch");
SetExitStatus(0, "failed to catch stop after launch");
Destroy(false);
} else if (state == eStateStopped || state == eStateCrashed) {
DidLaunch();
if (state == eStateInvalid || !event_sp) {
// We were able to launch the process, but we failed to catch the
// initial stop.
error.SetErrorString("failed to catch stop after launch");
SetExitStatus(0, "failed to catch stop after launch");
Destroy(false);
} else if (state == eStateStopped || state == eStateCrashed) {
DidLaunch();
DynamicLoader *dyld = GetDynamicLoader();
if (dyld)
dyld->DidLaunch();
DynamicLoader *dyld = GetDynamicLoader();
if (dyld)
dyld->DidLaunch();
GetJITLoaders().DidLaunch();
GetJITLoaders().DidLaunch();
SystemRuntime *system_runtime = GetSystemRuntime();
if (system_runtime)
system_runtime->DidLaunch();
SystemRuntime *system_runtime = GetSystemRuntime();
if (system_runtime)
system_runtime->DidLaunch();
if (!m_os_up)
LoadOperatingSystemPlugin(false);
if (!m_os_up)
LoadOperatingSystemPlugin(false);
// We successfully launched the process and stopped, now it the
// right time to set up signal filters before resuming.
UpdateAutomaticSignalFiltering();
// We successfully launched the process and stopped, now it the
// right time to set up signal filters before resuming.
UpdateAutomaticSignalFiltering();
// Note, the stop event was consumed above, but not handled. This
// was done to give DidLaunch a chance to run. The target is either
// stopped or crashed. Directly set the state. This is done to
// prevent a stop message with a bunch of spurious output on thread
// status, as well as not pop a ProcessIOHandler.
// We are done with the launch hijack listener, and this stop should
// go to the public state listener:
RestoreProcessEvents();
SetPublicState(state, false);
// Note, the stop event was consumed above, but not handled. This
// was done to give DidLaunch a chance to run. The target is either
// stopped or crashed. Directly set the state. This is done to
// prevent a stop message with a bunch of spurious output on thread
// status, as well as not pop a ProcessIOHandler.
// We are done with the launch hijack listener, and this stop should
// go to the public state listener:
RestoreProcessEvents();
SetPublicState(state, false);
if (PrivateStateThreadIsValid())
ResumePrivateStateThread();
else
StartPrivateStateThread();
if (PrivateStateThreadIsValid())
ResumePrivateStateThread();
else
StartPrivateStateThread();
// Target was stopped at entry as was intended. Need to notify the
// listeners about it.
if (state == eStateStopped &&
launch_info.GetFlags().Test(eLaunchFlagStopAtEntry))
HandlePrivateEvent(event_sp);
} else if (state == eStateExited) {
// We exited while trying to launch somehow. Don't call DidLaunch
// as that's not likely to work, and return an invalid pid.
// Target was stopped at entry as was intended. Need to notify the
// listeners about it.
if (state == eStateStopped &&
launch_info.GetFlags().Test(eLaunchFlagStopAtEntry))
HandlePrivateEvent(event_sp);
}
} else if (state == eStateExited) {
// We exited while trying to launch somehow. Don't call DidLaunch
// as that's not likely to work, and return an invalid pid.
HandlePrivateEvent(event_sp);
}
}
} else {
std::string local_exec_file_path = exe_spec_to_use.GetPath();
error.SetErrorStringWithFormat("file doesn't exist: '%s'",
local_exec_file_path);
local_exec_file_path.c_str());
}
return error;

View File

@ -0,0 +1,91 @@
import lldb
from lldbsuite.test.lldbtest import *
from lldbsuite.test.decorators import *
from lldbsuite.test.gdbclientutils import *
from lldbsuite.test.lldbgdbclient import GDBRemoteTestBase
class TestNoLocalFile(GDBRemoteTestBase):
""" Test the case where there is NO local copy of the file
being debugged. We shouldn't immediately error out, but
rather lldb should ask debugserver if it knows about the file. """
mydir = TestBase.compute_mydir(__file__)
@skipIfXmlSupportMissing
def test(self):
self.absent_file = '/nosuch_dir/nosuch_subdir/nosuch_executable'
self.a_packet_file = None
class MyResponder(MockGDBServerResponder):
def __init__(self, testcase):
MockGDBServerResponder.__init__(self)
self.after_launch = False
self.testcase = testcase
self.current_thread = 0
def A(self, packet):
# This is the main test, we want to see that lldb DID send the
# A packet to get debugserver to load the file.
# Skip the length and second length:
print("Got A packet: {0}".format(packet))
a_arr = packet.split(",")
self.testcase.a_packet_file = bytearray.fromhex(a_arr[2]).decode()
return "OK"
def qXferRead(self, obj, annex, offset, length):
if annex == "target.xml":
return """<?xml version="1.0"?>
<target version="1.0">
<architecture>i386:x86-64</architecture>
<feature name="org.gnu.gdb.i386.core">
<reg name="rip" bitsize="64" regnum="0" type="code_ptr" group="general"/>
</feature>
</target>""", False
else:
return None, False
def qC(self):
if not self.after_launch:
return "QC0"
return "0"
def qfThreadInfo(self):
if not self.after_launch:
return "OK"
return "m0"
def qsThreadInfo(self):
if not self.after_launch:
return "OK"
return "l"
def qLaunchSuccess(self):
return "OK"
def qProcessInfo(self):
return "$pid:10b70;parent-pid:10b20;real-uid:1f6;real-gid:14;effective-uid:1f6;effective-gid:14;cputype:1000007;cpusubtype:8;ptrsize:8;ostype:macosx;vendor:apple;endian:little;"
error = lldb.SBError()
self.server.responder = MyResponder(self)
target = self.dbg.CreateTarget(None, "x86_64-apple-macosx", "remote-macosx", False, error)
self.assertSuccess(error, "Made a valid target")
launch_info = target.GetLaunchInfo()
launch_info.SetExecutableFile(lldb.SBFileSpec(self.absent_file), True)
flags = launch_info.GetLaunchFlags()
flags |= lldb.eLaunchFlagStopAtEntry
launch_info.SetLaunchFlags(flags)
process = self.connect(target)
self.assertTrue(process.IsValid(), "Process is valid")
# We need to fetch the connected event:
lldbutil.expect_state_changes(self, self.dbg.GetListener(), process, [lldb.eStateConnected])
self.server.responder.after_launch = True
process = target.Launch(launch_info, error)
self.assertSuccess(error, "Successfully launched.")
self.assertEqual(process.GetState(), lldb.eStateStopped, "Should be stopped at entry")
self.assertIsNotNone(self.a_packet_file, "A packet was sent")
self.assertEqual(self.absent_file, self.a_packet_file, "The A packet file was correct")