mailing list of musl libc
 help / color / mirror / code / Atom feed
* [RFC] final time64 switch-over patch series
@ 2019-08-02 21:44 Rich Felker
  2019-08-04  4:31 ` Rich Felker
                   ` (3 more replies)
  0 siblings, 4 replies; 10+ messages in thread
From: Rich Felker @ 2019-08-02 21:44 UTC (permalink / raw)
  To: musl

[-- Attachment #1: Type: text/plain, Size: 1475 bytes --]

Final in the sense of "final steps needed", not "final version of
patch". These are very much drafts, and subject to change. The time64
commits I've already pushed added time64 syscall backends and removed
assumptions that time_t matches the type used by syscalls; the ones
attached here deal with changing over the public types in a way
compatible with existing code.

I'm looking for feedback on all of this. There are particularly a few
structs not yet adapted yet, where it's not clear what the best way to
lay them out is or how the definition should be selected (based on
_REDIR_TIME64 macro? new bits files? a separate macro controlling
struct layout? ...). There may be significant mistakes in the time32
compat shims, which would break old binaries but otherwise be
inconsequential; it wouldn't result in unfixable ABI breakage.
Analysis of possible things that might go wrong even if everything is
done right are also welcome at this point.

Schedule for going forward with this is open right now. I'm not sure
if it makes sense to quickly put out an experimental release with
this, marked as experimental in case there turn out to be ABI problems
we need to correct, or if it might be better to put out a fairly safe
release soon with the internal time64 support changes and other work,
and publish time64 switchover patches separately as something users
can try applying experimentally. Either 1.1.24 or 1.1.25 should ship
with fully 64-bit time_t, though.

Rich

[-- Attachment #2: 0001-add-time64-symbol-name-redirects-to-public-headers-u.patch --]
[-- Type: text/plain, Size: 12508 bytes --]

From 84a4c907b8ea52e11f8edb9ccc1050e31753e5ca Mon Sep 17 00:00:00 2001
From: Rich Felker <dalias@aerifal.cx>
Date: Wed, 31 Jul 2019 15:24:58 -0400
Subject: [PATCH 1/5] add time64 symbol name redirects to public headers, under
 arch control

a _REDIR_TIME64 macro is introduced, which the arch's alltypes.h is
expected to define, to control redirection of symbol names for
interfaces that involve time_t and derived types. this ensures that
object files will only be linked to libc interfaces matching the ABI
whose headers they were compiled against.

along with time32 compat shims, which will be introduced separately,
the redirection also makes it possible for a single libc (static or
shared) to be used with object files produced with either the old
(32-bit time_t) headers or the new ones after 64-bit time_t switchover
takes place. mixing of such object files (or shared libraries) in the
same program will also be possible, but must be done with care; ABI
between libc and a consumer of the libc interfaces is guaranteed to
match by the the symbol name redirection, but pairwise ABI between
consumers of libc that define interfaces between each other in terms
of time_t is not guaranteed to match.

this change adds a dependency on an additional "GNU C" feature to the
public headers for existing 32-bit archs, which is generally
undesirable; however, the feature is one which glibc has depended on
for a long time, and thus which any viable alternative compiler is
going to need to provide. 64-bit archs are not affected, nor will
future 32-bit archs be, regardless of whether they are "new" on the
kernel side (e.g. riscv32) or just newly-added (e.g. a new sparc or
xtensa port). the same applies to newly-added ABIs for existing
machine-level archs.
---
 include/aio.h          |  4 ++++
 include/mqueue.h       |  5 +++++
 include/poll.h         |  6 ++++++
 include/pthread.h      | 10 ++++++++++
 include/sched.h        |  4 ++++
 include/semaphore.h    |  4 ++++
 include/signal.h       |  8 ++++++++
 include/sys/resource.h |  4 ++++
 include/sys/select.h   |  5 +++++
 include/sys/sem.h      |  6 ++++++
 include/sys/socket.h   |  6 ++++++
 include/sys/stat.h     |  9 +++++++++
 include/sys/time.h     | 14 ++++++++++++++
 include/sys/timeb.h    |  4 ++++
 include/sys/timerfd.h  |  5 +++++
 include/sys/timex.h    |  5 +++++
 include/sys/wait.h     |  7 +++++++
 include/threads.h      |  6 ++++++
 include/time.h         | 28 ++++++++++++++++++++++++++++
 include/utime.h        |  4 ++++
 20 files changed, 144 insertions(+)

diff --git a/include/aio.h b/include/aio.h
index 19bc28a9..482b2a70 100644
--- a/include/aio.h
+++ b/include/aio.h
@@ -62,6 +62,10 @@ int lio_listio(int, struct aiocb *__restrict const *__restrict, int, struct sige
 #define off64_t off_t
 #endif
 
+#if _REDIR_TIME64
+int aio_suspend() __asm__("__aio_suspend_time64");
+#endif
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/include/mqueue.h b/include/mqueue.h
index f5cbe796..49525512 100644
--- a/include/mqueue.h
+++ b/include/mqueue.h
@@ -30,6 +30,11 @@ ssize_t mq_timedreceive(mqd_t, char *__restrict, size_t, unsigned *__restrict, c
 int mq_timedsend(mqd_t, const char *, size_t, unsigned, const struct timespec *);
 int mq_unlink(const char *);
 
+#if _REDIR_TIME64
+ssize_t mq_timedreceive() __asm__("__mq_timedreceive_time64");
+int mq_timedsend() __asm__("__mq_timedsend_time64");
+#endif
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/include/poll.h b/include/poll.h
index daccc760..2aa64ab9 100644
--- a/include/poll.h
+++ b/include/poll.h
@@ -44,6 +44,12 @@ int poll (struct pollfd *, nfds_t, int);
 int ppoll(struct pollfd *, nfds_t, const struct timespec *, const sigset_t *);
 #endif
 
+#if _REDIR_TIME64
+#ifdef _GNU_SOURCE
+int ppoll() __asm__("__ppoll_time64");
+#endif
+#endif
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/include/pthread.h b/include/pthread.h
index e238321b..252ed607 100644
--- a/include/pthread.h
+++ b/include/pthread.h
@@ -224,6 +224,16 @@ int pthread_tryjoin_np(pthread_t, void **);
 int pthread_timedjoin_np(pthread_t, void **, const struct timespec *);
 #endif
 
+#if _REDIR_TIME64
+int pthread_mutex_timedlock() __asm__("__pthread_mutex_timedlock_time64");
+int pthread_cond_timedwait() __asm__("__pthread_cond_timedwait_time64");
+int pthread_rwlock_timedrdlock() __asm__("__pthread_rwlock_timedrdlock_time64");
+int pthread_rwlock_timedwrlock() __asm__("__pthread_rwlock_timedwrlock_time64");
+#ifdef _GNU_SOURCE
+int pthread_timedjoin_np() __asm__("__pthread_timedjoin_np_time64");
+#endif
+#endif
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/include/sched.h b/include/sched.h
index 05d40b1e..6b0e1caa 100644
--- a/include/sched.h
+++ b/include/sched.h
@@ -130,6 +130,10 @@ __CPU_op_func_S(XOR, ^)
 
 #endif
 
+#if _REDIR_TIME64
+int sched_rr_get_interval() __asm__("__sched_rr_get_interval_time64");
+#endif
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/include/semaphore.h b/include/semaphore.h
index 277c47d6..0f9b3a32 100644
--- a/include/semaphore.h
+++ b/include/semaphore.h
@@ -29,6 +29,10 @@ int    sem_trywait(sem_t *);
 int    sem_unlink(const char *);
 int    sem_wait(sem_t *);
 
+#if _REDIR_TIME64
+int sem_timedwait() __asm__("__sem_timedwait_time64");
+#endif
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/include/signal.h b/include/signal.h
index 5c48cb83..5697b291 100644
--- a/include/signal.h
+++ b/include/signal.h
@@ -271,6 +271,14 @@ typedef int sig_atomic_t;
 void (*signal(int, void (*)(int)))(int);
 int raise(int);
 
+#if _REDIR_TIME64
+#if defined(_POSIX_SOURCE) || defined(_POSIX_C_SOURCE) \
+ || defined(_XOPEN_SOURCE) || defined(_GNU_SOURCE) \
+ || defined(_BSD_SOURCE)
+int sigtimedwait() __asm__("__sigtimedwait_time64");
+#endif
+#endif
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/include/sys/resource.h b/include/sys/resource.h
index 70d793d5..7f6771f2 100644
--- a/include/sys/resource.h
+++ b/include/sys/resource.h
@@ -104,6 +104,10 @@ int prlimit(pid_t, int, const struct rlimit *, struct rlimit *);
 #define rlim64_t rlim_t
 #endif
 
+#if _REDIR_TIME64
+int getrusage() __asm__("__getrusage_time64");
+#endif
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/include/sys/select.h b/include/sys/select.h
index d34cbf10..b5de95d3 100644
--- a/include/sys/select.h
+++ b/include/sys/select.h
@@ -35,6 +35,11 @@ int pselect (int, fd_set *__restrict, fd_set *__restrict, fd_set *__restrict, co
 #define NFDBITS (8*(int)sizeof(long))
 #endif
 
+#if _REDIR_TIME64
+int select() __asm__("__select_time64");
+int pselect() __asm__("__pselect_time64");
+#endif
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/include/sys/sem.h b/include/sys/sem.h
index 410c8774..f10bb2e8 100644
--- a/include/sys/sem.h
+++ b/include/sys/sem.h
@@ -62,6 +62,12 @@ int semop(int, struct sembuf *, size_t);
 int semtimedop(int, struct sembuf *, size_t, const struct timespec *);
 #endif
 
+#if _REDIR_TIME64
+#ifdef _GNU_SOURCE
+int semtimedop() __asm__("__semtimedop_time64");
+#endif
+#endif
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/include/sys/socket.h b/include/sys/socket.h
index 8692efa7..7391d48c 100644
--- a/include/sys/socket.h
+++ b/include/sys/socket.h
@@ -350,6 +350,12 @@ int setsockopt (int, int, int, const void *, socklen_t);
 
 int sockatmark (int);
 
+#if _REDIR_TIME64
+#ifdef _GNU_SOURCE
+int recvmmsg() __asm__("__recvmmsg_time64");
+#endif
+#endif
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/include/sys/stat.h b/include/sys/stat.h
index 9d096624..385faafe 100644
--- a/include/sys/stat.h
+++ b/include/sys/stat.h
@@ -110,6 +110,15 @@ int lchmod(const char *, mode_t);
 #define off64_t off_t
 #endif
 
+#if _REDIR_TIME64
+int stat() __asm__("__stat_time64");
+int fstat() __asm__("__fstat_time64");
+int lstat() __asm__("__lstat_time64");
+int fstatat() __asm__("__fstatat_time64");
+int futimens() __asm__("__futimens_time64");
+int utimensat() __asm__("__utimensat_time64");
+#endif
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/include/sys/time.h b/include/sys/time.h
index c5cab814..d4c5d024 100644
--- a/include/sys/time.h
+++ b/include/sys/time.h
@@ -56,6 +56,20 @@ int adjtime (const struct timeval *, struct timeval *);
 	(void)0 )
 #endif
 
+#if _REDIR_TIME64
+int gettimeofday() __asm__("__gettimeofday_time64");
+int getitimer() __asm__("__getitimer_time64");
+int setitimer() __asm__("__setitimer_time64");
+int utimes() __asm__("__utimes_time64");
+#if defined(_GNU_SOURCE) || defined(_BSD_SOURCE)
+int futimes() __asm__("__futimes_time64");
+int futimesat() __asm__("__futimesat_time64");
+int lutimes() __asm__("__lutimes_time64");
+int settimeofday() __asm__("__settimeofday_time64");
+int adjtime() __asm__("__adjtime64");
+#endif
+#endif
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/include/sys/timeb.h b/include/sys/timeb.h
index 108c1f5c..8f4a01da 100644
--- a/include/sys/timeb.h
+++ b/include/sys/timeb.h
@@ -16,6 +16,10 @@ struct timeb {
 
 int ftime(struct timeb *);
 
+#if _REDIR_TIME64
+int ftime() __asm__("__ftime64");
+#endif
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/include/sys/timerfd.h b/include/sys/timerfd.h
index 2794d36a..54b5e3a4 100644
--- a/include/sys/timerfd.h
+++ b/include/sys/timerfd.h
@@ -20,6 +20,11 @@ int timerfd_create(int, int);
 int timerfd_settime(int, int, const struct itimerspec *, struct itimerspec *);
 int timerfd_gettime(int, struct itimerspec *);
 
+#if _REDIR_TIME64
+int timerfd_settime() __asm__("__timerfd_settime64");
+int timerfd_gettime() __asm__("__timerfd_gettime64");
+#endif
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/include/sys/timex.h b/include/sys/timex.h
index 2e688880..9a28d3ed 100644
--- a/include/sys/timex.h
+++ b/include/sys/timex.h
@@ -91,6 +91,11 @@ struct timex {
 int adjtimex(struct timex *);
 int clock_adjtime(clockid_t, struct timex *);
 
+#if _REDIR_TIME64
+int adjtimex() __asm__("__adjtimex_time64");
+int clock_adjtime() __asm__("__clock_adjtime64");
+#endif
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/include/sys/wait.h b/include/sys/wait.h
index 50c5c709..2f40f15b 100644
--- a/include/sys/wait.h
+++ b/include/sys/wait.h
@@ -53,6 +53,13 @@ pid_t wait4 (pid_t, int *, int, struct rusage *);
 #define WIFSIGNALED(s) (((s)&0xffff)-1U < 0xffu)
 #define WIFCONTINUED(s) ((s) == 0xffff)
 
+#if _REDIR_TIME64
+#if defined(_GNU_SOURCE) || defined(_BSD_SOURCE)
+pid_t wait3() __asm__("__wait3_time64");
+pid_t wait4() __asm__("__wait4_time64");
+#endif
+#endif
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/include/threads.h b/include/threads.h
index 8122b3b1..7d683024 100644
--- a/include/threads.h
+++ b/include/threads.h
@@ -80,6 +80,12 @@ void tss_delete(tss_t);
 int tss_set(tss_t, void *);
 void *tss_get(tss_t);
 
+#if _REDIR_TIME64
+int thrd_sleep() __asm__("__thrd_sleep_time64");
+int mtx_timedlock() __asm__("__mtx_timedlock_time64");
+int cnd_timedwait() __asm__("__cnd_timedwait_time64");
+#endif
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/include/time.h b/include/time.h
index 672b3fc3..7fd7f8c4 100644
--- a/include/time.h
+++ b/include/time.h
@@ -130,6 +130,34 @@ int stime(const time_t *);
 time_t timegm(struct tm *);
 #endif
 
+#if _REDIR_TIME64
+time_t time() __asm__("__time64");
+double difftime() __asm__("__difftime64");
+time_t mktime() __asm__("__mktime64");
+struct tm *gmtime() __asm__("__gmtime64");
+struct tm *localtime() __asm__("__localtime64");
+char *ctime() __asm__("__ctime64");
+int timespec_get() __asm__("__timespec_get_time64");
+#if defined(_POSIX_SOURCE) || defined(_POSIX_C_SOURCE) \
+ || defined(_XOPEN_SOURCE) || defined(_GNU_SOURCE) \
+ || defined(_BSD_SOURCE)
+struct tm *gmtime_r() __asm__("__gmtime64_r");
+struct tm *localtime_r() __asm__("__localtime64_r");
+char *ctime_r() __asm__("__ctime64_r");
+int nanosleep() __asm__("__nanosleep_time64");
+int clock_getres() __asm__("__clock_getres_time64");
+int clock_gettime() __asm__("__clock_gettime64");
+int clock_settime() __asm__("__clock_settime64");
+int clock_nanosleep() __asm__("__clock_nanosleep_time64");
+int timer_settime() __asm__("__timer_settime64");
+int timer_gettime() __asm__("__timer_gettime64");
+#endif
+#if defined(_GNU_SOURCE) || defined(_BSD_SOURCE)
+int stime() __asm__("__stime64");
+time_t timegm() __asm__("__timegm_time64");
+#endif
+#endif
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/include/utime.h b/include/utime.h
index dd5ff927..7fc491de 100644
--- a/include/utime.h
+++ b/include/utime.h
@@ -16,6 +16,10 @@ struct utimbuf {
 
 int utime (const char *, const struct utimbuf *);
 
+#if _REDIR_TIME64
+int utime() __asm__("__utime64");
+#endif
+
 #ifdef __cplusplus
 }
 #endif
-- 
2.21.0


[-- Attachment #3: 0002-disable-lfs64-aliases-for-remapped-time64-functions.patch --]
[-- Type: text/plain, Size: 2763 bytes --]

From 830030b265ae3977cfec5a08ffdc231958f5dd7e Mon Sep 17 00:00:00 2001
From: Rich Felker <dalias@aerifal.cx>
Date: Thu, 1 Aug 2019 00:56:48 -0400
Subject: [PATCH 2/5] disable lfs64 aliases for remapped time64 functions

these functions cannot provide the glibc lfs64-ABI-compatible symbols
when time_t differs from what it was in that ABI. instead, the aliases
need to be provided by the time32 compat shims or through some other
mechanism.
---
 src/aio/aio_suspend.c | 2 ++
 src/stat/__xstat.c    | 4 ++++
 src/stat/fstat.c      | 2 ++
 src/stat/fstatat.c    | 2 ++
 src/stat/lstat.c      | 2 ++
 src/stat/stat.c       | 2 ++
 6 files changed, 14 insertions(+)

diff --git a/src/aio/aio_suspend.c b/src/aio/aio_suspend.c
index 9b24b6af..34b66f87 100644
--- a/src/aio/aio_suspend.c
+++ b/src/aio/aio_suspend.c
@@ -73,4 +73,6 @@ int aio_suspend(const struct aiocb *const cbs[], int cnt, const struct timespec
 	}
 }
 
+#if !_REDIR_TIME64
 weak_alias(aio_suspend, aio_suspend64);
+#endif
diff --git a/src/stat/__xstat.c b/src/stat/__xstat.c
index f6303430..630936a0 100644
--- a/src/stat/__xstat.c
+++ b/src/stat/__xstat.c
@@ -1,5 +1,7 @@
 #include <sys/stat.h>
 
+#if !_REDIR_TIME64
+
 int __fxstat(int ver, int fd, struct stat *buf)
 {
 	return fstat(fd, buf);
@@ -25,6 +27,8 @@ weak_alias(__fxstatat, __fxstatat64);
 weak_alias(__lxstat, __lxstat64);
 weak_alias(__xstat, __xstat64);
 
+#endif
+
 int __xmknod(int ver, const char *path, mode_t mode, dev_t *dev)
 {
 	return mknod(path, mode, *dev);
diff --git a/src/stat/fstat.c b/src/stat/fstat.c
index 07f9a5de..9bbb46de 100644
--- a/src/stat/fstat.c
+++ b/src/stat/fstat.c
@@ -10,4 +10,6 @@ int fstat(int fd, struct stat *st)
 	return fstatat(fd, "", st, AT_EMPTY_PATH);
 }
 
+#if !_REDIR_TIME64
 weak_alias(fstat, fstat64);
+#endif
diff --git a/src/stat/fstatat.c b/src/stat/fstatat.c
index d915fa10..bf0a554c 100644
--- a/src/stat/fstatat.c
+++ b/src/stat/fstatat.c
@@ -126,4 +126,6 @@ int fstatat(int fd, const char *restrict path, struct stat *restrict st, int fla
 	return __syscall_ret(ret);
 }
 
+#if !_REDIR_TIME64
 weak_alias(fstatat, fstatat64);
+#endif
diff --git a/src/stat/lstat.c b/src/stat/lstat.c
index 9f95218a..6fe004de 100644
--- a/src/stat/lstat.c
+++ b/src/stat/lstat.c
@@ -6,4 +6,6 @@ int lstat(const char *restrict path, struct stat *restrict buf)
 	return fstatat(AT_FDCWD, path, buf, AT_SYMLINK_NOFOLLOW);
 }
 
+#if !_REDIR_TIME64
 weak_alias(lstat, lstat64);
+#endif
diff --git a/src/stat/stat.c b/src/stat/stat.c
index 528870d2..ea70efc4 100644
--- a/src/stat/stat.c
+++ b/src/stat/stat.c
@@ -6,4 +6,6 @@ int stat(const char *restrict path, struct stat *restrict buf)
 	return fstatat(AT_FDCWD, path, buf, 0);
 }
 
+#if !_REDIR_TIME64
 weak_alias(stat, stat64);
+#endif
-- 
2.21.0


[-- Attachment #4: 0003-make-fstatat-fill-in-old-time32-stat-fields-too.patch --]
[-- Type: text/plain, Size: 2013 bytes --]

From 05a518efb87910dcb2e7814c2c875ded75d20fee Mon Sep 17 00:00:00 2001
From: Rich Felker <dalias@aerifal.cx>
Date: Thu, 1 Aug 2019 21:33:57 -0400
Subject: [PATCH 3/5] make fstatat fill in old time32 stat fields too

here _REDIR_TIME64 is used as an indication that there's an old ABI,
and thereby the old time32 timespec fields of struct stat.

keeping struct stat compatible and providing both versions of the
timespec fields is done so that ftw/nftw does not need painful compat
shims, and (more importantly) so that similar interfaces between pairs
of libc consumers (applications/libraries) will be less likely to
break when one has been rebuilt for time64 but the other has not.
---
 src/stat/fstatat.c | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/src/stat/fstatat.c b/src/stat/fstatat.c
index bf0a554c..de165b5c 100644
--- a/src/stat/fstatat.c
+++ b/src/stat/fstatat.c
@@ -57,6 +57,14 @@ static int fstatat_statx(int fd, const char *restrict path, struct stat *restric
 		.st_mtim.tv_nsec = stx.stx_mtime.tv_nsec,
 		.st_ctim.tv_sec = stx.stx_ctime.tv_sec,
 		.st_ctim.tv_nsec = stx.stx_ctime.tv_nsec,
+#if _REDIR_TIME64
+		.__st_atim32.tv_sec = stx.stx_atime.tv_sec,
+		.__st_atim32.tv_nsec = stx.stx_atime.tv_nsec,
+		.__st_mtim32.tv_sec = stx.stx_mtime.tv_sec,
+		.__st_mtim32.tv_nsec = stx.stx_mtime.tv_nsec,
+		.__st_ctim32.tv_sec = stx.stx_ctime.tv_sec,
+		.__st_ctim32.tv_nsec = stx.stx_ctime.tv_nsec,
+#endif
 	};
 	return 0;
 }
@@ -110,6 +118,14 @@ static int fstatat_kstat(int fd, const char *restrict path, struct stat *restric
 		.st_mtim.tv_nsec = kst.st_mtime_nsec,
 		.st_ctim.tv_sec = kst.st_ctime_sec,
 		.st_ctim.tv_nsec = kst.st_ctime_nsec,
+#if _REDIR_TIME64
+		.__st_atim32.tv_sec = kst.st_atime_sec,
+		.__st_atim32.tv_nsec = kst.st_atime_nsec,
+		.__st_mtim32.tv_sec = kst.st_mtime_sec,
+		.__st_mtim32.tv_nsec = kst.st_mtime_nsec,
+		.__st_ctim32.tv_sec = kst.st_ctime_sec,
+		.__st_ctim32.tv_nsec = kst.st_ctime_nsec,
+#endif
 	};
 
 	return 0;
-- 
2.21.0


[-- Attachment #5: 0004-add-time32-ABI-compat-shims-compat-source-tree.patch --]
[-- Type: text/plain, Size: 48069 bytes --]

From 58ca7e623694655aa17dbfff1206fb7c579256d5 Mon Sep 17 00:00:00 2001
From: Rich Felker <dalias@aerifal.cx>
Date: Fri, 2 Aug 2019 15:52:42 -0400
Subject: [PATCH 4/5] 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.
---
 compat/time32/__xstat.c                       | 22 +++++
 compat/time32/adjtime32.c                     | 19 ++++
 compat/time32/adjtimex_time32.c               | 37 ++++++++
 compat/time32/aio_suspend_time32.c            | 11 +++
 compat/time32/clock_adjtime32.c               | 37 ++++++++
 compat/time32/clock_getres_time32.c           | 12 +++
 compat/time32/clock_gettime32.c               | 18 ++++
 compat/time32/clock_nanosleep_time32.c        | 15 +++
 compat/time32/clock_settime32.c               |  9 ++
 compat/time32/cnd_timedwait_time32.c          |  9 ++
 compat/time32/ctime32.c                       |  7 ++
 compat/time32/ctime32_r.c                     |  7 ++
 compat/time32/difftime32.c                    |  7 ++
 compat/time32/fstat_time32.c                  | 17 ++++
 compat/time32/fstatat_time32.c                | 17 ++++
 compat/time32/ftime32.c                       | 25 +++++
 compat/time32/futimens_time32.c               | 10 ++
 compat/time32/futimes_time32.c                | 12 +++
 compat/time32/futimesat_time32.c              | 12 +++
 compat/time32/getitimer_time32.c              | 15 +++
 compat/time32/getrusage_time32.c              | 30 ++++++
 compat/time32/gettimeofday_time32.c           | 18 ++++
 compat/time32/gmtime32.c                      |  7 ++
 compat/time32/gmtime32_r.c                    |  7 ++
 compat/time32/localtime32.c                   |  7 ++
 compat/time32/localtime32_r.c                 |  7 ++
 compat/time32/lstat_time32.c                  | 17 ++++
 compat/time32/lutimes_time32.c                | 12 +++
 compat/time32/mktime32.c                      | 14 +++
 compat/time32/mq_timedreceive_time32.c        |  9 ++
 compat/time32/mq_timedsend_time32.c           |  9 ++
 compat/time32/mtx_timedlock_time32.c          |  9 ++
 compat/time32/nanosleep_time32.c              | 15 +++
 compat/time32/ppoll_time32.c                  | 10 ++
 compat/time32/pselect_time32.c                |  9 ++
 compat/time32/pthread_cond_timedwait_time32.c |  9 ++
 .../time32/pthread_mutex_timedlock_time32.c   |  9 ++
 .../pthread_rwlock_timedrdlock_time32.c       |  9 ++
 .../pthread_rwlock_timedwrlock_time32.c       |  9 ++
 compat/time32/pthread_timedjoin_np_time32.c   | 10 ++
 compat/time32/recvmmsg_time32.c               | 10 ++
 compat/time32/sched_rr_get_interval_time32.c  | 13 +++
 compat/time32/select_time32.c                 | 10 ++
 compat/time32/sem_timedwait_time32.c          |  9 ++
 compat/time32/semtimedop_time32.c             | 10 ++
 compat/time32/setitimer_time32.c              | 23 +++++
 compat/time32/settimeofday_time32.c           | 10 ++
 compat/time32/sigtimedwait_time32.c           |  9 ++
 compat/time32/stat_time32.c                   | 17 ++++
 compat/time32/stime32.c                       |  8 ++
 compat/time32/thrd_sleep_time32.c             | 16 ++++
 compat/time32/time32.c                        | 15 +++
 compat/time32/time32.h                        | 91 +++++++++++++++++++
 compat/time32/time32gm.c                      | 15 +++
 compat/time32/timer_gettime32.c               | 15 +++
 compat/time32/timer_settime32.c               | 22 +++++
 compat/time32/timerfd_gettime32.c             | 16 ++++
 compat/time32/timerfd_settime32.c             | 23 +++++
 compat/time32/timespec_get_time32.c           | 18 ++++
 compat/time32/utime_time32.c                  | 14 +++
 compat/time32/utimensat_time32.c              | 11 +++
 compat/time32/utimes_time32.c                 | 11 +++
 compat/time32/wait3_time32.c                  | 31 +++++++
 compat/time32/wait4_time32.c                  | 31 +++++++
 64 files changed, 992 insertions(+)
 create mode 100644 compat/time32/__xstat.c
 create mode 100644 compat/time32/adjtime32.c
 create mode 100644 compat/time32/adjtimex_time32.c
 create mode 100644 compat/time32/aio_suspend_time32.c
 create mode 100644 compat/time32/clock_adjtime32.c
 create mode 100644 compat/time32/clock_getres_time32.c
 create mode 100644 compat/time32/clock_gettime32.c
 create mode 100644 compat/time32/clock_nanosleep_time32.c
 create mode 100644 compat/time32/clock_settime32.c
 create mode 100644 compat/time32/cnd_timedwait_time32.c
 create mode 100644 compat/time32/ctime32.c
 create mode 100644 compat/time32/ctime32_r.c
 create mode 100644 compat/time32/difftime32.c
 create mode 100644 compat/time32/fstat_time32.c
 create mode 100644 compat/time32/fstatat_time32.c
 create mode 100644 compat/time32/ftime32.c
 create mode 100644 compat/time32/futimens_time32.c
 create mode 100644 compat/time32/futimes_time32.c
 create mode 100644 compat/time32/futimesat_time32.c
 create mode 100644 compat/time32/getitimer_time32.c
 create mode 100644 compat/time32/getrusage_time32.c
 create mode 100644 compat/time32/gettimeofday_time32.c
 create mode 100644 compat/time32/gmtime32.c
 create mode 100644 compat/time32/gmtime32_r.c
 create mode 100644 compat/time32/localtime32.c
 create mode 100644 compat/time32/localtime32_r.c
 create mode 100644 compat/time32/lstat_time32.c
 create mode 100644 compat/time32/lutimes_time32.c
 create mode 100644 compat/time32/mktime32.c
 create mode 100644 compat/time32/mq_timedreceive_time32.c
 create mode 100644 compat/time32/mq_timedsend_time32.c
 create mode 100644 compat/time32/mtx_timedlock_time32.c
 create mode 100644 compat/time32/nanosleep_time32.c
 create mode 100644 compat/time32/ppoll_time32.c
 create mode 100644 compat/time32/pselect_time32.c
 create mode 100644 compat/time32/pthread_cond_timedwait_time32.c
 create mode 100644 compat/time32/pthread_mutex_timedlock_time32.c
 create mode 100644 compat/time32/pthread_rwlock_timedrdlock_time32.c
 create mode 100644 compat/time32/pthread_rwlock_timedwrlock_time32.c
 create mode 100644 compat/time32/pthread_timedjoin_np_time32.c
 create mode 100644 compat/time32/recvmmsg_time32.c
 create mode 100644 compat/time32/sched_rr_get_interval_time32.c
 create mode 100644 compat/time32/select_time32.c
 create mode 100644 compat/time32/sem_timedwait_time32.c
 create mode 100644 compat/time32/semtimedop_time32.c
 create mode 100644 compat/time32/setitimer_time32.c
 create mode 100644 compat/time32/settimeofday_time32.c
 create mode 100644 compat/time32/sigtimedwait_time32.c
 create mode 100644 compat/time32/stat_time32.c
 create mode 100644 compat/time32/stime32.c
 create mode 100644 compat/time32/thrd_sleep_time32.c
 create mode 100644 compat/time32/time32.c
 create mode 100644 compat/time32/time32.h
 create mode 100644 compat/time32/time32gm.c
 create mode 100644 compat/time32/timer_gettime32.c
 create mode 100644 compat/time32/timer_settime32.c
 create mode 100644 compat/time32/timerfd_gettime32.c
 create mode 100644 compat/time32/timerfd_settime32.c
 create mode 100644 compat/time32/timespec_get_time32.c
 create mode 100644 compat/time32/utime_time32.c
 create mode 100644 compat/time32/utimensat_time32.c
 create mode 100644 compat/time32/utimes_time32.c
 create mode 100644 compat/time32/wait3_time32.c
 create mode 100644 compat/time32/wait4_time32.c

diff --git a/compat/time32/__xstat.c b/compat/time32/__xstat.c
new file mode 100644
index 00000000..d8cd5ef2
--- /dev/null
+++ b/compat/time32/__xstat.c
@@ -0,0 +1,22 @@
+#include "time32.h"
+#include <sys/stat.h>
+
+int __fxstat64(int ver, int fd, struct stat *buf)
+{
+	return __fstat_time32(fd, buf);
+}
+
+int __fxstatat64(int ver, int fd, const char *path, struct stat *buf, int flag)
+{
+	return __fstatat_time32(fd, path, buf, flag);
+}
+
+int __lxstat64(int ver, const char *path, struct stat *buf)
+{
+	return __lstat_time32(path, buf);
+}
+
+int __xstat64(int ver, const char *path, struct stat *buf)
+{
+	return __stat_time32(path, buf);
+}
diff --git a/compat/time32/adjtime32.c b/compat/time32/adjtime32.c
new file mode 100644
index 00000000..459be156
--- /dev/null
+++ b/compat/time32/adjtime32.c
@@ -0,0 +1,19 @@
+#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. */
+	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..d7541da8
--- /dev/null
+++ b/compat/time32/adjtimex_time32.c
@@ -0,0 +1,37 @@
+#include "time32.h"
+#include <time.h>
+#include <sys/time.h>
+#include <sys/timex.h>
+#include <string.h>
+#include <stddef.h>
+
+struct oldtimex {
+	unsigned modes;
+	long offset, freq, maxerror, esterror;
+	int status;
+	long constant, precision, tolerance;
+	long time_sec, time_usec;
+	long tick, ppsfreq, jitter;
+	int shift;
+	long stabil, jitcnt, calcnt, errcnt, stbcnt;
+	int tai;
+	int __padding[11];
+};
+
+int __adjtimex_time32(struct timex *tx32)
+{
+	struct timex utx;
+	memcpy(&utx, tx32, sizeof(struct oldtimex));
+	utx.time.tv_sec =
+		*(long *)((char *)tx32 + offsetof(struct oldtimex,time_sec));
+	utx.time.tv_usec =
+		*(long *)((char *)tx32 + offsetof(struct oldtimex,time_usec));
+	int r = adjtimex(&utx);
+	if (r<0) return r;
+	memcpy(tx32, &utx, sizeof(struct oldtimex));
+	*(long *)((char *)tx32 + offsetof(struct oldtimex,time_sec)) =
+		utx.time.tv_sec;
+	*(long *)((char *)tx32 + offsetof(struct oldtimex,time_usec)) =
+		utx.time.tv_usec;
+	return r;
+}
diff --git a/compat/time32/aio_suspend_time32.c b/compat/time32/aio_suspend_time32.c
new file mode 100644
index 00000000..0c43ddee
--- /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, (&(struct timespec){
+		.tv_sec = ts32->tv_sec, .tv_nsec = ts32->tv_nsec}));
+}
+
+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..36cc6053
--- /dev/null
+++ b/compat/time32/clock_adjtime32.c
@@ -0,0 +1,37 @@
+#include "time32.h"
+#include <time.h>
+#include <sys/time.h>
+#include <sys/timex.h>
+#include <string.h>
+#include <stddef.h>
+
+struct oldtimex {
+	unsigned modes;
+	long offset, freq, maxerror, esterror;
+	int status;
+	long constant, precision, tolerance;
+	long time_sec, time_usec;
+	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 timex *tx32)
+{
+	struct timex utx;
+	memcpy(&utx, tx32, sizeof(struct oldtimex));
+	utx.time.tv_sec =
+		*(long *)((char *)tx32 + offsetof(struct oldtimex,time_sec));
+	utx.time.tv_usec =
+		*(long *)((char *)tx32 + offsetof(struct oldtimex,time_usec));
+	int r = clock_adjtime(clock_id, &utx);
+	if (r<0) return r;
+	memcpy(tx32, &utx, sizeof(struct oldtimex));
+	*(long *)((char *)tx32 + offsetof(struct oldtimex,time_sec)) =
+		utx.time.tv_sec;
+	*(long *)((char *)tx32 + offsetof(struct oldtimex,time_usec)) =
+		utx.time.tv_usec;
+	return r;
+}
diff --git a/compat/time32/clock_getres_time32.c b/compat/time32/clock_getres_time32.c
new file mode 100644
index 00000000..ab110b36
--- /dev/null
+++ b/compat/time32/clock_getres_time32.c
@@ -0,0 +1,12 @@
+#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) return r;
+	ts32->tv_sec = ts.tv_sec;
+	ts32->tv_nsec = ts.tv_nsec;
+	return 0;
+}
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..46e7e289
--- /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, (&(struct timespec){
+		.tv_sec = ts32->tv_sec, .tv_nsec = ts32->tv_nsec}));
+}
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..15c65709
--- /dev/null
+++ b/compat/time32/difftime32.c
@@ -0,0 +1,7 @@
+#include "time32.h"
+#include <time.h>
+
+double __difftime32(time32_t t1, time_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..06fa1947
--- /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, ((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..edee9f48
--- /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, ((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..54d1573c
--- /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, ((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..f8bd46ba
--- /dev/null
+++ b/compat/time32/getrusage_time32.c
@@ -0,0 +1,30 @@
+#include "time32.h"
+#include <string.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 rusage *usage)
+{
+	struct rusage ru;
+	int r = getrusage(who, &ru);
+	if (!r) memcpy(usage, &ru, sizeof(struct compat_rusage));
+	return r;
+}
diff --git a/compat/time32/gettimeofday_time32.c b/compat/time32/gettimeofday_time32.c
new file mode 100644
index 00000000..3a39775c
--- /dev/null
+++ b/compat/time32/gettimeofday_time32.c
@@ -0,0 +1,18 @@
+#include "time32.h"
+#include <sys/time.h>
+#include <errno.h>
+#include <stdint.h>
+
+int __gettimeofday_time32(struct timeval32 *tv32, void *tz)
+{
+	struct timeval tv;
+	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..b418a2eb
--- /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, ((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..aeef9890
--- /dev/null
+++ b/compat/time32/mktime32.c
@@ -0,0 +1,14 @@
+#include "time32.h"
+#include <time.h>
+#include <errno.h>
+#include <stdint.h>
+
+time32_t __mktime32(struct tm *tm)
+{
+	time_t t = mktime(tm);
+	if (t < INT32_MIN || t > INT32_MAX) {
+		errno = EOVERFLOW;
+		return -1;
+	}
+	return t;
+}
diff --git a/compat/time32/mq_timedreceive_time32.c b/compat/time32/mq_timedreceive_time32.c
new file mode 100644
index 00000000..25abe9da
--- /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, (&(struct timespec){
+		.tv_sec = ts32->tv_sec, .tv_nsec = ts32->tv_nsec}));
+}
diff --git a/compat/time32/mq_timedsend_time32.c b/compat/time32/mq_timedsend_time32.c
new file mode 100644
index 00000000..2a2fea3d
--- /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, (&(struct timespec){
+		.tv_sec = ts32->tv_sec, .tv_nsec = ts32->tv_nsec}));
+}
diff --git a/compat/time32/mtx_timedlock_time32.c b/compat/time32/mtx_timedlock_time32.c
new file mode 100644
index 00000000..7f187be8
--- /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, (&(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..d1eef134
--- /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, (&(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..5b358c73
--- /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, (&(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..4f80a362
--- /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, (&(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..400a0120
--- /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, (&(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..20a8d1d0
--- /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, (&(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..d7c100b5
--- /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, (&(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..0cd84026
--- /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 timespec *at32)
+{
+	return pthread_timedjoin_np(t, res, (&(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..48db6940
--- /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, (&(struct timespec){
+		.tv_sec = ts32->tv_sec, .tv_nsec = ts32->tv_nsec}));
+}
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..2bd76e33
--- /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, (&(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..a27a41dc
--- /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, (&(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..cd3c5c4a
--- /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, (&(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..181d23a7
--- /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((&(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..f9a525c5
--- /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, (&(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..95d597fe
--- /dev/null
+++ b/compat/time32/timer_settime32.c
@@ -0,0 +1,22 @@
+#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}), &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_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..a16e2700
--- /dev/null
+++ b/compat/time32/timerfd_settime32.c
@@ -0,0 +1,23 @@
+#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}), &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_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..32454ddc
--- /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, (&(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..3d8e320d
--- /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, ((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..94abb5e7
--- /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, ((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..f8e63b91
--- /dev/null
+++ b/compat/time32/wait3_time32.c
@@ -0,0 +1,31 @@
+#define _BSD_SOURCE
+#include "time32.h"
+#include <string.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 rusage *usage)
+{
+	struct rusage ru;
+	int r = wait3(status, options, &ru);
+	if (!r) memcpy(usage, &ru, sizeof(struct compat_rusage));
+	return r;
+}
diff --git a/compat/time32/wait4_time32.c b/compat/time32/wait4_time32.c
new file mode 100644
index 00000000..7fbf5397
--- /dev/null
+++ b/compat/time32/wait4_time32.c
@@ -0,0 +1,31 @@
+#define _BSD_SOURCE
+#include "time32.h"
+#include <string.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 rusage *usage)
+{
+	struct rusage ru;
+	int r = wait4(pid, status, options, &ru);
+	if (!r) memcpy(usage, &ru, sizeof(struct compat_rusage));
+	return r;
+}
-- 
2.21.0


[-- Attachment #6: 0005-RFC-POC-switch-i386-to-64-bit-time_t.patch --]
[-- Type: text/plain, Size: 7573 bytes --]

From 3c6bde03ecf2aa7dac605f0a55a1be201f3d4c5f Mon Sep 17 00:00:00 2001
From: Rich Felker <dalias@aerifal.cx>
Date: Fri, 2 Aug 2019 15:41:27 -0400
Subject: [PATCH 5/5] [RFC] [POC] switch i386 to 64-bit time_t

this is a proof of concept for converting one 32-bit arch, i386, to
64-bit time_t. known issues:

1. the switchover of timespec padding is a hack, and needs to be done
right, but that involves making alltypes.h aware of endianness, which
probably should have been done a long time ago anyway and would get
rid of inappropriate inclusion of <endian.h> in some places.

2. the rusage, utmpx, and timex structs are not correct with regard to
ABI or functionality. they need to be fixed before this is safe to
use.

3. Makefile change should be its own thing.

there are likely a lot more problems.

before upstreaming this can even be considered, heavy review of the
new ABI that's produced is needed. once this is in use, mistakes will
be permanent. we may also need to review for interfaces where the new
type is not likely to match whatever glibc ends up adopting, and
renaming the redirected symbols for those interfaces to names that
don't overlap with what glibc intends to use, so that producing future
ABI-compat layers is less painful.
---
 Makefile                     |  5 +++--
 arch/i386/arch.mak           |  1 +
 arch/i386/bits/alltypes.h.in |  5 +++--
 arch/i386/bits/ipcstat.h     |  2 +-
 arch/i386/bits/msg.h         | 15 +++++++++------
 arch/i386/bits/sem.h         | 10 ++++++----
 arch/i386/bits/shm.h         | 16 ++++++++++------
 arch/i386/bits/stat.h        |  6 +++++-
 arch/i386/syscall_arch.h     |  4 +++-
 include/alltypes.h.in        |  2 +-
 10 files changed, 42 insertions(+), 24 deletions(-)
 create mode 100644 arch/i386/arch.mak

diff --git a/Makefile b/Makefile
index b46f8ca4..d636f5a9 100644
--- a/Makefile
+++ b/Makefile
@@ -17,7 +17,7 @@ includedir = $(prefix)/include
 libdir = $(prefix)/lib
 syslibdir = /lib
 
-SRC_DIRS = $(addprefix $(srcdir)/,src/* crt ldso)
+SRC_DIRS = $(addprefix $(srcdir)/,src/* crt ldso $(COMPAT_SRC_DIRS))
 BASE_GLOBS = $(addsuffix /*.c,$(SRC_DIRS))
 ARCH_GLOBS = $(addsuffix /$(ARCH)/*.[csS],$(SRC_DIRS))
 BASE_SRCS = $(sort $(wildcard $(BASE_GLOBS)))
@@ -27,7 +27,7 @@ ARCH_OBJS = $(patsubst $(srcdir)/%,%.o,$(basename $(ARCH_SRCS)))
 REPLACED_OBJS = $(sort $(subst /$(ARCH)/,/,$(ARCH_OBJS)))
 ALL_OBJS = $(addprefix obj/, $(filter-out $(REPLACED_OBJS), $(sort $(BASE_OBJS) $(ARCH_OBJS))))
 
-LIBC_OBJS = $(filter obj/src/%,$(ALL_OBJS))
+LIBC_OBJS = $(filter obj/src/%,$(ALL_OBJS)) $(filter obj/compat/%,$(ALL_OBJS))
 LDSO_OBJS = $(filter obj/ldso/%,$(ALL_OBJS:%.o=%.lo))
 CRT_OBJS = $(filter obj/crt/%,$(ALL_OBJS))
 
@@ -75,6 +75,7 @@ WRAPCC_CLANG = clang
 LDSO_PATHNAME = $(syslibdir)/ld-musl-$(ARCH)$(SUBARCH).so.1
 
 -include config.mak
+-include arch/$(ARCH)/arch.mak
 
 ifeq ($(ARCH),)
 
diff --git a/arch/i386/arch.mak b/arch/i386/arch.mak
new file mode 100644
index 00000000..aa4d05ce
--- /dev/null
+++ b/arch/i386/arch.mak
@@ -0,0 +1 @@
+COMPAT_SRC_DIRS = compat/time32
diff --git a/arch/i386/bits/alltypes.h.in b/arch/i386/bits/alltypes.h.in
index 1a8432d3..8069271b 100644
--- a/arch/i386/bits/alltypes.h.in
+++ b/arch/i386/bits/alltypes.h.in
@@ -1,3 +1,4 @@
+#define _REDIR_TIME64 1
 #define _Addr int
 #define _Int64 long long
 #define _Reg int
@@ -34,8 +35,8 @@ TYPEDEF struct { __attribute__((__aligned__(8))) long long __ll; long double __l
 TYPEDEF struct { alignas(8) long long __ll; long double __ld; } max_align_t;
 #endif
 
-TYPEDEF long time_t;
-TYPEDEF long suseconds_t;
+TYPEDEF long long time_t;
+TYPEDEF long long suseconds_t;
 
 TYPEDEF struct { union { int __i[9]; volatile int __vi[9]; unsigned __s[9]; } __u; } pthread_attr_t;
 TYPEDEF struct { union { int __i[6]; volatile int __vi[6]; volatile void *volatile __p[6]; } __u; } pthread_mutex_t;
diff --git a/arch/i386/bits/ipcstat.h b/arch/i386/bits/ipcstat.h
index 0018ad1e..4f4fcb0c 100644
--- a/arch/i386/bits/ipcstat.h
+++ b/arch/i386/bits/ipcstat.h
@@ -1 +1 @@
-#define IPC_STAT 2
+#define IPC_STAT 0x102
diff --git a/arch/i386/bits/msg.h b/arch/i386/bits/msg.h
index bc8436c4..7bbbb2bf 100644
--- a/arch/i386/bits/msg.h
+++ b/arch/i386/bits/msg.h
@@ -1,15 +1,18 @@
 struct msqid_ds {
 	struct ipc_perm msg_perm;
-	time_t msg_stime;
-	int __unused1;
-	time_t msg_rtime;
-	int __unused2;
-	time_t msg_ctime;
-	int __unused3;
+	unsigned long __msg_stime_lo;
+	unsigned long __msg_stime_hi;
+	unsigned long __msg_rtime_lo;
+	unsigned long __msg_rtime_hi;
+	unsigned long __msg_ctime_lo;
+	unsigned long __msg_ctime_hi;
 	unsigned long msg_cbytes;
 	msgqnum_t msg_qnum;
 	msglen_t msg_qbytes;
 	pid_t msg_lspid;
 	pid_t msg_lrpid;
 	unsigned long __unused[2];
+	time_t msg_stime;
+	time_t msg_rtime;
+	time_t msg_ctime;
 };
diff --git a/arch/i386/bits/sem.h b/arch/i386/bits/sem.h
index e61571c1..65661542 100644
--- a/arch/i386/bits/sem.h
+++ b/arch/i386/bits/sem.h
@@ -1,11 +1,13 @@
 struct semid_ds {
 	struct ipc_perm sem_perm;
-	time_t sem_otime;
-	long __unused1;
-	time_t sem_ctime;
-	long __unused2;
+	unsigned long __sem_otime_lo;
+	unsigned long __sem_otime_hi;
+	unsigned long __sem_ctime_lo;
+	unsigned long __sem_ctime_hi;
 	unsigned short sem_nsems;
 	char __sem_nsems_pad[sizeof(long)-sizeof(short)];
 	long __unused3;
 	long __unused4;
+	time_t sem_otime;
+	time_t sem_ctime;
 };
diff --git a/arch/i386/bits/shm.h b/arch/i386/bits/shm.h
index 81b2a29a..725fb469 100644
--- a/arch/i386/bits/shm.h
+++ b/arch/i386/bits/shm.h
@@ -3,17 +3,21 @@
 struct shmid_ds {
 	struct ipc_perm shm_perm;
 	size_t shm_segsz;
-	time_t shm_atime;
-	int __unused1;
-	time_t shm_dtime;
-	int __unused2;
-	time_t shm_ctime;
-	int __unused3;
+	unsigned long __shm_atime_lo;
+	unsigned long __shm_atime_hi;
+	unsigned long __shm_dtime_lo;
+	unsigned long __shm_dtime_hi;
+	unsigned long __shm_ctime_lo;
+	unsigned long __shm_ctime_hi;
 	pid_t shm_cpid;
 	pid_t shm_lpid;
 	unsigned long shm_nattch;
 	unsigned long __pad1;
 	unsigned long __pad2;
+	unsigned long __pad3;
+	time_t shm_atime;
+	time_t shm_dtime;
+	time_t shm_ctime;
 };
 
 struct shminfo {
diff --git a/arch/i386/bits/stat.h b/arch/i386/bits/stat.h
index 22b19bbf..5d7828cf 100644
--- a/arch/i386/bits/stat.h
+++ b/arch/i386/bits/stat.h
@@ -14,8 +14,12 @@ struct stat {
 	off_t st_size;
 	blksize_t st_blksize;
 	blkcnt_t st_blocks;
+	struct {
+		long tv_sec;
+		long tv_nsec;
+	} __st_atim32, __st_mtim32, __st_ctim32;
+	ino_t st_ino;
 	struct timespec st_atim;
 	struct timespec st_mtim;
 	struct timespec st_ctim;
-	ino_t st_ino;
 };
diff --git a/arch/i386/syscall_arch.h b/arch/i386/syscall_arch.h
index 22b0b28b..69642e57 100644
--- a/arch/i386/syscall_arch.h
+++ b/arch/i386/syscall_arch.h
@@ -83,7 +83,9 @@ static inline long __syscall6(long n, long a1, long a2, long a3, long a4, long a
 }
 
 #define VDSO_USEFUL
-#define VDSO_CGT_SYM "__vdso_clock_gettime"
+#define VDSO_CGT32_SYM "__vdso_clock_gettime"
+#define VDSO_CGT32_VER "LINUX_2.6"
+#define VDSO_CGT_SYM "__vdso_clock_gettime64"
 #define VDSO_CGT_VER "LINUX_2.6"
 
 #define SYSCALL_USE_SOCKETCALL
diff --git a/include/alltypes.h.in b/include/alltypes.h.in
index 4cc879b1..b90ea7fa 100644
--- a/include/alltypes.h.in
+++ b/include/alltypes.h.in
@@ -35,7 +35,7 @@ TYPEDEF void * timer_t;
 TYPEDEF int clockid_t;
 TYPEDEF long clock_t;
 STRUCT timeval { time_t tv_sec; suseconds_t tv_usec; };
-STRUCT timespec { time_t tv_sec; long tv_nsec; };
+STRUCT timespec { time_t tv_sec; long tv_nsec; long :8*(sizeof(time_t)-sizeof(long)); };
 
 TYPEDEF int pid_t;
 TYPEDEF unsigned id_t;
-- 
2.21.0


^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2019-08-15  0:30 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-08-02 21:44 [RFC] final time64 switch-over patch series Rich Felker
2019-08-04  4:31 ` Rich Felker
2019-08-09 16:30   ` Rich Felker
2019-08-09 17:00     ` Rich Felker
2019-08-04  4:33 ` Rich Felker
2019-08-04 18:11   ` Rich Felker
2019-08-09 14:48 ` Rich Felker
2019-08-14 23:55 ` Rich Felker
2019-08-15  0:13   ` Rich Felker
2019-08-15  0:30     ` Rich Felker

Code repositories for project(s) associated with this public inbox

	https://git.vuxu.org/mirror/musl/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).