1 /* Copyright (C) 1995-2022 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
4 The GNU C Library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 2.1 of the License, or (at your option) any later version.
9 The GNU C Library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
14 You should have received a copy of the GNU Lesser General Public
15 License along with the GNU C Library; if not, see
16 <https://www.gnu.org/licenses/>. */
22 #include <shlib-compat.h>
23 #include <linux/posix_types.h> /* For __kernel_mode_t. */
25 /* The struct used to issue the syscall. For architectures that assume
26 64-bit time as default (!__ASSUME_TIME64_SYSCALLS) the syscall will
27 split the resulting 64-bit sem_{o,c}time in two fields (sem_{o,c}time
28 and __sem_{o,c}time_high). */
31 int val
; /* value for SETVAL */
32 struct semid_ds
*buf
; /* buffer for IPC_STAT & IPC_SET */
33 unsigned short int *array
; /* array for GETALL & SETALL */
34 struct seminfo
*__buf
; /* buffer for IPC_INFO */
38 # define semun64 semun
39 typedef union semun semctl_arg_t
;
41 # include <struct_kernel_semid64_ds.h>
46 struct kernel_semid64_ds
*buf
;
47 unsigned short int *array
;
48 struct seminfo
*__buf
;
52 # define semun64 semun
54 /* The struct used when __semctl64 is called. */
58 struct __semid64_ds
*buf
;
59 unsigned short int *array
;
60 struct seminfo
*__buf
;
65 semid64_to_ksemid64 (const struct __semid64_ds
*semid64
,
66 struct kernel_semid64_ds
*ksemid
)
68 ksemid
->sem_perm
= semid64
->sem_perm
;
69 ksemid
->sem_otime
= semid64
->sem_otime
;
70 ksemid
->sem_otime_high
= semid64
->sem_otime
>> 32;
71 ksemid
->sem_ctime
= semid64
->sem_ctime
;
72 ksemid
->sem_ctime_high
= semid64
->sem_ctime
>> 32;
73 ksemid
->sem_nsems
= semid64
->sem_nsems
;
77 ksemid64_to_semid64 (const struct kernel_semid64_ds
*ksemid
,
78 struct __semid64_ds
*semid64
)
80 semid64
->sem_perm
= ksemid
->sem_perm
;
81 semid64
->sem_otime
= ksemid
->sem_otime
82 | ((__time64_t
) ksemid
->sem_otime_high
<< 32);
83 semid64
->sem_ctime
= ksemid
->sem_ctime
84 | ((__time64_t
) ksemid
->sem_ctime_high
<< 32);
85 semid64
->sem_nsems
= ksemid
->sem_nsems
;
89 semun64_to_ksemun64 (int cmd
, union semun64 semun64
,
90 struct kernel_semid64_ds
*buf
)
92 union ksemun64 r
= { 0 };
100 r
.array
= semun64
.array
;
107 semid64_to_ksemid64 (semun64
.buf
, r
.buf
);
111 r
.__buf
= semun64
.__buf
;
117 typedef union ksemun64 semctl_arg_t
;
121 semctl_syscall (int semid
, int semnum
, int cmd
, semctl_arg_t arg
)
123 #ifdef __ASSUME_DIRECT_SYSVIPC_SYSCALLS
124 return INLINE_SYSCALL_CALL (semctl
, semid
, semnum
, cmd
| __IPC_64
,
127 return INLINE_SYSCALL_CALL (ipc
, IPCOP_semctl
, semid
, semnum
, cmd
| __IPC_64
,
128 SEMCTL_ARG_ADDRESS (arg
));
132 /* POSIX states ipc_perm mode should have type of mode_t. */
133 _Static_assert (sizeof ((struct semid_ds
){0}.sem_perm
.mode
)
135 "sizeof (msqid_ds.msg_perm.mode) != sizeof (mode_t)");
138 __semctl64 (int semid
, int semnum
, int cmd
, ...)
140 union semun64 arg64
= { 0 };
143 /* Get the argument only if required. */
146 case SETVAL
: /* arg.val */
147 case GETALL
: /* arg.array */
149 case IPC_STAT
: /* arg.buf */
153 case IPC_INFO
: /* arg.__buf */
156 arg64
= va_arg (ap
, union semun64
);
159 case IPC_RMID
: /* arg ignored. */
166 __set_errno (EINVAL
);
171 struct kernel_semid64_ds ksemid
;
172 union ksemun64 ksemun
= semun64_to_ksemun64 (cmd
, arg64
, &ksemid
);
173 # ifdef __ASSUME_SYSVIPC_BROKEN_MODE_T
175 ksemid
.sem_perm
.mode
*= 0x10000U
;
177 union ksemun64 arg
= ksemun
;
179 union semun arg
= arg64
;
182 int ret
= semctl_syscall (semid
, semnum
, cmd
, arg
);
191 #ifdef __ASSUME_SYSVIPC_BROKEN_MODE_T
192 arg
.buf
->sem_perm
.mode
>>= 16;
194 /* Old Linux kernel versions might not clear the mode padding. */
195 if (sizeof ((struct semid_ds
){0}.sem_perm
.mode
)
196 != sizeof (__kernel_mode_t
))
197 arg
.buf
->sem_perm
.mode
&= 0xFFFF;
201 ksemid64_to_semid64 (arg
.buf
, arg64
.buf
);
208 libc_hidden_def (__semctl64
)
211 /* The 64-bit time_t semid_ds version might have a different layout and
212 internal field alignment. */
215 semid_to_semid64 (struct __semid64_ds
*ds64
, const struct semid_ds
*ds
)
217 ds64
->sem_perm
= ds
->sem_perm
;
218 ds64
->sem_otime
= ds
->sem_otime
219 | ((__time64_t
) ds
->__sem_otime_high
<< 32);
220 ds64
->sem_ctime
= ds
->sem_ctime
221 | ((__time64_t
) ds
->__sem_ctime_high
<< 32);
222 ds64
->sem_nsems
= ds
->sem_nsems
;
226 semid64_to_semid (struct semid_ds
*ds
, const struct __semid64_ds
*ds64
)
228 ds
->sem_perm
= ds64
->sem_perm
;
229 ds
->sem_otime
= ds64
->sem_otime
;
230 ds
->__sem_otime_high
= 0;
231 ds
->sem_ctime
= ds64
->sem_ctime
;
232 ds
->__sem_ctime_high
= 0;
233 ds
->sem_nsems
= ds64
->sem_nsems
;
237 semun_to_semun64 (int cmd
, union semun semun
, struct __semid64_ds
*semid64
)
239 union semun64 r
= { 0 };
247 r
.array
= semun
.array
;
254 semid_to_semid64 (r
.buf
, semun
.buf
);
258 r
.__buf
= semun
.__buf
;
265 __semctl (int semid
, int semnum
, int cmd
, ...)
267 union semun arg
= { 0 };
271 /* Get the argument only if required. */
274 case SETVAL
: /* arg.val */
275 case GETALL
: /* arg.array */
277 case IPC_STAT
: /* arg.buf */
281 case IPC_INFO
: /* arg.__buf */
284 arg
= va_arg (ap
, union semun
);
287 /* __semctl64 handles non-supported commands. */
290 struct __semid64_ds semid64
;
291 union semun64 arg64
= semun_to_semun64 (cmd
, arg
, &semid64
);
293 int ret
= __semctl64 (semid
, semnum
, cmd
, arg64
);
302 semid64_to_semid (arg
.buf
, arg64
.buf
);
309 #ifndef DEFAULT_VERSION
310 # ifndef __ASSUME_SYSVIPC_BROKEN_MODE_T
311 # define DEFAULT_VERSION GLIBC_2_2
313 # define DEFAULT_VERSION GLIBC_2_31
316 versioned_symbol (libc
, __semctl
, semctl
, DEFAULT_VERSION
);
318 #if defined __ASSUME_SYSVIPC_BROKEN_MODE_T \
319 && SHLIB_COMPAT (libc, GLIBC_2_2, GLIBC_2_31)
321 attribute_compat_text_section
322 __semctl_mode16 (int semid
, int semnum
, int cmd
, ...)
324 semctl_arg_t arg
= { 0 };
327 /* Get the argument only if required. */
330 case SETVAL
: /* arg.val */
331 case GETALL
: /* arg.array */
333 case IPC_STAT
: /* arg.buf */
337 case IPC_INFO
: /* arg.__buf */
340 arg
= va_arg (ap
, semctl_arg_t
);
345 return semctl_syscall (semid
, semnum
, cmd
, arg
);
347 compat_symbol (libc
, __semctl_mode16
, semctl
, GLIBC_2_2
);
350 #if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_2)
351 /* Since semctl use a variadic argument for semid_ds there is not need to
352 define and tie the compatibility symbol to the old 'union semun'
355 attribute_compat_text_section
356 __old_semctl (int semid
, int semnum
, int cmd
, ...)
358 union semun arg
= { 0 };
361 /* Get the argument only if required. */
364 case SETVAL
: /* arg.val */
365 case GETALL
: /* arg.array */
367 case IPC_STAT
: /* arg.buf */
371 case IPC_INFO
: /* arg.__buf */
374 arg
= va_arg (ap
, union semun
);
379 #if defined __ASSUME_DIRECT_SYSVIPC_SYSCALLS \
380 && !defined __ASSUME_SYSVIPC_DEFAULT_IPC_64
381 /* For architectures that have wire-up semctl but also have __IPC_64 to a
382 value different than default (0x0) it means the compat symbol used the
384 return INLINE_SYSCALL_CALL (semctl
, semid
, semnum
, cmd
, arg
.array
);
386 return INLINE_SYSCALL_CALL (ipc
, IPCOP_semctl
, semid
, semnum
, cmd
,
387 SEMCTL_ARG_ADDRESS (arg
));
390 compat_symbol (libc
, __old_semctl
, semctl
, GLIBC_2_0
);