(root)/
glib-2.79.0/
glib/
tests/
constructor.c
       1  /* constructor.c - Test for constructors
       2   *
       3   * Copyright © 2023 Luca Bacci
       4   *
       5   * SPDX-License-Identifier: LGPL-2.1-or-later
       6   *
       7   * This library is free software; you can redistribute it and/or
       8   * modify it under the terms of the GNU Lesser General Public
       9   * License as published by the Free Software Foundation; either
      10   * version 2.1 of the License, or (at your option) any later version.
      11   *
      12   * This library is distributed in the hope that it will be useful,
      13   * but WITHOUT ANY WARRANTY; without even the implied warranty of
      14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      15   * Lesser General Public License for more details.
      16   *
      17   * You should have received a copy of the GNU Lesser General Public License
      18   * along with this library; if not, see <http://www.gnu.org/licenses/>.
      19   */
      20  
      21  #include <glib.h>
      22  #include "../gconstructorprivate.h"
      23  
      24  #ifndef _WIN32
      25  #include <dlfcn.h>
      26  #else
      27  #include <windows.h>
      28  #endif
      29  
      30  #if defined(_WIN32)
      31    #define MODULE_IMPORT \
      32      __declspec (dllimport)
      33  #else
      34    #define MODULE_IMPORT
      35  #endif
      36  
      37  MODULE_IMPORT
      38  void string_add_exclusive (const char *string);
      39  
      40  MODULE_IMPORT
      41  void string_check (const char *string);
      42  
      43  MODULE_IMPORT
      44  int string_find (const char *string);
      45  
      46  #if G_HAS_CONSTRUCTORS
      47  
      48  #ifdef G_DEFINE_CONSTRUCTOR_NEEDS_PRAGMA
      49  #pragma G_DEFINE_CONSTRUCTOR_PRAGMA_ARGS (ctor)
      50  #endif
      51  
      52  G_DEFINE_CONSTRUCTOR (ctor)
      53  
      54  static void
      55  ctor (void)
      56  {
      57    string_add_exclusive (G_STRINGIFY (PREFIX) "_" "ctor");
      58  }
      59  
      60  #ifdef G_DEFINE_DESTRUCTOR_NEEDS_PRAGMA
      61  #pragma G_DEFINE_DESTRUCTOR_PRAGMA_ARGS (dtor)
      62  #endif
      63  
      64  G_DEFINE_DESTRUCTOR (dtor)
      65  
      66  static void
      67  dtor (void)
      68  {
      69    string_add_exclusive (G_STRINGIFY (PREFIX) "_" "dtor");
      70  
      71    if (string_find ("app_dtor") && string_find ("lib_dtor"))
      72      {
      73        /* All destructors were invoked, this is the last.
      74         * Call _Exit (EXIT_SUCCESS) to exit immediately
      75         * with a success code */
      76        _Exit (EXIT_SUCCESS);
      77      }
      78  }
      79  
      80  #endif /* G_HAS_CONSTRUCTORS */
      81  
      82  
      83  #if defined (_WIN32) && defined (G_HAS_TLS_CALLBACKS)
      84  
      85  extern IMAGE_DOS_HEADER __ImageBase;
      86  
      87  static inline HMODULE
      88  this_module (void)
      89  {
      90    return (HMODULE) &__ImageBase;
      91  }
      92  
      93  G_DEFINE_TLS_CALLBACK (tls_callback)
      94  
      95  static void NTAPI
      96  tls_callback (PVOID  hInstance,
      97                DWORD  dwReason,
      98                LPVOID lpvReserved)
      99  {
     100    /* The HINSTANCE we get must match the address of __ImageBase */
     101    g_assert_true (hInstance == this_module ());
     102  
     103  #ifdef BUILD_TEST_EXECUTABLE
     104    /* Yes, we can call GetModuleHandle (NULL) with the loader lock */
     105    g_assert_true (hInstance == GetModuleHandle (NULL));
     106  #endif
     107  
     108    switch (dwReason)
     109      {
     110      case DLL_PROCESS_ATTACH:
     111        {
     112  #ifndef BUILD_TEST_EXECUTABLE
     113          /* the library is explicitly loaded */
     114          g_assert_null (lpvReserved);
     115  #endif
     116          string_add_exclusive (G_STRINGIFY (PREFIX) "_" "tlscb_process_attach");
     117        }
     118        break;
     119  
     120      case DLL_THREAD_ATTACH:
     121        break;
     122  
     123      case DLL_THREAD_DETACH:
     124        break;
     125  
     126      case DLL_PROCESS_DETACH:
     127        {
     128  #ifndef BUILD_TEST_EXECUTABLE
     129          /* the library is explicitly unloaded */
     130          g_assert_null (lpvReserved);
     131  #endif
     132          string_add_exclusive (G_STRINGIFY (PREFIX) "_" "tlscb_process_detach");
     133        }
     134        break;
     135  
     136      default:
     137        g_assert_not_reached ();
     138        break;
     139      }
     140  }
     141  
     142  #endif /* _WIN32 && G_HAS_TLS_CALLBACKS */
     143  
     144  #ifdef BUILD_TEST_EXECUTABLE
     145  
     146  void *library;
     147  
     148  static void
     149  load_library (const char *path)
     150  {
     151  #ifndef _WIN32
     152    library = dlopen (path, RTLD_NOW);
     153    if (!library)
     154      {
     155        g_error ("%s (%s) failed: %s", "dlopen", path, dlerror ());
     156      }
     157  #else
     158    wchar_t *path_utf16 = g_utf8_to_utf16 (path, -1, NULL, NULL, NULL);
     159    g_assert_nonnull (path_utf16);
     160  
     161    library = LoadLibraryW (path_utf16);
     162    if (!library)
     163      {
     164        g_error ("%s (%s) failed with error code %u",
     165                 "FreeLibrary", path, (unsigned int) GetLastError ());
     166      }
     167  
     168    g_free (path_utf16);
     169  #endif
     170  }
     171  
     172  static void
     173  unload_library (void)
     174  {
     175  #ifndef _WIN32
     176    if (dlclose (library) != 0)
     177      {
     178        g_error ("%s failed: %s", "dlclose", dlerror ());
     179      }
     180  #else
     181    if (!FreeLibrary (library))
     182      {
     183        g_error ("%s failed with error code %u",
     184                 "FreeLibrary", (unsigned int) GetLastError ());
     185      }
     186  #endif
     187  }
     188  
     189  static void
     190  test_app (void)
     191  {
     192  #if G_HAS_CONSTRUCTORS
     193    string_check ("app_" "ctor");
     194  #endif
     195  #if defined (_WIN32) && defined (G_HAS_TLS_CALLBACKS)
     196    string_check ("app_" "tlscb_process_attach");
     197  #endif
     198  }
     199  
     200  static void
     201  test_lib (gconstpointer data)
     202  {
     203    const char *library_path = (const char*) data;
     204  
     205    /* Constructors */
     206    load_library (library_path);
     207  
     208  #if G_HAS_CONSTRUCTORS
     209    string_check ("lib_" "ctor");
     210  #endif
     211  #if defined (_WIN32) && defined (G_HAS_TLS_CALLBACKS)
     212    string_check ("lib_" "tlscb_process_attach");
     213  #endif
     214  
     215    /* Destructors */
     216    unload_library ();
     217  
     218  #if G_HAS_DESTRUCTORS
     219    /* Destructors in dynamically-loaded libraries do not
     220     * necessarily run on dlclose. On some systems dlclose
     221     * is effectively a no-op (e.g with the Musl LibC) and
     222     * destructors run at program exit */
     223    g_test_message ("Destructors run on module unload: %s\n",
     224                    string_find ("lib_" "dtor") ? "yes" : "no");
     225  #endif
     226  #if defined (_WIN32) && defined (G_HAS_TLS_CALLBACKS)
     227    string_check ("lib_" "tlscb_process_detach");
     228  #endif
     229  }
     230  
     231  int
     232  main (int argc, char *argv[])
     233  {
     234  
     235    const char *libname = G_STRINGIFY (LIB_NAME);
     236    const char *builddir;
     237    char *path;
     238    int ret;
     239  
     240    g_assert_nonnull ((builddir = g_getenv ("G_TEST_BUILDDIR")));
     241  
     242    path = g_build_filename (builddir, libname, NULL);
     243  
     244    g_test_init (&argc, &argv, NULL);
     245  
     246    g_test_add_func ("/constructor/application", test_app);
     247    g_test_add_data_func ("/constructor/library", path, test_lib);
     248  
     249    ret = g_test_run ();
     250    g_assert_cmpint (ret, ==, 0);
     251  
     252    g_free (path);
     253  
     254    /* Return EXIT_FAILURE from main. The last destructor will
     255     * call _Exit (EXIT_SUCCESS) if everything went well. This
     256     * is a way to test that destructors get invoked */
     257    return EXIT_FAILURE;
     258  }
     259  
     260  #endif /* BUILD_TEST_EXECUTABLE */