[Lldb-commits] [lldb] [lldb] Extract URI schemes manually to support Windows paths (PR #206985)
Charles Zablit via lldb-commits
lldb-commits at lists.llvm.org
Fri Jul 17 02:56:52 PDT 2026
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 <c_zablit at apple.com>
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 <c_zablit at apple.com>
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 <c_zablit at apple.com>
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 <c_zablit at apple.com>
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/"));
More information about the lldb-commits
mailing list