[libc-commits] [libc] [libc] Implement connect(2) on linux (PR #189668)

Pavel Labath via libc-commits libc-commits at lists.llvm.org
Tue Mar 31 06:44:46 PDT 2026


https://github.com/labath created https://github.com/llvm/llvm-project/pull/189668

I'm using the new syscall wrapper framework, and enabling the entry point for x86_64, aarch64 and riscv. The associated test currently only checks for the error code. Once we have listen&accept (which I'm probably going to tackle next), we can test that the two can talk to each other.

>From d55f1f45bc90914dc49c59c8122b0ce94a7f9235 Mon Sep 17 00:00:00 2001
From: Pavel Labath <pavel at labath.sk>
Date: Tue, 31 Mar 2026 15:30:39 +0200
Subject: [PATCH] [libc] Implement connect(2) on linux

I'm using the new syscall wrapper framework, and enabling the entry
point for x86_64, aarch64 and riscv. The associated test currently only
checks for the error code. Once we have listen&accept (which I'm
probably going to tackle next), we can test that the two can talk to
each other.
---
 libc/config/linux/aarch64/entrypoints.txt     |  1 +
 libc/config/linux/riscv/entrypoints.txt       |  1 +
 libc/config/linux/x86_64/entrypoints.txt      |  1 +
 .../linux/syscall_wrappers/CMakeLists.txt     | 15 ++++
 .../OSUtil/linux/syscall_wrappers/connect.h   | 49 +++++++++++++
 libc/src/sys/socket/CMakeLists.txt            |  7 ++
 libc/src/sys/socket/connect.h                 | 23 +++++++
 libc/src/sys/socket/linux/CMakeLists.txt      | 14 ++++
 libc/src/sys/socket/linux/connect.cpp         | 31 +++++++++
 libc/test/src/sys/socket/linux/CMakeLists.txt | 20 ++++++
 .../src/sys/socket/linux/connect_test.cpp     | 68 +++++++++++++++++++
 11 files changed, 230 insertions(+)
 create mode 100644 libc/src/__support/OSUtil/linux/syscall_wrappers/connect.h
 create mode 100644 libc/src/sys/socket/connect.h
 create mode 100644 libc/src/sys/socket/linux/connect.cpp
 create mode 100644 libc/test/src/sys/socket/linux/connect_test.cpp

diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt
index e683da7300e38..83088456fb4ac 100644
--- a/libc/config/linux/aarch64/entrypoints.txt
+++ b/libc/config/linux/aarch64/entrypoints.txt
@@ -1209,6 +1209,7 @@ if(LLVM_LIBC_FULL_BUILD)
 
     # sys/socket.h entrypoints
     libc.src.sys.socket.bind
+    libc.src.sys.socket.connect
     libc.src.sys.socket.socket
   )
 endif()
diff --git a/libc/config/linux/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt
index 5782acb727ee2..1f7180d23a840 100644
--- a/libc/config/linux/riscv/entrypoints.txt
+++ b/libc/config/linux/riscv/entrypoints.txt
@@ -1343,6 +1343,7 @@ if(LLVM_LIBC_FULL_BUILD)
 
     # sys/socket.h entrypoints
     libc.src.sys.socket.bind
+    libc.src.sys.socket.connect
     libc.src.sys.socket.socket
     libc.src.sys.socket.socketpair
     libc.src.sys.socket.send
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index 90a0dade5435a..20434fb9e838f 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -1414,6 +1414,7 @@ if(LLVM_LIBC_FULL_BUILD)
     # sys/socket.h entrypoints
     libc.src.sys.socket.socket
     libc.src.sys.socket.bind
+    libc.src.sys.socket.connect
     libc.src.sys.socket.socketpair
     libc.src.sys.socket.send
     libc.src.sys.socket.sendto
diff --git a/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt b/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt
index 50e31eba161d6..7f28ab7829d66 100644
--- a/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt
+++ b/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt
@@ -23,6 +23,21 @@ add_header_library(
     libc.include.sys_syscall
 )
 
