Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
alexrp opened this issue Oct 18, 2024 · 9 comments · May be fixed by #21440
Open

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

alexrp opened this issue Oct 18, 2024 · 9 comments · May be fixed by #21440
Labels
arch-arm 32-bit ARM arch-csky arch-m68k Motorola 68000 series arch-mips 32-bit and 64-bit MIPS arch-powerpc 32-bit and 64-bit Power ISA arch-sparc 32-bit and 64-bit SPARC arch-x86 32-bit x86 os-linux proposal This issue suggests modifications. If it also has the "accepted" label then it is planned. standard library This issue involves writing Zig code for the standard library.
Milestone

Comments

@alexrp
Copy link
Member

alexrp commented Oct 18, 2024

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.

@alexrp alexrp added standard library This issue involves writing Zig code for the standard library. proposal This issue suggests modifications. If it also has the "accepted" label then it is planned. os-linux arch-x86 32-bit x86 arch-arm 32-bit ARM arch-mips 32-bit and 64-bit MIPS arch-sparc 32-bit and 64-bit SPARC arch-powerpc 32-bit and 64-bit Power ISA arch-csky arch-m68k Motorola 68000 series labels Oct 18, 2024
@alexrp alexrp added this to the 0.15.0 milestone Oct 18, 2024
@alexrp
Copy link
Member Author

alexrp commented Oct 18, 2024

cc @andrewrk @The-King-of-Toasters

@The-King-of-Toasters
Copy link
Contributor

  • I'd like to note that glibc and musl also commit linker-script crimes too ;).
  • Glibc/gnulib recommends a kernel baseline of 5.1, presumably ignoring the asound/ALSA issues. Is it possible we could do the same?
  • Gnulib's y2038 page also lists other platforms we may target, though many of them are falling out of use. IMO the most important is ARMv7/x86 Android/Bionic, which will never be fixed.

@alexrp
Copy link
Member Author

alexrp commented Oct 18, 2024

Glibc/gnulib recommends a kernel baseline of 5.1, presumably ignoring the asound/ALSA issues. Is it possible we could do the same?

No idea. I guess we'd need to inspect the commit log between 5.1 and 5.6 and see if there's anything that affects us. If not, then we could just bump to 5.4 as we'd normally do.

Gnulib's y2038 page also lists other platforms we may target, though many of them are falling out of use. IMO the most important is ARMv7/x86 Android/Bionic, which will never be fixed.

I'm only concerned with Linux and glibc/musl here because we actually have a choice in time size. I've adjusted the issue title accordingly.

Other platforms (including Bionic) have to be handled separately, if they can be at all - many seem to prefer to just hope for 32-bit platforms to die out. This means that standard library abstractions like std.fs still have to be able to cope with an underlying 32-bit time_t, to whatever extent they can given the year 2038 problem.

The goal here is just that, if the platform actually lets us choose to use 64-bit time, we should do so. I'm only aware of that being possible on Linux at the moment.

@alexrp alexrp changed the title Proposal: A plan for the 64-bit time migration in Zig Proposal: A plan for the Linux 64-bit time migration in Zig Oct 18, 2024
@mikdusan
Copy link
Member

iiuc, for 32-bit arch musl >= 1.2.0 to support 64-bit time we'd need to update std.c to use the correct symbols such as __clock_gettime64 (essentially what the _REDIR_TIME64 macro does for the musl API).

@The-King-of-Toasters
Copy link
Contributor

One more point: On 32-bit targets the "proper" time64 symbols are marked private. There's nothing stopping us from linking against them (we already use private functions in std.c) but could move this logic into the build system so that std.c.clock_gettime always resolves to the right symbol (similar to how _TIME_BITS does it already in the headers)?

@alexrp
Copy link
Member Author

alexrp commented Oct 18, 2024

One more point: On 32-bit targets the "proper" time64 symbols are marked private.

How do you mean? In the output I showed in our discussion, you can see that it's public:

❯ nm -D /usr/lib/i386-linux-gnu/libc.so.6 | grep clock_gettime
000df1e0 T __clock_gettime@@GLIBC_PRIVATE
000df1e0 T clock_gettime@@GLIBC_2.17
000df1e0 T clock_gettime@GLIBC_2.2
000df0c0 T __clock_gettime64@@GLIBC_2.34

__clock_gettime64 is the one we want here, not __clock_gettime.

@The-King-of-Toasters
Copy link
Contributor

I understand, but glibc marks these as hidden definitions and it would be nice not to have to link against them directly. If not then I'm fine doing it as proposed.

@alexrp
Copy link
Member Author

alexrp commented Oct 18, 2024

I understand, but glibc marks these as hidden definitions

Where/how? Do you just mean that __clock_gettime64() isn't declared normally in the public headers?

Note that these functions are part of glibc's public ABI regardless; they're literally declared in the abilist files. If you compile a program using clock_gettime() with x86_64-linux-gnu-gcc -m32 -D_TIME_BITS=64 -D_FILE_OFFSET_BITS=64 and look at the executable's symbol table, you'll see that it links to __clock_gettime64(). So it's completely fine to use these functions. glibc can never break them, just like any other public function.

To be clear, I do agree that the public decl for this function should be spelled std.c.clock_gettime(). It should just link to a different symbol name when targeting glibc and a legacy 32-bit arch.

@The-King-of-Toasters
Copy link
Contributor

Fine by me.

It seems the logic in Target needs to be fixed to do glibc option 2.

The-King-of-Toasters added a commit to The-King-of-Toasters/zig that referenced this issue Oct 28, 2024
As per ziglang#21738, the minimum kernel has been bumped to 5.11, and glibc to
2.34. The maximum has also been updated to the new 6.11 release.
The-King-of-Toasters added a commit to The-King-of-Toasters/zig that referenced this issue Oct 29, 2024
As per ziglang#21738, the minimum kernel has been bumped to 5.11, and glibc to
2.34. The maximum has also been updated to the new 6.11 release.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
arch-arm 32-bit ARM arch-csky arch-m68k Motorola 68000 series arch-mips 32-bit and 64-bit MIPS arch-powerpc 32-bit and 64-bit Power ISA arch-sparc 32-bit and 64-bit SPARC arch-x86 32-bit x86 os-linux proposal This issue suggests modifications. If it also has the "accepted" label then it is planned. standard library This issue involves writing Zig code for the standard library.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants