eschaton 44 minutes ago

The way this was handled on Mac OS X for `off_t` and `ino_t` might provide some insight: The existing calls and structures using the types retained their behavior, new calls and types with `64` suffixes were added, and you could use a preprocessor macro to choose which calls and structs were actually referenced—but they were hardly ever used directly.

Instead, the OS and its SDK are versioned, and at build time you can also specify the earliest OS version your compiled binary needes to run on. So using this, the headers ensured the proper macros were selected automatically. (This is the same mechanism by which new/deprecated-in-some-version annotations would get set to enable weak linking for a symbol or to generate warnings for it respectively.)

And it was all handled initially via the preprocessor, though now the compilers have a much more sophisticated understanding of what Apple refers to as “API availability.” So it should be feasible to use the same mechanisms on any other platform too.

  • db48x 14 minutes ago

    Lol, that only works if you can force everyone on the platform to go along with it. It is a nice solution, but it requires you to control the c library. Gentoo doesn’t control what libc does; that’s either GNU libc or MUSL or some other thing that the user wants to use.

    • dmitrygr 7 minutes ago

      > requires you to control the c library

      Which is why basically every other same operating system does that. BSDs, macOS, WinNT. Having the stable boundary be the kernel system call interface is fucking insane. And somehow the Linux userspace people keep failing to learn this lesson, no matter how many times they get clobbered in the face by the consequences of not learning it.

codys 4 hours ago

There are a few options for Gentoo not discussed in the post, possibly because for Gentoo they would be a larger amount of work due to the design of their system:

1. Allow building against packages without installing them. The core issue here is that Gentoo package build and install happen as a single step: one can't "build a bunch of things that depend on one another" and then "atomically install all the build items into place". This means that Gentoo can easily be partially broken when one is doing updates when an ABI change occurs (modulo so versioning, see next option). This issue with 64-bit time_t is an example of an ABI change that folks are very aware of and is very widespread. It's also an example of something that causes an ABI change that isn't handled by the normal `.so` versioning scheme (see next option).

2. Extend the normal `.so` versioning to capture changes to the ABI of packages caused by packages they depend on. Normally, every `.so` (shared object/library) embeds a version number within it, and is also installed with that version number in the file name (`libfoo.so.1.0.0`, for example, would be the real `.so` file, and would have a symlink from `libfoo.so` to tell the linker which `.so` to use). This shared object version is normally managed by the package itself internally (iow: every package decides on their ABI version number to enable them to track their own internal ABI breakages). This allows Gentoo to upgrade without breaking everything while an update is going on as long as every package manages their `.so` version perfectly correctly (not a given, but does help in many cases). There is a process in Gentoo to remove old `.so.x.y.z` files that are no longer used after an install completes. What we'd need to do to support 64-bit time_t is add another component to this version that can be controlled by the inherited ABI of dependencies of each `.so`. This is very similar in result to the "use a different libdir" option from the post, but while it has the potential to set things up to enable the same kinds of ABI changes to be made in the future, it's likely that fixing this would be more invasive than using a different libdir.

  • akira2501 3 hours ago

    > Allow building against packages without installing them.

    A partial staged update system would work the best. I should be able to schedule the build of several new packages, have them built into a sandbox, and have new compilations use the sandbox first and then fallback to the system through a union, and then once everything is build finally package up all the pieces and then move them out of the sandbox and into the system at large.

    You could make all gentoo updates transactional that way which would be a huge boon in many other ways.

  • aaronmdjones 3 hours ago

    Gentoo already allows option 1 by specifying a directory to ultimately install the finished image to (normally it is set to /) [1]. One can do a complete rebuild of everything in @system and @world, install it to the specified subdirectory, and then sync it all over in one shot. Preferably you would do this from a live session, although in theory you could also bind-mount / onto a subdirectory of the place you reinstalled everything to, chroot into it, and then sync it (what is now /) into the bind-mounted real upper /.

    [1] https://devmanual.gentoo.org/ebuild-writing/variables/#root

    • codys 2 hours ago

      It's true that Gentoo has some pieces of what they need to build an implementation of option 1 (build packages that depend on other packages before completeing an install), but it's currently the case that that is not what happens when one runs `emerge` (the gentoo packaging build/installing tool), as you've noted one would need to write scripts that wrap emerge (or do the same work manually) to attempt to accomplish this today.

      I suspect that using bind mounts and overlays (to allow the "building packages" chroot a view of the "installed" root) could be used to accomplish this, or alternately some filesystem snapshotting features if we're thinking about this from the "external to emerge" angle. (It's my understanding that for Gentoo to do this ABI change, though, they need some solution integrated into emerge).

      To some extent, this kind of potential model also reminds me of systems that integrate their package updating with filesystem snapshotting to allow rollbacks and actually-atomic upgrades of many files. I think one or more of the solaris distributions did this?