+add_header_library(
+  connect
+  HDRS
+    connect.h
+  DEPENDS
+    libc.src.__support.OSUtil.linux.syscall_wrappers.connect
+    libc.src.__support.common
+    libc.src.__support.error_or
+    libc.src.__support.libc_errno
+    libc.src.__support.macros.config
+    libc.hdr.types.socklen_t
+    libc.hdr.types.struct_sockaddr
+    libc.include.sys_syscall
+)
+
 add_header_library(
   read
   HDRS
diff --git a/libc/src/__support/OSUtil/linux/syscall_wrappers/connect.h b/libc/src/__support/OSUtil/linux/syscall_wrappers/connect.h
new file mode 100644
index 0000000000000..6b36237f12685
--- /dev/null
+++ b/libc/src/__support/OSUtil/linux/syscall_wrappers/connect.h
@@ -0,0 +1,49 @@
+//===-- Implementation header for connect -----------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_CONNECT_H
+#define LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_CONNECT_H
+
+#include "src/__support/OSUtil/linux/syscall.h" // syscall_impl
+#include "src/__support/common.h"
+#include "src/__support/error_or.h"
+#include "src/__support/macros/config.h"
+
+#include "hdr/types/socklen_t.h"
+#include "hdr/types/struct_sockaddr.h"
+#include <linux/net.h>   // For SYS_SOCKET socketcall number.
+#include <sys/syscall.h> // For syscall numbers
+
+namespace LIBC_NAMESPACE_DECL {
+namespace linux_syscalls {
+
+LIBC_INLINE ErrorOr<int> connect(int sockfd, const struct sockaddr *addr,
+                                 socklen_t addrlen) {
+#ifdef SYS_connect
+  int ret =
+      LIBC_NAMESPACE::syscall_impl<int>(SYS_connect, sockfd, addr, addrlen);
+#elif defined(SYS_socketcall)
+  unsigned long sockcall_args[3] = {static_cast<unsigned long>(sockfd),
+                                    reinterpret_cast<unsigned long>(addr),
+                                    static_cast<unsigned long>(addrlen)};
+  int ret = LIBC_NAMESPACE::syscall_impl<int>(SYS_socketcall, SYS_CONNECT,
+                                              sockcall_args);
+#else
+#error "socket and socketcall syscalls unavailable for this platform."
+#endif
+
+  if (ret < 0)
+    return Error(-static_cast<int>(ret));
+  return ret;
+}
+
+} // namespace linux_syscalls
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_CONNECT_H
+
diff --git a/libc/src/sys/socket/CMakeLists.txt b/libc/src/sys/socket/CMakeLists.txt
index 5c58e16ef7b31..5d9e2ff2d2f76 100644
--- a/libc/src/sys/socket/CMakeLists.txt
+++ b/libc/src/sys/socket/CMakeLists.txt
@@ -16,6 +16,13 @@ add_entrypoint_object(
     .${LIBC_TARGET_OS}.bind
 )
 
+add_entrypoint_object(
+  connect
+  ALIAS
+  DEPENDS
+    .${LIBC_TARGET_OS}.connect
+)
+
 add_entrypoint_object(
   socketpair
   ALIAS
diff --git a/libc/src/sys/socket/connect.h b/libc/src/sys/socket/connect.h
new file mode 100644
index 0000000000000..8e338103c4a21
--- /dev/null
+++ b/libc/src/sys/socket/connect.h
@@ -0,0 +1,23 @@
+//===-- Implementation header for connect -----------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_SYS_SOCKET_CONNECT_H
+#define LLVM_LIBC_SRC_SYS_SOCKET_CONNECT_H
+
+#include "src/__support/macros/config.h"
+
+#include "hdr/types/socklen_t.h"
+#include "hdr/types/struct_sockaddr.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_SYS_SOCKET_CONNECT_H
diff --git a/libc/src/sys/socket/linux/CMakeLists.txt b/libc/src/sys/socket/linux/CMakeLists.txt
index e1226aaad381f..ad62dc83226cd 100644
--- a/libc/src/sys/socket/linux/CMakeLists.txt
+++ b/libc/src/sys/socket/linux/CMakeLists.txt
@@ -24,6 +24,20 @@ add_entrypoint_object(
     libc.src.errno.errno
 )
 
+add_entrypoint_object(
+  connect
+  SRCS
+    connect.cpp
+  HDRS
+    ../socket.h
+  DEPENDS
+    libc.include.sys_socket
+    libc.src.__support.OSUtil.osutil
+    libc.hdr.types.struct_sockaddr
+    libc.hdr.types.socklen_t
+    libc.src.errno.errno
+)
+
 add_entrypoint_object(
   socketpair
   SRCS
diff --git a/libc/src/sys/socket/linux/connect.cpp b/libc/src/sys/socket/linux/connect.cpp
new file mode 100644
index 0000000000000..b51a0c24a2793
--- /dev/null
+++ b/libc/src/sys/socket/linux/connect.cpp
@@ -0,0 +1,31 @@
+//===-- Linux implementation of connect -----------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/sys/socket/connect.h"
+
+#include "hdr/types/socklen_t.h"
+#include "hdr/types/struct_sockaddr.h"
+#include "src/__support/OSUtil/linux/syscall_wrappers/connect.h"
+#include "src/__support/common.h"
+#include "src/__support/libc_errno.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, connect,
+                   (int sockfd, const struct sockaddr *addr,
+                    socklen_t addrlen)) {
+auto result = linux_syscalls::connect(sockfd, addr, addrlen);
+if (!result.has_value()) {
+    libc_errno = result.error();
+    return -1;
+  }
+
+  return result.value();
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/test/src/sys/socket/linux/CMakeLists.txt b/libc/test/src/sys/socket/linux/CMakeLists.txt
index 1ff814029b330..95c0d622bdcdd 100644
--- a/libc/test/src/sys/socket/linux/CMakeLists.txt
+++ b/libc/test/src/sys/socket/linux/CMakeLists.txt
@@ -34,6 +34,26 @@ add_libc_unittest(
     libc.test.UnitTest.ErrnoSetterMatcher
 )
 
+add_libc_unittest(
+  connect_test
+  SUITE
+    libc_sys_socket_unittests
+  SRCS
+    connect_test.cpp
+  DEPENDS
+    libc.include.sys_socket
+    libc.hdr.sys_socket_macros
+    libc.hdr.types.struct_sockaddr_un
+    libc.src.errno.errno
+    libc.src.sys.socket.socket
+    libc.src.sys.socket.bind
+    libc.src.sys.socket.connect
+    libc.src.stdio.remove
+    libc.src.unistd.close
+    libc.test.UnitTest.ErrnoCheckingTest
+    libc.test.UnitTest.ErrnoSetterMatcher
+)
+
 add_libc_unittest(
   socketpair_test
   SUITE
diff --git a/libc/test/src/sys/socket/linux/connect_test.cpp b/libc/test/src/sys/socket/linux/connect_test.cpp
new file mode 100644
index 0000000000000..05501eb7dd560
--- /dev/null
+++ b/libc/test/src/sys/socket/linux/connect_test.cpp
@@ -0,0 +1,68 @@
+//===-- Unittests for connect ---------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "hdr/sys_socket_macros.h"
+#include "hdr/types/struct_sockaddr_un.h"
+#include "src/sys/socket/bind.h"
+#include "src/sys/socket/connect.h"
+#include "src/sys/socket/socket.h"
+
+#include "src/stdio/remove.h"
+#include "src/unistd/close.h"
+
+#include "test/UnitTest/ErrnoCheckingTest.h"
+#include "test/UnitTest/ErrnoSetterMatcher.h"
+#include "test/UnitTest/Test.h"
+
+using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Fails;
+using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Succeeds;
+using LlvmLibcConnectTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
+
+TEST_F(LlvmLibcConnectTest, ConnectLocalSocket) {
+
+  const char *FILENAME = "connect_file.test";
+  auto SOCK_PATH = libc_make_test_file_path(FILENAME);
+
+  int sock1 = LIBC_NAMESPACE::socket(AF_UNIX, SOCK_STREAM, 0);
+  ASSERT_GE(sock1, 0);
+  ASSERT_ERRNO_SUCCESS();
+
+  struct sockaddr_un my_addr;
+
+  my_addr.sun_family = AF_UNIX;
+  unsigned int i = 0;
+  for (;
+       SOCK_PATH[i] != '\0' && (i < sizeof(sockaddr_un) - sizeof(sa_family_t));
+       ++i)
+    my_addr.sun_path[i] = SOCK_PATH[i];
+
+  // It's important that the path fits in the struct, if it doesn't then we
+  // can't try to bind to the file.
+  ASSERT_LT(
+      i, static_cast<unsigned int>(sizeof(sockaddr_un) - sizeof(sa_family_t)));
+  my_addr.sun_path[i] = '\0';
+
+  ASSERT_THAT(
+      LIBC_NAMESPACE::bind(sock1, reinterpret_cast<struct sockaddr *>(&my_addr),
+                           sizeof(struct sockaddr_un)),
+      Succeeds(0));
+
+  int sock2 = LIBC_NAMESPACE::socket(AF_UNIX, SOCK_STREAM, 0);
+  ASSERT_GE(sock2, 0);
+  ASSERT_ERRNO_SUCCESS();
+
+  ASSERT_THAT(
+      LIBC_NAMESPACE::connect(sock2,
+                              reinterpret_cast<struct sockaddr *>(&my_addr),
+                              sizeof(struct sockaddr_un)),
+      Fails(ECONNREFUSED)); // Because the other side is not listen()ing.
+
+  ASSERT_THAT(LIBC_NAMESPACE::close(sock1), Succeeds(0));
+  ASSERT_THAT(LIBC_NAMESPACE::close(sock2), Succeeds(0));
+  ASSERT_THAT(LIBC_NAMESPACE::remove(SOCK_PATH), Succeeds(0));
+}



More information about the libc-commits mailing list