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.
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.
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.
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.
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, ...)HCI_TIME_STAMP
socket option (BlueZ)Filtering/tracing/interception of system calls
Intercepting/wrapping of libc functions via dlsym
/RTLD_NEXT
(fakeroot, ...)
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
%ld
format to print time_t
or suseconds_t
.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.
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.
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.)
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.
ioctl
translationA 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.