grantla 3 hours ago

> A number of other distributions such as Debian have taken the leap and switched. Unfortunately, source-based distributions such as Gentoo don’t have it that easy.

For Debian it was extremely painful. A few people probably burned out. Lots of people pointed to source-based distributions and said "they will have it very easy".

  • Denvercoder9 an hour ago

    > For Debian it was extremely painful.

    Do you have any references that elaborate on that? From an outsider perspective, the time64 transition in Debian seemed to have been relatively uncontroversial and smooth. Way better than e.g. the /usr-merge.

panzi 26 minutes ago

The only place where this is relevant for me is running old Windows games via wine. Wonder how wine is handling this? Might as well re-map the date for 32bit wine to the late 90s/early 2000s, where my games are from. Heck, with faketime I can do that already, but don't need it yet.

kccqzy 5 hours ago

My biggest takeaway (and perhaps besides-the-point) is this:

> Musl has already switched to that, glibc supports it as an option. A number of other distributions such as Debian have taken the leap and switched. Unfortunately, source-based distributions such as Gentoo don’t have it that easy.

While I applaud their efforts I just think as a user I want to be over and done with this problem by switching to a non source-based distribution such as Debian.

  • ordu 4 hours ago

    There is an easy way to deal with this in Gentoo. Boot from usb or something, run mkfs.ext4 (or .whatever fs you use) on your / and /usr partitions, mount them, unpack stage3 on them, chroot into them and run `emerge $all-my-packages-that-where-installed-before-mkfs`.

    You can install new copy of Gentoo instead of upgrading it incrementally.

    • robin_reala 4 hours ago

      “easy”

      • ordu 4 hours ago

        It is easy. It is just a tl;dr version of "how to install gentoo". :)

        • lnxg33k1 4 hours ago

          I hope I will get a day where I have the will to recompile everything in the next 14 years, I think I had the same install for the past 10 or so on the gentoo desktop. The only think I reinstalled recently has been the laptop to switch from arch to sid

  • viraptor an hour ago

    > by switching to a non source-based distribution such as Debian.

    The distinction has more nuance. Source based distros like nixos don't have the same issue. The problem is more in how Gentoo builds/installs the packages than in building from source.

    Also with third party closed source software, you're still going to have issues, even on binary systems. Actually you could even have issues with the first party packages if they're installed in separate independent steps.

  • wtallis 4 hours ago

    It sounds like the difficulty for source-based distributions comes from trying to do an in-place upgrade that makes incompatible changes to the ABI. So changing to an entirely different distribution would be at least as disruptive (though possibly less time consuming) as doing a clean install of Gentoo using a new ABI.

    • akira2501 3 hours ago

      Starting a few years ago I partition all my drives with two root partitions. Once is used and the other is blank. For precisely this reason. Sometimes it's easier to just roll out a brand new stage3 onto the unused partition, build an entirely new root, and then just move over to that once it's finished.

      The bonus is you can build your new system from your old system using just chroot. Very convenient.

n_plus_1_acc 5 hours ago

