Die Risiken der Umstellung auf 64-Bit-time_t
(blogs.gentoo.org)- Auf 32-Bit-glibc-Systemen können nach 2038 Abfragen der aktuellen Zeit oder
stat()-Aufrufe fehlschlagen; Gentoo braucht daher einen sicheren Migrationspfad zu 64-Bit-time_t - time64 in glibc muss zusammen mit Large File Support (LFS) verwendet werden; in 32-Bit-Umgebungen existieren dadurch die bisherige ABI, die LFS-ABI und die LFS+time64-ABI nebeneinander
- Wenn
time_tin APIs, Strukturen oder Funktionsargumenten vorkommt, führt die Änderung der Typbreite zu einem ABI-Bruch; das Mischen von time32- und time64-Binaries kann zu Laufzeitfehlern und Sicherheitsrisiken führen - Als quellbasierte Distribution kann Gentoo während eines Rebuilds von
@worldwegen Fehlern oder zirkulären Abhängigkeiten mit einem halb umgestellten System zurückbleiben - Nach der Korrektur vom 2024-09-30 reicht eine reine libdir-Umstellung nicht mehr aus; eine time64-Kennzeichnung, die auch den dynamischen Loader und Toolchains mehrerer Sprachen umfasst, bleibt die zentrale Einschränkung
Das Jahr-2038-Problem und der Umfang der time64-Umstellung
- 32-Bit-Anwendungen, die ein 32-Bit-
time_tverwenden, können im Jahr 2038 statt der aktuellen Zeit einen-1-Fehler erhalten oder für Dateien keinstat()mehr ausführen - Die grundlegende Richtung der Umstellung besteht darin,
time_tin einen 64-Bit-Typ zu ändern- musl hat die Umstellung bereits vollzogen
- glibc unterstützt sie als Option
- Einige Distributionen wie Debian haben bereits umgestellt
- Bei quellbasierten Distributionen wie Gentoo bauen Nutzer ihr System selbst neu; daher muss die Zeit minimiert werden, in der Pakete in unterschiedlichen ABI-Zuständen verbleiben
- Das Kernrisiko liegt darin, dass eine Änderung der Breite von
time_tdie ABI bricht- Wenn eine Bibliotheks-API
time_tenthält, muss sämtlicher Code, der gegen diese Bibliothek linkt, dieselbe Typbreite verwenden - Nur einzelne Teile auf time64 umzustellen, ist nicht sicher
- Wenn eine Bibliotheks-API
LFS und drei 32-Bit-Unter-ABIs
- Auf 32-Bit-Architekturen gab es schon länger Probleme mit der Breite dateibezogener Typen
off_twird für Datei-Offsets verwendetino_twird für Inode-Nummern verwendet- Ursprünglich waren sie 32 Bit breit, wodurch Dateien größer als 2 GiB oder Inode-Nummern außerhalb des 32-Bit-Bereichs problematisch wurden
- Zur Lösung wurde Large File Support (LFS) eingeführt
- Dabei werden
off_tundino_tauf 64-Bit-Varianten umgestellt - In glibc ist dies bis heute optional
- Viele Pakete haben upstream LFS aktiviert und ABI-Brüche behandelt, aber das Problem ist damit nicht vollständig gelöst
- Dabei werden
- Die time64-Unterstützung von glibc erfordert die Nutzung von LFS, sodass Datei- und Zeitprobleme gemeinsam gelöst werden
- Auf 32-Bit-Systemen existieren drei Unter-ABIs
- Bisherige ABI: 32-Bit-Typen
- LFS: 64-Bit-
off_t, 64-Bit-ino_t, 32-Bit-time_t - time64: LFS + 64-Bit-
time_t
- Ein einzelner glibc-Build ist mit allen drei Varianten kompatibel, aber Bibliotheken, die diese Typen in ihrer API verwenden, können die drei Varianten nicht miteinander mischen
Wie ABI-Änderungen tatsächlich brechen
- Wenn
time_tvon 32 Bit auf 64 Bit wechselt, ändert sich das Layout von Strukturen- Wenn in einer Beispielstruktur
int a,time_t b,int cin dieser Reihenfolge stehen, unterscheidet sich der Offset vonczwischen 32-Bit-time_tund 64-Bit-time_t - Mischt man time32- und time64-Binaries, können falsche Felder gelesen oder geschrieben werden; auch Zugriffe außerhalb der Grenzen werden möglich
- Wenn in einer Beispielstruktur
- Auch die Größe von
struct stathängt von der ABI ab- Standardwert bei 32-Bit-x86-glibc: 88 Byte
- LFS: 96 Byte
- LFS + time64: 108 Byte
- Selbst ohne Strukturen können Funktionsargumente Probleme verursachen
- Auf x86 werden Funktionsargumente über den Stack übergeben
- Wenn eines der Argumente
time_tist, verschiebt sich die Stack-Position der folgenden Argumente
- In einem Beispielversuch werden Werte beschädigt, wenn ein time32-Programm gegen eine als time64 neu gebaute Bibliothek linkt
- Die ursprüngliche Ausgabe ist
a = 1, ein normaler Zeitwert,c = 3 - Wird nur die Bibliothek mit
-D_FILE_OFFSET_BITS=64 -D_TIME_BITS=64neu gebaut, werdenbundcfalsch interpretiert
- Die ursprüngliche Ausgabe ist
- Derzeit gibt es keinen wirksamen Schutz, der eine solche ABI-Mischung verhindert; dadurch können Laufzeitbrüche und Sicherheitsprobleme entstehen
Warum die Gentoo-Umstellung schwieriger ist
- Binärdistributionen bauen alle Pakete neu, danach führen Nutzer ein Upgrade in relativ atomaren Schritten durch
- Drittanbieter-Repositories oder lokal gebaute Programme können Probleme verursachen, insgesamt ist der Prozess aber vergleichsweise sicher
- Gentoo muss die ABI im laufenden System ändern, während
@worldneu gebaut wird- Zwischen den jeweiligen Rebuilds zweier Pakete können inkompatible ABIs miteinander vermischt werden
- Wenn einige Rebuilds fehlschlagen, kann das System in einem halb umgestellten Zustand zurückbleiben
- Wegen zirkulärer Abhängigkeiten kann ein Rebuild abhängiger Pakete Build-Tools beschädigen, wodurch weitere Rebuilds unmöglich werden
Geprüfte Gegenmaßnahmen
- Diskutiert werden drei Richtungen
- Änderung von
CHOST, dem Plattform-Tupel, um die neue ABI von der bisherigen 32-Bit-ABI zu unterscheiden - Änderung des libdir der neuen ABI, damit neu gebaute Bibliotheken getrennt von bestehenden Bibliotheken installiert werden
- Einführung einer ABI-Unterscheidung auf Binärebene, damit Binaries unterschiedlicher Unter-ABIs nicht miteinander gelinkt werden
- Änderung von
- Die drei Methoden können bis zu einem gewissen Grad unabhängig umgesetzt werden, einige können jedoch voneinander abhängen
- Die Beispiel-Strings im Text sind möglicherweise nicht die tatsächlich endgültigen Strings einer Lösung
ABI-Unterscheidung per CHOST
- Das Plattform-Tupel identifiziert die Zielplattform der Toolchain und wird in Gentoo auch verwendet, um ABIs für Multilib-Unterstützung eindeutig zu unterscheiden
- Das Tupel besteht aus vier Teilen: Architektur, Vendor, Betriebssystem und libc
- Beispiel:
i386-pc-linux-gnu - Beispiel:
i686-pc-linux-gnu - Beispiel:
i686-unknown-linux-gnu
- Beispiel:
- Bei der Einführung neuer ABIs wurde entweder das Vendor-Feld geändert oder eine ABI-Kennzeichnung an das libc-Feld angehängt
- Bei der ARM-hardfloat-ABI wurden früher Formen wie
armv7a-hardfloat-linux-gnueabiundarmv7a-unknown-linux-gnueabihfverwendet
- Bei der ARM-hardfloat-ABI wurden früher Formen wie
- Für die time64-ABI gibt es ähnliche Optionen
i686-gentoo_t64-linux-gnui686-pc-linux-gnut64armv7a-gentoo_t64-linux-gnueabihfarmv7a-unknown-linux-gnueabihft64
- Eine Änderung des Tupels dürfte nicht viele Patches erfordern
- Die GNU toolchain und das GNU build system ignorieren im libc-Feld Inhalte nach
gnu - Clang benötigt einen Patch, damit je nach Tupel automatisch die richtige ABI gewählt wird
- Die GNU toolchain und das GNU build system ignorieren im libc-Feld Inhalte nach
libdir-Änderung und preserved-libs
- libdir ist der Standardname des Installationsverzeichnisses für Bibliotheken
- Der übliche Standardwert ist
lib - Auf Architekturen mit 64-Bit-Gegenstück wird konventionell häufig
lib64verwendet - Die x32-ABI auf x86 verwendet
libx32, die n32-ABI auf MIPS verwendetlib32
- Der übliche Standardwert ist
- Für die 32-Bit-ABI mit time64 wird erwogen, libdir von
libauf einen Wert wielibt64zu ändern - Ein eigenes libdir dient während der Umstellung als Mechanismus, um ABI-Mischungen zu reduzieren
- Es verringert das Risiko, dass time64-Executables versehentlich gegen time32-Bibliotheken linken
- Mit Portages preserved-libs-Funktion können time32-Bibliotheken erhalten bleiben
- Optional kann ein time32+time64-Multilib-Profil angeboten werden, um Kompatibilität mit bestehenden vorab gebauten time32-Anwendungen zu wahren
- Mit preserved-libs verwenden bestehende Executables bis zu ihrem Rebuild weiter time32-Bibliotheken, während als time64 neu gebaute Bibliotheken im neuen libdir installiert werden
- Eine libdir-Änderung erfordert Toolchain-Patches
- glibc kann gesondert behandelt werden, weil derselbe Bibliothekssatz für mehrere Unter-ABIs gültig ist
- Möglicherweise ist ein separater
ld.sonötig, damit.interpvon time64-Executables auf den time64-ld.soverweist
- Für echte Multilib-Unterstützung ist außerdem ein für diese ABI eindeutiges Plattform-Tupel erforderlich
Kennzeichnung von Inkompatibilität auf Binärebene
- Wenn Binaries unterschiedlicher ABIs gemischt werden, sollten Linker oder dynamischer Loader dies normalerweise verhindern
- Linkt man ein 64-Bit-Programm gegen eine 32-Bit-Bibliothek, weist der Linker dies mit
file in wrong formatzurück - Auch der dynamische Loader lehnt es mit Fehlern wie
wrong ELF class: ELFCLASS32ab
- Linkt man ein 64-Bit-Programm gegen eine 32-Bit-Bibliothek, weist der Linker dies mit
- Für bestehende ABI-Unterscheidungen werden mehrere Mechanismen genutzt
ELFCLASS32undELFCLASS64- Machine Identifier wie
EM_386undEM_X86_64 - Das flags-Feld bei ARM und MIPS
- Architekturspezifische Attribute Sections
- Auch für time32 und time64 wird ein ähnlicher Mechanismus benötigt, doch das ist nicht einfach
- Es scheint keinen wiederverwendbaren allgemeinen Mechanismus zu geben
- Es braucht eine Lösung für mehrere Architekturen
- Ein neuer ELF note section mit Toolchain-Unterstützung erscheint als realistischer Kandidat
- Es muss auch bedacht werden, dass Nutzer den Schutz möglicherweise deaktivieren wollen
- Vorab gebaute Software ohne Quellcode könnte weiterhin mit Systembibliotheken funktionieren, wenn sie keine APIs aufruft, die
time_tverwenden - Eine pauschale Blockade könnte schlimmer sein als das Problem selbst
- Vorab gebaute Software ohne Quellcode könnte weiterhin mit Systembibliotheken funktionieren, wenn sie keine APIs aufruft, die
- Mit einem separaten libdir lässt sich relativ einfach eine nicht fatale QA-Prüfung erstellen
- time64-Executables werden über
.interpunterschieden - Es wird geprüft, dass time32-Programme keine Bibliotheken aus
libt64laden - Es wird geprüft, dass time64-Programme keine Bibliotheken direkt aus
libladen
- time64-Executables werden über
Grenzen vorab gebauter 32-Bit-Anwendungen
- Abseits von Paketen, die aus dem Quellcode gebaut werden, gibt es für x86 und PowerPC Anwendungen, die nur als alte vorab gebaute Binaries verfügbar sind
- Das betrifft insbesondere proprietäre Software und ältere Spiele
- Sie sind sowohl von Kompatibilitätsproblemen mit Systembibliotheken als auch vom Jahr-2038-Problem selbst betroffen
- Für Kompatibilitätsprobleme bietet die bestehende Multilib-Struktur in gewissem Umfang eine Lösung
- Für die Unterstützung von 32-Bit-Software auf amd64 gibt es bereits ein Multilib-Layout und Mechanismen zum Bauen mehrerer Bibliotheksversionen
- Dies kann erweitert werden, indem
abi_x86_32undabi_x86_t64unterschieden werden - Es lässt sich ein neues Multilib-x86-Profil erstellen, das beide ABIs unterstützt
- Das Scheitern von 32-Bit-Programmen selbst nach 2038 bleibt schwieriger
- Eine Möglichkeit ist, die Systemzeit mit faketime zu steuern
- Möglich ist auch der Betrieb einer VM, deren Zeit in die Vergangenheit zurückgestellt wurde
Korrektur vom 2024-09-30: libdir allein reicht nicht aus
- Die ursprüngliche Idee war zu optimistisch; allein durch eine libdir-Änderung lässt sich keine stabile Trennung erreichen
- Da alle libdirs in
ld.so.confaufgelistet sind, kann man sich nicht darauf verlassen, libdir-Pfade inld.sohart zu codieren- Auch bei einem Custom-LLVM-Prefix werden Pfade bereits angepasst, und auch dieser Fall benötigt Sonderbehandlung
- Dadurch wird es wahrscheinlicher, dass eine libdir-Änderung von einer Unterscheidung der Binärinkompatibilität abhängen muss
- Drei grundlegende Ziele müssen erfüllt werden
- Der dynamische Loader muss time32- und time64-Binaries unterscheiden
- Alle Binaries ohne explizite time64-Kennzeichnung müssen aus Gründen der Abwärtskompatibilität als time32 gelten
- Alle neu gebauten Binaries müssen eine explizite time64-Kennzeichnung tragen, einschließlich Binaries, die in Nicht-C-Umgebungen wie Rust gebaut wurden
- Diese Ziele bedeuten Arbeit auf dem Niveau, mehrere Toolchains mehrerer Sprachen patchen zu müssen
- Für Gentoo ist es schwer, dies nur lokal zu pflegen; die Zusammenarbeit mehrerer Parteien ist nötig
- Die Zielarchitekturen gelten oft als Legacy oder werden nicht mehr ausreichend unterstützt
- Ob andere Toolchains korrekte time64-Executables erzeugen, ist ein eigenes Problem
- Wenn sie nicht wie C-Programme so angepasst werden, dass sie
_TIME_BITSfolgen, können sie eine bestimmtetime_t-Breite hart codieren und brechen
- Wenn sie nicht wie C-Programme so angepasst werden, dass sie
- Da alle Binaries ohne explizite time64-Kennzeichnung time32-Bibliotheken verwenden würden, könnte Gentoo keine Drittanbieter-Executables ausführen, die nicht gepatcht wurden, um die korrekte Kennzeichnung zu tragen
- Es wird auch eine Alternative mit niedrigerem Zielniveau geprüft
- Allen time64-Executables wird ein RPATH injiziert, der das time64-libdir direkt erzwingt
- Diese Methode verhindert nicht vollständig, dass der dynamische Loader time32-Bibliotheken verwendet, kann aber die Umstellung ohne große Kompatibilitätsprobleme unterstützen
- Umgekehrt gibt es auch die Möglichkeit, nicht das time64-libdir dauerhaft zu ändern, sondern das time32-libdir nur vorübergehend umzubenennen
- Bestehenden Programmen wird ein RPATH injiziert und der libdir-Name geändert
- Neue time64-Bibliotheken werden im bestehenden libdir installiert
- Neue time64-Programme tragen keinen RPATH, der time32-Bibliotheken erzwingt
- Der Vorteil ist, dass die Kompatibilität mit anderen Distributionen erhalten bleibt, die bereits umgestellt haben
Offene Aufgaben
- Wenn alle drei Lösungsansätze umgesetzt werden, kann Gentoo für 32-Bit-Systeme mit glibc einen saubereren und relativ sicheren Migrationspfad bieten
- Allerdings gelten diese Lösungen hauptsächlich für Pakete, die aus dem Quellcode gebaut werden
- Bei vorab gebauten 32-Bit-Anwendungen bleibt das Jahr-2038-Problem bestehen, selbst wenn die ABI-Kompatibilität erhalten wird
- Das gesamte Design ist noch ein Entwurf und kann sich durch Experimente, Diskussionen und eingereichte Patches weiter ändern
1 Kommentare
Hacker-News-Kommentare
Bei Gentoo gibt es einige Optionen, die im Artikel nicht behandelt wurden; vermutlich wurden sie wegen des hohen Aufwands ausgelassen, der sich aus dem Systemdesign von Gentoo ergibt
.so-Versioning so zu erweitern, dass auch ABI-Änderungen abhängiger Pakete berücksichtigt werden. Normalerweise enthalten Shared Libraries in Dateinamen und interner Version Versionsnummern, etwalibfoo.so.1.0.0, und Pakete verfolgen ihr eigenes ABI-Breaking. Um 64-Bit-time_tzu unterstützen, müsste man für jede.soeine Versionierungskomponente hinzufügen, die von der abhängigen ABI gesteuert wird. Das Ergebnis wäre ähnlich wie beim im Artikel erwähnten „anderen libdir“, könnte aber als wiederverwendbare Grundlage für künftige ABI-Änderungen dienen, wäre dafür vermutlich deutlich invasiverMan könnte mehrere neue Paket-Builds einplanen und in einer Sandbox bauen; neue Kompilierungen würden dann per Union zuerst in die Sandbox schauen und nur bei Bedarf auf das System zurückfallen. Wenn alles gebaut ist, werden die Ergebnisse paketiert und aus der Sandbox ins echte System verschoben. So ließe sich ein komplettes Gentoo-Update transaktionsartig machen, was auch in anderer Hinsicht große Vorteile hätte
ROOTändern, das normalerweise auf/gesetzt istMan kann das gesamte
@systemund@worldneu bauen und in ein angegebenes Unterverzeichnis installieren und dann auf einmal synchronisieren. Wenn möglich, macht man das besser in einer Live-Session; theoretisch wäre es auch möglich,/in ein Unterverzeichnis des neu installierten Ziels zu bind-mounten, dann per chroot hineinzugehen und auf das echte übergeordnete/zu synchronisierenhttps://devmanual.gentoo.org/ebuild-writing/variables/#root
Die Art, wie Mac OS X mit
off_tundino_tumging, könnte ein Hinweis sein. Bestehende Aufrufe und Strukturen behielten ihr Verhalten, und es wurden neue Aufrufe und Typen mit dem Suffix64hinzugefügt; per Präprozessor-Makro konnte ausgewählt werden, worauf tatsächlich verwiesen wird, auch wenn das nur selten direkt geschrieben wurdeStattdessen sind OS und SDK versioniert, und beim Build kann man angeben, welche älteste OS-Version die Binärdatei noch unterstützen muss. Die Header wählten darauf basierend automatisch die passenden Makros aus, und Kommentare zu neuen bzw. veralteten APIs erzeugten über denselben Mechanismus Weak Linking oder Warnungen. Anfangs lief das über den Präprozessor, aber heute versteht der Compiler laut Apple die API-Verfügbarkeit wesentlich genauer, sodass so etwas wohl auch auf anderen Plattformen möglich wäre
Selbst wenn sie auf OS v.B laufen, kann eine Anwendung X, die für OS v.B deklariert wurde, möglicherweise nicht mit einer Anwendung Y linken, die für OS v.A deklariert wurde. Tatsächlich ist das sehr nah an dem, was fast alle Plattformen ohnehin schon tun; andersherum würde die Binärkompatibilität mit bestehenden Programmen sofort brechen
off_t-Größe definieren, und es gäbe auch keinen Schalter, mit dem Header transparent den richtigen Funktionssatz entsprechend der vom Clientprogramm gewünschten Typgröße auswählenTrotzdem betont der Artikel, dass
time_tein größeres Problem ist alsoff_t. Ein plausibler Grund ist, dasstime_tviel weiter verbreitet ist.off_tist ein POSIX-Typ, der an relativ wenigen Schnittstellen beteiligt ist, währendtime_tzu ISO C gehört und überall verwendet wird. Außerdem gehen viele C-Programme davon aus, dasstime_tein ganzzahliger Typ mit derselben Breite wieintist; solche Annahmen überoff_tsind seltenerAuch Debian hatte große Schmerzen damit. Einige sind möglicherweise ausgebrannt, und viele zeigten auf eine source-basierte Distribution und sagten: „Dort drüben muss das doch ganz einfach sein“
/usr-ZusammenführungJedes Mal, wenn ich sehe, wie sehr Leute unter diesem Problem leiden, denke ich mir, wie gut es war, dass FreeBSD das damals beim ersten amd64-Port konsequent durchgezogen hat. Man konnte die Basis-Typen des ABI festlegen und entschied sich, in die Zukunft statt in die Vergangenheit zu schauen
amd64 hatte ein interessantes Merkmal, das diese Arbeit erleichterte. Da 32-Bit-Funktionsargumente bei Funktionsaufrufen automatisch auf 64 Bit gecastet wurden, funktionierte es in der frühen Portierungsphase meist einfach, selbst wenn man 32-Bit-Zeitwerte an Funktionen übergab, die ein 64-Bit-
time_terwarteten. So konnte man kleinere Restarbeiten auf später verschiebenEs gab damals auch andere 64-Bit-Plattformen, aber kein 64-Bit-
time_t, und FreeBSD/amd64 war meines Wissens um 2003–2005 in dieser Familie die erste. Soweit ich mich erinnere, ist auch sparc64 auf 64-Bit-time_tumgestiegenDas größte Problem war, dass tzcode damals nicht 64-Bit-sicher war. Der Normalisierungsalgorithmus von
struct tmgeriet in einen degenerierten Zustand, in dem er versuchte, Tag/Monat/Jahr fürtime_t(2^62)iterativ zu berechnen. Anstatt tzcode stark umzubauen, wurde es meines Wissens so gehandhabt, dass Daten grob vor 1900 oder nach 10000 einfach fehlschlagen. Vermutlich ist das inzwischen längst upstream behobenÜber einige Jahre hinweg musste man in Third-Party-Code immer wieder 32-/64-Bit-Verwechslungen bei Zeitwerten beseitigen, die dadurch entstanden, dass Datenstrukturen in Dateien oder im Netzwerk
int/long/time_tunsauber behandelten, aber insgesamt war das kein großes Problem. Dass man von Tag eins an 64-Bit-time_tverwendete, half, die meisten Probleme zu vermeiden, und ganz am Anfang ist so etwas leicht. Linux hat diese große Chance beim Start des amd64/x86_64-Ports verpasstZusätzlich ist anzumerken, dass man 64-Bit-
ino_tdamals nicht zu Ende gebracht hat. 32-Bit-Inode-Nummern waren an sehr vielen Stellen offengelegt, etwa in On-Disk-Strukturen von Dateisystemen, in UFS-Verzeichnisstrukturen usw. Als FreeBSD/amd64 noch eine nachrangige Plattform war, gab es keinen praktikablen Weg, das von Anfang an zu erledigen, ohne andere Tier-1-Architekturen stark durcheinanderzubringen. An der Arbeit wurde zweimal angesetzt, aber am Ende hat jemand anders sie fertiggestellt und dabei auch zu kurze Konstanten wie die Pfadlänge von Mountpoints korrigierttime_t,off_tundino_t. Das aktuelle Problem ist die Umstellung von 32-Bit-Linux auf 64-Bit-time_toff_tnoch mutiger umgegangen und hat es bereits seit 2.0 auf 64 Bit gesetzt. In den 32-Bit-Versionen von Linux gibt es noch Spuren der alten GrößeIch verstehe den Teil mit dem automatischen Cast von 32-Bit-Funktionsargumenten auf 64 Bit so, dass das nur für vorzeichenlose Argumente gilt. Wenn in
%edigeladen wird, wird der obere Teil von%rdigelöscht. Die SysV-ABI-Spezifikation für x86-64 sagt nicht, dass alle Register- oder Stackwerte auf einen vollständigen 64-Bit-Wert erweitert werden, und selbst die Anmerkung zu Booleans besagt nur, dass das niederwertige Byte Bedeutung hat, was darauf hindeutet, dass dies keine allgemeine Regel isttime_tauf i386 auf 64 Bit portiert hat, wäre das ziemlich überraschend. Ich frage mich, ob andere 32-Bit-Architekturen wie Motorola 68000 oder sparc32 ebenfalls auf 64-Bit-time_tumgestellt wurdenAuf einem alten großen 32-Bit-Unix-System habe ich einmal, um zukünftige Datumswerte verarbeiten zu können, libc-Funktionen mit signiertem 32-Bit-
time_tdurch Varianten für vorzeichenloses 32-Bit-time_tersetzt. Damit gewann man nach 2038 noch weitere 68 Jahre, und bis dahin werde ich ohnehin nicht mehr da seinDer Nachteil ist, dass sich damit keine Daten vor der Unix-Epoche 1970 darstellen lassen, aber für ein Terminplanungssystem war das kein Problem. Wenn vergangene Daten wichtig sind, könnte man die Epoche um einige Jahrzehnte verschieben oder die Zeitauflösung von 1 Sekunde auf 2 Sekunden reduzieren. Beides hat jeweils subtile Probleme, also hängt es vom Anwendungsfall ab
In der ursprünglichen BSD-Manpage hatte der Abschnitt „Bugs“ von
tunefsden berühmten Witz „You can tune a file system, but you can't tune a fish.“, und laut „Expert C Programming“ stand im Quelltext dieser Manpage neben dem Witz folgender Kommentar„Wenn du das entfernst, wird dir der UNIX-Dämon von jetzt an bis zu dem Zeitpunkt, an dem
time_tüberläuft, auf den Fersen bleiben.“Als dieser Satz in den 70ern geschrieben wurde, muss 2038 offensichtlich unvorstellbar weit in der Zukunft gelegen haben
https://progforperf.github.io/Expert_C_Programming.pdf
Mein stärkster Eindruck ist: Respekt für den Aufwand, aber aus Nutzersicht möchte ich dieses Problem einfach beenden, indem ich zu einer nicht quellbasierten Distribution wie Debian wechsle
/und/usrmkfs.ext4oder das verwendete Dateisystem aus, mountet sie, entpackt stage3, geht per chroot hinein und führtemerge $all-my-packages-that-where-installed-before-mkfsausStatt schrittweise zu aktualisieren, kann man also eine neue Gentoo-Kopie installieren
Mit Third-Party-Closed-Source-Software kann es auch auf binären Systemen weiterhin Probleme geben. Ebenso können Probleme bei First-Party-Paketen auftreten, die als getrennte Schritte separat installiert werden
Ich bin zwar kein C-Experte, dachte aber, dass Typ-Aliasse wie
off_tgerade eingeführt wurden, damit man sie später ändern kann. Es wirkt jedoch so, als würde das nicht wirklich eindeutig funktionieren, und ich frage mich, ob ich das falsch verstanden habe.off_tverwendet, muss man den Code normalerweise nicht neu schreiben, aber alles, was diesen Typ verwendet, muss neu kompiliert werden.off_t-Definition@worldatomar neu bauen könnte, gäbe es kein Problem, aber quellbasierte Distributionen bauen@worldnicht atomar neu, sondern kompilieren die Pakete nacheinander neu.Dann kann es passieren, dass
libc.sobereits 64-Bit-off_tverwendet, währendgccnoch gegen 32-Bit-off_tgebaut ist, sodassgcchängen bleibt. Auch Pakete wiebash,coreutils,makeundbinutils, die für den Neuaufbau von@worldnötig sind, können kaputtgehen, und an diesem Punkt ist man blockiert. Deshalb erfordern solche Upgrades besondere Vorsicht.off_tin einer Struktur landet, bei Funktionsaufrufen verwendet wird oder in ein Protokoll eingebaut ist, und die tatsächliche Größe wird wichtig.Mischt man beim Laden von Bibliotheken oder bei der Kommunikation über ein Protokoll alten und neuen Code, geraten die Offsets durcheinander und es kommt zu Abstürzen. Letztlich ist die Umstellung sehr schmerzhaft, weil alle Programme in „Legacy“ und „portiert oder zumindest geprüft“ aufgeteilt werden müssen.
Selbst ein Wechsel zu einem größeren Typ mit ähnlicher Bedeutung kann etwas kaputtmachen. Ein einfaches Beispiel ist Struct-Padding, und es gibt auch viele Anwendungsfälle, in denen Pointer in Integer umgewandelt und wieder zurückverwandelt werden, sodass Änderungen an der internen Darstellung zwangsläufig Dinge zerstören können. Ob das gute Praxis ist, steht auf einem anderen Blatt, aber ungewöhnlich ist es nicht. Der Kernpunkt ist ABI-Kompatibilität.
off_tgebaut wurden, mit solchen, die mit 64-Bit-off_tgebaut wurden, und das resultierende Verhalten kann äußerst unvorhersehbar sein.Im Beispiel-Struct wurde gesagt, dass der Offset von
cbei 32-Bit-time_t8 und bei einem 64-Bit-Typ 12 sei, aber eigentlich müsste es nicht 16 sein?bmüsste doch auf 64 Bit ausgerichtet werden, also müsste zwischenaundbPadding eingefügt werden. Das würde den Punkt, den der Autor machen will, eher noch stärker unterstreichen.Wenn man sich all das ansieht, dann hat selbst die seltsame Zeitdarstellung von Windows — also 64 Bit in 100-ns-Schritten gezählt seit dem 1. Januar 1601, 00:00 GMT, im gregorianischen Kalender — einen kleinen Vorteil. Die Auflösung ist ebenfalls hervorragend, und das wird vermutlich funktionieren, bis die gesamte Galaxie erobert ist.