[libc-commits] [libc] Implement linux-specific sendmmsg(2) (PR #198778)

Pavel Labath via libc-commits libc-commits at lists.llvm.org
Wed May 20 06:20:49 PDT 2026


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

Assisted by Gemini.

>From ada48c696134e354a59fa8217adee18a062c428a Mon Sep 17 00:00:00 2001
From: Pavel Labath <pavel at labath.sk>
Date: Tue, 19 May 2026 14:30:02 +0000
Subject: [PATCH] Implement linux-specific sendmmsg(2)

Assisted by Gemini.
---
 libc/config/linux/aarch64/entrypoints.txt     |  1 +
 libc/config/linux/riscv/entrypoints.txt       |  1 +
 libc/config/linux/x86_64/entrypoints.txt      |  1 +
 libc/hdr/types/CMakeLists.txt                 |  9 ++
 libc/hdr/types/struct_mmsghdr.h               | 26 ++++++
 libc/include/llvm-libc-types/CMakeLists.txt   |  1 +
 libc/include/llvm-libc-types/struct_mmsghdr.h | 24 ++++++
 libc/include/sys/socket.yaml                  | 10 +++
 .../linux/syscall_wrappers/CMakeLists.txt     | 13 +++
 .../OSUtil/linux/syscall_wrappers/sendmmsg.h  | 35 ++++++++
 libc/src/sys/socket/CMakeLists.txt            |  7 ++
 libc/src/sys/socket/linux/CMakeLists.txt      | 13 +++
 libc/src/sys/socket/linux/sendmmsg.cpp        | 32 +++++++
 libc/src/sys/socket/sendmmsg.h                | 26 ++++++
 libc/test/src/sys/socket/linux/CMakeLists.txt | 20 +++++
 .../src/sys/socket/linux/sendmmsg_test.cpp    | 84 +++++++++++++++++++
 libc/utils/docgen/sys/socket.yaml             |  2 +
 17 files changed, 305 insertions(+)
 create mode 100644 libc/hdr/types/struct_mmsghdr.h
 create mode 100644 libc/include/llvm-libc-types/struct_mmsghdr.h
 create mode 100644 libc/src/__support/OSUtil/linux/syscall_wrappers/sendmmsg.h
 create mode 100644 libc/src/sys/socket/linux/sendmmsg.cpp
 create mode 100644 libc/src/sys/socket/sendmmsg.h
 create mode 100644 libc/test/src/sys/socket/linux/sendmmsg_test.cpp

diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt
index c7458ef36c941..f4b13ec300083 100644
--- a/libc/config/linux/aarch64/entrypoints.txt
+++ b/libc/config/linux/aarch64/entrypoints.txt
@@ -303,6 +303,7 @@ set(TARGET_LIBC_ENTRYPOINTS
     # TODO: These functions are not compatible with 64-bit musl. They need to
     # be excluded if overlaying musl.
     libc.src.sys.socket.recvmsg
+    libc.src.sys.socket.sendmmsg
     libc.src.sys.socket.sendmsg
 
     # sys/stat.h entrypoints
diff --git a/libc/config/linux/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt
index e3336420cbbcc..32c4342f713aa 100644
--- a/libc/config/linux/riscv/entrypoints.txt
+++ b/libc/config/linux/riscv/entrypoints.txt
@@ -303,6 +303,7 @@ set(TARGET_LIBC_ENTRYPOINTS
     # TODO: These functions are not compatible with 64-bit musl. They need to
     # be excluded if overlaying musl.
     libc.src.sys.socket.recvmsg
+    libc.src.sys.socket.sendmmsg
     libc.src.sys.socket.sendmsg
 
     # sys/stat.h entrypoints
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index 3c3b0e835429f..641aba1a029fe 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -327,6 +327,7 @@ set(TARGET_LIBC_ENTRYPOINTS
     # TODO: These functions are not compatible with 64-bit musl. They need to
     # be excluded if overlaying musl.
     libc.src.sys.socket.recvmsg
+    libc.src.sys.socket.sendmmsg
     libc.src.sys.socket.sendmsg
 
     # sys/stat.h entrypoints
diff --git a/libc/hdr/types/CMakeLists.txt b/libc/hdr/types/CMakeLists.txt
index 626c5fb82883e..09b6cc5b7fd9a 100644
--- a/libc/hdr/types/CMakeLists.txt
+++ b/libc/hdr/types/CMakeLists.txt
@@ -411,6 +411,15 @@ add_proxy_header_library(
     libc.include.sys_socket
 )
 
+add_proxy_header_library(
+  struct_mmsghdr
+  HDRS
+    struct_mmsghdr.h
+  FULL_BUILD_DEPENDS
+    libc.include.llvm-libc-types.struct_mmsghdr
+    libc.include.sys_socket
+)
+
 add_proxy_header_library(
   struct_msghdr
   HDRS
diff --git a/libc/hdr/types/struct_mmsghdr.h b/libc/hdr/types/struct_mmsghdr.h
new file mode 100644
index 0000000000000..84473bbd46685
--- /dev/null
+++ b/libc/hdr/types/struct_mmsghdr.h
@@ -0,0 +1,26 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Proxy for struct mmsghdr.
+///
+//===----------------------------------------------------------------------===//
+#ifndef LLVM_LIBC_HDR_TYPES_STRUCT_MMSGHDR_H
+#define LLVM_LIBC_HDR_TYPES_STRUCT_MMSGHDR_H
+
+#ifdef LIBC_FULL_BUILD
+
+#include "include/llvm-libc-types/struct_mmsghdr.h"
+
+#else
+
+#include <sys/socket.h>
+
+#endif // LIBC_FULL_BUILD
+
+#endif // LLVM_LIBC_HDR_TYPES_STRUCT_MMSGHDR_H
diff --git a/libc/include/llvm-libc-types/CMakeLists.txt b/libc/include/llvm-libc-types/CMakeLists.txt
index 2904e0d07f76c..1bf09f5337a48 100644
--- a/libc/include/llvm-libc-types/CMakeLists.txt
+++ b/libc/include/llvm-libc-types/CMakeLists.txt
@@ -205,6 +205,7 @@ add_header(struct_iovec HDR struct_iovec.h DEPENDS .size_t)
 add_header(struct_linger HDR struct_linger.h)
 add_header(struct_cmsghdr HDR struct_cmsghdr.h DEPENDS .size_t)
 add_header(struct_msghdr HDR struct_msghdr.h DEPENDS .size_t .socklen_t .struct_iovec)
+add_header(struct_mmsghdr HDR struct_mmsghdr.h DEPENDS .struct_msghdr)
 add_header(ACTION HDR ACTION.h)
 add_header(ENTRY HDR ENTRY.h)
 add_header(VISIT HDR VISIT.h)
diff --git a/libc/include/llvm-libc-types/struct_mmsghdr.h b/libc/include/llvm-libc-types/struct_mmsghdr.h
new file mode 100644
index 0000000000000..8f4297d124a9b
--- /dev/null
+++ b/libc/include/llvm-libc-types/struct_mmsghdr.h
@@ -0,0 +1,24 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Definition of struct mmsghdr.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_TYPES_STRUCT_MMSGHDR_H
+#define LLVM_LIBC_TYPES_STRUCT_MMSGHDR_H
+
+#include "struct_msghdr.h"
+
+struct mmsghdr {
+  struct msghdr msg_hdr; /* Message header */
+  unsigned int msg_len;  /* Number of bytes sent/received */
+};
+
+#endif // LLVM_LIBC_TYPES_STRUCT_MMSGHDR_H
diff --git a/libc/include/sys/socket.yaml b/libc/include/sys/socket.yaml
index e5e9c9d2df183..900ce1f717e60 100644
--- a/libc/include/sys/socket.yaml
+++ b/libc/include/sys/socket.yaml
@@ -10,6 +10,7 @@ types:
   - type_name: socklen_t
   - type_name: sa_family_t
   - type_name: struct_cmsghdr
+  - type_name: struct_mmsghdr
   - type_name: struct_msghdr
   - type_name: struct_iovec
   - type_name: struct_linger
@@ -121,6 +122,15 @@ functions:
       - type: const void*
       - type: size_t
       - type: int
+  - name: sendmmsg
+    standards:
+      - Linux
+    return_type: int
+    arguments:
+      - type: int
+      - type: struct mmsghdr *
+      - type: unsigned int
+      - type: int
   - name: sendmsg
     standards:
       - POSIX
diff --git a/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt b/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt
index dc1702e1e4539..1673f92a2c57b 100644
--- a/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt
+++ b/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt
@@ -189,6 +189,19 @@ add_header_library(
     libc.include.sys_syscall
 )
 
+add_header_library(
+  sendmmsg
+  HDRS
+    sendmmsg.h
+  DEPENDS
+    libc.src.__support.OSUtil.osutil
+    libc.src.__support.common
+    libc.src.__support.error_or
+    libc.src.__support.macros.config
+    libc.hdr.types.struct_mmsghdr
+    libc.include.sys_syscall
+)
+
 add_header_library(
   sendmsg
   HDRS
diff --git a/libc/src/__support/OSUtil/linux/syscall_wrappers/sendmmsg.h b/libc/src/__support/OSUtil/linux/syscall_wrappers/sendmmsg.h
new file mode 100644
index 0000000000000..3e943539303e0
--- /dev/null
+++ b/libc/src/__support/OSUtil/linux/syscall_wrappers/sendmmsg.h
@@ -0,0 +1,35 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Syscall wrapper for sendmmsg.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_SENDMMSG_H
+#define LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_SENDMMSG_H
+
+#include "hdr/types/struct_mmsghdr.h"
+#include "src/__support/OSUtil/linux/syscall.h" // For syscall_checked
+#include "src/__support/common.h"
+#include "src/__support/error_or.h"
+#include "src/__support/macros/config.h"
+#include <sys/syscall.h> // For syscall numbers
+
+namespace LIBC_NAMESPACE_DECL {
+namespace linux_syscalls {
+
+LIBC_INLINE ErrorOr<int> sendmmsg(int sockfd, struct mmsghdr *msgvec,
+                                  unsigned int vlen, int flags) {
+  return syscall_checked<int>(SYS_sendmmsg, sockfd, msgvec, vlen, flags);
+}
+
+} // namespace linux_syscalls
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_SENDMMSG_H
diff --git a/libc/src/sys/socket/CMakeLists.txt b/libc/src/sys/socket/CMakeLists.txt
index a100e90fe3f2d..22b665392a4d6 100644
--- a/libc/src/sys/socket/CMakeLists.txt
+++ b/libc/src/sys/socket/CMakeLists.txt
@@ -93,6 +93,13 @@ add_entrypoint_object(
     .${LIBC_TARGET_OS}.setsockopt
 )
 
+add_entrypoint_object(
+  sendmmsg
+  ALIAS
+  DEPENDS
+    .${LIBC_TARGET_OS}.sendmmsg
+)
+
 add_entrypoint_object(
   sendmsg
   ALIAS
diff --git a/libc/src/sys/socket/linux/CMakeLists.txt b/libc/src/sys/socket/linux/CMakeLists.txt
index 4c7352dbd0200..b8ff28f94dd2c 100644
--- a/libc/src/sys/socket/linux/CMakeLists.txt
+++ b/libc/src/sys/socket/linux/CMakeLists.txt
@@ -180,6 +180,19 @@ add_entrypoint_object(
     libc.src.errno.errno
 )
 
+add_entrypoint_object(
+  sendmmsg
+  SRCS
+    sendmmsg.cpp
+  HDRS
+    ../sendmmsg.h
+  DEPENDS
+    libc.hdr.types.struct_mmsghdr
+    libc.src.__support.common
+    libc.src.__support.OSUtil.linux.syscall_wrappers.sendmmsg
+    libc.src.errno.errno
+)
+
 add_entrypoint_object(
   sendmsg
   SRCS
diff --git a/libc/src/sys/socket/linux/sendmmsg.cpp b/libc/src/sys/socket/linux/sendmmsg.cpp
new file mode 100644
index 0000000000000..1181fea1f999e
--- /dev/null
+++ b/libc/src/sys/socket/linux/sendmmsg.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Linux implementation of sendmmsg.
+///
+//===----------------------------------------------------------------------===//
+#include "src/sys/socket/sendmmsg.h"
+#include "hdr/types/struct_mmsghdr.h"
+#include "src/__support/OSUtil/linux/syscall_wrappers/sendmmsg.h"
+#include "src/__support/common.h"
+#include "src/__support/libc_errno.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, sendmmsg,
+                   (int sockfd, struct mmsghdr *msgvec, unsigned int vlen,
+                    int flags)) {
+  auto result = linux_syscalls::sendmmsg(sockfd, msgvec, vlen, flags);
+  if (!result.has_value()) {
+    libc_errno = result.error();
+    return -1;
+  }
+  return result.value();
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/sys/socket/sendmmsg.h b/libc/src/sys/socket/sendmmsg.h
new file mode 100644
index 0000000000000..6701f6cacab43
--- /dev/null
+++ b/libc/src/sys/socket/sendmmsg.h
@@ -0,0 +1,26 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Declaration of the sendmmsg function.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_SYS_SOCKET_SENDMMSG_H
+#define LLVM_LIBC_SRC_SYS_SOCKET_SENDMMSG_H
+
+#include "hdr/types/struct_mmsghdr.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+int sendmmsg(int sockfd, struct mmsghdr *msgvec, unsigned int vlen, int flags);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_SYS_SOCKET_SENDMMSG_H
diff --git a/libc/test/src/sys/socket/linux/CMakeLists.txt b/libc/test/src/sys/socket/linux/CMakeLists.txt
index 3b0c9748056cf..473a85502d55a 100644
--- a/libc/test/src/sys/socket/linux/CMakeLists.txt
+++ b/libc/test/src/sys/socket/linux/CMakeLists.txt
@@ -204,6 +204,26 @@ add_libc_unittest(
     libc.test.UnitTest.ErrnoSetterMatcher
 )
 
+add_libc_unittest(
+  sendmmsg_test
+  SUITE
+    libc_sys_socket_unittests
+  SRCS
+    sendmmsg_test.cpp
+  DEPENDS
+    libc.hdr.sys_socket_macros
+    libc.hdr.types.struct_mmsghdr
+    libc.src.errno.errno
+    libc.src.string.strlen
+    libc.src.sys.socket.socketpair
+    libc.src.sys.socket.sendmmsg
+    libc.src.sys.socket.recvmsg
+    libc.src.unistd.close
+    libc.src.__support.CPP.scope
+    libc.test.UnitTest.ErrnoCheckingTest
+    libc.test.UnitTest.ErrnoSetterMatcher
+)
+
 add_libc_unittest(
   sendmsg_recvmsg_test
   SUITE
diff --git a/libc/test/src/sys/socket/linux/sendmmsg_test.cpp b/libc/test/src/sys/socket/linux/sendmmsg_test.cpp
new file mode 100644
index 0000000000000..adbfe20205b8f
--- /dev/null
+++ b/libc/test/src/sys/socket/linux/sendmmsg_test.cpp
@@ -0,0 +1,84 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Unit tests for sendmmsg.
+///
+//===----------------------------------------------------------------------===//
+
+#include "hdr/sys_socket_macros.h"
+#include "hdr/types/struct_mmsghdr.h"
+#include "src/__support/CPP/scope.h"
+#include "src/string/strlen.h"
+#include "src/sys/socket/recvmsg.h"
+#include "src/sys/socket/sendmmsg.h"
+#include "src/sys/socket/socketpair.h"
+#include "src/unistd/close.h"
+#include "test/UnitTest/ErrnoCheckingTest.h"
+#include "test/UnitTest/ErrnoSetterMatcher.h"
+#include "test/UnitTest/LibcTest.h"
+#include "test/UnitTest/Test.h"
+
+using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Fails;
+using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Succeeds;
+using LlvmLibcSendMmsgTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
+
+TEST_F(LlvmLibcSendMmsgTest, SendMmsgSucceedsWithSocketPair) {
+  const char *const TEST_MESSAGES[] = {"message one", "message two"};
+  const size_t MESSAGES_COUNT = 2;
+
+  int sockpair[2] = {0, 0};
+
+  ASSERT_THAT(LIBC_NAMESPACE::socketpair(AF_UNIX, SOCK_DGRAM, 0, sockpair),
+              Succeeds(0));
+  LIBC_NAMESPACE::cpp::scope_exit close_sockpair([&] {
+    ASSERT_THAT(LIBC_NAMESPACE::close(sockpair[0]), Succeeds(0));
+    ASSERT_THAT(LIBC_NAMESPACE::close(sockpair[1]), Succeeds(0));
+  });
+
+  struct iovec send_msg_vec[MESSAGES_COUNT] = {};
+  struct mmsghdr send_msg_hdr[MESSAGES_COUNT] = {};
+  for (size_t i = 0; i < MESSAGES_COUNT; ++i) {
+    send_msg_vec[i].iov_base =
+        reinterpret_cast<void *>(const_cast<char *>(TEST_MESSAGES[i]));
+    send_msg_vec[i].iov_len = LIBC_NAMESPACE::strlen(TEST_MESSAGES[i]) + 1;
+    send_msg_hdr[i].msg_hdr.msg_iov = &send_msg_vec[i];
+    send_msg_hdr[i].msg_hdr.msg_iovlen = 1;
+  }
+
+  ASSERT_THAT(
+      LIBC_NAMESPACE::sendmmsg(sockpair[0], send_msg_hdr, MESSAGES_COUNT, 0),
+      Succeeds(static_cast<int>(MESSAGES_COUNT)));
+
+  for (size_t i = 0; i < MESSAGES_COUNT; ++i) {
+    ASSERT_EQ(static_cast<size_t>(send_msg_hdr[i].msg_len),
+              LIBC_NAMESPACE::strlen(TEST_MESSAGES[i]) + 1);
+  }
+
+  for (size_t i = 0; i < MESSAGES_COUNT; ++i) {
+    char recv_buffer[256] = {};
+    struct iovec recv_msg_vec;
+    recv_msg_vec.iov_base = reinterpret_cast<void *>(recv_buffer);
+    recv_msg_vec.iov_len = sizeof(recv_buffer);
+
+    struct msghdr recv_msg_hdr = {};
+    recv_msg_hdr.msg_iov = &recv_msg_vec;
+    recv_msg_hdr.msg_iovlen = 1;
+
+    ASSERT_THAT(LIBC_NAMESPACE::recvmsg(sockpair[1], &recv_msg_hdr, 0),
+                Succeeds(static_cast<ssize_t>(
+                    LIBC_NAMESPACE::strlen(TEST_MESSAGES[i]) + 1)));
+    ASSERT_STREQ(recv_buffer, TEST_MESSAGES[i]);
+  }
+}
+
+TEST_F(LlvmLibcSendMmsgTest, SendMmsgFails) {
+  struct mmsghdr msg_hdrs = {};
+
+  ASSERT_THAT(LIBC_NAMESPACE::sendmmsg(-1, &msg_hdrs, 1, 0), Fails(EBADF, -1));
+}
diff --git a/libc/utils/docgen/sys/socket.yaml b/libc/utils/docgen/sys/socket.yaml
index d0b8a7e087e70..1c9af64a76331 100644
--- a/libc/utils/docgen/sys/socket.yaml
+++ b/libc/utils/docgen/sys/socket.yaml
@@ -126,6 +126,8 @@ functions:
     in-latest-posix: ''
   send:
     in-latest-posix: ''
+  sendmmsg:
+    c-definition: ''
   sendmsg:
     in-latest-posix: ''
   sendto:



More information about the libc-commits mailing list