Skip to content

Proposal: A plan for the Linux 64-bit time migration in Zig #21738

Open
@alexrp

Description

@alexrp

Introduction

I've been discussing this topic on Zulip with @The-King-of-Toasters (who's doing the actual work in #21440) over the past few weeks, but it's gotten to the point where some high-level decisions have to be made, so I think it's time to write down a formal plan for review.

Background

Older 32-bit architectures have historically used a 32-bit time_t and corresponding libc functions and syscalls. This runs into the year 2038 problem which is not too far off now (and in some cases already being hit). There has been effort to migrate the Linux syscall interface, as well as glibc and musl APIs/ABIs, to 64-bit time_t on these architectures. We need a strategy for doing this migration in Zig as well.

It's important to understand the difference between 64-bit time at the API and ABI level in C - these are not the same, and this is a source of great confusion. For glibc, for example, the default for older 32-bit architectures remains 32-bit time_t even though the underlying libc.so.6 (on a modern system) in all likelihood has 64-bit time functions. In this case, you have to define _TIME_BITS=64 which enables some preprocessor/compiler crimes tricks to redirect clock_gettime() to __clock_gettime64() and define time_t as 64-bit. Fortunately, we're only concerned with the ABI level in the Zig standard library, i.e. which functions are available at link time.

Proposal

This is a proposed resolution to #4726. If accepted, I think we should merge the changes in the 0.15.0 release cycle.

An overarching theme here is that I don't think it's a good use of any Zig contributor's time and effort to maintain support for 32-bit time - a historical mistake that is on its way out - especially when Zig 1.0 is likely some years out still.

Linux (std.os.linux)

To my knowledge, Linux 5.6 (March, 2020) is the first version that has full support for 64-bit time on 32-bit architectures across the board. We currently require Linux 4.19 (October, 2018) for the Zig standard library, but that version is about to EOL in December, 2024. After that, we have to make a choice:

  1. Bump to the next LTS version, 5.4 (November, 2019) which is EOL in December, 2025. This only gets us partial 64-bit time support (from 5.1). We'd have to research where the gaps are and how to deal with them appropriately, if possible.
  2. Bump to the next LTS version after that, 5.10 (December, 2020) which is EOL in December, 2026.
  3. Bump to 5.4 for architectures with 64-bit time and 5.10 for architectures that need to migrate to 64-bit time.

I favor either (2) or (3); both would allow us to keep std.os.linux simple by just switching all syscalls and types over to 64-bit time. I don't think (1) is worthwhile.

(Independent of what we choose here, we should also take this opportunity to migrate to 64-bit file offsets for any cases where that hasn't yet been done.)

For what it's worth, even Debian Buster (LTS EOL'd in June, 2024) had Linux 5.10.

glibc (std.c)

The first glibc version that has thorough support for 64-bit time on 32-bit architectures is 2.34 (August, 2021). We currently require glibc 2.28 (August, 2018) for the Zig standard library. I believe we already require 64-bit file offsets for glibc, which is a prerequisite for 64-bit time.

Our choices here are:

  1. Do nothing. We maintain support for 32-bit and 64-bit time when using glibc and pick appropriately in std.c based on whether the user specified glibc 2.34+.
  2. Bump the glibc version requirement to 2.34 across the board. This is a jump by 6 versions / 3 years, so may be a bridge too far.
  3. Bump the glibc version requirement to 2.34 for affected 32-bit architectures only. In practice, this means arm(eb), csky, m68k, mips(el), powerpc, sparc, and x86. Notably, it does not include arc and riscv32 as these had 64-bit time from the beginning.

I favor either (2) or (3); both would allow us to keep std.c simple for glibc. Again, I don't consider (1) worthwhile.

Some relevant facts: Debian Bullseye (LTS EOL in August, 2026) is currently on glibc 2.31, while Bookworm (LTS EOL in June, 2028) is on 2.36. I think Bullseye's glibc version may be a fairly compelling argument in favor of (3) over (2), and at least limits the fallout to the affected architectures if anyone tries to use Zig or Zig-compiled binaries on Bullseye.

musl (std.c)

We currently bundle musl 1.2.5 (February, 2024). The first musl version to have 64-bit time support was 1.2.0 (February, 2020). (musl has always had 64-bit file offsets.)

For static linking, there is no problem; we obviously have 64-bit time support. However, for dynamic linking, things get messy. We currently have no support for specifying the target musl version, and even if we did, we have no code in place to detect the musl version when building natively. These are solvable problems, but it would be nice if we could avoid having to tackle that for the 64-bit time migration.

I took a look at the major distros that are either fully musl-based or optionally support using musl as system libc: Alpine, Adelie, Gentoo, and Void. The first three are on musl 1.2.0+. Void is the strange outlier, having failed to cope with the 64-bit time migration for years. That said, there seems to be some recent movement on this, where maintainers appear to be willing to just drop the affected, hard-to-fix packages and get on with life. So this situation hopefully won't last much longer.

musl-based distros, for the most part, tend to move a lot faster in general than glibc-based ones, so I think the compatibility concern isn't as significant here as it may be for glibc. I acknowledge that there's the Void issue, but at the same time, I can't bring myself to care; they're on glibc 2.39 (January, 2024)! Yet they're also on a hacked-up, patched-to-hell musl 1.1.24 (October, 2019). At some point, it has to be reasonable for us to say that that's their problem, not ours.

So, I suggest that we proceed on the assumption that, regardless of link mode, the target musl library has 64-bit time support, allowing us to keep std.c simple for musl. Even if we do add support for specifying the musl version in the future, I think we should still consider 1.2.0 the minimum for the Zig standard library.

The alternative is basically the same as for glibc, except we have to take note of the link mode, and also add musl versioning support and detection. Seems even less worthwhile.

zig cc

I just want to close this proposal out by making it clear that all of the above only affects the Zig standard library. zig cc will still allow you to cross-compile for any glibc version going back to 2.17, any kernel version, and whatever musl version is bundled with Zig. Nothing changes here at all.

Metadata

Metadata

Assignees

No one assigned

    Labels

    arch-arm32-bit ARMarch-cskyarch-m68kMotorola 68000 seriesarch-mips32-bit and 64-bit MIPSarch-powerpc32-bit and 64-bit Power ISAarch-sparc32-bit and 64-bit SPARCarch-x8632-bit x86breakingImplementing this issue could cause existing code to no longer compile or have different behavior.os-linuxproposalThis issue suggests modifications. If it also has the "accepted" label then it is planned.standard libraryThis issue involves writing Zig code for the standard library.

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions