For some 32-bit targets Glibc supports changing the size of time_t to be
64 bits by defining _TIME_BITS=64. That causes an ABI change which
would affect std::chrono::system_clock::to_time_t. Because to_time_t is
not a function template, its mangled name does not depend on the return
type, so it has the same mangled name whether it returns a 32-bit time_t
or a 64-bit time_t. On targets where the size of time_t can be selected
at preprocessing time, that can cause ODR violations, e.g. the linker
selects a definition of to_time_t that returns a 32-bit value but a
caller expects 64-bit and so reads 32 bits of garbage from the stack.

This commit adds always_inline to to_time_t when time_t has been changed
from a 32-bit type to a 64-bit type by defining _TIME_BITS=64. This
ensures that callers expecting a 64-bit time_t can't link to a
definition returning a 32-bit time_t.

We use the internal Glibc macro __USE_TIME64_REDIRECTS to detect the
case where time_t defaults to 32-bit for the target but has been
explicitly changed to 64-bit by the user.

libstdc++-v3/ChangeLog:

        PR libstdc++/99832
        * include/bits/chrono.h (system_clock::to_time_t): Add always
        inline_attribute for 64-bit time_t on 32-bit target.
        * testsuite/20_util/system_clock/time64.cc: New test.
---

Tested x86_64-linux (-m64 and -m32, with recent glibc).

 libstdc++-v3/include/bits/chrono.h            |  3 +++
 .../testsuite/20_util/system_clock/time64.cc  | 21 +++++++++++++++++++
 2 files changed, 24 insertions(+)
 create mode 100644 libstdc++-v3/testsuite/20_util/system_clock/time64.cc

diff --git a/libstdc++-v3/include/bits/chrono.h 
b/libstdc++-v3/include/bits/chrono.h
index fad216203d2f..5c6ee759b381 100644
--- a/libstdc++-v3/include/bits/chrono.h
+++ b/libstdc++-v3/include/bits/chrono.h
@@ -1244,6 +1244,9 @@ _GLIBCXX_BEGIN_INLINE_ABI_NAMESPACE(_V2)
       now() noexcept;
 
       // Map to C API
+#ifdef __USE_TIME64_REDIRECTS
+      [[__gnu__::__always_inline__]]
+#endif
       static std::time_t
       to_time_t(const time_point& __t) noexcept
       {
diff --git a/libstdc++-v3/testsuite/20_util/system_clock/time64.cc 
b/libstdc++-v3/testsuite/20_util/system_clock/time64.cc
new file mode 100644
index 000000000000..3cbf80e0f06e
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/system_clock/time64.cc
@@ -0,0 +1,21 @@
+// { dg-options "-D_FILE_OFFSET_BITS=64 -D_TIME_BITS=64 -O0 -g0" }
+// { dg-do compile { target *-*-linux-gnu } }
+// { dg-require-effective-target c++20 }
+// { dg-require-effective-target ilp32 }
+// { dg-final { scan-assembler-not "system_clock9to_time_t" } }
+
+#include <chrono>
+
+template<typename T>
+std::time_t
+test()
+{
+  using std::chrono::system_clock;
+
+  if constexpr (sizeof(T) == 8)
+    return system_clock::to_time_t(system_clock::now());
+  else // _TIME_BITS=64 had no effect, maybe an old Glibc
+    return 0;
+}
+
+auto t = test<std::time_t>();
-- 
2.49.0

Reply via email to