[RFC] final time64 switch-over patch series

Submitted by Rich Felker on Aug. 2, 2019, 9:44 p.m.

Details

Message ID 20190802214433.GA25193@brightrain.aerifal.cx
State New
Series "final time64 switch-over patch series"
Headers show

Commit Message

Rich Felker Aug. 2, 2019, 9:44 p.m.
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
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(+)

Patch hide | download patch | download mbox

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

Comments

Rich Felker Aug. 4, 2019, 4:31 a.m.
On Fri, Aug 02, 2019 at 05:44:33PM -0400, Rich Felker wrote:
> >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.

Add a significant incomplete prerequisite I forgot about: dlsym. There
needs to be a redirection for dlsym, and 32-bit archs need a second
asm entry point for __dlsym_time64 which first checks the symbol name
against the list of time64 redirections and rewrites the request if
it's a match.

Rich
Rich Felker Aug. 4, 2019, 4:33 a.m.
On Fri, Aug 02, 2019 at 05:44:33PM -0400, Rich Felker wrote:
> 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/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}));
> +}

These all fail to check the timeout argument is non-null before
accessing it; caught in testing against Adelie and Alpine i386.

There may be other functions with the same problem; need to review
them all for this.

Rich
Rich Felker Aug. 9, 2019, 2:48 p.m.
On Fri, Aug 02, 2019 at 05:44:33PM -0400, Rich Felker wrote:
> 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.

There's also struct sched_param, which contains gratuitous timespecs
for functionality we don't (and probably won't ever) support. This
will gratuitously change size when we change time_t unless action is
taken to prevent it.

