[libc-commits] [libc] [libc][sys/sem] Add sys v sem headers and syscall wrapper implementation (PR #185914)

Pengxiang Huang via libc-commits libc-commits at lists.llvm.org
Wed Mar 18 17:06:30 PDT 2026


https://github.com/Pengxiang-Huang updated https://github.com/llvm/llvm-project/pull/185914

>From 51420abf67ae71ca17f89530d40b52095312af52 Mon Sep 17 00:00:00 2001
From: Pengxiang Huang <huangpengxiang70 at gmail.com>
Date: Wed, 11 Mar 2026 12:10:55 -0400
Subject: [PATCH 1/5] [libc][sys/sem] add sem headers and syscall wrapper
 implmentation

---
 libc/config/linux/x86_64/entrypoints.txt      |  5 ++
 libc/config/linux/x86_64/headers.txt          |  1 +
 libc/hdr/CMakeLists.txt                       | 18 ++++
 libc/hdr/sys_ipc_macros.h                     | 23 +++++
 libc/hdr/sys_sem_macros.h                     | 23 +++++
 libc/hdr/types/CMakeLists.txt                 | 27 ++++++
 libc/hdr/types/struct_sembuf.h                | 22 +++++
 libc/hdr/types/struct_semid_ds.h              | 22 +++++
 libc/hdr/types/struct_seminfo.h               | 22 +++++
 libc/include/CMakeLists.txt                   | 15 ++++
 libc/include/llvm-libc-macros/CMakeLists.txt  |  6 ++
 .../llvm-libc-macros/linux/CMakeLists.txt     |  6 ++
 .../llvm-libc-macros/linux/sys-ipc-macros.h   |  1 +
 .../llvm-libc-macros/linux/sys-sem-macros.h   | 29 +++++++
 .../include/llvm-libc-macros/sys-sem-macros.h | 16 ++++
 libc/include/llvm-libc-types/CMakeLists.txt   |  9 ++
 libc/include/llvm-libc-types/struct_sembuf.h  | 18 ++++
 .../include/llvm-libc-types/struct_semid_ds.h | 33 +++++++
 libc/include/llvm-libc-types/struct_seminfo.h | 27 ++++++
 libc/include/sys/sem.yaml                     | 84 ++++++++++++++++++
 libc/src/sys/CMakeLists.txt                   |  1 +
 libc/src/sys/sem/CMakeLists.txt               | 24 ++++++
 libc/src/sys/sem/linux/CMakeLists.txt         | 46 ++++++++++
 libc/src/sys/sem/linux/semctl.cpp             | 86 +++++++++++++++++++
 libc/src/sys/sem/linux/semget.cpp             | 27 ++++++
 libc/src/sys/sem/linux/semop.cpp              | 27 ++++++
 libc/src/sys/sem/semctl.h                     | 21 +++++
 libc/src/sys/sem/semget.h                     | 22 +++++
 libc/src/sys/sem/semop.h                      | 23 +++++
 libc/test/src/sys/CMakeLists.txt              |  1 +
 libc/test/src/sys/sem/CMakeLists.txt          |  3 +
 libc/test/src/sys/sem/linux/CMakeLists.txt    | 20 +++++
 libc/test/src/sys/sem/linux/sem_test.cpp      | 72 ++++++++++++++++
 33 files changed, 780 insertions(+)
 create mode 100644 libc/hdr/sys_ipc_macros.h
 create mode 100644 libc/hdr/sys_sem_macros.h
 create mode 100644 libc/hdr/types/struct_sembuf.h
 create mode 100644 libc/hdr/types/struct_semid_ds.h
 create mode 100644 libc/hdr/types/struct_seminfo.h
 create mode 100644 libc/include/llvm-libc-macros/linux/sys-sem-macros.h
 create mode 100644 libc/include/llvm-libc-macros/sys-sem-macros.h
 create mode 100644 libc/include/llvm-libc-types/struct_sembuf.h
 create mode 100644 libc/include/llvm-libc-types/struct_semid_ds.h
 create mode 100644 libc/include/llvm-libc-types/struct_seminfo.h
 create mode 100644 libc/include/sys/sem.yaml
 create mode 100644 libc/src/sys/sem/CMakeLists.txt
 create mode 100644 libc/src/sys/sem/linux/CMakeLists.txt
 create mode 100644 libc/src/sys/sem/linux/semctl.cpp
 create mode 100644 libc/src/sys/sem/linux/semget.cpp
 create mode 100644 libc/src/sys/sem/linux/semop.cpp
 create mode 100644 libc/src/sys/sem/semctl.h
 create mode 100644 libc/src/sys/sem/semget.h
 create mode 100644 libc/src/sys/sem/semop.h
 create mode 100644 libc/test/src/sys/sem/CMakeLists.txt
 create mode 100644 libc/test/src/sys/sem/linux/CMakeLists.txt
 create mode 100644 libc/test/src/sys/sem/linux/sem_test.cpp

diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index 24df7e63e3ebf..68a58a6225a4b 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -284,6 +284,11 @@ set(TARGET_LIBC_ENTRYPOINTS
     libc.src.sys.resource.getrlimit
     libc.src.sys.resource.setrlimit
 
+    # sys/sem.h entrypoints
+    libc.src.sys.sem.semget
+    libc.src.sys.sem.semctl
+    libc.src.sys.sem.semop
+
     # sys/sendfile entrypoints
     libc.src.sys.sendfile.sendfile
 
diff --git a/libc/config/linux/x86_64/headers.txt b/libc/config/linux/x86_64/headers.txt
index 543d151465663..157740033aabf 100644
--- a/libc/config/linux/x86_64/headers.txt
+++ b/libc/config/linux/x86_64/headers.txt
@@ -45,6 +45,7 @@ set(TARGET_PUBLIC_HEADERS
     libc.include.sys_random
     libc.include.sys_resource
     libc.include.sys_select
+    libc.include.sys_sem
     libc.include.sys_socket
     libc.include.sys_stat
     libc.include.sys_statvfs
diff --git a/libc/hdr/CMakeLists.txt b/libc/hdr/CMakeLists.txt
index 20d210c0c443f..2d7c83d3ab6cd 100644
--- a/libc/hdr/CMakeLists.txt
+++ b/libc/hdr/CMakeLists.txt
@@ -149,6 +149,24 @@ add_proxy_header_library(
     libc.include.llvm-libc-macros.sys_ioctl_macros
 )
 
+add_proxy_header_library(
+  sys_ipc_macros
+  HDRS
+    sys_ipc_macros.h
+  FULL_BUILD_DEPENDS
+    libc.include.sys_ipc
+    libc.include.llvm-libc-macros.sys_ipc_macros
+)
+
+add_proxy_header_library(
+  sys_sem_macros
+  HDRS
+    sys_sem_macros.h
+  FULL_BUILD_DEPENDS
+    libc.include.sys_sem
+    libc.include.llvm-libc-macros.sys_sem_macros
+)
+
 add_proxy_header_library(
   sys_socket_macros
   HDRS
diff --git a/libc/hdr/sys_ipc_macros.h b/libc/hdr/sys_ipc_macros.h
new file mode 100644
index 0000000000000..f17e02be92e35
--- /dev/null
+++ b/libc/hdr/sys_ipc_macros.h
@@ -0,0 +1,23 @@
+//===-- Definition of macros from sys/ipc.h
+//--------------------------------===//
+//
+// 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_HDR_SYS_IPC_MACROS_H
+#define LLVM_LIBC_HDR_SYS_IPC_MACROS_H
+
+#ifdef LIBC_FULL_BUILD
+
+#include "include/llvm-libc-macros/sys-ipc-macros.h"
+
+#else // Overlay mode
+
+#include <sys/ipc.h>
+
+#endif // LLVM_LIBC_FULL_BUILD
+
+#endif // LLVM_LIBC_HDR_SYS_IPC_MACROS_H
diff --git a/libc/hdr/sys_sem_macros.h b/libc/hdr/sys_sem_macros.h
new file mode 100644
index 0000000000000..fa60aec969f5f
--- /dev/null
+++ b/libc/hdr/sys_sem_macros.h
@@ -0,0 +1,23 @@
+//===-- Definition of macros from sys/sem.h
+//--------------------------------===//
+//
+// 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_HDR_SYS_SEM_MACROS_H
+#define LLVM_LIBC_HDR_SYS_SEM_MACROS_H
+
+#ifdef LIBC_FULL_BUILD
+
+#include "include/llvm-libc-macros/sys-sem-macros.h"
+
+#else // Overlay mode
+
+#include <sys/sem.h>
+
+#endif // LLVM_LIBC_FULL_BUILD
+
+#endif // LLVM_LIBC_HDR_SYS_SEM_MACROS_H
diff --git a/libc/hdr/types/CMakeLists.txt b/libc/hdr/types/CMakeLists.txt
index 1832f906d774c..e0653a1a5e834 100644
--- a/libc/hdr/types/CMakeLists.txt
+++ b/libc/hdr/types/CMakeLists.txt
@@ -498,6 +498,33 @@ add_proxy_header_library(
     libc.include.sys_resource
 )
 
+add_proxy_header_library(
+  struct_semid_ds
+  HDRS
+    struct_semid_ds.h
+  FULL_BUILD_DEPENDS
+    libc.include.llvm-libc-types.struct_semid_ds
+    libc.include.sys_sem
+)
+
+add_proxy_header_library(
+  struct_sembuf
+  HDRS
+    struct_sembuf.h
+  FULL_BUILD_DEPENDS
+    libc.include.llvm-libc-types.struct_sembuf
+    libc.include.sys_sem
+)
+
+add_proxy_header_library(
+  struct_seminfo
+  HDRS
+    struct_seminfo.h
+  FULL_BUILD_DEPENDS
+    libc.include.llvm-libc-types.struct_seminfo
+    libc.include.sys_sem
+)
+
 add_proxy_header_library(
   gid_t
   HDRS
diff --git a/libc/hdr/types/struct_sembuf.h b/libc/hdr/types/struct_sembuf.h
new file mode 100644
index 0000000000000..d5efcbaa01526
--- /dev/null
+++ b/libc/hdr/types/struct_sembuf.h
@@ -0,0 +1,22 @@
+//===-- Proxy for struct sembuf -------------------------------------------===//
+//
+// 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_HDR_TYPES_STRUCT_SEMBUF_H
+#define LLVM_LIBC_HDR_TYPES_STRUCT_SEMBUF_H
+
+#ifdef LIBC_FULL_BUILD
+
+#include "include/llvm-libc-types/struct_sembuf.h"
+
+#else // Overlay mode
+
+#include <sys/sem.h>
+
+#endif // LLVM_LIBC_FULL_BUILD
+
+#endif // LLVM_LIBC_HDR_TYPES_STRUCT_SEMBUF_H
diff --git a/libc/hdr/types/struct_semid_ds.h b/libc/hdr/types/struct_semid_ds.h
new file mode 100644
index 0000000000000..f4d8b36362d41
--- /dev/null
+++ b/libc/hdr/types/struct_semid_ds.h
@@ -0,0 +1,22 @@
+//===-- Proxy for struct semid_ds -----------------------------------------===//
+//
+// 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_HDR_TYPES_STRUCT_SEMID_DS_H
+#define LLVM_LIBC_HDR_TYPES_STRUCT_SEMID_DS_H
+
+#ifdef LIBC_FULL_BUILD
+
+#include "include/llvm-libc-types/struct_semid_ds.h"
+
+#else // Overlay mode
+
+#include <sys/sem.h>
+
+#endif // LLVM_LIBC_FULL_BUILD
+
+#endif // LLVM_LIBC_HDR_TYPES_STRUCT_SEMID_DS_H
diff --git a/libc/hdr/types/struct_seminfo.h b/libc/hdr/types/struct_seminfo.h
new file mode 100644
index 0000000000000..86259425712da
--- /dev/null
+++ b/libc/hdr/types/struct_seminfo.h
@@ -0,0 +1,22 @@
+//===-- Proxy for struct seminfo ------------------------------------------===//
+//
+// 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_HDR_TYPES_STRUCT_SEMINFO_H
+#define LLVM_LIBC_HDR_TYPES_STRUCT_SEMINFO_H
+
+#ifdef LIBC_FULL_BUILD
+
+#include "include/llvm-libc-types/struct_seminfo.h"
+
+#else // Overlay mode
+
+#include <sys/sem.h>
+
+#endif // LLVM_LIBC_FULL_BUILD
+
+#endif // LLVM_LIBC_HDR_TYPES_STRUCT_SEMINFO_H
diff --git a/libc/include/CMakeLists.txt b/libc/include/CMakeLists.txt
index 94c663c7d3917..5398d53806395 100644
--- a/libc/include/CMakeLists.txt
+++ b/libc/include/CMakeLists.txt
@@ -657,6 +657,21 @@ add_header_macro(
     .llvm-libc-types.struct_timeval
 )
 
+add_header_macro(
+  sys_sem
+  ../libc/include/sys/sem.yaml
+  sys/sem.h
+  DEPENDS
+    .llvm_libc_common_h
+    .llvm-libc-macros.sys_sem_macros
+    .llvm-libc-types.key_t
+    .llvm-libc-types.size_t
+    .llvm-libc-types.struct_ipc_perm
+    .llvm-libc-types.struct_semid_ds
+    .llvm-libc-types.struct_sembuf
+    .llvm-libc-types.struct_seminfo
+)
+
 add_header_macro(
   sys_sendfile
   ../libc/include/sys/sendfile.yaml
diff --git a/libc/include/llvm-libc-macros/CMakeLists.txt b/libc/include/llvm-libc-macros/CMakeLists.txt
index 553879e27d3d2..6a91b394a8786 100644
--- a/libc/include/llvm-libc-macros/CMakeLists.txt
+++ b/libc/include/llvm-libc-macros/CMakeLists.txt
@@ -232,6 +232,12 @@ add_macro_header(
     sys-ipc-macros.h
 )
 
+add_macro_header(
+  sys_sem_macros
+  HDR
+    sys-sem-macros.h
+)
+
 add_macro_header(
   sys_stat_macros
   HDR
diff --git a/libc/include/llvm-libc-macros/linux/CMakeLists.txt b/libc/include/llvm-libc-macros/linux/CMakeLists.txt
index 60376641b50f6..e6a95457a5b91 100644
--- a/libc/include/llvm-libc-macros/linux/CMakeLists.txt
+++ b/libc/include/llvm-libc-macros/linux/CMakeLists.txt
@@ -46,6 +46,12 @@ add_header(
     sys-random-macros.h
 )
 
+add_header(
+  sys_sem_macros
+  HDR
+    sys-sem-macros.h
+)
+
 add_header(
   sys_socket_macros
   HDR
diff --git a/libc/include/llvm-libc-macros/linux/sys-ipc-macros.h b/libc/include/llvm-libc-macros/linux/sys-ipc-macros.h
index 87546ac314d83..6c8443cef5b3c 100644
--- a/libc/include/llvm-libc-macros/linux/sys-ipc-macros.h
+++ b/libc/include/llvm-libc-macros/linux/sys-ipc-macros.h
@@ -20,5 +20,6 @@
 #define IPC_RMID 0
 #define IPC_SET 1
 #define IPC_STAT 2
+#define IPC_INFO 3
 
 #endif // LLVM_LIBC_MACROS_LINUX_SYS_IPC_MACROS_H
diff --git a/libc/include/llvm-libc-macros/linux/sys-sem-macros.h b/libc/include/llvm-libc-macros/linux/sys-sem-macros.h
new file mode 100644
index 0000000000000..1ca62e6b1e29d
--- /dev/null
+++ b/libc/include/llvm-libc-macros/linux/sys-sem-macros.h
@@ -0,0 +1,29 @@
+//===-- Definition of macros from sys/sem.h -------------------------------===//
+//
+// 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_MACROS_LINUX_SYS_SEM_MACROS_H
+#define LLVM_LIBC_MACROS_LINUX_SYS_SEM_MACROS_H
+
+// semop flags
+#define SEM_UNDO 0x1000
+
+// semctl command definitions
+#define GETPID 11
+#define GETVAL 12
+#define GETALL 13
+#define GETNCNT 14
+#define GETZCNT 15
+#define SETVAL 16
+#define SETALL 17
+
+// linux specific extensions
+#define SEM_STAT 18
+#define SEM_INFO 19
+#define SEM_STAT_ANY 20
+
+#endif // LLVM_LIBC_MACROS_LINUX_SYS_SEM_MACROS_H
diff --git a/libc/include/llvm-libc-macros/sys-sem-macros.h b/libc/include/llvm-libc-macros/sys-sem-macros.h
new file mode 100644
index 0000000000000..801f82e79f5d4
--- /dev/null
+++ b/libc/include/llvm-libc-macros/sys-sem-macros.h
@@ -0,0 +1,16 @@
+//===-- Macros defined in sys/sem.h header file ---------------------------===//
+//
+// 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_MACROS_SYS_SEM_MACROS_H
+#define LLVM_LIBC_MACROS_SYS_SEM_MACROS_H
+
+#ifdef __linux__
+#include "linux/sys-sem-macros.h"
+#endif
+
+#endif // LLVM_LIBC_MACROS_SYS_SEM_MACROS_H
diff --git a/libc/include/llvm-libc-types/CMakeLists.txt b/libc/include/llvm-libc-types/CMakeLists.txt
index eb8356cbfb8bb..88555405a7daa 100644
--- a/libc/include/llvm-libc-types/CMakeLists.txt
+++ b/libc/include/llvm-libc-types/CMakeLists.txt
@@ -92,6 +92,15 @@ add_header(
 add_header(struct_pollfd HDR struct_pollfd.h)
 add_header(struct_rlimit HDR struct_rlimit.h DEPENDS .rlim_t)
 add_header(struct_sched_param HDR struct_sched_param.h)
+add_header(struct_sembuf HDR struct_sembuf.h)
+add_header(struct_seminfo HDR struct_seminfo.h)
+add_header(
+  struct_semid_ds
+  HDR struct_semid_ds.h
+  DEPENDS
+    .struct_ipc_perm
+    .time_t
+)
 add_header(struct_timeval HDR struct_timeval.h DEPENDS .suseconds_t .time_t)
 add_header(struct_itimerval HDR struct_itimerval.h DEPENDS .struct_timeval)
 add_header(struct_rusage HDR struct_rusage.h DEPENDS .struct_timeval)
diff --git a/libc/include/llvm-libc-types/struct_sembuf.h b/libc/include/llvm-libc-types/struct_sembuf.h
new file mode 100644
index 0000000000000..4f1171698acc6
--- /dev/null
+++ b/libc/include/llvm-libc-types/struct_sembuf.h
@@ -0,0 +1,18 @@
+//===-- Definition of struct sembuf ---------------------------------------===//
+//
+// 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_TYPES_STRUCT_SEMBUF_H
+#define LLVM_LIBC_TYPES_STRUCT_SEMBUF_H
+
+struct sembuf {
+  unsigned short sem_num;
+  short sem_op;
+  short sem_flg;
+};
+
+#endif // LLVM_LIBC_TYPES_STRUCT_SEMBUF_H
diff --git a/libc/include/llvm-libc-types/struct_semid_ds.h b/libc/include/llvm-libc-types/struct_semid_ds.h
new file mode 100644
index 0000000000000..775f52604b0a3
--- /dev/null
+++ b/libc/include/llvm-libc-types/struct_semid_ds.h
@@ -0,0 +1,33 @@
+//===-- Definition of struct semid_ds -------------------------------------===//
+//
+// 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_TYPES_STRUCT_SEMID_DS_H
+#define LLVM_LIBC_TYPES_STRUCT_SEMID_DS_H
+
+#include "struct_ipc_perm.h"
+#include "time_t.h"
+
+struct semid_ds {
+  struct ipc_perm sem_perm;
+#ifdef __linux__
+  time_t sem_otime;
+  unsigned long __unused1;
+  time_t sem_ctime;
+  unsigned long __unused2;
+#else
+  time_t sem_otime;
+  time_t sem_ctime;
+#endif
+  unsigned long sem_nsems;
+#ifdef __linux__
+  unsigned long __unused3;
+  unsigned long __unused4;
+#endif
+};
+
+#endif // LLVM_LIBC_TYPES_STRUCT_SEMID_DS_H
diff --git a/libc/include/llvm-libc-types/struct_seminfo.h b/libc/include/llvm-libc-types/struct_seminfo.h
new file mode 100644
index 0000000000000..90610ac828a7f
--- /dev/null
+++ b/libc/include/llvm-libc-types/struct_seminfo.h
@@ -0,0 +1,27 @@
+//===-- Definition of struct seminfo --------------------------------------===//
+//
+// 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_TYPES_STRUCT_SEMINFO_H
+#define LLVM_LIBC_TYPES_STRUCT_SEMINFO_H
+
+#ifdef __linux__
+struct seminfo {
+  int semmap;
+  int semmni;
+  int semmns;
+  int semmnu;
+  int semmsl;
+  int semopm;
+  int semume;
+  int semusz;
+  int semvmx;
+  int semaem;
+};
+#endif
+
+#endif // LLVM_LIBC_TYPES_STRUCT_SEMINFO_H
diff --git a/libc/include/sys/sem.yaml b/libc/include/sys/sem.yaml
new file mode 100644
index 0000000000000..6e583d669d250
--- /dev/null
+++ b/libc/include/sys/sem.yaml
@@ -0,0 +1,84 @@
+header: sys/sem.h
+standards:
+  - posix
+  - linux
+macros:
+  - macro_name: SEM_UNDO
+    macro_header: sys-sem-macros.h
+    standards:
+      - posix
+  - macro_name: GETNCNT
+    macro_header: sys-sem-macros.h
+    standards:
+      - posix
+  - macro_name: GETPID
+    macro_header: sys-sem-macros.h
+    standards:
+      - posix
+  - macro_name: GETVAL
+    macro_header: sys-sem-macros.h
+    standards:
+      - posix
+  - macro_name: GETALL
+    macro_header: sys-sem-macros.h
+    standards:
+      - posix
+  - macro_name: GETZCNT
+    macro_header: sys-sem-macros.h
+    standards:
+      - posix
+  - macro_name: SETVAL
+    macro_header: sys-sem-macros.h
+    standards:
+      - posix
+  - macro_name: SETALL
+    macro_header: sys-sem-macros.h
+    standards:
+      - posix
+  - macro_name: SEM_STAT
+    macro_header: sys-sem-macros.h
+    standards:
+      - linux
+  - macro_name: SEM_INFO
+    macro_header: sys-sem-macros.h
+    standards:
+      - linux
+  - macro_name: SEM_STAT_ANY
+    macro_header: sys-sem-macros.h
+    standards:
+      - linux
+types:
+  - type_name: key_t
+  - type_name: size_t
+  - type_name: struct_ipc_perm
+  - type_name: struct_semid_ds
+  - type_name: struct_sembuf
+  - type_name: struct_seminfo
+    standards:
+      - linux
+functions:
+  - name: semctl
+    standards:
+      - posix
+    return_type: int
+    arguments:
+      - type: int
+      - type: int
+      - type: int
+      - type: '...'
+  - name: semget
+    standards:
+      - posix
+    return_type: int
+    arguments:
+      - type: key_t
+      - type: int
+      - type: int
+  - name: semop
+    standards:
+      - posix
+    return_type: int
+    arguments:
+      - type: int
+      - type: struct sembuf *
+      - type: size_t
diff --git a/libc/src/sys/CMakeLists.txt b/libc/src/sys/CMakeLists.txt
index 1c17ec31a8592..41becce798ddd 100644
--- a/libc/src/sys/CMakeLists.txt
+++ b/libc/src/sys/CMakeLists.txt
@@ -6,6 +6,7 @@ add_subdirectory(random)
 add_subdirectory(resource)
 add_subdirectory(select)
 add_subdirectory(socket)
+add_subdirectory(sem)
 add_subdirectory(sendfile)
 add_subdirectory(stat)
 add_subdirectory(statvfs)
diff --git a/libc/src/sys/sem/CMakeLists.txt b/libc/src/sys/sem/CMakeLists.txt
new file mode 100644
index 0000000000000..4b9bd851b9d2d
--- /dev/null
+++ b/libc/src/sys/sem/CMakeLists.txt
@@ -0,0 +1,24 @@
+if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS})
+  add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS})
+endif()
+
+add_entrypoint_object(
+  semget
+  ALIAS
+  DEPENDS
+    .${LIBC_TARGET_OS}.semget
+)
+
+add_entrypoint_object(
+  semctl
+  ALIAS
+  DEPENDS
+    .${LIBC_TARGET_OS}.semctl
+)
+
+add_entrypoint_object(
+  semop
+  ALIAS
+  DEPENDS
+    .${LIBC_TARGET_OS}.semop
+)
diff --git a/libc/src/sys/sem/linux/CMakeLists.txt b/libc/src/sys/sem/linux/CMakeLists.txt
new file mode 100644
index 0000000000000..0be957ab660d0
--- /dev/null
+++ b/libc/src/sys/sem/linux/CMakeLists.txt
@@ -0,0 +1,46 @@
+add_entrypoint_object(
+  semget
+  SRCS
+    semget.cpp
+  HDRS
+    ../semget.h
+  DEPENDS
+    libc.hdr.types.key_t
+    libc.include.sys_syscall
+    libc.src.__support.OSUtil.osutil
+    libc.src.__support.common
+    libc.src.errno.errno
+)
+
+add_entrypoint_object(
+  semctl
+  SRCS
+    semctl.cpp
+  HDRS
+    ../semctl.h
+  DEPENDS
+    libc.hdr.errno_macros
+    libc.hdr.sys_ipc_macros
+    libc.hdr.sys_sem_macros
+    libc.hdr.types.struct_semid_ds
+    libc.hdr.types.struct_seminfo
+    libc.include.sys_syscall
+    libc.src.__support.OSUtil.osutil
+    libc.src.__support.common
+    libc.src.errno.errno
+)
+
+add_entrypoint_object(
+  semop
+  SRCS
+    semop.cpp
+  HDRS
+    ../semop.h
+  DEPENDS
+    libc.hdr.types.size_t
+    libc.hdr.types.struct_sembuf
+    libc.include.sys_syscall
+    libc.src.__support.OSUtil.osutil
+    libc.src.__support.common
+    libc.src.errno.errno
+)
diff --git a/libc/src/sys/sem/linux/semctl.cpp b/libc/src/sys/sem/linux/semctl.cpp
new file mode 100644
index 0000000000000..91b27cc797b2e
--- /dev/null
+++ b/libc/src/sys/sem/linux/semctl.cpp
@@ -0,0 +1,86 @@
+//===-- Linux implementation of semctl ------------------------------------===//
+//
+// 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/sem/semctl.h"
+
+#include "hdr/errno_macros.h"
+#include "hdr/sys_ipc_macros.h"
+#include "hdr/sys_sem_macros.h"
+#include "hdr/types/struct_semid_ds.h"
+#include "hdr/types/struct_seminfo.h"
+#include "src/__support/OSUtil/syscall.h"
+#include "src/__support/common.h"
+#include "src/__support/libc_errno.h"
+#include <stdarg.h>
+#include <sys/syscall.h>
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, semctl, (int semid, int semnum, int cmd, ...)) {
+  unsigned long cmd_arg = 0;
+
+  switch (cmd) {
+  case IPC_RMID:
+  case GETVAL:
+  case GETPID:
+  case GETNCNT:
+  case GETZCNT:
+    break;
+
+  case SETVAL: {
+    va_list vargs;
+    va_start(vargs, cmd);
+    cmd_arg = static_cast<unsigned long>(va_arg(vargs, int));
+    va_end(vargs);
+    break;
+  }
+
+  case IPC_SET:
+  case IPC_STAT:
+  case SEM_STAT:
+  case SEM_STAT_ANY: {
+    va_list vargs;
+    va_start(vargs, cmd);
+    cmd_arg = reinterpret_cast<unsigned long>(va_arg(vargs, struct semid_ds *));
+    va_end(vargs);
+    break;
+  }
+
+  case GETALL:
+  case SETALL: {
+    va_list vargs;
+    va_start(vargs, cmd);
+    cmd_arg = reinterpret_cast<unsigned long>(va_arg(vargs, unsigned short *));
+    va_end(vargs);
+    break;
+  }
+
+  case IPC_INFO:
+  case SEM_INFO: {
+    va_list vargs;
+    va_start(vargs, cmd);
+    cmd_arg = reinterpret_cast<unsigned long>(va_arg(vargs, struct seminfo *));
+    va_end(vargs);
+    break;
+  }
+
+  default:
+    libc_errno = EINVAL;
+    return -1;
+  }
+
+  int ret = LIBC_NAMESPACE::syscall_impl<int>(SYS_semctl, semid, semnum, cmd,
+                                              cmd_arg);
+  if (ret < 0) {
+    libc_errno = -ret;
+    return -1;
+  }
+  return ret;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/sys/sem/linux/semget.cpp b/libc/src/sys/sem/linux/semget.cpp
new file mode 100644
index 0000000000000..b5a69056ccbd8
--- /dev/null
+++ b/libc/src/sys/sem/linux/semget.cpp
@@ -0,0 +1,27 @@
+//===-- Linux implementation of semget ------------------------------------===//
+//
+// 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/sem/semget.h"
+
+#include "src/__support/OSUtil/syscall.h"
+#include "src/__support/common.h"
+#include "src/__support/libc_errno.h"
+#include <sys/syscall.h>
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, semget, (key_t key, int nsems, int semflg)) {
+  int ret = LIBC_NAMESPACE::syscall_impl<int>(SYS_semget, key, nsems, semflg);
+  if (ret < 0) {
+    libc_errno = -ret;
+    return -1;
+  }
+  return ret;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/sys/sem/linux/semop.cpp b/libc/src/sys/sem/linux/semop.cpp
new file mode 100644
index 0000000000000..97d7d12daf898
--- /dev/null
+++ b/libc/src/sys/sem/linux/semop.cpp
@@ -0,0 +1,27 @@
+//===-- Linux implementation of semop -------------------------------------===//
+//
+// 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/sem/semop.h"
+
+#include "src/__support/OSUtil/syscall.h"
+#include "src/__support/common.h"
+#include "src/__support/libc_errno.h"
+#include <sys/syscall.h>
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, semop, (int semid, struct sembuf *sops, size_t nsops)) {
+  int ret = LIBC_NAMESPACE::syscall_impl<int>(SYS_semop, semid, sops, nsops);
+  if (ret < 0) {
+    libc_errno = -ret;
+    return -1;
+  }
+  return ret;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/sys/sem/semctl.h b/libc/src/sys/sem/semctl.h
new file mode 100644
index 0000000000000..78d689d62f3a5
--- /dev/null
+++ b/libc/src/sys/sem/semctl.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for semctl -------------------------*- 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_SEM_SEMCTL_H
+#define LLVM_LIBC_SRC_SYS_SEM_SEMCTL_H
+
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+int semctl(int semid, int semnum, int cmd, ...);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_SYS_SEM_SEMCTL_H
diff --git a/libc/src/sys/sem/semget.h b/libc/src/sys/sem/semget.h
new file mode 100644
index 0000000000000..76972dcedeeaa
--- /dev/null
+++ b/libc/src/sys/sem/semget.h
@@ -0,0 +1,22 @@
+//===-- Implementation header for semget -------------------------*- 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_SEM_SEMGET_H
+#define LLVM_LIBC_SRC_SYS_SEM_SEMGET_H
+
+#include "hdr/types/key_t.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+int semget(key_t key, int nsems, int semflg);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_SYS_SEM_SEMGET_H
diff --git a/libc/src/sys/sem/semop.h b/libc/src/sys/sem/semop.h
new file mode 100644
index 0000000000000..0c05f5cc54402
--- /dev/null
+++ b/libc/src/sys/sem/semop.h
@@ -0,0 +1,23 @@
+//===-- Implementation header for semop --------------------------*- 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_SEM_SEMOP_H
+#define LLVM_LIBC_SRC_SYS_SEM_SEMOP_H
+
+#include "hdr/types/size_t.h"
+#include "hdr/types/struct_sembuf.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+int semop(int semid, struct sembuf *sops, size_t nsops);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_SYS_SEM_SEMOP_H
diff --git a/libc/test/src/sys/CMakeLists.txt b/libc/test/src/sys/CMakeLists.txt
index 3b5ea64fb1448..444f86f5d00a9 100644
--- a/libc/test/src/sys/CMakeLists.txt
+++ b/libc/test/src/sys/CMakeLists.txt
@@ -15,3 +15,4 @@ add_subdirectory(ipc)
 add_subdirectory(uio)
 add_subdirectory(time)
 add_subdirectory(ioctl)
+add_subdirectory(sem)
diff --git a/libc/test/src/sys/sem/CMakeLists.txt b/libc/test/src/sys/sem/CMakeLists.txt
new file mode 100644
index 0000000000000..b4bbe81c92ff2
--- /dev/null
+++ b/libc/test/src/sys/sem/CMakeLists.txt
@@ -0,0 +1,3 @@
+if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS})
+  add_subdirectory(${LIBC_TARGET_OS})
+endif()
diff --git a/libc/test/src/sys/sem/linux/CMakeLists.txt b/libc/test/src/sys/sem/linux/CMakeLists.txt
new file mode 100644
index 0000000000000..7713c3c927b92
--- /dev/null
+++ b/libc/test/src/sys/sem/linux/CMakeLists.txt
@@ -0,0 +1,20 @@
+add_custom_target(libc_sys_sem_unittests)
+
+add_libc_unittest(
+  sem_test
+  SUITE
+    libc_sys_sem_unittests
+  SRCS
+    sem_test.cpp
+  DEPENDS
+    libc.hdr.sys_ipc_macros
+    libc.hdr.sys_sem_macros
+    libc.hdr.types.struct_sembuf
+    libc.hdr.types.struct_semid_ds
+    libc.src.errno.errno
+    libc.src.sys.sem.semget
+    libc.src.sys.sem.semctl
+    libc.src.sys.sem.semop
+    libc.test.UnitTest.ErrnoCheckingTest
+    libc.test.UnitTest.ErrnoSetterMatcher
+)
diff --git a/libc/test/src/sys/sem/linux/sem_test.cpp b/libc/test/src/sys/sem/linux/sem_test.cpp
new file mode 100644
index 0000000000000..0e21189f9bfbf
--- /dev/null
+++ b/libc/test/src/sys/sem/linux/sem_test.cpp
@@ -0,0 +1,72 @@
+//===-- Unittests for sys/sem.h entrypoints ------------------------------===//
+//
+// 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_ipc_macros.h"
+#include "hdr/sys_sem_macros.h"
+#include "hdr/types/struct_sembuf.h"
+#include "hdr/types/struct_semid_ds.h"
+#include "src/sys/sem/semctl.h"
+#include "src/sys/sem/semget.h"
+#include "src/sys/sem/semop.h"
+#include "test/UnitTest/ErrnoCheckingTest.h"
+#include "test/UnitTest/ErrnoSetterMatcher.h"
+#include "test/UnitTest/Test.h"
+
+using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Succeeds;
+using LlvmLibcSysSemTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
+
+union semun {
+  int val;
+  struct semid_ds *buf;
+  unsigned short *array;
+};
+
+TEST_F(LlvmLibcSysSemTest, SemgetSemctlSemopFlow) {
+
+  // create a semaphore
+  int semid =
+      LIBC_NAMESPACE::semget(IPC_PRIVATE, 1, IPC_CREAT | IPC_EXCL | 0600);
+  ASSERT_ERRNO_SUCCESS();
+  ASSERT_GT(semid, -1);
+
+  union semun set_val;
+  set_val.val = 1;
+
+  // set the semaphore value to 1
+  ASSERT_THAT(LIBC_NAMESPACE::semctl(semid, 0, SETVAL, set_val), Succeeds(0));
+
+  // get the value of semaphore should be 1
+  ASSERT_THAT(LIBC_NAMESPACE::semctl(semid, 0, GETVAL), Succeeds(1));
+
+  // decrement the semaphore value with IPC_NOWAIT flag
+  struct sembuf decrement_op = {0, -1, IPC_NOWAIT};
+  ASSERT_THAT(LIBC_NAMESPACE::semop(semid, &decrement_op, 1), Succeeds(0));
+
+  // get the value of semaphore should be 0
+  ASSERT_THAT(LIBC_NAMESPACE::semctl(semid, 0, GETVAL), Succeeds(0));
+
+  // increment the semaphore with IPC_NOWAIT flag
+  struct sembuf increment_op = {0, 1, IPC_NOWAIT};
+  ASSERT_THAT(LIBC_NAMESPACE::semop(semid, &increment_op, 1), Succeeds(0));
+
+  // get the semaphore value should be 1
+  ASSERT_THAT(LIBC_NAMESPACE::semctl(semid, 0, GETVAL), Succeeds(1));
+
+  // get the IPC stats
+  struct semid_ds sem_ds;
+  union semun stat_arg;
+  stat_arg.buf = &sem_ds;
+  ASSERT_THAT(LIBC_NAMESPACE::semctl(semid, 0, IPC_STAT, stat_arg),
+              Succeeds(0));
+
+  // the number of sem is 1
+  ASSERT_EQ(sem_ds.sem_nsems, 1UL);
+
+  // destroy the semaphore
+  ASSERT_THAT(LIBC_NAMESPACE::semctl(semid, 0, IPC_RMID), Succeeds(0));
+}

>From e1fd951f506f0378574a974f0535789f9a699cca Mon Sep 17 00:00:00 2001
From: Pengxiang Huang <huangpengxiang70 at gmail.com>
Date: Wed, 11 Mar 2026 12:34:26 -0400
Subject: [PATCH 2/5] format some files

---
 libc/src/sys/sem/semctl.h | 3 +--
 libc/src/sys/sem/semget.h | 3 +--
 libc/src/sys/sem/semop.h  | 3 +--
 3 files changed, 3 insertions(+), 6 deletions(-)

diff --git a/libc/src/sys/sem/semctl.h b/libc/src/sys/sem/semctl.h
index 78d689d62f3a5..b7046edba18b9 100644
--- a/libc/src/sys/sem/semctl.h
+++ b/libc/src/sys/sem/semctl.h
@@ -1,5 +1,4 @@
-//===-- Implementation header for semctl -------------------------*- C++
-//-*-===//
+//===-- Implementation header for semctl ------------------------*- C++ -*-===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
diff --git a/libc/src/sys/sem/semget.h b/libc/src/sys/sem/semget.h
index 76972dcedeeaa..7730b21eb0f7d 100644
--- a/libc/src/sys/sem/semget.h
+++ b/libc/src/sys/sem/semget.h
@@ -1,5 +1,4 @@
-//===-- Implementation header for semget -------------------------*- C++
-//-*-===//
+//===-- Implementation header for semget ------------------------*- C++ -*-===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
diff --git a/libc/src/sys/sem/semop.h b/libc/src/sys/sem/semop.h
index 0c05f5cc54402..c1af69bd9218e 100644
--- a/libc/src/sys/sem/semop.h
+++ b/libc/src/sys/sem/semop.h
@@ -1,5 +1,4 @@
-//===-- Implementation header for semop --------------------------*- C++
-//-*-===//
+//===-- Implementation header for semop -------------------------*- C++ -*-===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.

>From 94ea81a4eeee9fee042ae263ad094f0b17417e5a Mon Sep 17 00:00:00 2001
From: Pengxiang Huang <huangpengxiang70 at gmail.com>
Date: Thu, 12 Mar 2026 15:15:30 -0400
Subject: [PATCH 3/5] format files and add comments

---
 libc/hdr/sys_ipc_macros.h         |  3 +--
 libc/hdr/sys_sem_macros.h         |  3 +--
 libc/src/sys/sem/linux/semctl.cpp | 15 +++++++++++++++
 3 files changed, 17 insertions(+), 4 deletions(-)

diff --git a/libc/hdr/sys_ipc_macros.h b/libc/hdr/sys_ipc_macros.h
index f17e02be92e35..f62ae4b6ed259 100644
--- a/libc/hdr/sys_ipc_macros.h
+++ b/libc/hdr/sys_ipc_macros.h
@@ -1,5 +1,4 @@
-//===-- Definition of macros from sys/ipc.h
-//--------------------------------===//
+//===-- Definition of macros from sys/ipc.h -------------------------------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
diff --git a/libc/hdr/sys_sem_macros.h b/libc/hdr/sys_sem_macros.h
index fa60aec969f5f..1082840ea5983 100644
--- a/libc/hdr/sys_sem_macros.h
+++ b/libc/hdr/sys_sem_macros.h
@@ -1,5 +1,4 @@
-//===-- Definition of macros from sys/sem.h
-//--------------------------------===//
+//===-- Definition of macros from sys/sem.h -------------------------------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
diff --git a/libc/src/sys/sem/linux/semctl.cpp b/libc/src/sys/sem/linux/semctl.cpp
index 91b27cc797b2e..1dd38f773004e 100644
--- a/libc/src/sys/sem/linux/semctl.cpp
+++ b/libc/src/sys/sem/linux/semctl.cpp
@@ -22,9 +22,19 @@
 namespace LIBC_NAMESPACE_DECL {
 
 LLVM_LIBC_FUNCTION(int, semctl, (int semid, int semnum, int cmd, ...)) {
+  // used to parse the fourth varargs argument
+  // its expected to be explicitly declared by application as an union type:
+  // union semun {
+  //  int val;
+  //  struct semid_ds *buf;
+  //  unsigned short  *array;
+  //  struct seminfo  *__buf; (* linux specific *)
+  // } arg;
   unsigned long cmd_arg = 0;
 
+  // parse cmd_arg based on the flags
   switch (cmd) {
+    // does not use the vargs
   case IPC_RMID:
   case GETVAL:
   case GETPID:
@@ -32,6 +42,7 @@ LLVM_LIBC_FUNCTION(int, semctl, (int semid, int semnum, int cmd, ...)) {
   case GETZCNT:
     break;
 
+    // use vargs as int, semun->val
   case SETVAL: {
     va_list vargs;
     va_start(vargs, cmd);
@@ -40,6 +51,7 @@ LLVM_LIBC_FUNCTION(int, semctl, (int semid, int semnum, int cmd, ...)) {
     break;
   }
 
+    // use vargs as semid_ds*, semun->buf
   case IPC_SET:
   case IPC_STAT:
   case SEM_STAT:
@@ -51,6 +63,7 @@ LLVM_LIBC_FUNCTION(int, semctl, (int semid, int semnum, int cmd, ...)) {
     break;
   }
 
+    // use vargs as short*, semun->array
   case GETALL:
   case SETALL: {
     va_list vargs;
@@ -60,6 +73,7 @@ LLVM_LIBC_FUNCTION(int, semctl, (int semid, int semnum, int cmd, ...)) {
     break;
   }
 
+    // linux specific, use vargs as seminfo*, semun->__buf
   case IPC_INFO:
   case SEM_INFO: {
     va_list vargs;
@@ -69,6 +83,7 @@ LLVM_LIBC_FUNCTION(int, semctl, (int semid, int semnum, int cmd, ...)) {
     break;
   }
 
+    // unrecognized flags
   default:
     libc_errno = EINVAL;
     return -1;

>From c906536ce9369fddbafb2f2859836b923804d48e Mon Sep 17 00:00:00 2001
From: Pengxiang Huang <huangpengxiang70 at gmail.com>
Date: Thu, 12 Mar 2026 16:21:31 -0400
Subject: [PATCH 4/5] add one more comment to indicate the historical abi
 change

---
 libc/include/llvm-libc-types/struct_sembuf.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libc/include/llvm-libc-types/struct_sembuf.h b/libc/include/llvm-libc-types/struct_sembuf.h
index 4f1171698acc6..8358effdf790b 100644
--- a/libc/include/llvm-libc-types/struct_sembuf.h
+++ b/libc/include/llvm-libc-types/struct_sembuf.h
@@ -10,6 +10,7 @@
 #define LLVM_LIBC_TYPES_STRUCT_SEMBUF_H
 
 struct sembuf {
+  // changed to unsigned short from short since POSIX issue 7
   unsigned short sem_num;
   short sem_op;
   short sem_flg;

>From e8103723ae7a1582a62e6de9c4d5b3cdcedf5501 Mon Sep 17 00:00:00 2001
From: Pengxiang Huang <huangpengxiang70 at gmail.com>
Date: Wed, 18 Mar 2026 20:06:14 -0400
Subject: [PATCH 5/5] add one integration test similar with semop example in
 posix issue 8

---
 libc/test/integration/src/CMakeLists.txt      |   1 +
 libc/test/integration/src/sys/CMakeLists.txt  |   1 +
 .../integration/src/sys/sem/CMakeLists.txt    |  29 +++
 .../test/integration/src/sys/sem/sem_test.cpp | 171 ++++++++++++++++++
 4 files changed, 202 insertions(+)
 create mode 100644 libc/test/integration/src/sys/CMakeLists.txt
 create mode 100644 libc/test/integration/src/sys/sem/CMakeLists.txt
 create mode 100644 libc/test/integration/src/sys/sem/sem_test.cpp

diff --git a/libc/test/integration/src/CMakeLists.txt b/libc/test/integration/src/CMakeLists.txt
index 1104b3de99e9b..47c0716dc22b4 100644
--- a/libc/test/integration/src/CMakeLists.txt
+++ b/libc/test/integration/src/CMakeLists.txt
@@ -4,4 +4,5 @@ add_subdirectory(spawn)
 add_subdirectory(stdio)
 add_subdirectory(stdlib)
 add_subdirectory(threads)
+add_subdirectory(sys)
 add_subdirectory(unistd)
diff --git a/libc/test/integration/src/sys/CMakeLists.txt b/libc/test/integration/src/sys/CMakeLists.txt
new file mode 100644
index 0000000000000..9a6b3cd220e86
--- /dev/null
+++ b/libc/test/integration/src/sys/CMakeLists.txt
@@ -0,0 +1 @@
+add_subdirectory(sem)
diff --git a/libc/test/integration/src/sys/sem/CMakeLists.txt b/libc/test/integration/src/sys/sem/CMakeLists.txt
new file mode 100644
index 0000000000000..286ccb72dae90
--- /dev/null
+++ b/libc/test/integration/src/sys/sem/CMakeLists.txt
@@ -0,0 +1,29 @@
+add_custom_target(sys-sem-integration-tests)
+add_dependencies(libc-integration-tests sys-sem-integration-tests)
+
+add_integration_test(
+  sem_test
+  SUITE
+    sys-sem-integration-tests
+  SRCS
+    sem_test.cpp
+  DEPENDS
+    libc.hdr.fcntl_macros
+    libc.hdr.sys_ipc_macros
+    libc.hdr.sys_sem_macros
+    libc.hdr.types.struct_sembuf
+    libc.src.__support.threads.sleep
+    libc.src.fcntl.open
+    libc.src.signal.kill
+    libc.src.stdlib.exit
+    libc.src.sys.ipc.ftok
+    libc.src.sys.sem.semctl
+    libc.src.sys.sem.semget
+    libc.src.sys.sem.semop
+    libc.src.sys.wait.waitpid
+    libc.src.unistd.close
+    libc.src.unistd.fork
+    libc.src.unistd.unlink
+    libc.include.signal
+    libc.include.sys_wait
+)
diff --git a/libc/test/integration/src/sys/sem/sem_test.cpp b/libc/test/integration/src/sys/sem/sem_test.cpp
new file mode 100644
index 0000000000000..0d8532df89bea
--- /dev/null
+++ b/libc/test/integration/src/sys/sem/sem_test.cpp
@@ -0,0 +1,171 @@
+//===-- Integration test for sys/sem --------------------------------------===//
+//
+// 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/__support/threads/sleep.h"
+#include "src/fcntl/open.h"
+#include "src/signal/kill.h"
+#include "src/stdlib/exit.h"
+#include "src/sys/ipc/ftok.h"
+#include "src/sys/sem/semctl.h"
+#include "src/sys/sem/semget.h"
+#include "src/sys/sem/semop.h"
+#include "src/sys/wait/waitpid.h"
+#include "src/unistd/close.h"
+#include "src/unistd/fork.h"
+#include "src/unistd/unlink.h"
+
+#include "test/IntegrationTest/test.h"
+
+#include "hdr/fcntl_macros.h"
+#include "hdr/sys_ipc_macros.h"
+#include "hdr/sys_sem_macros.h"
+#include "hdr/types/struct_sembuf.h"
+
+#include <errno.h>
+#include <signal.h>
+#include <sys/wait.h>
+
+// child try IPC_CREAT|IPC_EXCL,
+// but expect EEXIST then fall back to get the existing semaphore.
+static int sem_joiner_get(key_t semkey) {
+  // this expect to fail and return -1 because we used the same key
+  // and this semaphore has been initialized.
+  int sid = LIBC_NAMESPACE::semget(semkey, 1, IPC_CREAT | IPC_EXCL | 0666);
+  if (sid == -1 && errno == EEXIST)
+    // get the initialized semaphore id
+    sid = LIBC_NAMESPACE::semget(semkey, 0, 0);
+  return sid;
+}
+
+// fork a child that acquires the semaphore and hold untill killed.
+// returns child pid.
+static pid_t fork_sem_holder(key_t semkey) {
+  pid_t pid = LIBC_NAMESPACE::fork();
+  if (pid == 0) {
+    int sid = sem_joiner_get(semkey);
+    if (sid < 0)
+      LIBC_NAMESPACE::exit(1);
+    // try acquire and hold
+    // set flag to UNDO if process terminate unexpectedly
+    struct sembuf acq = {0, -1, SEM_UNDO};
+    if (LIBC_NAMESPACE::semop(sid, &acq, 1) != 0)
+      LIBC_NAMESPACE::exit(2);
+
+    // hold the semaphore until killed by parent.
+    while (1)
+      LIBC_NAMESPACE::sleep_briefly();
+  }
+  return pid;
+}
+
+// fork a child that tries to acquire (IPC_NOWAIT).
+// exit code: 0 = acquired, 1 = EAGAIN (full, try later again), 2+ = error.
+static pid_t fork_sem_try_acquire(key_t semkey) {
+  pid_t pid = LIBC_NAMESPACE::fork();
+  if (pid == 0) {
+    int sid = sem_joiner_get(semkey);
+    if (sid < 0)
+      LIBC_NAMESPACE::exit(3);
+    // try acquire
+    struct sembuf acq = {0, -1, SEM_UNDO | IPC_NOWAIT};
+
+    if (LIBC_NAMESPACE::semop(sid, &acq, 1) == 0)
+      LIBC_NAMESPACE::exit(0); // acquired
+
+    // exit with code 1 if EAGAIN
+    LIBC_NAMESPACE::exit(errno == EAGAIN ? 1 : 2);
+  }
+  return pid;
+}
+
+// wait for child to terminate.
+static int wait_for_child(pid_t pid) {
+  int status;
+  LIBC_NAMESPACE::waitpid(pid, &status, 0);
+  return status;
+}
+
+// semaphore gate that allows at most two processes to enter
+void sem_gate_test() {
+  constexpr const char *TEST_FILE = "/tmp/sem_gate_test";
+  int fd = LIBC_NAMESPACE::open(TEST_FILE, O_CREAT | O_WRONLY, 0666);
+  ASSERT_TRUE(fd >= 0);
+  ASSERT_ERRNO_SUCCESS();
+  ASSERT_TRUE(LIBC_NAMESPACE::close(fd) == 0);
+
+  key_t semkey = LIBC_NAMESPACE::ftok(TEST_FILE, 'a');
+  ASSERT_TRUE(semkey != key_t(-1));
+  ASSERT_ERRNO_SUCCESS();
+
+  // clean up stale semaphore from previous run because semaphore
+  // are kernel persistent, they can remain on the system if previous
+  // test fail and exit.
+  int old_semid = LIBC_NAMESPACE::semget(semkey, 1, 0);
+  if (old_semid != -1)
+    LIBC_NAMESPACE::semctl(old_semid, 0, IPC_RMID);
+  errno = 0;
+
+  // create a semaphore
+  int semid = LIBC_NAMESPACE::semget(semkey, 1, IPC_CREAT | IPC_EXCL | 0666);
+  ASSERT_TRUE(semid >= 0);
+  ASSERT_ERRNO_SUCCESS();
+
+  // increment the first semaphore by 2 that allows two processes to access
+  struct sembuf sbuf = {0, 2, 0};
+  ASSERT_TRUE(LIBC_NAMESPACE::semop(semid, &sbuf, 1) == 0);
+
+  // verify the first semaphore value
+  ASSERT_TRUE(LIBC_NAMESPACE::semctl(semid, 0, GETVAL) == 2);
+
+  // fork two children to hold the semaphore
+  pid_t holder1 = fork_sem_holder(semkey);
+  ASSERT_TRUE(holder1 > 0);
+  pid_t holder2 = fork_sem_holder(semkey);
+  ASSERT_TRUE(holder2 > 0);
+
+  // wait until both children have acquired so semaphore value reaches 0.
+  while (LIBC_NAMESPACE::semctl(semid, 0, GETVAL) > 0)
+    LIBC_NAMESPACE::sleep_briefly();
+
+  // check my current semaphore value is 0
+  ASSERT_TRUE(LIBC_NAMESPACE::semctl(semid, 0, GETVAL) == 0);
+
+  // now the semaphore gate is full
+  // fork another process to try to acquire it
+  pid_t blocked = fork_sem_try_acquire(semkey);
+  ASSERT_TRUE(blocked > 0);
+  int blocked_status = wait_for_child(blocked);
+  // check the exit status
+  ASSERT_TRUE(WIFEXITED(blocked_status));
+  ASSERT_EQ(WEXITSTATUS(blocked_status), 1); // EAGAIN (blocked)
+
+  // kill the first holder to make one slot avalaible in semaphore
+  LIBC_NAMESPACE::kill(holder1, SIGKILL);
+  wait_for_child(holder1);
+  ASSERT_TRUE(LIBC_NAMESPACE::semctl(semid, 0, GETVAL) == 1);
+
+  // then fork child again to try acquire
+  pid_t unblocked = fork_sem_try_acquire(semkey);
+  ASSERT_TRUE(unblocked > 0);
+  int unblocked_status = wait_for_child(unblocked);
+  // this child should get it since the slot is avalaible now
+  ASSERT_TRUE(WIFEXITED(unblocked_status));
+  ASSERT_EQ(WEXITSTATUS(unblocked_status), 0); // acquired
+
+  // cleanup
+  LIBC_NAMESPACE::kill(holder2, SIGKILL);
+  wait_for_child(holder2);
+  ASSERT_TRUE(LIBC_NAMESPACE::semctl(semid, 0, IPC_RMID) == 0);
+  ASSERT_TRUE(LIBC_NAMESPACE::unlink(TEST_FILE) == 0);
+}
+
+TEST_MAIN([[maybe_unused]] int argc, [[maybe_unused]] char **argv,
+          [[maybe_unused]] char **envp) {
+  sem_gate_test();
+  return 0;
+}



More information about the libc-commits mailing list