I'm no expert on C, but I was under the impression that type aliases like off_t are introduced to have the possibility to change then later. This clearly doesn't work. Am I wrong?

  • wtallis 4 hours ago

    Source vs binary compatibility. Using typedefs like off_t mean you usually don't have to re-write code, but you do have to re-compile everything that uses that type.

    • rblatz 2 hours ago

      But isn’t the point of a source only distribution like Gentoo, to build everything yourself? Who is running gentoo but also lugging around old precompiled stuff they don’t have the source for?

      • mananaysiempre 2 hours ago

        As the post describes, the problem is that on Gentoo you can’t really build everything then switch binaries for everything, or at least that’s not what happens when you update things the usual way.

        Instead, dependencies are built and then installed, then dependents are built against the installed versions and then installed, etc. In the middle of this process, the system can technically be half-broken, because it could be attempting to run older dependents against newer dependencies. Usually this is not a real problem. Because of how pervasive time_t is, though, the half-broken state here is potentially very broken to the point that you may be unable to resume the update process if anything breaks for any reason.

  • ordu 4 hours ago

    It kinda work, but not in a source based distro. If you can atomically rebuild @world with changed definition of off_t, then there will be no problem. But source based distro doesn't rebuild @world atomically. It rebuild one package at time, so there would be inconveniences like libc.so has 64-bit off_t, while gcc was build for 32-bit off_t, so gcc stops working. Or maybe bash, coreutils, make, binutils or any other package that is needed for rebuilding @world. At this point you are stuck.

    So such upgrade needs care.

    • jasomill 3 hours ago

      As someone whose nearest exposure to a "source based distro" is FreeBSD, this sounds like madness, as it means a broken build could not only impair attempts to repair the build, but render the system unbootable and/or unusable.

      And as someone who regularly uses traditional Linux distros like Debian and Fedora, the idea that a package management system would allow a set of packages known to be incompatible with one another to be installed without force, or that core package maintainers would knowingly specify incorrect requirements, is terrifying.

      While I'm not familiar with Gentoo, my reading of this article suggests that its maintainers are well aware of these sorts of problems and that Gentoo does not, in fact, suffer from them (intentionally; inevitably mistakes happen just as they occasionally do on the bleeding edge of FreeBSD and other Linux distros).

    • Athas 4 hours ago

      Why not essentially treat it as a cross compilation scenario? NixOS is also source based, but I don't think such a migration would be particularly difficult. You'd use the 32-bit off_t gcc to compile a glibc with 64-bit off_t, then compile a 64-bit off_t gcc linked against that new glibc, and so on. The host compiler shouldn't matter.

      I always understood the challenge as binary compatibility, when you can't just switch the entire world at once.

      • codys 4 hours ago

        Nixos has it easier here because they don't require packages to be "installed" before building code against them. For Gentoo, none of their build scripts (ebuilds) are written to support that. It's plausible that they might change the embuild machinery so that this kind of build (against non-installed packages) could work, but it would need investigation and might be a difficult lift to get it working for all packages.

        "treat it as a cross compilation scenario" is essentially what the post discusses when they mention "use a different CHOST". A CHOST is a unique name identifying a system configuration, like "x86_64-unknown-linux-gnu" (etc). Gentoo treats building for different CHOSTs as cross compiling.

      • andrewaylett 3 hours ago

        NixOS isn't the same kind of source-based. At some level, even Debian could be said to be source based: there's nothing stopping you from deciding to build every package from source before installing it, and obviously the packages are themselves built from source at some point.

        NixOS sits between Debian and Gentoo, as it maintains an output that's capable of existing independently of the rest of the system (like Debian) but is designed to use the current host as a builder (like Gentoo). Gentoo doesn't have any way to keep individual builds separate from the system as a whole, as intimated in the article, so you need to work out how to keep the two worlds separate while you do the build.

        I think what they're suggesting winds up being pretty similar to what you suggest, just with the right plumbing to make it work in a Gentoo system. NixOS would need different plumbing, I'm not sure whether they've done it yet or how but I can easily imagine it being more straightforward than what Gentoo is needing to do.

        • tadfisher 2 hours ago

          There absolutely will be problems with different Nix profiles that aren't updated together; for example, if you update some packages installed in your user's profile but not the running system profile. But this is common enough with other glibc ABI breakage that folks tend to update home and system profiles together, or know that they need to reboot.

          Where it will be hell is running Nix-built packages on a non-NixOS system with non-ABI-compatible glibc. That is something that desperately needs fixing on the glibc side, mostly from the design of nss and networking, that prevent linking against glibc statically.

    • codys 4 hours ago

      > there would be inconveniences like libc.so has 64-bit off_t

      glibc specifically has support for the 32-bit and 64-bit time_t abi simultaneously.

      From the post:

      > What’s important here is that a single glibc build remains compatible with all three variants. However, libraries that use these types in their API are not.

      • ordu 4 hours ago

        Yeah, glibc was a bad example. Probably libz.so or something would be better.

        • codys 4 hours ago

          Yep, agreed. Though it does expose an option here which would be "have glibc provide a mechanism for other libraries (likely more core/widely used libs) to support both ABIs simultaneously.

          Presumably if that had been done in glibc when 64-bit time_t support was added, we could have had multi-size-time ABI support in things like zlib by now. Seems like a mistake on glibc's part not to create that initially (years ago).

          Though if other distros have already switched, I'd posit that perhaps Gentoo needs to rethink its design a bit so it doesn't run into this issue instead.

          • o11c 3 hours ago

            > have glibc provide a mechanism for other libraries (likely more core/widely used libs) to support both ABIs simultaneously

            The linked article is wrong to imply this isn't possible - and it really doesn't depend on "GLIBC provide a mechanism". All you have to do is:

            * Compile the library itself with traditional time_t (32-bit or 64-bit depending on platform), but convert and call time64 APIs internally.

            * Make a copy of every public structure that embeds a time_t (directly or indirectly), using time64_t (or whatever contains it) instead.

            * Make a copy of every function that takes any time-using type, to use the time64 types.

            * In the public headers, check if everything is compiled with 64-bit time_t, and if so make all the traditional types/functions aliases for the time64 versions.

            * Disable most of this on platforms that already used 64-bit time_t. Instead (for convenience of external callers) make all the time64 names aliases for the traditional names.

            It's just that this is a lot of work, and little benefit to any particular library. the GCC 5 std::string transition is probably a better story than LFS, in particular the compiler-supported `abi_tag` to help detect errors (but I think that's only for C++, ugh - language without room for automatic mangling suck).

            (minor note: using typedefs rather than struct tags for your API makes this easier)

          • jasomill 3 hours ago

            This sounds like something similar to NeXTSTEP/macOS fat binaries, only with the possibility of code sharing between "architectures".

            I like it, though it sounds like something that'd be unlikely to see adoption in the Linux world without the endorsement of multiple major distros.

    • ArsenArsen 4 hours ago

      this is just as much of a problem on binary distros mind you. just because there might be a smaller time delta does not mean that the problem is solved

    • codys 4 hours ago

      nixos is an example of a distro that is source based and can do the atomic rebuild here because it has a way to build packages against other packages that aren't "installed". In nixos, this is because there are very few things that get "installed" as the global, only thing to use. But one could imagine that Gentoo could build something that would allow them to at least build up one set of new packages without installing them and then install the files all at once.

  • jandrese 3 hours ago

    It's only the first step of the puzzle. And arguably only a half step. As the article points out anytime that off_t is stuffed into a struct, or used in a function call, or integrated into a protocol, the abstraction is lost and the actual size matters. Mixing old and new code, either by loading a library or communicating over a protocol, means you get offsets wrong and things start crashing. Ultimately the changeover requires everybody to segregate their programs between "legacy" and "has been ported or at least looked over", which is incredibly painful.

  • smueller1234 4 hours ago

    They make it easier, but just at a source code level. They're not a real (and certainly not full) abstraction. An example that'll be making it obvious: if you replace the underlying type with a floating point type, the semantics would change dramatically, fully visible to the user code.

    With larger types that otherwise have similar semantics, you can still have breakage. A straightforward one would be padding in structs. Another one is that a lot of use cases convert pointers to integers and back, so if you change the underlying representation, that's guaranteed to break. Whether that's a good or not is another question, but it's certainly not uncommon.

    (Edit: sibling comments make the same point much more succinctly: ABI compatibility!)

  • jonathrg 4 hours ago

    It does work, the problem with ABI changes is that when you change it, you have to change it everywhere at the same time. By default there's nothing stopping you from linking one library built using 32-bit off_t with another library built using 64-bit off_t, and the resulting behaviour can be incredibly unpredictable.

  • devit 2 hours ago

    The C library uses macros so that the referenced symbols are different depending on ABI (e.g. open becomes open64, etc.), but most 3rd party libraries don't bother with that, so they break if their API uses time_t/off_t.

  • raldi 4 hours ago

    I think the main reason they’re introduced is to provide a hint to the programmer and maybe some typesafety against accidentally passing, say, a file descriptor.

  • progbits 4 hours ago

    Edit: nevermind others raise better points.

    • cataphract 4 hours ago

      That's not the problem. If time_t was a struct with a single int32_t, you'd be in the same situation when you changed it to int64_t (ABI incompatibility: you need more space to store the value now).

      In order not to have this problem you'd need the API to use opaque struts, for which only pointers would be exchanged.

  • beached_whale 4 hours ago

    at the source level it does, but when you have compiled libraries it breaks.

  • SkiFire13 4 hours ago

    Yeah, the problem in general with type aliases is that they're just that, aliases, not proper types. This means that they leak all the details of the underlying type, and don't work for proper encapsulation, which is what's needed for being able to change what should be implementation details.

    • tsimionescu 2 hours ago

      The problem here would be exactly the same regardless of what form this type took. The issue is fundamental to any low level language: the size of a type is a part of its public API (well, ABI), by definition. This is true in C++ with private fields as well, for example: if you add a private field to a class, or change the type of a private field with one of a different size, all code using that class has to be re-built. The only way to abstract this is to use types strictly through pointers, the way Java does.

