From 364f9fd8505a162a6cacd0223c3b55927804981c Mon Sep 17 00:00:00 2001
From: "H. Vetinari" <h.vetinari@gmx.com>
Date: Fri, 21 Jun 2024 12:41:07 +1100
Subject: [PATCH 3/5] patch zoneinfo_dir_override to point to our tzdata

---
 libstdc++-v3/src/c++20/tzdb.cc | 78 ++++++++++++++++++++++++++++------
 1 file changed, 65 insertions(+), 13 deletions(-)

diff --git a/libstdc++-v3/src/c++20/tzdb.cc b/libstdc++-v3/src/c++20/tzdb.cc
index c7c7cc9de..1596a77b2 100644
--- a/libstdc++-v3/src/c++20/tzdb.cc
+++ b/libstdc++-v3/src/c++20/tzdb.cc
@@ -37,8 +37,12 @@
 #include <mutex>      // mutex
 #include <filesystem> // filesystem::read_symlink
 
-#ifndef _AIX
-# include <cstdlib>   // getenv
+#if defined(__linux__)
+# include <dlfcn.h>   // dladdr
+# include <cstdlib>   // free, malloc, realpath
+# include <cstring>   // memcpy, strlen
+#else defined(_WIN32)
+# include <windows.h>
 #endif
 
 #if defined __GTHREADS && ATOMIC_POINTER_LOCK_FREE == 2
@@ -64,25 +68,73 @@
 
 namespace __gnu_cxx
 {
-#ifdef _AIX
-  // Cannot override weak symbols on AIX.
-  const char* (*zoneinfo_dir_override)() = nullptr;
-#else
-  [[gnu::weak]] const char* zoneinfo_dir_override();
-
-#if defined(__APPLE__) || defined(__hpux__) \
-  || (defined(__VXWORKS__) && !defined(__RTP__))
-  // Need a weak definition for Mach-O et al.
   [[gnu::weak]] const char* zoneinfo_dir_override()
   {
+#ifdef _GLIBCXX_SHARED
+    // get path to library we're in, to determine our location relative to $PREFIX;
+    // with help from the MIT-licensed https://github.com/gpakosz/whereami
+    void* addr = __builtin_extract_return_addr(__builtin_return_address(0));
+    char* this_lib;
+    int i;
+    // <string> is included through <chrono>
+    static std::string tz_dir;
+    if (!tz_dir.empty()) {
+      return tz_dir.c_str();
+    }
+#ifdef _WIN32
+    // we're in %PREFIX%\Library\bin\libstdc++-6.dll
+#   define TO_PREFIX "/../.."
+    // needs single quotes for character (not string) literal
+#   define PATH_SEP '\\'
+    wchar_t buffer[MAX_PATH];
+    HMODULE hm = NULL;
+    // non-zero return means success
+    if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
+                          GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
+                          (LPCSTR) addr, &hm)) {
+      // returns length of string (not counting null byte), see
+      // https://learn.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-getmodulefilenamew#return-value
+      DWORD total_length = GetModuleFileNameW(hm, buffer, sizeof(buffer));
+      if (total_length) {
+        this_lib = (char*)malloc(total_length + 1);
+        memcpy(this_lib, buffer, total_length);
+#else
+    // we're in $PREFIX/lib/libstdcxx.so
+#   define TO_PREFIX "/.."
+#   define PATH_SEP '/'
+    char buffer[PATH_MAX];
+    Dl_info info;
+
+    if (dladdr(addr, &info)) {
+      char* resolved = realpath(info.dli_fname, buffer);
+      if (resolved) {
+        int total_length = (int)strlen(resolved);
+        this_lib = (char*)malloc(total_length + 1);
+        memcpy(this_lib, resolved, total_length);
+#endif
+
+        for (i = (int)total_length - 1; i >= 0; --i) {
+          if (this_lib[i] == PATH_SEP) {
+            // set to null byte so the string ends before the basename
+            this_lib[i] = '\0';
+            break;
+          }
+        }
+        tz_dir = {this_lib};
+        tz_dir += TO_PREFIX;
+        tz_dir += "/share/zoneinfo";
+        // std::string constructor for tz_dir deep-copies
+        free(this_lib);
+        return tz_dir.c_str();
+      }
+    }
+#endif // _GLIBCXX_SHARED
 #ifdef _GLIBCXX_ZONEINFO_DIR
     return _GLIBCXX_ZONEINFO_DIR;
 #else
     return nullptr;
 #endif
   }
-#endif
-#endif
 }
 
 #if ! defined _GLIBCXX_ZONEINFO_DIR && ! defined _GLIBCXX_STATIC_TZDATA
