diff options
Diffstat (limited to 'source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp')
-rw-r--r-- | source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp | 232 |
1 files changed, 136 insertions, 96 deletions
diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp b/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp index 9c263c8..2ea1f20 100644 --- a/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp +++ b/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp @@ -30,6 +30,7 @@ #include "lldb/Host/StringConvert.h" #include "lldb/Host/ThreadLauncher.h" #include "lldb/Host/TimeValue.h" +#include "lldb/Target/Platform.h" #include "lldb/Target/Process.h" #include "llvm/ADT/SmallString.h" @@ -574,15 +575,24 @@ GDBRemoteCommunication::DecompressPacket () return true; if (m_bytes[1] != 'C' && m_bytes[1] != 'N') return true; - if (m_bytes[pkt_size - 3] != '#') + + size_t hash_mark_idx = m_bytes.find ('#'); + if (hash_mark_idx == std::string::npos) + return true; + if (hash_mark_idx + 2 >= m_bytes.size()) return true; - if (!::isxdigit (m_bytes[pkt_size - 2]) || !::isxdigit (m_bytes[pkt_size - 1])) + + if (!::isxdigit (m_bytes[hash_mark_idx + 1]) || !::isxdigit (m_bytes[hash_mark_idx + 2])) return true; - size_t content_length = pkt_size - 5; // not counting '$', 'C' | 'N', '#', & the two hex checksum chars - size_t content_start = 2; // The first character of the compressed/not-compressed text of the packet - size_t hash_mark_idx = pkt_size - 3; // The '#' character marking the end of the packet - size_t checksum_idx = pkt_size - 2; // The first character of the two hex checksum characters + size_t content_length = pkt_size - 5; // not counting '$', 'C' | 'N', '#', & the two hex checksum chars + size_t content_start = 2; // The first character of the compressed/not-compressed text of the packet + size_t checksum_idx = hash_mark_idx + 1; // The first character of the two hex checksum characters + + // Normally size_of_first_packet == m_bytes.size() but m_bytes may contain multiple packets. + // size_of_first_packet is the size of the initial packet which we'll replace with the decompressed + // version of, leaving the rest of m_bytes unmodified. + size_t size_of_first_packet = hash_mark_idx + 3; // Compressed packets ("$C") start with a base10 number which is the size of the uncompressed payload, // then a : and then the compressed data. e.g. $C1024:<binary>#00 @@ -604,7 +614,7 @@ GDBRemoteCommunication::DecompressPacket () decompressed_bufsize = ::strtoul (bufsize_str.c_str(), NULL, 10); if (errno != 0 || decompressed_bufsize == ULONG_MAX) { - m_bytes.erase (0, pkt_size); + m_bytes.erase (0, size_of_first_packet); return false; } } @@ -633,7 +643,7 @@ GDBRemoteCommunication::DecompressPacket () if (!success) { SendNack(); - m_bytes.erase (0, pkt_size); + m_bytes.erase (0, size_of_first_packet); return false; } else @@ -677,7 +687,7 @@ GDBRemoteCommunication::DecompressPacket () decompressed_buffer = (uint8_t *) malloc (decompressed_bufsize + 1); if (decompressed_buffer == nullptr) { - m_bytes.erase (0, pkt_size); + m_bytes.erase (0, size_of_first_packet); return false; } @@ -751,7 +761,7 @@ GDBRemoteCommunication::DecompressPacket () { if (decompressed_buffer) free (decompressed_buffer); - m_bytes.erase (0, pkt_size); + m_bytes.erase (0, size_of_first_packet); return false; } @@ -773,7 +783,7 @@ GDBRemoteCommunication::DecompressPacket () new_packet.push_back ('0'); } - m_bytes = new_packet; + m_bytes.replace (0, size_of_first_packet, new_packet.data(), new_packet.size()); free (decompressed_buffer); return true; @@ -927,8 +937,10 @@ GDBRemoteCommunication::CheckForPacket (const uint8_t *src, size_t src_len, Stri { for (size_t i=0; !binary && i<total_length; ++i) { - if (isprint(m_bytes[i]) == 0) + if (isprint (m_bytes[i]) == 0 && isspace (m_bytes[i]) == 0) + { binary = true; + } } } if (binary) @@ -1100,16 +1112,16 @@ GDBRemoteCommunication::ListenThread (lldb::thread_arg_t arg) } Error -GDBRemoteCommunication::StartDebugserverProcess (const char *hostname, - uint16_t in_port, +GDBRemoteCommunication::StartDebugserverProcess (const char *url, + Platform *platform, ProcessLaunchInfo &launch_info, - uint16_t &out_port) + uint16_t *port, + const Args& inferior_args) { 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); + log->Printf ("GDBRemoteCommunication::%s(url=%s, port=%" PRIu16, __FUNCTION__, url ? url : "<empty>", port ? *port : uint16_t(0)); - out_port = in_port; Error error; // If we locate debugserver, keep that located version around static FileSpec g_debugserver_file_spec; @@ -1146,11 +1158,20 @@ GDBRemoteCommunication::StartDebugserverProcess (const char *hostname, } else { - if (log) - log->Printf ("GDBRemoteCommunication::%s() could not find gdb-remote stub exe '%s'", __FUNCTION__, debugserver_file_spec.GetPath ().c_str ()); - + debugserver_file_spec = platform->LocateExecutable(DEBUGSERVER_BASENAME); + if (debugserver_file_spec) + { + // Platform::LocateExecutable() wouldn't return a path if it doesn't exist + debugserver_exists = true; + } + else + { + if (log) + log->Printf ("GDBRemoteCommunication::%s() could not find gdb-remote stub exe '%s'", __FUNCTION__, debugserver_file_spec.GetPath ().c_str ()); + } + // Don't cache the platform specific GDB server binary as it could change + // from platform to platform g_debugserver_file_spec.Clear(); - debugserver_file_spec.Clear(); } } } @@ -1171,17 +1192,9 @@ GDBRemoteCommunication::StartDebugserverProcess (const char *hostname, 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'; - } + // If a url is supplied then use it + if (url) + debugserver_args.AppendArgument(url); // use native registers, not the GDB registers debugserver_args.AppendArgument("--native-regs"); @@ -1192,30 +1205,41 @@ GDBRemoteCommunication::StartDebugserverProcess (const char *hostname, } llvm::SmallString<PATH_MAX> named_pipe_path; - Pipe port_pipe; - - if (host_and_port[0] && in_port == 0) + // socket_pipe is used by debug server to communicate back either + // TCP port or domain socket name which it listens on. + // The second purpose of the pipe to serve as a synchronization point - + // once data is written to the pipe, debug server is up and running. + Pipe socket_pipe; + + // port is null when debug server should listen on domain socket - + // we're not interested in port value but rather waiting for debug server + // to become available. + if ((port != nullptr && *port == 0) || port == nullptr) { - // 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" - - // 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()) + if (url) { + // 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 defined(__APPLE__) + // Binding to port zero, we need to figure out what port it ends up + // using using a named pipe... + error = socket_pipe.CreateWithUniqueName("debugserver-named-pipe", false, named_pipe_path); + if (error.Fail()) + { + if (log) + log->Printf("GDBRemoteCommunication::%s() " + "named pipe creation failed: %s", + __FUNCTION__, error.AsCString()); + return error; + } 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); +#else + // Binding to port zero, we need to figure out what port it ends up + // using using an unnamed pipe... + error = socket_pipe.CreateNew(true); if (error.Fail()) { if (log) @@ -1224,42 +1248,43 @@ GDBRemoteCommunication::StartDebugserverProcess (const char *hostname, __FUNCTION__, error.AsCString()); return error; } - int write_fd = port_pipe.GetWriteFileDescriptor(); + int write_fd = socket_pipe.GetWriteFileDescriptor(); debugserver_args.AppendArgument("--pipe"); debugserver_args.AppendArgument(std::to_string(write_fd).c_str()); - launch_info.AppendCloseFileAction(port_pipe.GetReadFileDescriptor()); - } - } - 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()) - { - if (log) - log->Printf ("GDBRemoteCommunication::%s() unable to start listen thread: %s", __FUNCTION__, error.AsCString()); - 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); + launch_info.AppendCloseFileAction(socket_pipe.GetReadFileDescriptor()); +#endif } else { - error.SetErrorString ("failed to bind to port 0 on 127.0.0.1"); - if (log) - log->Printf ("GDBRemoteCommunication::%s() failed: %s", __FUNCTION__, error.AsCString()); - return error; + // 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()) + { + if (log) + log->Printf ("GDBRemoteCommunication::%s() unable to start listen thread: %s", __FUNCTION__, error.AsCString()); + return error; + } + + ConnectionFileDescriptor *connection = (ConnectionFileDescriptor *)GetConnection (); + // Wait for 10 seconds to resolve the bound port + *port = connection->GetListeningPort(10); + if (*port > 0) + { + char port_cstr[32]; + snprintf(port_cstr, sizeof(port_cstr), "127.0.0.1:%i", *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"); + if (log) + log->Printf ("GDBRemoteCommunication::%s() failed: %s", __FUNCTION__, error.AsCString()); + return error; + } } } @@ -1304,6 +1329,20 @@ GDBRemoteCommunication::StartDebugserverProcess (const char *hostname, } } while (has_env_var); + if (inferior_args.GetArgumentCount() > 0) + { + debugserver_args.AppendArgument ("--"); + debugserver_args.AppendArguments (inferior_args); + } + + // Copy the current environment to the gdbserver/debugserver instance + StringList env; + if (Host::GetEnvironment(env)) + { + for (size_t i = 0; i < env.GetSize(); ++i) + launch_info.GetEnvironmentEntries().AppendArgument(env[i].c_str()); + } + // Close STDIN, STDOUT and STDERR. launch_info.AppendCloseFileAction (STDIN_FILENO); launch_info.AppendCloseFileAction (STDOUT_FILENO); @@ -1316,11 +1355,12 @@ GDBRemoteCommunication::StartDebugserverProcess (const char *hostname, error = Host::LaunchProcess(launch_info); - if (error.Success() && launch_info.GetProcessID() != LLDB_INVALID_PROCESS_ID) + if (error.Success() && + launch_info.GetProcessID() != LLDB_INVALID_PROCESS_ID) { if (named_pipe_path.size() > 0) { - error = port_pipe.OpenAsReader(named_pipe_path, false); + error = socket_pipe.OpenAsReader(named_pipe_path, false); if (error.Fail()) if (log) log->Printf("GDBRemoteCommunication::%s() " @@ -1328,24 +1368,24 @@ GDBRemoteCommunication::StartDebugserverProcess (const char *hostname, __FUNCTION__, named_pipe_path.c_str(), error.AsCString()); } - if (port_pipe.CanWrite()) - port_pipe.CloseWriteFileDescriptor(); - if (port_pipe.CanRead()) + if (socket_pipe.CanWrite()) + socket_pipe.CloseWriteFileDescriptor(); + if (socket_pipe.CanRead()) { - char port_cstr[256]; + char port_cstr[PATH_MAX] = {0}; 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, + error = socket_pipe.ReadWithTimeout(port_cstr, num_bytes, std::chrono::seconds{10}, num_bytes); - if (error.Success()) + if (error.Success() && (port != nullptr)) { assert(num_bytes > 0 && port_cstr[num_bytes-1] == '\0'); - out_port = StringConvert::ToUInt32(port_cstr, 0); + *port = StringConvert::ToUInt32(port_cstr, 0); if (log) log->Printf("GDBRemoteCommunication::%s() " - "debugserver listens %u port", - __FUNCTION__, out_port); + "debugserver listens %u port", + __FUNCTION__, *port); } else { @@ -1355,12 +1395,12 @@ GDBRemoteCommunication::StartDebugserverProcess (const char *hostname, __FUNCTION__, named_pipe_path.c_str(), error.AsCString()); } - port_pipe.Close(); + socket_pipe.Close(); } if (named_pipe_path.size() > 0) { - const auto err = port_pipe.Delete(named_pipe_path); + const auto err = socket_pipe.Delete(named_pipe_path); if (err.Fail()) { if (log) |