nobluster 4 hours ago

For a large legacy 32 bit unix system dealing with forward dates I replaced all the signed 32 bit time_t libc functions with unsigned 32 bit time_t equivalents. This bought the system another 68 years beyond 2038 - long after I'll be gone. The downside is that it cannot represent dates before the unix epoch, 1970, but as it was a scheduling system it wasn't an issue.

If legacy dates were a concern one could shift the epoch by a couple of decades, or even reduce the time granularity from 1 second to 2 seconds. Each alternative has subtle problems of their own. It depends on the use case.

wpollock 3 hours ago

I'm probably naive, but I see another way forward. If all the t64 binaries are static linked, they have no danger of mixing abis. After 2038, all the t32 code is broken anyway so there's no additional risk for going back to dynamic linking then. I feel if this was a solution the author would have mentioned it but I'm willing to look foolish to hear what others will say.

  • layer8 2 hours ago

    Static linking is probably impractical for applying security updates, as often you’d have to recompile/relink basically the whole system. In some cases it could also significantly increase memory usage.

    • GeorgeTirebiter 2 hours ago

      with fast networks, huge disks, fast processors --- it seems wasteful to me to even consider shared libraries. Shared libraries is a technology that was useful when we were memory starved. We are no longer memory starved. So you replace the static binary? Big deal, size is not an issue (for 99% of the cases) given what we have today.

      Recall, too, that the "link" step of those .o files is actually a "link / edit" step, where routines in the libraries not used are not linked.

      • layer8 2 hours ago

        It’s much more straightforward to ensure consistency with shared libraries, and not having to rebuild stuff. Wasting disk space, RAM, network bandwidth and processing time is what seems wasteful to me.

      • jiggawatts 10 minutes ago

        Static linking is the root cause for “modern” apps based on Electron taking minutes to start up and be useful. They’re statically linking almost an entire operating system (Chromium), an entire web server, server runtime framework, and an equivalent client framework for good measure.

        On the fastest PC that money can buy this is somewhere between “slow” and “molasses”.

        I miss the good old days when useful GUU programs were mere kilobytes in size and launched instantly.

      • gotoeleven an hour ago

        Are you by chance a javascript programmer?

