summaryrefslogtreecommitdiff
path: root/compat/time32
diff options
context:
space:
mode:
authorRich Felker <dalias@aerifal.cx>2019-08-02 15:52:42 -0400
committerRich Felker <dalias@aerifal.cx>2019-11-02 18:30:56 -0400
commitc0450320940c8c45f3847f2d0a22c0ebc545b291 (patch)
tree3403c819f3fb9ddc96e1c0136b8807a0aa7eb582 /compat/time32
parent0961bb94162896d358937e4979e5830f39818920 (diff)
downloadmusl-c0450320940c8c45f3847f2d0a22c0ebc545b291.tar.gz
add time32 ABI compat shims, compat source tree
these files provide the symbols for the traditional 32-bit time_t ABI on existing 32-bit archs by wrapping the real, internal versions of the corresponding functions, which always work with 64-bit time_t. they are written to be as agnostic as possible to the implementation details of the real functions, so that they can be written once and mostly forgotten, but they are aware of details of the old (and sometimes new) ABI, which is okay since ABI is fixed and cannot change. a new compat tree is added, separate from src, which the Makefile does not see or use now, but which archs will be able to add to the build process. we could also consider moving other things that are compat shims here, like functions which are purely for glibc-ABI-compat, with the goal of making it optional or just cleaning up the main src tree to make the distinction between actual implementation/API files and ABI-compat shims clear.
Diffstat (limited to 'compat/time32')
-rw-r--r--compat/time32/__xstat.c24
-rw-r--r--compat/time32/adjtime32.c21
-rw-r--r--compat/time32/adjtimex_time32.c10
-rw-r--r--compat/time32/aio_suspend_time32.c11
-rw-r--r--compat/time32/clock_adjtime32.c70
-rw-r--r--compat/time32/clock_getres_time32.c13
-rw-r--r--compat/time32/clock_gettime32.c18
-rw-r--r--compat/time32/clock_nanosleep_time32.c15
-rw-r--r--compat/time32/clock_settime32.c9
-rw-r--r--compat/time32/cnd_timedwait_time32.c9
-rw-r--r--compat/time32/ctime32.c7
-rw-r--r--compat/time32/ctime32_r.c7
-rw-r--r--compat/time32/difftime32.c7
-rw-r--r--compat/time32/fstat_time32.c17
-rw-r--r--compat/time32/fstatat_time32.c17
-rw-r--r--compat/time32/ftime32.c25
-rw-r--r--compat/time32/futimens_time32.c10
-rw-r--r--compat/time32/futimes_time32.c12
-rw-r--r--compat/time32/futimesat_time32.c12
-rw-r--r--compat/time32/getitimer_time32.c15
-rw-r--r--compat/time32/getrusage_time32.c39
-rw-r--r--compat/time32/gettimeofday_time32.c19
-rw-r--r--compat/time32/gmtime32.c7
-rw-r--r--compat/time32/gmtime32_r.c7
-rw-r--r--compat/time32/localtime32.c7
-rw-r--r--compat/time32/localtime32_r.c7
-rw-r--r--compat/time32/lstat_time32.c17
-rw-r--r--compat/time32/lutimes_time32.c12
-rw-r--r--compat/time32/mktime32.c16
-rw-r--r--compat/time32/mq_timedreceive_time32.c9
-rw-r--r--compat/time32/mq_timedsend_time32.c9
-rw-r--r--compat/time32/mtx_timedlock_time32.c9
-rw-r--r--compat/time32/nanosleep_time32.c15
-rw-r--r--compat/time32/ppoll_time32.c10
-rw-r--r--compat/time32/pselect_time32.c9
-rw-r--r--compat/time32/pthread_cond_timedwait_time32.c9
-rw-r--r--compat/time32/pthread_mutex_timedlock_time32.c9
-rw-r--r--compat/time32/pthread_rwlock_timedrdlock_time32.c9
-rw-r--r--compat/time32/pthread_rwlock_timedwrlock_time32.c9
-rw-r--r--compat/time32/pthread_timedjoin_np_time32.c10
-rw-r--r--compat/time32/recvmmsg_time32.c10
-rw-r--r--compat/time32/sched_rr_get_interval_time32.c13
-rw-r--r--compat/time32/select_time32.c10
-rw-r--r--compat/time32/sem_timedwait_time32.c9
-rw-r--r--compat/time32/semtimedop_time32.c10
-rw-r--r--compat/time32/setitimer_time32.c23
-rw-r--r--compat/time32/settimeofday_time32.c10
-rw-r--r--compat/time32/sigtimedwait_time32.c9
-rw-r--r--compat/time32/stat_time32.c17
-rw-r--r--compat/time32/stime32.c8
-rw-r--r--compat/time32/thrd_sleep_time32.c16
-rw-r--r--compat/time32/time32.c15
-rw-r--r--compat/time32/time32.h91
-rw-r--r--compat/time32/time32gm.c15
-rw-r--r--compat/time32/timer_gettime32.c15
-rw-r--r--compat/time32/timer_settime32.c25
-rw-r--r--compat/time32/timerfd_gettime32.c16
-rw-r--r--compat/time32/timerfd_settime32.c26
-rw-r--r--compat/time32/timespec_get_time32.c18
-rw-r--r--compat/time32/utime_time32.c14
-rw-r--r--compat/time32/utimensat_time32.c11
-rw-r--r--compat/time32/utimes_time32.c11
-rw-r--r--compat/time32/wait3_time32.c40
-rw-r--r--compat/time32/wait4_time32.c40
64 files changed, 1039 insertions, 0 deletions
diff --git a/compat/time32/__xstat.c b/compat/time32/__xstat.c
new file mode 100644
index 00000000..acfbd3cc
--- /dev/null
+++ b/compat/time32/__xstat.c
@@ -0,0 +1,24 @@
+#include "time32.h"
+#include <sys/stat.h>
+
+struct stat32;
+
+int __fxstat64(int ver, int fd, struct stat32 *buf)
+{
+ return __fstat_time32(fd, buf);
+}
+
+int __fxstatat64(int ver, int fd, const char *path, struct stat32 *buf, int flag)
+{
+ return __fstatat_time32(fd, path, buf, flag);
+}
+
+int __lxstat64(int ver, const char *path, struct stat32 *buf)
+{
+ return __lstat_time32(path, buf);
+}
+
+int __xstat64(int ver, const char *path, struct stat32 *buf)
+{
+ return __stat_time32(path, buf);
+}
diff --git a/compat/time32/adjtime32.c b/compat/time32/adjtime32.c
new file mode 100644
index 00000000..b0042c63
--- /dev/null
+++ b/compat/time32/adjtime32.c
@@ -0,0 +1,21 @@
+#define _GNU_SOURCE
+#include "time32.h"
+#include <time.h>
+#include <sys/time.h>
+#include <sys/timex.h>
+
+int __adjtime32(const struct timeval32 *in32, struct timeval32 *out32)
+{
+ struct timeval out;
+ int r = adjtime((&(struct timeval){
+ .tv_sec = in32->tv_sec,
+ .tv_usec = in32->tv_usec}), &out);
+ if (r) return r;
+ /* We can't range-check the result because success was already
+ * committed by the above call. */
+ if (out32) {
+ out32->tv_sec = out.tv_sec;
+ out32->tv_usec = out.tv_usec;
+ }
+ return r;
+}
diff --git a/compat/time32/adjtimex_time32.c b/compat/time32/adjtimex_time32.c
new file mode 100644
index 00000000..9c6f190a
--- /dev/null
+++ b/compat/time32/adjtimex_time32.c
@@ -0,0 +1,10 @@
+#include "time32.h"
+#include <time.h>
+#include <sys/timex.h>
+
+struct timex32;
+
+int __adjtimex_time32(struct timex32 *tx32)
+{
+ return __clock_adjtime32(CLOCK_REALTIME, tx32);
+}
diff --git a/compat/time32/aio_suspend_time32.c b/compat/time32/aio_suspend_time32.c
new file mode 100644
index 00000000..ed5119bd
--- /dev/null
+++ b/compat/time32/aio_suspend_time32.c
@@ -0,0 +1,11 @@
+#include "time32.h"
+#include <time.h>
+#include <aio.h>
+
+int __aio_suspend_time32(const struct aiocb *const cbs[], int cnt, const struct timespec32 *ts32)
+{
+ return aio_suspend(cbs, cnt, ts32 ? (&(struct timespec){
+ .tv_sec = ts32->tv_sec, .tv_nsec = ts32->tv_nsec}) : 0);
+}
+
+weak_alias(aio_suspend, aio_suspend64);
diff --git a/compat/time32/clock_adjtime32.c b/compat/time32/clock_adjtime32.c
new file mode 100644
index 00000000..5a25b8ac
--- /dev/null
+++ b/compat/time32/clock_adjtime32.c
@@ -0,0 +1,70 @@
+#include "time32.h"
+#include <time.h>
+#include <sys/time.h>
+#include <sys/timex.h>
+#include <string.h>
+#include <stddef.h>
+
+struct timex32 {
+ unsigned modes;
+ long offset, freq, maxerror, esterror;
+ int status;
+ long constant, precision, tolerance;
+ struct timeval32 time;
+ long tick, ppsfreq, jitter;
+ int shift;
+ long stabil, jitcnt, calcnt, errcnt, stbcnt;
+ int tai;
+ int __padding[11];
+};
+
+int __clock_adjtime32(clockid_t clock_id, struct timex32 *tx32)
+{
+ struct timex utx = {
+ .modes = tx32->modes,
+ .offset = tx32->offset,
+ .freq = tx32->freq,
+ .maxerror = tx32->maxerror,
+ .esterror = tx32->esterror,
+ .status = tx32->status,
+ .constant = tx32->constant,
+ .precision = tx32->precision,
+ .tolerance = tx32->tolerance,
+ .time.tv_sec = tx32->time.tv_sec,
+ .time.tv_usec = tx32->time.tv_usec,
+ .tick = tx32->tick,
+ .ppsfreq = tx32->ppsfreq,
+ .jitter = tx32->jitter,
+ .shift = tx32->shift,
+ .stabil = tx32->stabil,
+ .jitcnt = tx32->jitcnt,
+ .calcnt = tx32->calcnt,
+ .errcnt = tx32->errcnt,
+ .stbcnt = tx32->stbcnt,
+ .tai = tx32->tai,
+ };
+ int r = clock_adjtime(clock_id, &utx);
+ if (r<0) return r;
+ tx32->modes = utx.modes;
+ tx32->offset = utx.offset;
+ tx32->freq = utx.freq;
+ tx32->maxerror = utx.maxerror;
+ tx32->esterror = utx.esterror;
+ tx32->status = utx.status;
+ tx32->constant = utx.constant;
+ tx32->precision = utx.precision;
+ tx32->tolerance = utx.tolerance;
+ tx32->time.tv_sec = utx.time.tv_sec;
+ tx32->time.tv_usec = utx.time.tv_usec;
+ tx32->tick = utx.tick;
+ tx32->ppsfreq = utx.ppsfreq;
+ tx32->jitter = utx.jitter;
+ tx32->shift = utx.shift;
+ tx32->stabil = utx.stabil;
+ tx32->jitcnt = utx.jitcnt;
+ tx32->calcnt = utx.calcnt;
+ tx32->errcnt = utx.errcnt;
+ tx32->stbcnt = utx.stbcnt;
+ tx32->tai = utx.tai;
+ return r;
+}
diff --git a/compat/time32/clock_getres_time32.c b/compat/time32/clock_getres_time32.c
new file mode 100644
index 00000000..47a24c13
--- /dev/null
+++ b/compat/time32/clock_getres_time32.c
@@ -0,0 +1,13 @@
+#include "time32.h"
+#include <time.h>
+
+int __clock_getres_time32(clockid_t clk, struct timespec32 *ts32)
+{
+ struct timespec ts;
+ int r = clock_getres(clk, &ts);
+ if (!r && ts32) {
+ ts32->tv_sec = ts.tv_sec;
+ ts32->tv_nsec = ts.tv_nsec;
+ }
+ return r;
+}
diff --git a/compat/time32/clock_gettime32.c b/compat/time32/clock_gettime32.c
new file mode 100644
index 00000000..0cac7bbd
--- /dev/null
+++ b/compat/time32/clock_gettime32.c
@@ -0,0 +1,18 @@
+#include "time32.h"
+#include <time.h>
+#include <errno.h>
+#include <stdint.h>
+
+int __clock_gettime32(clockid_t clk, struct timespec32 *ts32)
+{
+ struct timespec ts;
+ int r = clock_gettime(clk, &ts);
+ if (r) return r;
+ if (ts.tv_sec < INT32_MIN || ts.tv_sec > INT32_MAX) {
+ errno = EOVERFLOW;
+ return -1;
+ }
+ ts32->tv_sec = ts.tv_sec;
+ ts32->tv_nsec = ts.tv_nsec;
+ return 0;
+}
diff --git a/compat/time32/clock_nanosleep_time32.c b/compat/time32/clock_nanosleep_time32.c
new file mode 100644
index 00000000..91ef067d
--- /dev/null
+++ b/compat/time32/clock_nanosleep_time32.c
@@ -0,0 +1,15 @@
+#include "time32.h"
+#include <time.h>
+#include <errno.h>
+
+int __clock_nanosleep_time32(clockid_t clk, int flags, const struct timespec32 *req32, struct timespec32 *rem32)
+{
+ struct timespec rem;
+ int ret = clock_nanosleep(clk, flags, (&(struct timespec){
+ .tv_sec = req32->tv_sec, .tv_nsec = req32->tv_nsec}), &rem);
+ if (ret==EINTR && rem32 && !(flags & TIMER_ABSTIME)) {
+ rem32->tv_sec = rem.tv_sec;
+ rem32->tv_nsec = rem.tv_nsec;
+ }
+ return ret;
+}
diff --git a/compat/time32/clock_settime32.c b/compat/time32/clock_settime32.c
new file mode 100644
index 00000000..7ca4f0e9
--- /dev/null
+++ b/compat/time32/clock_settime32.c
@@ -0,0 +1,9 @@
+#include "time32.h"
+#include <time.h>
+
+int __clock_settime32(clockid_t clk, const struct timespec32 *ts32)
+{
+ return clock_settime(clk, (&(struct timespec){
+ .tv_sec = ts32->tv_sec,
+ .tv_nsec = ts32->tv_nsec}));
+}
diff --git a/compat/time32/cnd_timedwait_time32.c b/compat/time32/cnd_timedwait_time32.c
new file mode 100644
index 00000000..314251d1
--- /dev/null
+++ b/compat/time32/cnd_timedwait_time32.c
@@ -0,0 +1,9 @@
+#include "time32.h"
+#include <time.h>
+#include <threads.h>
+
+int __cnd_timedwait_time32(cnd_t *restrict c, mtx_t *restrict m, const struct timespec32 *restrict ts32)
+{
+ return cnd_timedwait(c, m, ts32 ? (&(struct timespec){
+ .tv_sec = ts32->tv_sec, .tv_nsec = ts32->tv_nsec}) : 0);
+}
diff --git a/compat/time32/ctime32.c b/compat/time32/ctime32.c
new file mode 100644
index 00000000..a057274e
--- /dev/null
+++ b/compat/time32/ctime32.c
@@ -0,0 +1,7 @@
+#include "time32.h"
+#include <time.h>
+
+char *__ctime32(time32_t *t)
+{
+ return ctime(&(time_t){*t});
+}
diff --git a/compat/time32/ctime32_r.c b/compat/time32/ctime32_r.c
new file mode 100644
index 00000000..e1ad2e28
--- /dev/null
+++ b/compat/time32/ctime32_r.c
@@ -0,0 +1,7 @@
+#include "time32.h"
+#include <time.h>
+
+char *__ctime32_r(time32_t *t, char *buf)
+{
+ return ctime_r(&(time_t){*t}, buf);
+}
diff --git a/compat/time32/difftime32.c b/compat/time32/difftime32.c
new file mode 100644
index 00000000..5950943a
--- /dev/null
+++ b/compat/time32/difftime32.c
@@ -0,0 +1,7 @@
+#include "time32.h"
+#include <time.h>
+
+double __difftime32(time32_t t1, time32_t t2)
+{
+ return difftime(t1, t2);
+}
diff --git a/compat/time32/fstat_time32.c b/compat/time32/fstat_time32.c
new file mode 100644
index 00000000..3e084398
--- /dev/null
+++ b/compat/time32/fstat_time32.c
@@ -0,0 +1,17 @@
+#include "time32.h"
+#include <time.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <stddef.h>
+
+struct stat32;
+
+int __fstat_time32(int fd, struct stat32 *restrict st32)
+{
+ struct stat st;
+ int r = fstat(fd, &st);
+ if (!r) memcpy(st32, &st, offsetof(struct stat, st_atim));
+ return r;
+}
+
+weak_alias(fstat, fstat64);
diff --git a/compat/time32/fstatat_time32.c b/compat/time32/fstatat_time32.c
new file mode 100644
index 00000000..85dcb008
--- /dev/null
+++ b/compat/time32/fstatat_time32.c
@@ -0,0 +1,17 @@
+#include "time32.h"
+#include <time.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <stddef.h>
+
+struct stat32;
+
+int __fstatat_time32(int fd, const char *restrict path, struct stat32 *restrict st32, int flag)
+{
+ struct stat st;
+ int r = fstatat(fd, path, &st, flag);
+ if (!r) memcpy(st32, &st, offsetof(struct stat, st_atim));
+ return r;
+}
+
+weak_alias(fstatat, fstatat64);
diff --git a/compat/time32/ftime32.c b/compat/time32/ftime32.c
new file mode 100644
index 00000000..166a6dae
--- /dev/null
+++ b/compat/time32/ftime32.c
@@ -0,0 +1,25 @@
+#include "time32.h"
+#include <sys/timeb.h>
+#include <errno.h>
+#include <stdint.h>
+
+struct timeb32 {
+ int32_t time;
+ unsigned short millitm;
+ short timezone, dstflag;
+};
+
+int __ftime32(struct timeb32 *tp)
+{
+ struct timeb tb;
+ if (ftime(&tb) < 0) return -1;
+ if (tb.time < INT32_MIN || tb.time > INT32_MAX) {
+ errno = EOVERFLOW;
+ return -1;
+ }
+ tp->time = tb.time;
+ tp->millitm = tb.millitm;
+ tp->timezone = tb.timezone;
+ tp->dstflag = tb.dstflag;
+ return 0;
+}
diff --git a/compat/time32/futimens_time32.c b/compat/time32/futimens_time32.c
new file mode 100644
index 00000000..7856f176
--- /dev/null
+++ b/compat/time32/futimens_time32.c
@@ -0,0 +1,10 @@
+#include "time32.h"
+#include <time.h>
+#include <sys/stat.h>
+
+int __futimens_time32(int fd, const struct timespec32 *times32)
+{
+ return futimens(fd, !times32 ? 0 : ((struct timespec[2]){
+ {.tv_sec = times32[0].tv_sec,.tv_nsec = times32[0].tv_nsec},
+ {.tv_sec = times32[1].tv_sec,.tv_nsec = times32[1].tv_nsec}}));
+}
diff --git a/compat/time32/futimes_time32.c b/compat/time32/futimes_time32.c
new file mode 100644
index 00000000..f29533f1
--- /dev/null
+++ b/compat/time32/futimes_time32.c
@@ -0,0 +1,12 @@
+#define _GNU_SOURCE
+#include "time32.h"
+#include <time.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+
+int __futimes_time32(int fd, const struct timeval32 times32[2])
+{
+ return futimes(fd, !times32 ? 0 : ((struct timeval[2]){
+ {.tv_sec = times32[0].tv_sec,.tv_usec = times32[0].tv_usec},
+ {.tv_sec = times32[1].tv_sec,.tv_usec = times32[1].tv_usec}}));
+}
diff --git a/compat/time32/futimesat_time32.c b/compat/time32/futimesat_time32.c
new file mode 100644
index 00000000..5a1295bd
--- /dev/null
+++ b/compat/time32/futimesat_time32.c
@@ -0,0 +1,12 @@
+#define _GNU_SOURCE
+#include "time32.h"
+#include <time.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+
+int __futimesat_time32(int dirfd, const char *pathname, const struct timeval32 times32[2])
+{
+ return futimesat(dirfd, pathname, !times32 ? 0 : ((struct timeval[2]){
+ {.tv_sec = times32[0].tv_sec,.tv_usec = times32[0].tv_usec},
+ {.tv_sec = times32[1].tv_sec,.tv_usec = times32[1].tv_usec}}));
+}
diff --git a/compat/time32/getitimer_time32.c b/compat/time32/getitimer_time32.c
new file mode 100644
index 00000000..4bac4bf5
--- /dev/null
+++ b/compat/time32/getitimer_time32.c
@@ -0,0 +1,15 @@
+#include "time32.h"
+#include <time.h>
+#include <sys/time.h>
+
+int __getitimer_time32(int which, struct itimerval32 *old32)
+{
+ struct itimerval old;
+ int r = getitimer(which, &old);
+ if (r) return r;
+ old32->it_interval.tv_sec = old.it_interval.tv_sec;
+ old32->it_interval.tv_usec = old.it_interval.tv_usec;
+ old32->it_value.tv_sec = old.it_value.tv_sec;
+ old32->it_value.tv_usec = old.it_value.tv_usec;
+ return 0;
+}
diff --git a/compat/time32/getrusage_time32.c b/compat/time32/getrusage_time32.c
new file mode 100644
index 00000000..d7487dee
--- /dev/null
+++ b/compat/time32/getrusage_time32.c
@@ -0,0 +1,39 @@
+#include "time32.h"
+#include <string.h>
+#include <stddef.h>
+#include <sys/resource.h>
+
+struct compat_rusage {
+ struct timeval32 ru_utime;
+ struct timeval32 ru_stime;
+ long ru_maxrss;
+ long ru_ixrss;
+ long ru_idrss;
+ long ru_isrss;
+ long ru_minflt;
+ long ru_majflt;
+ long ru_nswap;
+ long ru_inblock;
+ long ru_oublock;
+ long ru_msgsnd;
+ long ru_msgrcv;
+ long ru_nsignals;
+ long ru_nvcsw;
+ long ru_nivcsw;
+};
+
+int __getrusage_time32(int who, struct compat_rusage *usage)
+{
+ struct rusage ru;
+ int r = getrusage(who, &ru);
+ if (!r) {
+ usage->ru_utime.tv_sec = ru.ru_utime.tv_sec;
+ usage->ru_utime.tv_usec = ru.ru_utime.tv_usec;
+ usage->ru_stime.tv_sec = ru.ru_stime.tv_sec;
+ usage->ru_stime.tv_usec = ru.ru_stime.tv_usec;
+ memcpy(&usage->ru_maxrss, &ru.ru_maxrss,
+ sizeof(struct compat_rusage) -
+ offsetof(struct compat_rusage, ru_maxrss));
+ }
+ return r;
+}
diff --git a/compat/time32/gettimeofday_time32.c b/compat/time32/gettimeofday_time32.c
new file mode 100644
index 00000000..1f3ce68e
--- /dev/null
+++ b/compat/time32/gettimeofday_time32.c
@@ -0,0 +1,19 @@
+#include "time32.h"
+#include <sys/time.h>
+#include <errno.h>
+#include <stdint.h>
+
+int __gettimeofday_time32(struct timeval32 *tv32, void *tz)
+{
+ struct timeval tv;
+ if (!tv32) return 0;
+ int r = gettimeofday(&tv, 0);
+ if (r) return r;
+ if (tv.tv_sec < INT32_MIN || tv.tv_sec > INT32_MAX) {
+ errno = EOVERFLOW;
+ return -1;
+ }
+ tv32->tv_sec = tv.tv_sec;
+ tv32->tv_usec = tv.tv_usec;
+ return 0;
+}
diff --git a/compat/time32/gmtime32.c b/compat/time32/gmtime32.c
new file mode 100644
index 00000000..963f0e05
--- /dev/null
+++ b/compat/time32/gmtime32.c
@@ -0,0 +1,7 @@
+#include "time32.h"
+#include <time.h>
+
+struct tm *__gmtime32(time32_t *t)
+{
+ return gmtime(&(time_t){*t});
+}
diff --git a/compat/time32/gmtime32_r.c b/compat/time32/gmtime32_r.c
new file mode 100644
index 00000000..7d72bfb3
--- /dev/null
+++ b/compat/time32/gmtime32_r.c
@@ -0,0 +1,7 @@
+#include "time32.h"
+#include <time.h>
+
+struct tm *__gmtime32_r(time32_t *t, struct tm *tm)
+{
+ return gmtime_r(&(time_t){*t}, tm);
+}
diff --git a/compat/time32/localtime32.c b/compat/time32/localtime32.c
new file mode 100644
index 00000000..96bc3034
--- /dev/null
+++ b/compat/time32/localtime32.c
@@ -0,0 +1,7 @@
+#include "time32.h"
+#include <time.h>
+
+struct tm *__localtime32(time32_t *t)
+{
+ return localtime(&(time_t){*t});
+}
diff --git a/compat/time32/localtime32_r.c b/compat/time32/localtime32_r.c
new file mode 100644
index 00000000..633ec829
--- /dev/null
+++ b/compat/time32/localtime32_r.c
@@ -0,0 +1,7 @@
+#include "time32.h"
+#include <time.h>
+
+struct tm *__localtime32_r(time32_t *t, struct tm *tm)
+{
+ return localtime_r(&(time_t){*t}, tm);
+}
diff --git a/compat/time32/lstat_time32.c b/compat/time32/lstat_time32.c
new file mode 100644
index 00000000..c1257a14
--- /dev/null
+++ b/compat/time32/lstat_time32.c
@@ -0,0 +1,17 @@
+#include "time32.h"
+#include <time.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <stddef.h>
+
+struct stat32;
+
+int __lstat_time32(const char *restrict path, struct stat32 *restrict st32)
+{
+ struct stat st;
+ int r = lstat(path, &st);
+ if (!r) memcpy(st32, &st, offsetof(struct stat, st_atim));
+ return r;
+}
+
+weak_alias(lstat, lstat64);
diff --git a/compat/time32/lutimes_time32.c b/compat/time32/lutimes_time32.c
new file mode 100644
index 00000000..7f75cd4a
--- /dev/null
+++ b/compat/time32/lutimes_time32.c
@@ -0,0 +1,12 @@
+#define _GNU_SOURCE
+#include "time32.h"
+#include <time.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+
+int __lutimes_time32(const char *path, const struct timeval32 times32[2])
+{
+ return lutimes(path, !times32 ? 0 : ((struct timeval[2]){
+ {.tv_sec = times32[0].tv_sec,.tv_usec = times32[0].tv_usec},
+ {.tv_sec = times32[1].tv_sec,.tv_usec = times32[1].tv_usec}}));
+}
diff --git a/compat/time32/mktime32.c b/compat/time32/mktime32.c
new file mode 100644
index 00000000..e6f15d51
--- /dev/null
+++ b/compat/time32/mktime32.c
@@ -0,0 +1,16 @@
+#include "time32.h"
+#include <time.h>
+#include <errno.h>
+#include <stdint.h>
+
+time32_t __mktime32(struct tm *tm)
+{
+ struct tm tmp = *tm;
+ time_t t = mktime(&tmp);
+ if (t < INT32_MIN || t > INT32_MAX) {
+ errno = EOVERFLOW;
+ return -1;
+ }
+ *tm = tmp;
+ return t;
+}
diff --git a/compat/time32/mq_timedreceive_time32.c b/compat/time32/mq_timedreceive_time32.c
new file mode 100644
index 00000000..211cea4b
--- /dev/null
+++ b/compat/time32/mq_timedreceive_time32.c
@@ -0,0 +1,9 @@
+#include "time32.h"
+#include <mqueue.h>
+#include <time.h>
+
+ssize_t __mq_timedreceive_time32(mqd_t mqd, char *restrict msg, size_t len, unsigned *restrict prio, const struct timespec32 *restrict ts32)
+{
+ return mq_timedreceive(mqd, msg, len, prio, ts32 ? (&(struct timespec){
+ .tv_sec = ts32->tv_sec, .tv_nsec = ts32->tv_nsec}) : 0);
+}
diff --git a/compat/time32/mq_timedsend_time32.c b/compat/time32/mq_timedsend_time32.c
new file mode 100644
index 00000000..93b697a7
--- /dev/null
+++ b/compat/time32/mq_timedsend_time32.c
@@ -0,0 +1,9 @@
+#include "time32.h"
+#include <mqueue.h>
+#include <time.h>
+
+int __mq_timedsend_time32(mqd_t mqd, const char *msg, size_t len, unsigned prio, const struct timespec32 *ts32)
+{
+ return mq_timedsend(mqd, msg, len, prio, ts32 ? (&(struct timespec){
+ .tv_sec = ts32->tv_sec, .tv_nsec = ts32->tv_nsec}) : 0);
+}
diff --git a/compat/time32/mtx_timedlock_time32.c b/compat/time32/mtx_timedlock_time32.c
new file mode 100644
index 00000000..a01f09b8
--- /dev/null
+++ b/compat/time32/mtx_timedlock_time32.c
@@ -0,0 +1,9 @@
+#include "time32.h"
+#include <time.h>
+#include <threads.h>
+
+int __mtx_timedlock_time32(mtx_t *restrict m, const struct timespec32 *restrict ts32)
+{
+ return mtx_timedlock(m, !ts32 ? 0 : (&(struct timespec){
+ .tv_sec = ts32->tv_sec, .tv_nsec = ts32->tv_nsec}));
+}
diff --git a/compat/time32/nanosleep_time32.c b/compat/time32/nanosleep_time32.c
new file mode 100644
index 00000000..ea6bdd81
--- /dev/null
+++ b/compat/time32/nanosleep_time32.c
@@ -0,0 +1,15 @@
+#include "time32.h"
+#include <time.h>
+#include <errno.h>
+
+int __nanosleep_time32(const struct timespec32 *req32, struct timespec32 *rem32)
+{
+ struct timespec rem;
+ int ret = nanosleep((&(struct timespec){
+ .tv_sec = req32->tv_sec, .tv_nsec = req32->tv_nsec}), &rem);
+ if (ret<0 && errno==EINTR && rem32) {
+ rem32->tv_sec = rem.tv_sec;
+ rem32->tv_nsec = rem.tv_nsec;
+ }
+ return ret;
+}
diff --git a/compat/time32/ppoll_time32.c b/compat/time32/ppoll_time32.c
new file mode 100644
index 00000000..43b4b0df
--- /dev/null
+++ b/compat/time32/ppoll_time32.c
@@ -0,0 +1,10 @@
+#include "time32.h"
+#define _GNU_SOURCE
+#include <time.h>
+#include <poll.h>
+
+int __ppoll_time32(struct pollfd *fds, nfds_t n, const struct timespec32 *ts32, const sigset_t *mask)
+{
+ return ppoll(fds, n, !ts32 ? 0 : (&(struct timespec){
+ .tv_sec = ts32->tv_sec, .tv_nsec = ts32->tv_nsec}), mask);
+}
diff --git a/compat/time32/pselect_time32.c b/compat/time32/pselect_time32.c
new file mode 100644
index 00000000..ecaa8f86
--- /dev/null
+++ b/compat/time32/pselect_time32.c
@@ -0,0 +1,9 @@
+#include "time32.h"
+#include <time.h>
+#include <sys/select.h>
+
+int __pselect_time32(int n, fd_set *restrict rfds, fd_set *restrict wfds, fd_set *restrict efds, const struct timespec32 *restrict ts32, const sigset_t *restrict mask)
+{
+ return pselect(n, rfds, wfds, efds, !ts32 ? 0 : (&(struct timespec){
+ .tv_sec = ts32->tv_sec, .tv_nsec = ts32->tv_nsec}), mask);
+}
diff --git a/compat/time32/pthread_cond_timedwait_time32.c b/compat/time32/pthread_cond_timedwait_time32.c
new file mode 100644
index 00000000..fba1f2a9
--- /dev/null
+++ b/compat/time32/pthread_cond_timedwait_time32.c
@@ -0,0 +1,9 @@
+#include "time32.h"
+#include <time.h>
+#include <pthread.h>
+
+int __pthread_cond_timedwait_time32(pthread_cond_t *restrict c, pthread_mutex_t *restrict m, const struct timespec32 *restrict ts32)
+{
+ return pthread_cond_timedwait(c, m, !ts32 ? 0 : (&(struct timespec){
+ .tv_sec = ts32->tv_sec, .tv_nsec = ts32->tv_nsec}));
+}
diff --git a/compat/time32/pthread_mutex_timedlock_time32.c b/compat/time32/pthread_mutex_timedlock_time32.c
new file mode 100644
index 00000000..2d29602c
--- /dev/null
+++ b/compat/time32/pthread_mutex_timedlock_time32.c
@@ -0,0 +1,9 @@
+#include "time32.h"
+#include <time.h>
+#include <pthread.h>
+
+int __pthread_mutex_timedlock_time32(pthread_mutex_t *restrict m, const struct timespec32 *restrict ts32)
+{
+ return pthread_mutex_timedlock(m, !ts32 ? 0 : (&(struct timespec){
+ .tv_sec = ts32->tv_sec, .tv_nsec = ts32->tv_nsec}));
+}
diff --git a/compat/time32/pthread_rwlock_timedrdlock_time32.c b/compat/time32/pthread_rwlock_timedrdlock_time32.c
new file mode 100644
index 00000000..33df27a4
--- /dev/null
+++ b/compat/time32/pthread_rwlock_timedrdlock_time32.c
@@ -0,0 +1,9 @@
+#include "time32.h"
+#include <time.h>
+#include <pthread.h>
+
+int __pthread_rwlock_timedrdlock_time32(pthread_rwlock_t *restrict rw, const struct timespec32 *restrict ts32)
+{
+ return pthread_rwlock_timedrdlock(rw, !ts32 ? 0 : (&(struct timespec){
+ .tv_sec = ts32->tv_sec, .tv_nsec = ts32->tv_nsec}));
+}
diff --git a/compat/time32/pthread_rwlock_timedwrlock_time32.c b/compat/time32/pthread_rwlock_timedwrlock_time32.c
new file mode 100644
index 00000000..99f24f73
--- /dev/null
+++ b/compat/time32/pthread_rwlock_timedwrlock_time32.c
@@ -0,0 +1,9 @@
+#include "time32.h"
+#include <time.h>
+#include <pthread.h>
+
+int __pthread_rwlock_timedwrlock_time32(pthread_rwlock_t *restrict rw, const struct timespec32 *restrict ts32)
+{
+ return pthread_rwlock_timedwrlock(rw, !ts32 ? 0 : (&(struct timespec){
+ .tv_sec = ts32->tv_sec, .tv_nsec = ts32->tv_nsec}));
+}
diff --git a/compat/time32/pthread_timedjoin_np_time32.c b/compat/time32/pthread_timedjoin_np_time32.c
new file mode 100644
index 00000000..3ec29951
--- /dev/null
+++ b/compat/time32/pthread_timedjoin_np_time32.c
@@ -0,0 +1,10 @@
+#define _GNU_SOURCE
+#include "time32.h"
+#include <time.h>
+#include <pthread.h>
+
+int __pthread_timedjoin_np_time32(pthread_t t, void **res, const struct timespec32 *at32)
+{
+ return pthread_timedjoin_np(t, res, !at32 ? 0 : (&(struct timespec){
+ .tv_sec = at32->tv_sec, .tv_nsec = at32->tv_nsec}));
+}
diff --git a/compat/time32/recvmmsg_time32.c b/compat/time32/recvmmsg_time32.c
new file mode 100644
index 00000000..acf1cfb8
--- /dev/null
+++ b/compat/time32/recvmmsg_time32.c
@@ -0,0 +1,10 @@
+#include "time32.h"
+#define _GNU_SOURCE
+#include <time.h>
+#include <sys/socket.h>
+
+int __recvmmsg_time32(int fd, struct mmsghdr *msgvec, unsigned int vlen, unsigned int flags, struct timespec32 *ts32)
+{
+ return recvmmsg(fd, msgvec, vlen, flags, ts32 ? (&(struct timespec){
+ .tv_sec = ts32->tv_sec, .tv_nsec = ts32->tv_nsec}) : 0);
+}
diff --git a/compat/time32/sched_rr_get_interval_time32.c b/compat/time32/sched_rr_get_interval_time32.c
new file mode 100644
index 00000000..36cbbaca
--- /dev/null
+++ b/compat/time32/sched_rr_get_interval_time32.c
@@ -0,0 +1,13 @@
+#include "time32.h"
+#include <time.h>
+#include <sched.h>
+
+int __sched_rr_get_interval_time32(pid_t pid, struct timespec32 *ts32)
+{
+ struct timespec ts;
+ int r = sched_rr_get_interval(pid, &ts);
+ if (r) return r;
+ ts32->tv_sec = ts.tv_sec;
+ ts32->tv_nsec = ts.tv_nsec;
+ return r;
+}
diff --git a/compat/time32/select_time32.c b/compat/time32/select_time32.c
new file mode 100644
index 00000000..2d8df9ac
--- /dev/null
+++ b/compat/time32/select_time32.c
@@ -0,0 +1,10 @@
+#include "time32.h"
+#include <time.h>
+#include <sys/time.h>
+#include <sys/select.h>
+
+int __select_time32(int n, fd_set *restrict rfds, fd_set *restrict wfds, fd_set *restrict efds, struct timeval32 *restrict tv32)
+{
+ return select(n, rfds, wfds, efds, !tv32 ? 0 : (&(struct timeval){
+ .tv_sec = tv32->tv_sec, .tv_usec = tv32->tv_usec}));
+}
diff --git a/compat/time32/sem_timedwait_time32.c b/compat/time32/sem_timedwait_time32.c
new file mode 100644
index 00000000..c3469f9b
--- /dev/null
+++ b/compat/time32/sem_timedwait_time32.c
@@ -0,0 +1,9 @@
+#include "time32.h"
+#include <time.h>
+#include <semaphore.h>
+
+int __sem_timedwait_time32(sem_t *sem, const struct timespec32 *restrict ts32)
+{
+ return sem_timedwait(sem, !ts32 ? 0 : (&(struct timespec){
+ .tv_sec = ts32->tv_sec, .tv_nsec = ts32->tv_nsec}));
+}
diff --git a/compat/time32/semtimedop_time32.c b/compat/time32/semtimedop_time32.c
new file mode 100644
index 00000000..34ec5281
--- /dev/null
+++ b/compat/time32/semtimedop_time32.c
@@ -0,0 +1,10 @@
+#include "time32.h"
+#define _GNU_SOURCE
+#include <sys/sem.h>
+#include <time.h>
+
+int __semtimedop_time32(int id, struct sembuf *buf, size_t n, const struct timespec32 *ts32)
+{
+ return semtimedop(id, buf, n, !ts32 ? 0 : (&(struct timespec){
+ .tv_sec = ts32->tv_sec, .tv_nsec = ts32->tv_nsec}));
+}
diff --git a/compat/time32/setitimer_time32.c b/compat/time32/setitimer_time32.c
new file mode 100644
index 00000000..4651dacb
--- /dev/null
+++ b/compat/time32/setitimer_time32.c
@@ -0,0 +1,23 @@
+#include "time32.h"
+#include <time.h>
+#include <sys/time.h>
+
+int __setitimer_time32(int which, const struct itimerval32 *restrict new32, struct itimerval32 *restrict old32)
+{
+ struct itimerval old;
+ int r = setitimer(which, (&(struct itimerval){
+ .it_interval.tv_sec = new32->it_interval.tv_sec,
+ .it_interval.tv_usec = new32->it_interval.tv_usec,
+ .it_value.tv_sec = new32->it_value.tv_sec,
+ .it_value.tv_usec = new32->it_value.tv_usec}), &old);
+ if (r) return r;
+ /* The above call has already committed to success by changing the
+ * timer setting, so we can't fail on out-of-range old value.
+ * Since these are relative times, values large enough to overflow
+ * don't make sense anyway. */
+ old32->it_interval.tv_sec = old.it_interval.tv_sec;
+ old32->it_interval.tv_usec = old.it_interval.tv_usec;
+ old32->it_value.tv_sec = old.it_value.tv_sec;
+ old32->it_value.tv_usec = old.it_value.tv_usec;
+ return 0;
+}
diff --git a/compat/time32/settimeofday_time32.c b/compat/time32/settimeofday_time32.c
new file mode 100644
index 00000000..09e625cb
--- /dev/null
+++ b/compat/time32/settimeofday_time32.c
@@ -0,0 +1,10 @@
+#define _BSD_SOURCE
+#include "time32.h"
+#include <sys/time.h>
+
+int __settimeofday_time32(const struct timeval32 *tv32, const void *tz)
+{
+ return settimeofday(!tv32 ? 0 : (&(struct timeval){
+ .tv_sec = tv32->tv_sec,
+ .tv_usec = tv32->tv_usec}), 0);
+}
diff --git a/compat/time32/sigtimedwait_time32.c b/compat/time32/sigtimedwait_time32.c
new file mode 100644
index 00000000..6b3aa39c
--- /dev/null
+++ b/compat/time32/sigtimedwait_time32.c
@@ -0,0 +1,9 @@
+#include "time32.h"
+#include <time.h>
+#include <signal.h>
+
+int __sigtimedwait_time32(const sigset_t *restrict set, siginfo_t *restrict si, const struct timespec32 *restrict ts32)
+{
+ return sigtimedwait(set, si, !ts32 ? 0 : (&(struct timespec){
+ .tv_sec = ts32->tv_sec, .tv_nsec = ts32->tv_nsec}));
+}
diff --git a/compat/time32/stat_time32.c b/compat/time32/stat_time32.c
new file mode 100644
index 00000000..8c6121da
--- /dev/null
+++ b/compat/time32/stat_time32.c
@@ -0,0 +1,17 @@
+#include "time32.h"
+#include <time.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <stddef.h>
+
+struct stat32;
+
+int __stat_time32(const char *restrict path, struct stat32 *restrict st32)
+{
+ struct stat st;
+ int r = stat(path, &st);
+ if (!r) memcpy(st32, &st, offsetof(struct stat, st_atim));
+ return r;
+}
+
+weak_alias(stat, stat64);
diff --git a/compat/time32/stime32.c b/compat/time32/stime32.c
new file mode 100644
index 00000000..cc76364d
--- /dev/null
+++ b/compat/time32/stime32.c
@@ -0,0 +1,8 @@
+#define _GNU_SOURCE
+#include "time32.h"
+#include <time.h>
+
+int __stime32(const time32_t *t)
+{
+ return stime(&(time_t){*t});
+}
diff --git a/compat/time32/thrd_sleep_time32.c b/compat/time32/thrd_sleep_time32.c
new file mode 100644
index 00000000..59088001
--- /dev/null
+++ b/compat/time32/thrd_sleep_time32.c
@@ -0,0 +1,16 @@
+#include "time32.h"
+#include <time.h>
+#include <threads.h>
+#include <errno.h>
+
+int __thrd_sleep_time32(const struct timespec32 *req32, struct timespec32 *rem32)
+{
+ struct timespec rem;
+ int ret = thrd_sleep((&(struct timespec){
+ .tv_sec = req32->tv_sec, .tv_nsec = req32->tv_nsec}), &rem);
+ if (ret<0 && errno==EINTR && rem32) {
+ rem32->tv_sec = rem.tv_sec;
+ rem32->tv_nsec = rem.tv_nsec;
+ }
+ return ret;
+}
diff --git a/compat/time32/time32.c b/compat/time32/time32.c
new file mode 100644
index 00000000..4b8fac1c
--- /dev/null
+++ b/compat/time32/time32.c
@@ -0,0 +1,15 @@
+#include "time32.h"
+#include <time.h>
+#include <errno.h>
+#include <stdint.h>
+
+time32_t __time32(time32_t *p)
+{
+ time_t t = time(0);
+ if (t < INT32_MIN || t > INT32_MAX) {
+ errno = EOVERFLOW;
+ return -1;
+ }
+ if (p) *p = t;
+ return t;
+}
diff --git a/compat/time32/time32.h b/compat/time32/time32.h
new file mode 100644
index 00000000..fdec17c3
--- /dev/null
+++ b/compat/time32/time32.h
@@ -0,0 +1,91 @@
+#ifndef TIME32_H
+#define TIME32_H
+
+#include <sys/types.h>
+
+typedef long time32_t;
+
+struct timeval32 {
+ long tv_sec;
+ long tv_usec;
+};
+
+struct itimerval32 {
+ struct timeval32 it_interval;
+ struct timeval32 it_value;
+};
+
+struct timespec32 {
+ long tv_sec;
+ long tv_nsec;
+};
+
+struct itimerspec32 {
+ struct timespec32 it_interval;
+ struct timespec32 it_value;
+};
+
+int __adjtime32() __asm__("adjtime");
+int __adjtimex_time32() __asm__("adjtimex");
+int __aio_suspend_time32() __asm__("aio_suspend");
+int __clock_adjtime32() __asm__("clock_adjtime");
+int __clock_getres_time32() __asm__("clock_getres");
+int __clock_gettime32() __asm__("clock_gettime");
+int __clock_nanosleep_time32() __asm__("clock_nanosleep");
+int __clock_settime32() __asm__("clock_settime");
+int __cnd_timedwait_time32() __asm__("cnd_timedwait");
+char *__ctime32() __asm__("ctime");
+char *__ctime32_r() __asm__("ctime_r");
+double __difftime32() __asm__("difftime");
+int __fstat_time32() __asm__("fstat");
+int __fstatat_time32() __asm__("fstatat");
+int __ftime32() __asm__("ftime");
+int __futimens_time32() __asm__("futimens");
+int __futimes_time32() __asm__("futimes");
+int __futimesat_time32() __asm__("futimesat");
+int __getitimer_time32() __asm__("getitimer");
+int __getrusage_time32() __asm__("getrusage");
+int __gettimeofday_time32() __asm__("gettimeofday");
+struct tm *__gmtime32() __asm__("gmtime");
+struct tm *__gmtime32_r() __asm__("gmtime_r");
+struct tm *__localtime32() __asm__("localtime");
+struct tm *__localtime32_r() __asm__("localtime_r");
+int __lstat_time32() __asm__("lstat");
+int __lutimes_time32() __asm__("lutimes");
+time32_t __mktime32() __asm__("mktime");
+ssize_t __mq_timedreceive_time32() __asm__("mq_timedreceive");
+int __mq_timedsend_time32() __asm__("mq_timedsend");
+int __mtx_timedlock_time32() __asm__("mtx_timedlock");
+int __nanosleep_time32() __asm__("nanosleep");
+int __ppoll_time32() __asm__("ppoll");
+int __pselect_time32() __asm__("pselect");
+int __pthread_cond_timedwait_time32() __asm__("pthread_cond_timedwait");
+int __pthread_mutex_timedlock_time32() __asm__("pthread_mutex_timedlock");
+int __pthread_rwlock_timedrdlock_time32() __asm__("pthread_rwlock_timedrdlock");
+int __pthread_rwlock_timedwrlock_time32() __asm__("pthread_rwlock_timedwrlock");
+int __pthread_timedjoin_np_time32() __asm__("pthread_timedjoin_np");
+int __recvmmsg_time32() __asm__("recvmmsg");
+int __sched_rr_get_interval_time32() __asm__("sched_rr_get_interval");
+int __select_time32() __asm__("select");
+int __sem_timedwait_time32() __asm__("sem_timedwait");
+int __semtimedop_time32() __asm__("semtimedop");
+int __setitimer_time32() __asm__("setitimer");
+int __settimeofday_time32() __asm__("settimeofday");
+int __sigtimedwait_time32() __asm__("sigtimedwait");
+int __stat_time32() __asm__("stat");
+int __stime32() __asm__("stime");
+int __thrd_sleep_time32() __asm__("thrd_sleep");
+time32_t __time32() __asm__("time");
+time32_t __time32gm() __asm__("timegm");
+int __timer_gettime32() __asm__("timer_gettime");
+int __timer_settime32() __asm__("timer_settime");
+int __timerfd_gettime32() __asm__("timerfd_gettime");
+int __timerfd_settime32() __asm__("timerfd_settime");
+int __timespec_get_time32() __asm__("timespec_get");
+int __utime_time32() __asm__("utime");
+int __utimensat_time32() __asm__("utimensat");
+int __utimes_time32() __asm__("utimes");
+pid_t __wait3_time32() __asm__("wait3");
+pid_t __wait4_time32() __asm__("wait4");
+
+#endif
diff --git a/compat/time32/time32gm.c b/compat/time32/time32gm.c
new file mode 100644
index 00000000..60d68fbf
--- /dev/null
+++ b/compat/time32/time32gm.c
@@ -0,0 +1,15 @@
+#define _GNU_SOURCE
+#include "time32.h"
+#include <time.h>
+#include <errno.h>
+#include <stdint.h>
+
+time32_t __time32gm(struct tm *tm)
+{
+ time_t t = timegm(tm);
+ if (t < INT32_MIN || t > INT32_MAX) {
+ errno = EOVERFLOW;
+ return -1;
+ }
+ return t;
+}
diff --git a/compat/time32/timer_gettime32.c b/compat/time32/timer_gettime32.c
new file mode 100644
index 00000000..b4184cc2
--- /dev/null
+++ b/compat/time32/timer_gettime32.c
@@ -0,0 +1,15 @@
+#include "time32.h"
+#include <time.h>
+
+int __timer_gettime32(timer_t t, struct itimerspec32 *val32)
+{
+ struct itimerspec old;
+ int r = timer_gettime(t, &old);
+ if (r) return r;
+ /* No range checking for consistency with settime */
+ val32->it_interval.tv_sec = old.it_interval.tv_sec;
+ val32->it_interval.tv_nsec = old.it_interval.tv_nsec;
+ val32->it_value.tv_sec = old.it_value.tv_sec;
+ val32->it_value.tv_nsec = old.it_value.tv_nsec;
+ return 0;
+}
diff --git a/compat/time32/timer_settime32.c b/compat/time32/timer_settime32.c
new file mode 100644
index 00000000..a447e7d4
--- /dev/null
+++ b/compat/time32/timer_settime32.c
@@ -0,0 +1,25 @@
+#include "time32.h"
+#include <time.h>
+
+int __timer_settime32(timer_t t, int flags, const struct itimerspec32 *restrict val32, struct itimerspec32 *restrict old32)
+{
+ struct itimerspec old;
+ int r = timer_settime(t, flags, (&(struct itimerspec){
+ .it_interval.tv_sec = val32->it_interval.tv_sec,
+ .it_interval.tv_nsec = val32->it_interval.tv_nsec,
+ .it_value.tv_sec = val32->it_value.tv_sec,
+ .it_value.tv_nsec = val32->it_value.tv_nsec}),
+ old32 ? &old : 0);
+ if (r) return r;
+ /* The above call has already committed to success by changing the
+ * timer setting, so we can't fail on out-of-range old value.
+ * Since these are relative times, values large enough to overflow
+ * don't make sense anyway. */
+ if (old32) {
+ old32->it_interval.tv_sec = old.it_interval.tv_sec;
+ old32->it_interval.tv_nsec = old.it_interval.tv_nsec;
+ old32->it_value.tv_sec = old.it_value.tv_sec;
+ old32->it_value.tv_nsec = old.it_value.tv_nsec;
+ }
+ return 0;
+}
diff --git a/compat/time32/timerfd_gettime32.c b/compat/time32/timerfd_gettime32.c
new file mode 100644
index 00000000..75e5435f
--- /dev/null
+++ b/compat/time32/timerfd_gettime32.c
@@ -0,0 +1,16 @@
+#include "time32.h"
+#include <time.h>
+#include <sys/timerfd.h>
+
+int __timerfd_gettime32(int t, struct itimerspec32 *val32)
+{
+ struct itimerspec old;
+ int r = timerfd_gettime(t, &old);
+ if (r) return r;
+ /* No range checking for consistency with settime */
+ val32->it_interval.tv_sec = old.it_interval.tv_sec;
+ val32->it_interval.tv_nsec = old.it_interval.tv_nsec;
+ val32->it_value.tv_sec = old.it_value.tv_sec;
+ val32->it_value.tv_nsec = old.it_value.tv_nsec;
+ return 0;
+}
diff --git a/compat/time32/timerfd_settime32.c b/compat/time32/timerfd_settime32.c
new file mode 100644
index 00000000..67830d34
--- /dev/null
+++ b/compat/time32/timerfd_settime32.c
@@ -0,0 +1,26 @@
+#include "time32.h"
+#include <time.h>
+#include <sys/timerfd.h>
+
+int __timerfd_settime32(int t, int flags, const struct itimerspec32 *restrict val32, struct itimerspec32 *restrict old32)
+{
+ struct itimerspec old;
+ int r = timerfd_settime(t, flags, (&(struct itimerspec){
+ .it_interval.tv_sec = val32->it_interval.tv_sec,
+ .it_interval.tv_nsec = val32->it_interval.tv_nsec,
+ .it_value.tv_sec = val32->it_value.tv_sec,
+ .it_value.tv_nsec = val32->it_value.tv_nsec}),
+ old32 ? &old : 0);
+ if (r) return r;
+ /* The above call has already committed to success by changing the
+ * timer setting, so we can't fail on out-of-range old value.
+ * Since these are relative times, values large enough to overflow
+ * don't make sense anyway. */
+ if (old32) {
+ old32->it_interval.tv_sec = old.it_interval.tv_sec;
+ old32->it_interval.tv_nsec = old.it_interval.tv_nsec;
+ old32->it_value.tv_sec = old.it_value.tv_sec;
+ old32->it_value.tv_nsec = old.it_value.tv_nsec;
+ }
+ return 0;
+}
diff --git a/compat/time32/timespec_get_time32.c b/compat/time32/timespec_get_time32.c
new file mode 100644
index 00000000..e9ca94cb
--- /dev/null
+++ b/compat/time32/timespec_get_time32.c
@@ -0,0 +1,18 @@
+#include "time32.h"
+#include <time.h>
+#include <errno.h>
+#include <stdint.h>
+
+int __timespec_get_time32(struct timespec32 *ts32, int base)
+{
+ struct timespec ts;
+ int r = timespec_get(&ts, base);
+ if (!r) return r;
+ if (ts.tv_sec < INT32_MIN || ts.tv_sec > INT32_MAX) {
+ errno = EOVERFLOW;
+ return 0;
+ }
+ ts32->tv_sec = ts.tv_sec;
+ ts32->tv_nsec = ts.tv_nsec;
+ return r;
+}
diff --git a/compat/time32/utime_time32.c b/compat/time32/utime_time32.c
new file mode 100644
index 00000000..65f11d46
--- /dev/null
+++ b/compat/time32/utime_time32.c
@@ -0,0 +1,14 @@
+#include "time32.h"
+#include <time.h>
+#include <utime.h>
+
+struct utimbuf32 {
+ time32_t actime;
+ time32_t modtime;
+};
+
+int __utime_time32(const char *path, const struct utimbuf32 *times32)
+{
+ return utime(path, !times32 ? 0 : (&(struct utimbuf){
+ .actime = times32->actime, .modtime = times32->modtime}));
+}
diff --git a/compat/time32/utimensat_time32.c b/compat/time32/utimensat_time32.c
new file mode 100644
index 00000000..c687b8d1
--- /dev/null
+++ b/compat/time32/utimensat_time32.c
@@ -0,0 +1,11 @@
+#include "time32.h"
+#include <time.h>
+#include <sys/stat.h>
+
+int __utimensat_time32(int fd, const char *path, const struct timespec32 times32[2], int flags)
+{
+ return utimensat(fd, path, !times32 ? 0 : ((struct timespec[2]){
+ {.tv_sec = times32[0].tv_sec,.tv_nsec = times32[0].tv_nsec},
+ {.tv_sec = times32[1].tv_sec,.tv_nsec = times32[1].tv_nsec}}),
+ flags);
+}
diff --git a/compat/time32/utimes_time32.c b/compat/time32/utimes_time32.c
new file mode 100644
index 00000000..59248f62
--- /dev/null
+++ b/compat/time32/utimes_time32.c
@@ -0,0 +1,11 @@
+#include "time32.h"
+#include <time.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+
+int __utimes_time32(const char *path, const struct timeval32 times32[2])
+{
+ return utimes(path, !times32 ? 0 : ((struct timeval[2]){
+ {.tv_sec = times32[0].tv_sec,.tv_usec = times32[0].tv_usec},
+ {.tv_sec = times32[1].tv_sec,.tv_usec = times32[1].tv_usec}}));
+}
diff --git a/compat/time32/wait3_time32.c b/compat/time32/wait3_time32.c
new file mode 100644
index 00000000..8fe128ed
--- /dev/null
+++ b/compat/time32/wait3_time32.c
@@ -0,0 +1,40 @@
+#define _BSD_SOURCE
+#include "time32.h"
+#include <string.h>
+#include <stddef.h>
+#include <sys/wait.h>
+
+struct compat_rusage {
+ struct timeval32 ru_utime;
+ struct timeval32 ru_stime;
+ long ru_maxrss;
+ long ru_ixrss;
+ long ru_idrss;
+ long ru_isrss;
+ long ru_minflt;
+ long ru_majflt;
+ long ru_nswap;
+ long ru_inblock;
+ long ru_oublock;
+ long ru_msgsnd;
+ long ru_msgrcv;
+ long ru_nsignals;
+ long ru_nvcsw;
+ long ru_nivcsw;
+};
+
+pid_t __wait3_time32(int *status, int options, struct compat_rusage *usage)
+{
+ struct rusage ru;
+ int r = wait3(status, options, usage ? &ru : 0);
+ if (!r && usage) {
+ usage->ru_utime.tv_sec = ru.ru_utime.tv_sec;
+ usage->ru_utime.tv_usec = ru.ru_utime.tv_usec;
+ usage->ru_stime.tv_sec = ru.ru_stime.tv_sec;
+ usage->ru_stime.tv_usec = ru.ru_stime.tv_usec;
+ memcpy(&usage->ru_maxrss, &ru.ru_maxrss,
+ sizeof(struct compat_rusage) -
+ offsetof(struct compat_rusage, ru_maxrss));
+ }
+ return r;
+}
diff --git a/compat/time32/wait4_time32.c b/compat/time32/wait4_time32.c
new file mode 100644
index 00000000..918548e7
--- /dev/null
+++ b/compat/time32/wait4_time32.c
@@ -0,0 +1,40 @@
+#define _BSD_SOURCE
+#include "time32.h"
+#include <string.h>
+#include <stddef.h>
+#include <sys/wait.h>
+
+struct compat_rusage {
+ struct timeval32 ru_utime;
+ struct timeval32 ru_stime;
+ long ru_maxrss;
+ long ru_ixrss;
+ long ru_idrss;
+ long ru_isrss;
+ long ru_minflt;
+ long ru_majflt;
+ long ru_nswap;
+ long ru_inblock;
+ long ru_oublock;
+ long ru_msgsnd;
+ long ru_msgrcv;
+ long ru_nsignals;
+ long ru_nvcsw;
+ long ru_nivcsw;
+};
+
+pid_t __wait4_time32(pid_t pid, int *status, int options, struct compat_rusage *usage)
+{
+ struct rusage ru;
+ int r = wait4(pid, status, options, usage ? &ru : 0);
+ if (!r && usage) {
+ usage->ru_utime.tv_sec = ru.ru_utime.tv_sec;
+ usage->ru_utime.tv_usec = ru.ru_utime.tv_usec;
+ usage->ru_stime.tv_sec = ru.ru_stime.tv_sec;
+ usage->ru_stime.tv_usec = ru.ru_stime.tv_usec;
+ memcpy(&usage->ru_maxrss, &ru.ru_maxrss,
+ sizeof(struct compat_rusage) -
+ offsetof(struct compat_rusage, ru_maxrss));
+ }
+ return r;
+}