https://github.com/charles-zablit updated https://github.com/llvm/llvm-project/pull/206985
>From 8b897129938714430e907c8e7780c8df622b0f87 Mon Sep 17 00:00:00 2001 From: Charles Zablit <[email protected]> Date: Wed, 1 Jul 2026 14:44:35 +0100 Subject: [PATCH 1/4] [lldb] Extract URI schemes manually to support Windows paths --- .../gdb-server/PlatformRemoteGDBServer.cpp | 24 ++++++++++++++----- .../GDBRemoteCommunicationServerLLGS.cpp | 12 ++++++---- 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp b/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp index 1f69ef5f3f6ca..55fdc94df4b99 100644 --- a/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp +++ b/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp @@ -19,6 +19,7 @@ #include "lldb/Host/Host.h" #include "lldb/Host/HostInfo.h" #include "lldb/Host/PosixApi.h" +#include "lldb/Host/Socket.h" #include "lldb/Target/Process.h" #include "lldb/Target/Target.h" #include "lldb/Utility/FileSpec.h" @@ -226,13 +227,24 @@ Status PlatformRemoteGDBServer::ConnectRemote(Args &args) { if (!url) return Status::FromErrorString("URL is null."); - std::optional<URI> parsed_url = URI::Parse(url); - if (!parsed_url) - return Status::FromErrorStringWithFormat("Invalid URL: %s", url); + // Parse the scheme manually because URI::parse does not handle Windows paths. + llvm::StringRef scheme = llvm::StringRef(url).split("://").first; + std::optional<Socket::ProtocolModePair> protocol_and_mode = + Socket::GetProtocolAndMode(scheme); + if (protocol_and_mode && + (protocol_and_mode->first == Socket::ProtocolUnixDomain || + protocol_and_mode->first == Socket::ProtocolUnixAbstract)) { + m_platform_scheme = scheme.str(); + m_platform_hostname = ""; + } else { + std::optional<URI> parsed_url = URI::Parse(url); + if (!parsed_url) + return Status::FromErrorStringWithFormat("Invalid URL: %s", url); - // We're going to reuse the hostname when we connect to the debugserver. - m_platform_scheme = parsed_url->scheme.str(); - m_platform_hostname = parsed_url->hostname.str(); + // We're going to reuse the hostname when we connect to the debugserver. + m_platform_scheme = parsed_url->scheme.str(); + m_platform_hostname = parsed_url->hostname.str(); + } auto client_up = std::make_unique<process_gdb_remote::GDBRemoteCommunicationClient>(); diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp index 769705588fbdf..948e939a02613 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp @@ -4560,20 +4560,22 @@ void GDBRemoteCommunicationServerLLGS::AppendThreadIDToResponse( std::string lldb_private::process_gdb_remote::LLGSArgToURL(llvm::StringRef url_arg, bool reverse_connect) { - // Try parsing the argument as URL. - if (std::optional<URI> url = URI::Parse(url_arg)) { + // Parse the scheme manually because URI::parse does not handle Windows paths. + constexpr llvm::StringRef kSchemeSep = "://"; + if (size_t pos = url_arg.find(kSchemeSep); pos != llvm::StringRef::npos) { if (reverse_connect) return url_arg.str(); // Translate the scheme from LLGS notation to ConnectionFileDescriptor. // If the scheme doesn't match any, pass it through to support using CFD // schemes directly. - std::string new_url = llvm::StringSwitch<std::string>(url->scheme) + llvm::StringRef scheme = url_arg.substr(0, pos); + std::string new_url = llvm::StringSwitch<std::string>(scheme) .Case("tcp", "listen") .Case("unix", "unix-accept") .Case("unix-abstract", "unix-abstract-accept") - .Default(url->scheme.str()); - llvm::append_range(new_url, url_arg.substr(url->scheme.size())); + .Default(scheme.str()); + llvm::append_range(new_url, url_arg.substr(scheme.size())); return new_url; } >From 52b7d2cf85f6ffaaacf5d61720c9fd4d40a93d11 Mon Sep 17 00:00:00 2001 From: Charles Zablit <[email protected]> Date: Wed, 1 Jul 2026 16:38:40 +0100 Subject: [PATCH 2/4] inline variable --- .../Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp index 948e939a02613..f550c82d0885f 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp @@ -4561,8 +4561,7 @@ std::string lldb_private::process_gdb_remote::LLGSArgToURL(llvm::StringRef url_arg, bool reverse_connect) { // Parse the scheme manually because URI::parse does not handle Windows paths. - constexpr llvm::StringRef kSchemeSep = "://"; - if (size_t pos = url_arg.find(kSchemeSep); pos != llvm::StringRef::npos) { + if (size_t pos = url_arg.find("://"); pos != llvm::StringRef::npos) { if (reverse_connect) return url_arg.str(); >From b58e5d550d04029a021e9b728408f75df77db57a Mon Sep 17 00:00:00 2001 From: Charles Zablit <[email protected]> Date: Wed, 1 Jul 2026 17:00:55 +0100 Subject: [PATCH 3/4] add comment --- .../Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp b/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp index 55fdc94df4b99..22d30d660d9c3 100644 --- a/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp +++ b/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp @@ -235,6 +235,7 @@ Status PlatformRemoteGDBServer::ConnectRemote(Args &args) { (protocol_and_mode->first == Socket::ProtocolUnixDomain || protocol_and_mode->first == Socket::ProtocolUnixAbstract)) { m_platform_scheme = scheme.str(); + // The URI contains a filepath, the hostname is empty. m_platform_hostname = ""; } else { std::optional<URI> parsed_url = URI::Parse(url); >From 46672423e0c244d84a47dd360e2fb50ffd1f3cfd Mon Sep 17 00:00:00 2001 From: Charles Zablit <[email protected]> Date: Fri, 17 Jul 2026 10:56:32 +0100 Subject: [PATCH 4/4] use the RFC 8089 form --- lldb/include/lldb/Host/common/DomainSocket.h | 11 +++++ lldb/source/Host/common/DomainSocket.cpp | 42 +++++++++++++++---- .../gdb-server/PlatformRemoteGDBServer.cpp | 25 +++-------- .../GDBRemoteCommunicationServerLLGS.cpp | 11 +++-- lldb/unittests/Host/SocketTest.cpp | 29 +++++++++++-- .../GDBRemoteCommunicationServerLLGSTest.cpp | 5 +++ lldb/unittests/Utility/UriParserTest.cpp | 5 +++ 7 files changed, 92 insertions(+), 36 deletions(-) diff --git a/lldb/include/lldb/Host/common/DomainSocket.h b/lldb/include/lldb/Host/common/DomainSocket.h index 4d2b657cf88e4..fa96d38316851 100644 --- a/lldb/include/lldb/Host/common/DomainSocket.h +++ b/lldb/include/lldb/Host/common/DomainSocket.h @@ -45,6 +45,17 @@ class DomainSocket : public Socket { static llvm::Expected<std::unique_ptr<DomainSocket>> FromBoundNativeSocket(NativeSocket sockfd, bool should_close); + /// Convert between a native filesystem path and the path component of a + /// domain-socket URI. + /// On Windows a drive-letter path (e.g. "C:\\dir\\sock") is not a valid URI + /// authority, so it is carried in the RFC 8089 file-URI form "/C:/dir/sock" + /// (leading slash, forward slashes). On other platforms the path is already a + /// valid URI path and is returned unchanged. + /// @{ + static std::string NativePathToURIPath(llvm::StringRef path); + static std::string URIPathToNativePath(llvm::StringRef path); + /// @} + protected: DomainSocket(SocketProtocol protocol); DomainSocket(SocketProtocol protocol, NativeSocket socket, bool should_close); diff --git a/lldb/source/Host/common/DomainSocket.cpp b/lldb/source/Host/common/DomainSocket.cpp index 988b8238a3cce..13b4a8650a6cd 100644 --- a/lldb/source/Host/common/DomainSocket.cpp +++ b/lldb/source/Host/common/DomainSocket.cpp @@ -12,10 +12,12 @@ #include <lldb/Host/linux/AbstractSocket.h> #endif +#include "llvm/ADT/StringExtras.h" #include "llvm/Support/Errno.h" #include "llvm/Support/Error.h" #include "llvm/Support/FileSystem.h" +#include <algorithm> #include <cstddef> #include <memory> @@ -32,6 +34,29 @@ using namespace lldb_private; static const int kDomain = AF_UNIX; static const int kType = SOCK_STREAM; +std::string DomainSocket::NativePathToURIPath(llvm::StringRef path) { +#ifdef _WIN32 + if (path.size() >= 2 && llvm::isAlpha(path[0]) && path[1] == ':') { + std::string uri_path = "/" + path.str(); + std::replace(uri_path.begin(), uri_path.end(), '\\', '/'); + return uri_path; + } +#endif + return path.str(); +} + +std::string DomainSocket::URIPathToNativePath(llvm::StringRef path) { +#ifdef _WIN32 + if (path.size() >= 3 && path[0] == '/' && llvm::isAlpha(path[1]) && + path[2] == ':') { + std::string native = path.drop_front().str(); + std::replace(native.begin(), native.end(), '/', '\\'); + return native; + } +#endif + return path.str(); +} + static bool SetSockAddr(llvm::StringRef name, const size_t name_offset, sockaddr_un *saddr_un, socklen_t &saddr_un_len) { if (name.size() + name_offset > sizeof(saddr_un->sun_path)) @@ -79,9 +104,10 @@ DomainSocket::DomainSocket(SocketProtocol protocol, NativeSocket socket, } Status DomainSocket::Connect(llvm::StringRef name) { + std::string native_name = URIPathToNativePath(name); sockaddr_un saddr_un; socklen_t saddr_un_len; - if (!SetSockAddr(name, GetNameOffset(), &saddr_un, saddr_un_len)) + if (!SetSockAddr(native_name, GetNameOffset(), &saddr_un, saddr_un_len)) return Status::FromErrorString("Failed to set socket address"); Status error; @@ -97,12 +123,13 @@ Status DomainSocket::Connect(llvm::StringRef name) { } Status DomainSocket::Listen(llvm::StringRef name, int backlog) { + std::string native_name = URIPathToNativePath(name); sockaddr_un saddr_un; socklen_t saddr_un_len; - if (!SetSockAddr(name, GetNameOffset(), &saddr_un, saddr_un_len)) + if (!SetSockAddr(native_name, GetNameOffset(), &saddr_un, saddr_un_len)) return Status::FromErrorString("Failed to set socket address"); - DeleteSocketFile(name); + DeleteSocketFile(native_name); Status error; m_socket = CreateSocket(kDomain, kType, 0, error); @@ -175,9 +202,9 @@ std::string DomainSocket::GetRemoteConnectionURI() const { if (name.empty()) return name; - return llvm::formatv( - "{0}://{1}", - GetNameOffset() == 0 ? "unix-connect" : "unix-abstract-connect", name); + if (GetNameOffset() == 0) + return llvm::formatv("unix-connect://{0}", NativePathToURIPath(name)); + return llvm::formatv("unix-abstract-connect://{0}", name); } std::vector<std::string> DomainSocket::GetListeningConnectionURI() const { @@ -191,7 +218,8 @@ std::vector<std::string> DomainSocket::GetListeningConnectionURI() const { if (::getsockname(m_socket, (struct sockaddr *)&addr, &addr_len) != 0) return {}; - return {llvm::formatv("unix-connect://{0}", addr.sun_path)}; + return { + llvm::formatv("unix-connect://{0}", NativePathToURIPath(addr.sun_path))}; } llvm::Expected<std::unique_ptr<DomainSocket>> diff --git a/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp b/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp index 22d30d660d9c3..1f69ef5f3f6ca 100644 --- a/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp +++ b/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp @@ -19,7 +19,6 @@ #include "lldb/Host/Host.h" #include "lldb/Host/HostInfo.h" #include "lldb/Host/PosixApi.h" -#include "lldb/Host/Socket.h" #include "lldb/Target/Process.h" #include "lldb/Target/Target.h" #include "lldb/Utility/FileSpec.h" @@ -227,25 +226,13 @@ Status PlatformRemoteGDBServer::ConnectRemote(Args &args) { if (!url) return Status::FromErrorString("URL is null."); - // Parse the scheme manually because URI::parse does not handle Windows paths. - llvm::StringRef scheme = llvm::StringRef(url).split("://").first; - std::optional<Socket::ProtocolModePair> protocol_and_mode = - Socket::GetProtocolAndMode(scheme); - if (protocol_and_mode && - (protocol_and_mode->first == Socket::ProtocolUnixDomain || - protocol_and_mode->first == Socket::ProtocolUnixAbstract)) { - m_platform_scheme = scheme.str(); - // The URI contains a filepath, the hostname is empty. - m_platform_hostname = ""; - } else { - std::optional<URI> parsed_url = URI::Parse(url); - if (!parsed_url) - return Status::FromErrorStringWithFormat("Invalid URL: %s", url); + std::optional<URI> parsed_url = URI::Parse(url); + if (!parsed_url) + return Status::FromErrorStringWithFormat("Invalid URL: %s", url); - // We're going to reuse the hostname when we connect to the debugserver. - m_platform_scheme = parsed_url->scheme.str(); - m_platform_hostname = parsed_url->hostname.str(); - } + // We're going to reuse the hostname when we connect to the debugserver. + m_platform_scheme = parsed_url->scheme.str(); + m_platform_hostname = parsed_url->hostname.str(); auto client_up = std::make_unique<process_gdb_remote::GDBRemoteCommunicationClient>(); diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp index f550c82d0885f..769705588fbdf 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp @@ -4560,21 +4560,20 @@ void GDBRemoteCommunicationServerLLGS::AppendThreadIDToResponse( std::string lldb_private::process_gdb_remote::LLGSArgToURL(llvm::StringRef url_arg, bool reverse_connect) { - // Parse the scheme manually because URI::parse does not handle Windows paths. - if (size_t pos = url_arg.find("://"); pos != llvm::StringRef::npos) { + // Try parsing the argument as URL. + if (std::optional<URI> url = URI::Parse(url_arg)) { if (reverse_connect) return url_arg.str(); // Translate the scheme from LLGS notation to ConnectionFileDescriptor. // If the scheme doesn't match any, pass it through to support using CFD // schemes directly. - llvm::StringRef scheme = url_arg.substr(0, pos); - std::string new_url = llvm::StringSwitch<std::string>(scheme) + std::string new_url = llvm::StringSwitch<std::string>(url->scheme) .Case("tcp", "listen") .Case("unix", "unix-accept") .Case("unix-abstract", "unix-abstract-accept") - .Default(scheme.str()); - llvm::append_range(new_url, url_arg.substr(scheme.size())); + .Default(url->scheme.str()); + llvm::append_range(new_url, url_arg.substr(url->scheme.size())); return new_url; } diff --git a/lldb/unittests/Host/SocketTest.cpp b/lldb/unittests/Host/SocketTest.cpp index 45dcce83ca0a7..c7fdea2cc0d26 100644 --- a/lldb/unittests/Host/SocketTest.cpp +++ b/lldb/unittests/Host/SocketTest.cpp @@ -161,9 +161,12 @@ TEST_F(SocketTest, DomainListenGetListeningConnectionURI) { ASSERT_THAT_ERROR(error.ToError(), llvm::Succeeded()); ASSERT_TRUE(listen_socket_up->IsValid()); - ASSERT_THAT( - listen_socket_up->GetListeningConnectionURI(), - testing::ElementsAre(llvm::formatv("unix-connect://{0}", Path).str())); + std::string expected_uri = + llvm::formatv("unix-connect://{0}", + DomainSocket::NativePathToURIPath(Path)) + .str(); + ASSERT_THAT(listen_socket_up->GetListeningConnectionURI(), + testing::ElementsAre(expected_uri)); } TEST_F(SocketTest, DomainMainLoopAccept) { @@ -392,12 +395,30 @@ TEST_F(SocketTest, DomainGetConnectURI) { CreateDomainConnectedSockets(domain_path, &socket_a_up, &socket_b_up); std::string uri(socket_a_up->GetRemoteConnectionURI()); - EXPECT_EQ((URI{"unix-connect", "", std::nullopt, domain_path}), + std::string expected_path = DomainSocket::NativePathToURIPath(domain_path); + EXPECT_EQ((URI{"unix-connect", "", std::nullopt, expected_path}), URI::Parse(uri)); EXPECT_EQ(socket_b_up->GetRemoteConnectionURI(), ""); } +TEST_F(SocketTest, DomainSocketPathURIConversion) { + // Paths that are already valid URI paths (no drive letter) are unchanged on + // every platform. + EXPECT_EQ(DomainSocket::NativePathToURIPath("/tmp/foo"), "/tmp/foo"); + EXPECT_EQ(DomainSocket::URIPathToNativePath("/tmp/foo"), "/tmp/foo"); + +#ifdef _WIN32 + // A Windows drive-letter path round-trips through the RFC 8089 "/C:/..." + // URI form. + EXPECT_EQ(DomainSocket::NativePathToURIPath("C:\\dir\\sock"), "/C:/dir/sock"); + EXPECT_EQ(DomainSocket::URIPathToNativePath("/C:/dir/sock"), "C:\\dir\\sock"); + EXPECT_EQ(DomainSocket::URIPathToNativePath( + DomainSocket::NativePathToURIPath("C:\\dir\\sock")), + "C:\\dir\\sock"); +#endif +} + TEST_F(SocketTest, DomainSocketFromBoundNativeSocket) { if (!HostSupportsDomainSockets()) GTEST_SKIP() << "Domain sockets unavailable"; diff --git a/lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationServerLLGSTest.cpp b/lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationServerLLGSTest.cpp index b4704606e8692..d2cd5bf8df327 100644 --- a/lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationServerLLGSTest.cpp +++ b/lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationServerLLGSTest.cpp @@ -31,6 +31,11 @@ TEST(GDBRemoteCommunicationServerLLGSTest, LLGSArgToURL) { EXPECT_EQ(LLGSArgToURL("unix-abstract://foo", false), "unix-abstract-accept://foo"); + // LLGS legacy listen URLs carrying a Windows drive-letter path (in RFC 8089 + // "/C:/..." form) should be converted, keeping the path intact. + EXPECT_EQ(LLGSArgToURL("unix:///C:/Users/a/foo", false), + "unix-accept:///C:/Users/a/foo"); + // LLGS listen host:port pairs should be converted to listen:// EXPECT_EQ(LLGSArgToURL("127.0.0.1:1234", false), "listen://127.0.0.1:1234"); EXPECT_EQ(LLGSArgToURL("[::1]:1234", false), "listen://[::1]:1234"); diff --git a/lldb/unittests/Utility/UriParserTest.cpp b/lldb/unittests/Utility/UriParserTest.cpp index 9923d3a9af936..34ea314149660 100644 --- a/lldb/unittests/Utility/UriParserTest.cpp +++ b/lldb/unittests/Utility/UriParserTest.cpp @@ -24,6 +24,11 @@ TEST(UriParserTest, LongPath) { URI::Parse("x://y/abc/def/xyz")); } +TEST(UriParserTest, WindowsDriveLetterPath) { + EXPECT_EQ((URI{"unix-connect", "", std::nullopt, "/C:/Users/a/sock"}), + URI::Parse("unix-connect:///C:/Users/a/sock")); +} + TEST(UriParserTest, TypicalPortPathIPv4) { EXPECT_EQ((URI{"connect", "192.168.100.132", 5432, "/"}), URI::Parse("connect://192.168.100.132:5432/")); _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