ok123456 24 minutes ago

What about making time_t 64-bit across all profiles and incrementing all the profile versions by one? Gentoo users are used to breaking changes when upgrading profiles.

ddoolin an hour ago

Why was it 32 bits to begin with? Wasn't it known that 2038 would be the cutoff?

  • wongarsu an hour ago

    It has been that way since Unix V4 which released in 1973. Back then moving to something that breaks after 65 years was a massive upgrade over the old format that wrapped after two and a half years. And at least from the standpoint of Unix engineers 32 bits was more than enough: Nobody is using Unix V4 in production in 2024, never mind 2038.

    Why it made it into Posix and wasn't updated is a different question that's a bit more difficult to answer

  • int_19h an hour ago

    It was not exactly a big concern when this was designed in early 70s, especially when you consider that Unix itself was kind of a hack at the time.

  • modeless an hour ago

    In the 1970s people probably would have laughed at the idea that Unix would still be running in the year 2038.

  • NegativeLatency an hour ago

    Ultimately probably hardware? I suspect it’s also been like this for a long time.

  • jdndhdhd an hour ago

    Why is it 64 bit now? Isn't it know that 292277026596 would be the cutoff?

loeg 4 hours ago

The C standard does not require time_t to be signed (nor does POSIX). Just changing the 32-bit type to unsigned would (in some senses) extend the lifetime of the type out to 2106. You could at least avoid some classes of ABI breakage in this way. (On the other hand, Glibc has explicitly documented that its time_t is always signed. So they do not have this option.)

  • pwg 4 hours ago

    While that avoids 2038 as a "drop dead" date for 32-bit time_t, it also removes the ability to represent any date/time prior to 00:00:00 UTC on 1 January 1970 using 32-bit time_t because you lose the ability to represent negative values.

    Having all existing stored date/times that are currently prior to the epoch suddenly become dates post 2038 is also not a good scenario.

    • loeg 3 hours ago

      > it also removes the ability to represent any date/time prior to 00:00:00 UTC on 1 January 1970 using 32-bit time_t

      Yes, of course. This is probably not the main use of negative values with signed time_t, though -- which is just representing the result of subtraction when the operand happened before the subtrahend.

      > Having all existing stored date/times that are currently prior to the epoch suddenly become dates post 2038 is also not a good scenario.

      In practice, there are ~zero of these on systems with 32-bit time_t and a challenging migration path as we approach 2038.

      • cryptonector 3 hours ago

        > Yes, of course. This is probably not the main use of negative values with signed time_t, though -- which is just representing the result of subtraction when the operand happened before the subtrahend.

        This is definitely a bigger concern, yes. One has to be very careful with subtraction of timestamps. But to be fair one already had to be very careful before because POSIX doesn't say what the size or signedness of `time_t` is to begin with.

        Indeed, in POSIX `time_t` can even be `float` or `double`[0]!

          time_t and clock_t shall be integer or real-floating types.
        
        Though on all Unix, BSD, Linux, and any Unix-like systems thankfully `time_t` is always integral. It's really only size and signedness that one has to be careful with.

        Thus one should always subtract only the smaller value from the larger, and cast the result to a signed integer. And one has to be careful with overflow. Fortunately `difftime()` exists in POSIX. And there's a reason that `difftime()` returns a `double`: to avoid having the caller have to deal with overflows.

        Basically working safely with `time_t` arithmetic is a real PITA.

          [0] https://pubs.opengroup.org/onlinepubs/009696799/basedefs/sys/types.h.html
        • loeg 2 hours ago

          > Indeed, in POSIX `time_t` can even be `float` or `double`[0]!

          Standard C, yes. Newer POSIX (your link is to the 2004 version) requires time_t be an integer type: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sy...

          > Though on all Unix, BSD, Linux, and any Unix-like systems thankfully `time_t` is always integral. It's really only size and signedness that one has to be careful with. Thus one should always subtract only the smaller value from the larger, and cast the result to a signed integer. And one has to be careful with overflow. Fortunately `difftime()` exists in POSIX. And there's a reason that `difftime()` returns a `double`: to avoid having the caller have to deal with overflows.

          > Basically working safely with `time_t` arithmetic is a real PITA.

          Yes.

          • cryptonector 2 hours ago

            Yes, I know that was older POSIX. But we're talking about old code, the unstated context is portability over a long period of time, and I wanted to make a point :)

            So use `difftime()`, don't assume signedness or size, but do assume that it's an integral type.

  • umanwizard 4 hours ago

    You would then lose the ability to represent times before Jan. 1st, 1970. Which is not just a theoretical concern; those times appear e.g. in databases with people's date of birth.

    • bloak 3 hours ago

      Serious question: do people use time_t for representing a date of birth?

      To me that wouldn't seem right: a date of birth isn't a timestamp and you typically receive it without a corresponding place or time zone so there's no reasonable way to convert it into a timestamp.

      (The other problem is that a signed 32-bit time_t only goes back to 1901. You might not have to deal with a date of birth before 1901 today, unless you're doing genealogy, of course, but until fairly recently it's something you'd probably want to be able to handle.)

      • umanwizard 3 hours ago

        > Serious question: do people use time_t for representing a date of birth?

        I have seen people in real life use seconds since the UNIX epoch to represent DOB, yes.

      • nomel 3 hours ago

        My birth certificate has location, day, year, hour, and minute of birth. Birth is an event in time, perfectly represented with a (UTC) timestamp.

        • Merad 3 hours ago

          Your birth is an event that happened at an instant in time, but very few systems concern themselves with that detail. The vast majority need to store birth _date_ and have no interest in the time or location.

        • cryptonector 3 hours ago

          > Birth is an event in time, perfectly represented with a (UTC) timestamp.

          That's not the same thing as `time_t` though. `time_t` is UTC. UTC is not `time_t`.

    • nobluster 4 hours ago

      And a signed 32 bit time_t with an epoch of 1970 cannot represent dates before 1902. Using time_t to store legacy dates is not advisable - even if you ignore all the issues with time zones and changing local laws pertaining to offsets from UTC and daylight saving time.

      • pwg 4 hours ago

        While true, that limitation has always existed, so everyone has already implemented whatever was necessary to represent dates earlier than that.

        Changing 32-bit time_t to unsigned suddenly makes all dates from 1902 to Jan 1 1970 which were stored using time_t (even if it was non-advisable, it still will have occurred) appear to teleport into the future beyond 2038.

        • cryptonector 3 hours ago

          That's alright, see, because I have no filesystems, no tarballs, no backups, no files on any kind of media with timestamps before 1970, and indeed, no one can except by manually setting those timestamps -- and why bother doing that?!

          So any pre-1970 32-bit signed timestamps will be in... not spreadsheets, in what? In databases? No, not either. So in some sort of documents, so software consuming those will need fixing, but we're not going to see much teleporting of 1902-1970 timestamps to 2038-2106. I'm not concerned.

          • jasomill 2 hours ago

            Many important software projects predate UNIX. Perhaps you want to create a Git repository for one of them, with historically accurate commit timestamps?

            • cryptonector an hour ago

              Well, Git doesn't seem to set file timestamps when cloning. And as the sibling comment says, Git doesn't use 32-bit signed integers to store timestamps. So this is not a problem.

              If, however, Git were to some day get an option to set file mtimes and atimes at clone time to the last-modified time of the files based on commit history, then you could always just use a 64-bit system where `time_t` is 64-bit.

            • loeg 2 hours ago

              Git represents timestamps in ASCII strings, like "12345." Not time_t. (I am not sure if it even allows negative integers in this format.)

    • loeg 3 hours ago

      Databases do not and can not use system time_t. Consider how their on-disk state would be impacted by a change from 32-bit time_t to 64-bit! Instead they use specific or variable size integer types.

    • cryptonector 3 hours ago

      Those databases might not even use `time_t`. It's their problem anyways, not the OS's.

  • petee 3 hours ago

    Openbsd did just this for 32bit compat when they changed to 64bit time 12 years ago, and seems to have worked out fine.

    • brynet 2 hours ago

      No, time_t is a signed 64-bit type on all architectures, 64-bit architectures have no "32bit compat".

      https://www.openbsd.org/55.html

      • petee 7 minutes ago

        From the notes, this is what i was referring to, I think I just mistook the meaning -

        Parts of the system that could not use 64-bit time_t were converted to use unsigned 32-bit instead, so they are good till the year 2106

  • scheme271 4 hours ago

    If time_t is unsigned, how do times before the unix epoch get represented?

    • loeg 3 hours ago

      They don't, much like they already do not. 32-bit time_t has always been finite, and 1970 was a long, long time ago. (See "in some senses" in my earlier comment.)

ndesaulniers 2 hours ago

I think we should start putting details of the ABI in ELF (not the compiler flags as a string). Wait.. Who owns the ELF spec??

Then the linker and loader could error if two incompatible objects with different ABIs were attempted to be linked together.

For instance, I suspect you could have fields to denote the size of certain types. I guess DWARF has that... But DWARF is optional and sucks to parse.

fred_is_fred 4 hours ago

Besides epoch time and the LFS support mentioned, are there any other 32-bit bombs waiting for Linux systems like this?

  • tredre3 3 hours ago

    The ext file system uses many 32bit counters. Admittedly, version 4 fixed most of that (when formatted with the correct options).

    • kbolino 3 hours ago

      In a similar vein, inodes can run out. On most conventional Linux file systems, inode numbers are 32 bits.

      For many, this is not going to be a practical problem yet, as real volumes will run out of usable space before exhausting 2^32 inodes. However, it is theoretically possible with a volume as small as ~18 TiB (using 16 TiB for 2^32 4096-byte or smaller files, 1-2 TiB for 2^32 256- or 512-byte inodes, plus file system overheads).

      Anticipating this problem, most newer file systems use 64-bit inode numbers, and some older ones have been retrofitted (e.g. inode64 option in XFS). I don't think ext4 is one of them, though.

  • fph 4 hours ago

    IPv4, technically, is another 32-bit bomb, but that's not Linux-specific.

    • samatman 3 hours ago

      I wouldn't call this a bomb at all. Bombs are events, not processes.

      Resource contention for IPv4 has been around for a long time, with a number of workarounds and the ultimate out of supporting IPv6. There has been, to date, no moment of crisis, nor do I expect one in the future.

      It will just get steadily more annoying/expensive to use IPv4 and IPv6 will relieve that pressure incrementally. We're at least two decades into that process already.

      • poincaredisk 3 hours ago

        Two decades in, and IPv6 is still the more annoying option.

        I wish they were less ambitious and just increased address sizes when designing ipv6.

dark-star 4 hours ago

Are there other distros where the switch to 64-bit time_t has already happened? What's the easiest way to figure out whether $distro uses 32-bit or 64-bit time_t? Is there something easier/quicker than writing a program to print `sizeof(struct stat)` and check if that's 88, 96 or 1098 bytes (as hinted in the article)?

  • SAI_Peregrinus 3 hours ago

    NixOS switched. And they're source-based, but with actual dependency tracking and all the (insanely complex) machinery needed to allow different programs to use different C library versions simultaneously.

    `printf("sizeof (time_t) = %zu, %zu bits", sizeof (time_t), sizeof (time_t) * CHAR_BIT);` gives you the size in bytes and in bits. Needs time.h, stddef.h, and stdio.h.

  • jonathrg 3 hours ago

    printf '#include <stdio.h>\n#include <time.h>\nint main() { printf("time_t is %%zu-bit\\n", sizeof(time_t)*8); }\n' | gcc -x c -o timesize - && ./timesize && rm ./timesize

    If you're wondering about a specific distro that you're not using right now - just look it up.

  • teddyh 3 hours ago

    Like the article says, Debian has already switched.

BoingBoomTschak 4 hours ago

A very thoughtful way of handling a problem much trickier than the earlier /usr merge. Had thought about 2 but not 1 and 3. I also had kinda forgotten that 2038 thing, time sure is flying!

I must say, mgorny's posts are always a treat for those who like to peer under the hood! (The fact that Gentoo has remained my happy place for years doesn't influence this position, though there's probably some correlation)

layer8 3 hours ago

> The second part is much harder. Obviously, as soon as we’re past the 2038 cutoff date, all 32-bit programs — using system libraries or not — will simply start failing in horrible ways. One possibility is to work with faketime to control the system clock. Another is to run a whole VM that’s moved back in time.

Yet another is to freeze the time for those programs at the last 32-bit POSIX second. They would just appear to execute incredibly fast :). Of course some will still break, and it’s obviously not suitable for many use cases (but neither is running in the past), but some might be just fine.

jeffbee 3 hours ago

Are we still keeping shared libraries? They are a complex solution to a problem that arguably stopped existing 20 years ago. Might be time to rethink the entire scheme.

  • filmor 2 hours ago

    On Gentoo, definitely. I really don't want to rebuild my whole system whenever some foundational library fixes a bug. It already annoys me quite a bit that I need to set up sccache to get half-way reasonable compile times out of Rust projects (and I'm saying that as someone who enjoys gradually replacing significant portions of the userspace with Rust tools).

  • otabdeveloper4 an hour ago

    The Docker image layers you so dearly love are an implementation of shared libraries, except done in a broken way that's a thousand times less performant and more insecure.

cryptonector 3 hours ago

The simplest thing to do is to make any 32-bit `time_t`s be unsigned. That buys another 68 years to get the transition done. Not that that's exactly easy, but it's easier than switching to 64-bit time_t.