musl time64 Release Notes
musl time64 Release Notes
musl 1.2.0 changes the definition of
time_t
, and thereby the
definitions of all derived types, to be 64-bit across all archs. This
and related changes are collectively referred to as "time64", and are
necessary so that data types and functions dealing with time can
represent time past early 2038, where the existing 32-bit type on
32-bit archs would overflow.
Caveats
Individual users and distributions upgrading 32-bit systems to musl
1.2.x need to be aware of a number of ways things can break with the
time64 transition, most of them outside the scope and control of musl
itself. These are discussed in detail below.
64-bit systems are unchanged and are not affected by any of the
following.
Library ABIs
musl 1.2.0 does not change or remove any part of the existing ABI
between libc and libc consumers (that is, applications or libraries
using libc-defined types/interfaces). In this sense, it is
non-ABI-breaking. Existing binaries built against musl prior to the
time64 change will run against new musl
libc.so
, and existing object
files can even be static-linked against the new
libc.a
However, the types defined by libc can also participate in the
interface boundaries between pairs of libc consumers
(application-library or library-library), and when this happens, an
ABI mismatch will arise if they were built with mismatching
definitions of
time_t
Prior to the start of the time64 conversion of musl,
a list of
potentially affected libraries
was generated programmatically from
Debian package repository header files and metadata. Applications and
libraries that do not use any of these libraries should be safe to
rebuild for time64 or hold back independently. Applications and
libraries which do use one of the affected libraries may need to be
rebuilt in sync with that library, to avoid ABI mismatch.
System integrators and distributions need to make their own
determination as to whether it makes sense to use package
dependency/conflict tracking to ensure that such upgrades are made in
sync where needed, or to impose global updates to time64.
Kernel Headers
musl itself does not use kernel headers whatsoever. However, if
building applications or libraries which do use them, particularly
anything making use of ALSA, v4l, or evdev/input, you must build
against sufficiently new kernel headers.
As of Linux 4.19, all important kernel header fixes except those
needed by asound/ALSA are upstream. The latter were not ready for 5.4,
and are expected to land in 5.6.
An aggregate patch
to make the
necessary header changes is shipped with musl-cross-make, but it's
incompatible with actually building the kernel, so headers need to be
built/installed separately if using it.
Application Compatibility
Some applications are not ready upstream for running in a time64
environment on 32-bit. Possible issues include:
Use of kernel APIs that require changes for time64 compatibility
struct input_event
from
linux/input.h
(libinput, qemu-system,
Xorg input device modules, ...)
Bluetooth
HCI_TIME_STAMP
socket option (BlueZ)
Filtering/tracing/interception of system calls
Interpretation of syscall argument structures needs to use kernel
UAPI types, not libc types (strace).
Seccomp filters need to allow new time64 versions of syscalls
(OpenSSH sshd, Firefox, Chromium, ...).
Intercepting/wrapping of libc functions via
dlsym
RTLD_NEXT
(fakeroot, ...)
As long as interceptor uses headers correctly, it should just
work, but will only work with new time64 binaries not legacy ones.
A compat stub library
can be linked into such interceptors
to make them simultaneously work with legacy binaries.
Outdated, time64-incompatible kernel headers shipped with the
application (alsa-lib)
Direct use of syscalls with
time_t
-derived arguments (libstdc++,
Busybox, non-C language runtimes, ...)
Undefined behavior and unwarranted assumptions
Manual declaration of time functions or types, bypassing or
suppressing definitions in libc headers (Berkeley DB, doxygen).
Use of
%ld
format to print
time_t
or
suseconds_t
Access to time structures in objects not suitably aligned.
Adélie Linux
and
Yoe/Yocto/OpenEmbedded
did the early
groundwork building large package corpuses against time64 musl during
the 1.2.0 release cycle and found and patched all build-time breakage
found, as well as obvious run-time problems. Some of these patches are
now upstream in the corresponding projects. The
Adélie time64 wiki
page
is a good starting point for information about packages that
need patching.
Implementation of the transition
New time64 symbols
To change the definition of
time_t
and all derived types without
breaking the ABI boundary between libc-consumers and libc, musl 1.2.0
redirects all functions using these types as part of their interface
to alternate symbol names via the
__asm__("name")
construct in their
declarations in the public headers. To be namespace-clean, the
redirected names all begin with double-underscore. Otherwise, the
pattern for naming follows the Linux kernel's pattern for syscall
names: if the name ends in "time", "64" is appended; otherwise,
"_time64" is appended. A final "_r", if present, is removed and
re-added before and after the renaming, so that, for example,
gmtime_r
becomes
__gmtime64_r
, not
__gmtime_r_time64
This symbol redirection introduces a depedency (for existing 32-bit
archs) on an additional "GNU C" feature, but it is one which glibc has
depended on for handling its
_FILE_OFFSET_BITS=64
feature for
decades, and one which any viable alternative-compiler already needs
to implement.
Since these functions depend on
time_t
or derived types defined by
the headers, conforming applications cannot bypass the headers and use
their own declarations; they're required by the standards (C and
POSIX) to include the headers.
The
dlsym
function is also redirected, to a version that redirects
names for affected functions, so that programs which lookup one of
them by its symbol name get the correct version using matching types.
Legacy compatibility
musl does not retain duplicate implementations for legacy ("time32")
versions of the functions. Instead, they are all thin wrappers defined
in the new
compat/time32
source tree. These wrappers generally just
convert the affected input structures from time32 to time64 form,
convert the affected output structures from time64 to time32 form, and
if possible, translate values that overflow into errors. (Such
translation is not possible, however, if the function already
performed an operation with side effects.)
Syscall usage
Internally, functions which use time only as an input perform the old
time32 syscall as long as the value is representable in 32 bits. This
avoids having to fail and fallback on older kernels. Functions that
produce time as an output have to start with the time64 syscall, in
case the value does not fit in 32 bits, and fallback if the new
syscall is not available.
clock_gettime
would be the most-impacted
by this in terms of performance, but as long as vdso version is
available, it's used to avoid the need for making a syscall at all.
Both the time64 and legacy time32 versions of the vdso function are
supported.
Socket option, ancillary data, and
ioctl
translation
A number of socket options and
ioctl
commands involve
time_t
-derived types. These all have new values defined by Linux,
which will necessarily be missing from older kernels. musl contains
code to handle the case where they are missing and translate to/from
the appropriate argument forms for the old 32-bit versions.
A few socket options, particularly the
SO_TIMESTAMP
family, cause
ancillary data to be delivered to the application along with socket
reads via
recvmsg
and
recvmmsg
. These functions now translate
32-bit versions of the ancillary data if found, and append a 64-bit
translation of it, so that both legacy time32 binaries and time64
binaries running on old kernels work correctly. On new kernels with
the native time64 socket options, no translation is necessary.