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/7] [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/7] 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/7] 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/7] 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/")); >From 39a477437c017e5d7c22ce3a6b9391c3390267e0 Mon Sep 17 00:00:00 2001 From: Charles Zablit <[email protected]> Date: Fri, 17 Jul 2026 12:09:03 +0100 Subject: [PATCH 5/7] handle UNC paths in domain-socket URI conversion --- lldb/include/lldb/Host/common/DomainSocket.h | 6 ++++-- lldb/source/Host/common/DomainSocket.cpp | 16 ++++++++++++++++ lldb/unittests/Host/SocketTest.cpp | 9 +++++++++ 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/lldb/include/lldb/Host/common/DomainSocket.h b/lldb/include/lldb/Host/common/DomainSocket.h index fa96d38316851..9a2aedc21e1f7 100644 --- a/lldb/include/lldb/Host/common/DomainSocket.h +++ b/lldb/include/lldb/Host/common/DomainSocket.h @@ -49,8 +49,10 @@ class DomainSocket : public Socket { /// 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. + /// (leading slash, forward slashes). A UNC path (e.g. "\\\\server\\share") + /// only needs its backslashes replaced to become the URI path + /// "//server/share". 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); diff --git a/lldb/source/Host/common/DomainSocket.cpp b/lldb/source/Host/common/DomainSocket.cpp index 13b4a8650a6cd..666ec54046761 100644 --- a/lldb/source/Host/common/DomainSocket.cpp +++ b/lldb/source/Host/common/DomainSocket.cpp @@ -36,23 +36,39 @@ static const int kType = SOCK_STREAM; std::string DomainSocket::NativePathToURIPath(llvm::StringRef path) { #ifdef _WIN32 + // A drive-letter path (e.g. "C:\\dir\\sock") is not a valid URI authority, so + // it is carried in the RFC 8089 URI form "/C:/dir/sock". 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; } + // A UNC path (e.g. "\\\\server\\share") is already anchored by its leading + // slashes. + if (path.starts_with("\\\\")) { + 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 + // Reverse of the drive-letter mapping: "/C:/dir/sock" -> "C:\\dir\\sock". 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; } + // Reverse of the UNC mapping: "//server/share" -> "\\\\server\\share". + if (path.starts_with("//")) { + std::string native = path.str(); + std::replace(native.begin(), native.end(), '/', '\\'); + return native; + } #endif return path.str(); } diff --git a/lldb/unittests/Host/SocketTest.cpp b/lldb/unittests/Host/SocketTest.cpp index c7fdea2cc0d26..5e40cdcd333f8 100644 --- a/lldb/unittests/Host/SocketTest.cpp +++ b/lldb/unittests/Host/SocketTest.cpp @@ -416,6 +416,15 @@ TEST_F(SocketTest, DomainSocketPathURIConversion) { EXPECT_EQ(DomainSocket::URIPathToNativePath( DomainSocket::NativePathToURIPath("C:\\dir\\sock")), "C:\\dir\\sock"); + + // A Windows UNC path round-trips by swapping backslashes for forward slashes. + EXPECT_EQ(DomainSocket::NativePathToURIPath("\\\\server\\share\\sock"), + "//server/share/sock"); + EXPECT_EQ(DomainSocket::URIPathToNativePath("//server/share/sock"), + "\\\\server\\share\\sock"); + EXPECT_EQ(DomainSocket::URIPathToNativePath(DomainSocket::NativePathToURIPath( + "\\\\server\\share\\sock")), + "\\\\server\\share\\sock"); #endif } >From 29da0dba645ae8b9a8b1ffc86f0120a58070c8c0 Mon Sep 17 00:00:00 2001 From: Charles Zablit <[email protected]> Date: Fri, 17 Jul 2026 15:27:44 +0100 Subject: [PATCH 6/7] make domain-socket path/URI conversion host-independent --- lldb/include/lldb/Host/common/DomainSocket.h | 15 ++++++++------- lldb/source/Host/common/DomainSocket.cpp | 4 ---- lldb/unittests/Host/SocketTest.cpp | 9 +++------ 3 files changed, 11 insertions(+), 17 deletions(-) diff --git a/lldb/include/lldb/Host/common/DomainSocket.h b/lldb/include/lldb/Host/common/DomainSocket.h index 9a2aedc21e1f7..65850e6777593 100644 --- a/lldb/include/lldb/Host/common/DomainSocket.h +++ b/lldb/include/lldb/Host/common/DomainSocket.h @@ -46,13 +46,14 @@ class DomainSocket : public Socket { 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). A UNC path (e.g. "\\\\server\\share") - /// only needs its backslashes replaced to become the URI path - /// "//server/share". On other platforms the path is already a valid URI path - /// and is returned unchanged. + /// domain-socket URI. This is a pure, content-driven string transformation + /// (independent of the build host) so that, for example, a Unix build of + /// LLDB remote debugging a Windows target can still interpret Windows paths. + /// 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). A UNC path (e.g. "\\\\server\\share") only needs + /// its backslashes replaced to become the URI path "//server/share". Any + /// other 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); diff --git a/lldb/source/Host/common/DomainSocket.cpp b/lldb/source/Host/common/DomainSocket.cpp index 666ec54046761..28ec1b7d25dae 100644 --- a/lldb/source/Host/common/DomainSocket.cpp +++ b/lldb/source/Host/common/DomainSocket.cpp @@ -35,7 +35,6 @@ static const int kDomain = AF_UNIX; static const int kType = SOCK_STREAM; std::string DomainSocket::NativePathToURIPath(llvm::StringRef path) { -#ifdef _WIN32 // A drive-letter path (e.g. "C:\\dir\\sock") is not a valid URI authority, so // it is carried in the RFC 8089 URI form "/C:/dir/sock". if (path.size() >= 2 && llvm::isAlpha(path[0]) && path[1] == ':') { @@ -50,12 +49,10 @@ std::string DomainSocket::NativePathToURIPath(llvm::StringRef path) { std::replace(uri_path.begin(), uri_path.end(), '\\', '/'); return uri_path; } -#endif return path.str(); } std::string DomainSocket::URIPathToNativePath(llvm::StringRef path) { -#ifdef _WIN32 // Reverse of the drive-letter mapping: "/C:/dir/sock" -> "C:\\dir\\sock". if (path.size() >= 3 && path[0] == '/' && llvm::isAlpha(path[1]) && path[2] == ':') { @@ -69,7 +66,6 @@ std::string DomainSocket::URIPathToNativePath(llvm::StringRef path) { std::replace(native.begin(), native.end(), '/', '\\'); return native; } -#endif return path.str(); } diff --git a/lldb/unittests/Host/SocketTest.cpp b/lldb/unittests/Host/SocketTest.cpp index 5e40cdcd333f8..cdc02e96f40b7 100644 --- a/lldb/unittests/Host/SocketTest.cpp +++ b/lldb/unittests/Host/SocketTest.cpp @@ -403,12 +403,10 @@ TEST_F(SocketTest, DomainGetConnectURI) { } TEST_F(SocketTest, DomainSocketPathURIConversion) { - // Paths that are already valid URI paths (no drive letter) are unchanged on - // every platform. + // Paths that are already valid URI paths (no drive letter) are unchanged. 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"); @@ -422,10 +420,9 @@ TEST_F(SocketTest, DomainSocketPathURIConversion) { "//server/share/sock"); EXPECT_EQ(DomainSocket::URIPathToNativePath("//server/share/sock"), "\\\\server\\share\\sock"); - EXPECT_EQ(DomainSocket::URIPathToNativePath(DomainSocket::NativePathToURIPath( - "\\\\server\\share\\sock")), + EXPECT_EQ(DomainSocket::URIPathToNativePath( + DomainSocket::NativePathToURIPath("\\\\server\\share\\sock")), "\\\\server\\share\\sock"); -#endif } TEST_F(SocketTest, DomainSocketFromBoundNativeSocket) { >From 78d1c7b28801bb476771904ea135da54d5b93778 Mon Sep 17 00:00:00 2001 From: Charles Zablit <[email protected]> Date: Fri, 17 Jul 2026 15:30:14 +0100 Subject: [PATCH 7/7] add UNC path tests for URI parsing and LLGS URL conversion --- .../gdb-remote/GDBRemoteCommunicationServerLLGSTest.cpp | 4 ++++ lldb/unittests/Utility/UriParserTest.cpp | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationServerLLGSTest.cpp b/lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationServerLLGSTest.cpp index d2cd5bf8df327..9018cbc83f5d5 100644 --- a/lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationServerLLGSTest.cpp +++ b/lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationServerLLGSTest.cpp @@ -36,6 +36,10 @@ TEST(GDBRemoteCommunicationServerLLGSTest, LLGSArgToURL) { EXPECT_EQ(LLGSArgToURL("unix:///C:/Users/a/foo", false), "unix-accept:///C:/Users/a/foo"); + // Same for a Windows UNC path (in "//server/share/..." form). + EXPECT_EQ(LLGSArgToURL("unix:////server/share/foo", false), + "unix-accept:////server/share/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 34ea314149660..da3f5a978dcea 100644 --- a/lldb/unittests/Utility/UriParserTest.cpp +++ b/lldb/unittests/Utility/UriParserTest.cpp @@ -29,6 +29,11 @@ TEST(UriParserTest, WindowsDriveLetterPath) { URI::Parse("unix-connect:///C:/Users/a/sock")); } +TEST(UriParserTest, WindowsUNCPath) { + EXPECT_EQ((URI{"unix-connect", "", std::nullopt, "//server/share/sock"}), + URI::Parse("unix-connect:////server/share/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