My leaning here is to just remove all the sched_ss_* stuff and replace
it with same-sized reserved space. But nothing in libc accesses (read
or write) anything but a single int (sched_prio) via the sched_param
pointer, so we could just leave it and let the size change; this would
not affect any ABI between libc and the libc-consumer, but it would
affect ABI between pairs of libc-consumers using the type (which seems
pretty far-fetched. Also, we would never be able to access the
additional space it's expanded to, because the caller could plausibly
be an old binary that allocated less space. So I think just getting
rid of it all is best.

Note that we lack a proper type (what would essentially be
"__old_time_t") to define same-size replacement for the members to be
removed and replaced with "reserved" space. This is the same type we
need in rusage, timex, and possibly utmpx as well.

I could make alltypes.h define __OLD_TIME_T or something, and
conditionally use it if it's defined. But I think it may be cleaner to
use _REDIR_TIME64 as a proxy for this. If _REDIR_TIME64 is defined,
that means there's an old ABI to be matched, and "old time_t"
necessarily has type long, which is necessarily 32-bit in such a case.
So it could be something like:

struct sched_param {
	int sched_priority;
	int __reserved1;
#if _REDIR_TIME64
	long __reserved2[4];
#else
	long long __reserved2[4];
#endif
	int __reserved3;
};

Note that the only reason this is needed is x32; otherwise, long would
always be the right size to match the old time_t. But in the other
places, it's more useful:

struct rusage {
#if _REDIR_TIME64
	struct {
		long tv_sec, tv_usec;
	} __ru_utime32, __ru_stime32;
#else
	struct timeval ru_utime;
	struct timeval ru_stime;
#endif
	/* linux extentions, but useful */
	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;
#if _REDIR_TIME64
	long    __reserved[8];
	struct timeval ru_utime;
	struct timeval ru_stime;
#else
	long    __reserved[16];
#endif
};

Note that using the reserved space to avoid increasing the size here
isn't really useful, since the alignment requirement may change too
due to introduction of 64-bit members, but it might make things more
robust against ABI mismatches (likely SIGBUS instead of OOB
load/store).

Rich
Rich Felker Aug. 9, 2019, 4:30 p.m.
On Sun, Aug 04, 2019 at 12:31:09AM -0400, Rich Felker wrote:
> On Fri, Aug 02, 2019 at 05:44:33PM -0400, Rich Felker wrote:
> > >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.
> 
> Add a significant incomplete prerequisite I forgot about: dlsym. There
> needs to be a redirection for dlsym, and 32-bit archs need a second
> asm entry point for __dlsym_time64 which first checks the symbol name
> against the list of time64 redirections and rewrites the request if
> it's a match.

For reference, here's the first draft of the redirection. The worst
part of it is the large relro string table.

Rich
#include <dlfcn.h>
#include <string.h>
#include <stdlib.h>
#include "dynlink.h"

const char *const mapping[][2] = {
{ "adjtime", "__adjtime64" },
{ "adjtimex", "__adjtimex_time64" },
{ "aio_suspend", "__aio_suspend_time64" },
{ "clock_adjtime", "__clock_adjtime64" },
{ "clock_getres", "__clock_getres_time64" },
{ "clock_gettime", "__clock_gettime64" },
{ "clock_nanosleep", "__clock_nanosleep_time64" },
{ "clock_settime", "__clock_settime64" },
{ "cnd_timedwait", "__cnd_timedwait_time64" },
{ "ctime", "__ctime64" },
{ "ctime_r", "__ctime64_r" },
{ "difftime", "__difftime64" },
{ "dlsym", "__dlsym_time64" },
{ "fstat", "__fstat_time64" },
{ "fstatat", "__fstatat_time64" },
{ "ftime", "__ftime64" },
{ "futimens", "__futimens_time64" },
{ "futimes", "__futimes_time64" },
{ "futimesat", "__futimesat_time64" },
{ "getitimer", "__getitimer_time64" },
{ "getrusage", "__getrusage_time64" },
{ "gettimeofday", "__gettimeofday_time64" },
{ "gmtime", "__gmtime64" },
{ "gmtime_r", "__gmtime64_r" },
{ "localtime", "__localtime64" },
{ "localtime_r", "__localtime64_r" },
{ "lstat", "__lstat_time64" },
{ "lutimes", "__lutimes_time64" },
{ "mktime", "__mktime64" },
{ "mq_timedreceive", "__mq_timedreceive_time64" },
{ "mq_timedsend", "__mq_timedsend_time64" },
{ "mtx_timedlock", "__mtx_timedlock_time64" },
{ "nanosleep", "__nanosleep_time64" },
{ "ppoll", "__ppoll_time64" },
{ "pselect", "__pselect_time64" },
{ "pthread_cond_timedwait", "__pthread_cond_timedwait_time64" },
{ "pthread_mutex_timedlock", "__pthread_mutex_timedlock_time64" },
{ "pthread_rwlock_timedrdlock", "__pthread_rwlock_timedrdlock_time64" },
{ "pthread_rwlock_timedwrlock", "__pthread_rwlock_timedwrlock_time64" },
{ "pthread_timedjoin_np", "__pthread_timedjoin_np_time64" },
{ "recvmmsg", "__recvmmsg_time64" },
{ "sched_rr_get_interval", "__sched_rr_get_interval_time64" },
{ "select", "__select_time64" },
{ "sem_timedwait", "__sem_timedwait_time64" },
{ "semtimedop", "__semtimedop_time64" },
{ "setitimer", "__setitimer_time64" },
{ "settimeofday", "__settimeofday_time64" },
{ "sigtimedwait", "__sigtimedwait_time64" },
{ "stat", "__stat_time64" },
{ "stime", "__stime64" },
{ "thrd_sleep", "__thrd_sleep_time64" },
{ "time", "__time64" },
{ "timegm", "__timegm_time64" },
{ "timer_gettime", "__timer_gettime64" },
{ "timer_settime", "__timer_settime64" },
{ "timerfd_gettime", "__timerfd_gettime64" },
{ "timerfd_settime", "__timerfd_settime64" },
{ "timespec_get", "__timespec_get_time64" },
{ "utime", "__utime64" },
{ "utimensat", "__utimensat_time64" },
{ "utimes", "__utimes_time64" },
{ "wait3", "__wait3_time64" },
{ "wait4", "__wait4_time64" },
};

static int cmp(const void *a, const void *b)
{
	return strcmp(*(const char *const *)a, *(const char *const *)b);
}

hidden void *__dlsym_redir_time64(void *restrict p, const char *restrict s, void *restrict ra)
{
	const char *const *map = bsearch(&(const char *const[]){s, 0}, mapping,
		sizeof mapping/sizeof mapping[0],
		sizeof mapping[0], cmp);
	return __dlsym(p, map ? map[1] : s, ra);
}
Rich Felker Aug. 9, 2019, 5 p.m.
On Fri, Aug 09, 2019 at 12:30:21PM -0400, Rich Felker wrote:
> On Sun, Aug 04, 2019 at 12:31:09AM -0400, Rich Felker wrote:
> > On Fri, Aug 02, 2019 at 05:44:33PM -0400, Rich Felker wrote:
> > > >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.
> > 
> > Add a significant incomplete prerequisite I forgot about: dlsym. There
> > needs to be a redirection for dlsym, and 32-bit archs need a second
> > asm entry point for __dlsym_time64 which first checks the symbol name
> > against the list of time64 redirections and rewrites the request if
> > it's a match.
> 
> For reference, here's the first draft of the redirection. The worst
> part of it is the large relro string table.

Here's a v2, also for historical reference, using arrays without
pointers to trade ~2k of additional pure-rodata space for ~500 bytes
of per-task dirty relro space.

I have a better design in mind already so this probably won't be used.

Rich
#include <dlfcn.h>
#include <string.h>
#include <stdlib.h>
#include "dynlink.h"

static const struct mapping {
	char from[28];
	char to[36];
} mapping[] = {
{ "adjtime", "__adjtime64" },
{ "adjtimex", "__adjtimex_time64" },
{ "aio_suspend", "__aio_suspend_time64" },
{ "clock_adjtime", "__clock_adjtime64" },
{ "clock_getres", "__clock_getres_time64" },
{ "clock_gettime", "__clock_gettime64" },
{ "clock_nanosleep", "__clock_nanosleep_time64" },
{ "clock_settime", "__clock_settime64" },
{ "cnd_timedwait", "__cnd_timedwait_time64" },
{ "ctime", "__ctime64" },
{ "ctime_r", "__ctime64_r" },
{ "difftime", "__difftime64" },
{ "dlsym", "__dlsym_time64" },
{ "fstat", "__fstat_time64" },
{ "fstatat", "__fstatat_time64" },
{ "ftime", "__ftime64" },
{ "futimens", "__futimens_time64" },
{ "futimes", "__futimes_time64" },
{ "futimesat", "__futimesat_time64" },
{ "getitimer", "__getitimer_time64" },
{ "getrusage", "__getrusage_time64" },
{ "gettimeofday", "__gettimeofday_time64" },
{ "gmtime", "__gmtime64" },
{ "gmtime_r", "__gmtime64_r" },
{ "localtime", "__localtime64" },
{ "localtime_r", "__localtime64_r" },
{ "lstat", "__lstat_time64" },
{ "lutimes", "__lutimes_time64" },
{ "mktime", "__mktime64" },
{ "mq_timedreceive", "__mq_timedreceive_time64" },
{ "mq_timedsend", "__mq_timedsend_time64" },
{ "mtx_timedlock", "__mtx_timedlock_time64" },
{ "nanosleep", "__nanosleep_time64" },
{ "ppoll", "__ppoll_time64" },
{ "pselect", "__pselect_time64" },
{ "pthread_cond_timedwait", "__pthread_cond_timedwait_time64" },
{ "pthread_mutex_timedlock", "__pthread_mutex_timedlock_time64" },
{ "pthread_rwlock_timedrdlock", "__pthread_rwlock_timedrdlock_time64" },
{ "pthread_rwlock_timedwrlock", "__pthread_rwlock_timedwrlock_time64" },
{ "pthread_timedjoin_np", "__pthread_timedjoin_np_time64" },
{ "recvmmsg", "__recvmmsg_time64" },
{ "sched_rr_get_interval", "__sched_rr_get_interval_time64" },
{ "select", "__select_time64" },
{ "sem_timedwait", "__sem_timedwait_time64" },
{ "semtimedop", "__semtimedop_time64" },
{ "setitimer", "__setitimer_time64" },
{ "settimeofday", "__settimeofday_time64" },
{ "sigtimedwait", "__sigtimedwait_time64" },
{ "stat", "__stat_time64" },
{ "stime", "__stime64" },
{ "thrd_sleep", "__thrd_sleep_time64" },
{ "time", "__time64" },
{ "timegm", "__timegm_time64" },
{ "timer_gettime", "__timer_gettime64" },
{ "timer_settime", "__timer_settime64" },
{ "timerfd_gettime", "__timerfd_gettime64" },
{ "timerfd_settime", "__timerfd_settime64" },
{ "timespec_get", "__timespec_get_time64" },
{ "utime", "__utime64" },
{ "utimensat", "__utimensat_time64" },
{ "utimes", "__utimes_time64" },
{ "wait3", "__wait3_time64" },
{ "wait4", "__wait4_time64" },
};

static int cmp(const void *a, const void *b)
{
	return strcmp(a, b);
}

hidden void *__dlsym_redir_time64(void *restrict p, const char *restrict s, void *restrict ra)
{
	const struct mapping *map = bsearch(s, mapping,
		sizeof mapping/sizeof mapping[0],
		sizeof mapping[0], cmp);
	return __dlsym(p, map ? map->to : s, ra);
}
Rich Felker Aug. 14, 2019, 11:55 p.m.
On Fri, Aug 02, 2019 at 05:44:33PM -0400, Rich Felker wrote:
> 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

These non-prototype declarations for the redirections are not going to
work, since they're not compatible with C++. So either:

1. We repeat the full declarations with prototype, or
2. We use the GNU C __typeof__ for the redirections, or
3. We eliminate the separate #if _REDIR_TIME64 blocks and instead put
   conditionally-defined redirections on the end of the main
   declarations.

I kind of lean towards 3, in that it avoids the possibility of
introducing ABI-breaking bugs via inconsistency between the feature
test macro checks at the point of original declaration and the point
of redirection.

I don't like the verbosity and duplication of option 1. I don't like
the GNU C of option 2, but it's probably no worse than the __asm__ we
can't avoid anyway.

In order to do option 3 though we need a place to conditionally define
the macro appropriately. features.h seems natural but it doesn't
necessarily have access to alltypes.h without adding more inclusions
(which impacts compile times on all targets).

It should be possible to do some macro magic to make features.h do a
_T64() function-like macro that expands differently depending on the
definition of _REDIR_TIME64 at the time it's expanded, but this will
require that all archs define it to 0 or 1 rather than leaving it
undefined when not used. Then we can just unconditionally do:

int aio_suspend(const struct aiocb *const [], int, const struct timespec *) _T64(__aio_suspend_time64);

etc.

Does this seem reasonable?

Rich
Rich Felker Aug. 15, 2019, 12:13 a.m.
On Wed, Aug 14, 2019 at 07:55:21PM -0400, Rich Felker wrote:
> It should be possible to do some macro magic to make features.h do a
> _T64() function-like macro that expands differently depending on the
> definition of _REDIR_TIME64 at the time it's expanded, but this will
> require that all archs define it to 0 or 1 rather than leaving it
> undefined when not used. Then we can just unconditionally do:
> 
> int aio_suspend(const struct aiocb *const [], int, const struct timespec *) _T64(__aio_suspend_time64);
> 
> etc.
> 
> Does this seem reasonable?

Hm, I think it's probably simpler/more readable just to have
alltypes.h define _T64(x) appropriately. I hate header mechanisms that
require you to trace back across arcane stuff in multiple places to
find how/where something is actually defined.

Rich
Rich Felker Aug. 15, 2019, 12:30 a.m.
On Wed, Aug 14, 2019 at 08:13:31PM -0400, Rich Felker wrote:
> On Wed, Aug 14, 2019 at 07:55:21PM -0400, Rich Felker wrote:
> > It should be possible to do some macro magic to make features.h do a
> > _T64() function-like macro that expands differently depending on the
> > definition of _REDIR_TIME64 at the time it's expanded, but this will
> > require that all archs define it to 0 or 1 rather than leaving it
> > undefined when not used. Then we can just unconditionally do:
> > 
> > int aio_suspend(const struct aiocb *const [], int, const struct timespec *) _T64(__aio_suspend_time64);
> > 
> > etc.
> > 
> > Does this seem reasonable?
> 
> Hm, I think it's probably simpler/more readable just to have
> alltypes.h define _T64(x) appropriately. I hate header mechanisms that
> require you to trace back across arcane stuff in multiple places to
> find how/where something is actually defined.

OK, I started a draft for this and I hate it. It spreads the change
all out interspersed with content that's generic and has nothing to do
with time64-specific archs or even with musl, and makes the headers
gratuitously dependent on a musl-specific _T64 macro being defined
(even if defined away as nothing).

I should probably go the __typeof__ route...

Rich