| # dlloader.at -- test dlloader functionality -*- Autotest -*- |
| # |
| # Copyright (C) 2010-2019, 2021-2025 Free Software Foundation, Inc. |
| # This file is part of GNU Libtool. |
| # |
| # GNU Libtool is free software; you can redistribute it and/or |
| # modify it under the terms of the GNU General Public License as |
| # published by the Free Software Foundation; either version 2 of |
| # the License, or (at your option) any later version. |
| # |
| # GNU Libtool is distributed in the hope that it will be useful, |
| # but WITHOUT ANY WARRANTY; without even the implied warranty of |
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| # GNU General Public License for more details. |
| # |
| # You should have received a copy of the GNU General Public License |
| # along with GNU Libtool. If not, see <https://www.gnu.org/licenses/>. |
| #### |
| |
| AT_SETUP([dlloader API]) |
| AT_KEYWORDS([libltdl]) |
| |
| AT_DATA([main.c], |
| [[#include <ltdl.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| |
| static int |
| first_init (lt_user_data data) |
| { |
| const char *ctx = (const char *) data; |
| |
| printf ("first_init: %s\n", ctx); |
| |
| return 0; |
| } |
| |
| static lt_module |
| first_open (lt_user_data data, const char *filename, lt_dladvise advise) |
| { |
| static const char *first_module = "first"; |
| const char *ctx = (const char *) data; |
| |
| /* Use a magic string to avoid possible interactions with file system |
| * objects. Prepend '/' to short-circuit libltdl's search of |
| * $shlibpath_var (e.g. PATH, LD_LIBRARY_PATH, or similar). |
| */ |
| if (!filename || strcmp (filename, "/libltdl_dlloader_api_test_first")) |
| { |
| printf ("first_open denies a request\n"); |
| lt_dlseterror (LT_ERROR_FILE_NOT_FOUND); |
| return NULL; |
| } |
| |
| printf ("first_open (\"%s\"): %s\n", filename, ctx); |
| |
| return (lt_module) first_module; |
| } |
| |
| static const char * |
| first_symbol (void) |
| { |
| return "first_symbol"; |
| } |
| |
| static void * |
| first_sym (lt_user_data data, lt_module module, const char *symbolname) |
| { |
| const char *ctx = (const char *) data; |
| const char *filename = (const char *) module; |
| |
| printf ("first_sym (%s): %s\n", filename, ctx); |
| |
| return (void *) first_symbol; |
| } |
| |
| static int |
| first_close (lt_user_data data, lt_module module) |
| { |
| const char *ctx = (const char *) data; |
| const char *filename = (const char *) module; |
| |
| printf ("first_close (%s): %s\n", filename, ctx); |
| |
| return 0; |
| } |
| |
| static int |
| first_exit (lt_user_data data) |
| { |
| const char *ctx = (const char *) data; |
| |
| printf ("first_exit: %s\n", ctx); |
| |
| return 0; |
| } |
| |
| static int |
| last_init (lt_user_data data) |
| { |
| const char *ctx = (const char *) data; |
| |
| printf ("last_init: %s\n", ctx); |
| |
| return 0; |
| } |
| |
| static lt_module |
| last_open (lt_user_data data, const char *filename, lt_dladvise advise) |
| { |
| static const char *last_module = "last"; |
| const char *ctx = (const char *) data; |
| |
| /* Use a magic string to avoid possible interactions with file system |
| * objects. Prepend '/' to short-circuit libltdl's search of |
| * $shlibpath_var (e.g. PATH, LD_LIBRARY_PATH, or similar). |
| */ |
| if (!filename || strcmp (filename, "/libltdl_dlloader_api_test_last")) |
| { |
| printf ("last_open denies a request\n"); |
| lt_dlseterror (LT_ERROR_FILE_NOT_FOUND); |
| return NULL; |
| } |
| |
| printf ("last_open (\"%s\"): %s\n", filename, ctx); |
| |
| return (lt_module) last_module; |
| } |
| |
| static const char * |
| last_symbol (void) |
| { |
| return "last_symbol"; |
| } |
| |
| static void * |
| last_sym (lt_user_data data, lt_module module, const char *symbolname) |
| { |
| const char *ctx = (const char *) data; |
| const char *filename = (const char *) module; |
| |
| printf ("last_sym (%s): %s\n", filename, ctx); |
| |
| return (void *) last_symbol; |
| } |
| |
| static int |
| last_close (lt_user_data data, lt_module module) |
| { |
| const char *ctx = (const char *) data; |
| const char *filename = (const char *) module; |
| |
| printf ("last_close (%s): %s\n", filename, ctx); |
| |
| return 0; |
| } |
| |
| static int |
| last_exit (lt_user_data data) |
| { |
| const char *ctx = (const char *) data; |
| |
| printf ("last_exit: %s\n", ctx); |
| |
| return 0; |
| } |
| |
| typedef const char *module_func (void); |
| |
| int |
| main (int argc, char* argv[]) |
| { |
| int err = 0; |
| lt_dlvtable *first; |
| lt_dlvtable *last; |
| lt_dlhandle module = NULL; |
| module_func *symbol; |
| const char *first_ctx = "first_ctx"; |
| const char *last_ctx = "last_ctx"; |
| const lt_dlvtable *finder; |
| |
| LTDL_SET_PRELOADED_SYMBOLS (); |
| |
| if (lt_dlinit ()) |
| { |
| printf ("lt_dlinit failed\n"); |
| return 1; |
| } |
| |
| first = (lt_dlvtable *) malloc (sizeof (*first)); |
| if (!first) |
| { |
| printf ("malloc failed\n"); |
| err = 1; |
| goto cleanup; |
| } |
| |
| first->name = "first"; |
| first->sym_prefix = NULL; |
| first->module_open = first_open; |
| first->module_close = first_close; |
| first->find_sym = first_sym; |
| first->dlloader_init = first_init; /* test that it isn't called twice */ |
| first->dlloader_exit = first_exit; |
| first->dlloader_data = (lt_user_data) first_ctx; |
| first->priority = LT_DLLOADER_PREPEND; |
| |
| if (first_init (first->dlloader_data)) |
| { |
| printf ("first_init failed\n"); |
| err = 1; |
| goto cleanup; |
| } |
| |
| if (lt_dlloader_add (first)) |
| { |
| printf ("lt_dlloader_add failed: %s\n", lt_dlerror ()); |
| err = 1; |
| goto cleanup; |
| } |
| |
| finder = lt_dlloader_find ("first"); |
| |
| if (!finder) |
| { |
| printf ("lt_dlloader_find failed: %s\n", lt_dlerror ()); |
| err = 1; |
| goto cleanup; |
| } |
| |
| printf ("Found loader \"%s\"\n", finder->name); |
| |
| last = (lt_dlvtable *) malloc (sizeof (*last)); |
| if (!last) |
| { |
| printf ("malloc failed\n"); |
| err = 1; |
| goto cleanup; |
| } |
| |
| last->name = "last"; |
| last->sym_prefix = NULL; |
| last->module_open = last_open; |
| last->module_close = last_close; |
| last->find_sym = last_sym; |
| last->dlloader_init = last_init; /* test that it isn't called twice */ |
| last->dlloader_exit = last_exit; |
| last->dlloader_data = (lt_user_data) last_ctx; |
| last->priority = LT_DLLOADER_APPEND; |
| |
| if (last_init (last->dlloader_data)) |
| { |
| printf ("last_init failed\n"); |
| err = 1; |
| goto cleanup; |
| } |
| |
| if (lt_dlloader_add (last)) |
| { |
| printf ("lt_dlloader_add failed: %s\n", lt_dlerror ()); |
| err = 1; |
| goto cleanup; |
| } |
| |
| finder = lt_dlloader_find ("last"); |
| |
| if (!finder) |
| { |
| printf ("lt_dlloader_find failed: %s\n", lt_dlerror ()); |
| err = 1; |
| goto cleanup; |
| } |
| |
| printf ("Found loader \"%s\"\n", finder->name); |
| |
| /* Use a magic string to avoid possible interactions with file system |
| * objects. Prepend '/' to short-circuit libltdl's search of |
| * $shlibpath_var (e.g. PATH, LD_LIBRARY_PATH, or similar). |
| */ |
| module = lt_dlopen ("/libltdl_dlloader_api_test_first"); |
| |
| if (!module) |
| { |
| printf ("lt_dlopen failed: %s\n", lt_dlerror ()); |
| err = 1; |
| goto cleanup; |
| } |
| |
| symbol = (module_func *) lt_dlsym (module, "symbol"); |
| |
| if (!symbol) |
| { |
| printf ("lt_dlsym failed: %s\n", lt_dlerror ()); |
| err = 1; |
| goto cleanup; |
| } |
| |
| printf ("result: %s\n", symbol ()); |
| |
| lt_dlclose (module); |
| module = lt_dlopen ("./module.la"); |
| |
| if (!module) |
| { |
| printf ("lt_dlopen failed: %s\n", lt_dlerror ()); |
| err = 1; |
| goto cleanup; |
| } |
| |
| symbol = (module_func *) lt_dlsym (module, "symbol"); |
| |
| if (!symbol) |
| { |
| printf ("lt_dlsym failed: %s\n", lt_dlerror ()); |
| err = 1; |
| goto cleanup; |
| } |
| |
| printf ("result: %s\n", symbol ()); |
| |
| lt_dlclose (module); |
| |
| /* Use a magic string to avoid possible interactions with file system |
| * objects. Prepend '/' to short-circuit libltdl's search of |
| * $shlibpath_var (e.g. PATH, LD_LIBRARY_PATH, or similar). |
| */ |
| module = lt_dlopen ("/libltdl_dlloader_api_test_last"); |
| |
| if (!module) |
| { |
| printf ("lt_dlopen failed: %s\n", lt_dlerror ()); |
| err = 1; |
| goto cleanup; |
| } |
| |
| symbol = (module_func *) lt_dlsym (module, "symbol"); |
| |
| if (!symbol) |
| { |
| printf ("lt_dlsym failed: %s\n", lt_dlerror ()); |
| err = 1; |
| goto cleanup; |
| } |
| |
| printf ("result: %s\n", symbol ()); |
| |
| if (lt_dlopen ("no-module")) |
| { |
| printf ("lt_dlopen unexpectedly opened \"no-module\"\n"); |
| err = 1; |
| goto cleanup; |
| } |
| |
| if (lt_dlloader_remove ("first") != first) |
| { |
| printf ("vtable of first loader has changed\n"); |
| err = 1; |
| goto cleanup; |
| } |
| |
| free (first); |
| |
| cleanup: |
| if (module) |
| { |
| lt_dlclose (module); |
| } |
| lt_dlexit (); |
| return err; |
| } |
| ]]) |
| |
| AT_DATA([module.c], |
| [[ |
| #ifdef __cplusplus |
| extern "C" |
| #endif |
| const char *symbol (void); |
| const char * |
| symbol (void) |
| { |
| return "module_symbol"; |
| } |
| ]]) |
| |
| LT_AT_HOST_DATA(expout, |
| [[first_init: first_ctx |
| Found loader "first" |
| last_init: last_ctx |
| Found loader "last" |
| first_open ("/libltdl_dlloader_api_test_first"): first_ctx |
| first_sym (first): first_ctx |
| result: first_symbol |
| first_close (first): first_ctx |
| first_open denies a request |
| result: module_symbol |
| first_open denies a request |
| last_open ("/libltdl_dlloader_api_test_last"): last_ctx |
| last_sym (last): last_ctx |
| result: last_symbol |
| first_open denies a request |
| last_open denies a request |
| first_exit: first_ctx |
| last_close (last): last_ctx |
| last_exit: last_ctx |
| ]]) |
| |
| : ${LTDLINCL="-I$abs_top_srcdir/libltdl"} |
| : ${LIBLTDL="$abs_builddir/../libltdl/libltdlc.la"} |
| |
| # Skip this test when called from: |
| # make distcheck DISTCHECK_CONFIGURE_FLAGS=--disable-ltdl-install |
| AT_CHECK([case $LIBLTDL in #( |
| */_inst/lib/*) test -f "$LIBLTDL" || (exit 77) ;; |
| esac], [], [ignore]) |
| |
| CPPFLAGS="$LTDLINCL $CPPFLAGS" |
| |
| AT_CHECK([$LIBTOOL --mode=compile $CC $CPPFLAGS $CFLAGS -c module.c], |
| [], [ignore], [ignore]) |
| AT_CHECK([$LIBTOOL --mode=link $CC $CFLAGS $LDFLAGS -o module.la ]dnl |
| [-rpath /nowhere -module -avoid-version -no-undefined ]dnl |
| [module.lo], |
| [], [ignore], [ignore]) |
| |
| dnl Not possible to override the preopen loader, so skip if not shared. |
| . ./module.la |
| AT_CHECK([test -n "$dlname" || (exit 77)]) |
| |
| AT_CHECK([$CC $CPPFLAGS $CFLAGS -c main.c], [], [ignore], [ignore]) |
| AT_CHECK([$LIBTOOL --mode=link $CC $CFLAGS $LDFLAGS -o main$EXEEXT ]dnl |
| [main.$OBJEXT -dlopen module.la $LIBLTDL], |
| [], [ignore], [ignore]) |
| |
| LT_AT_EXEC_CHECK([./main], [], [expout], [ignore], []) |
| |
| AT_CLEANUP |