| /* Test recursive dlopen using malloc hooks. |
| Copyright (C) 2015-2016 Free Software Foundation, Inc. |
| This file is part of the GNU C Library. |
| |
| The GNU C Library is free software; you can redistribute it and/or |
| modify it under the terms of the GNU Lesser General Public |
| License as published by the Free Software Foundation; either |
| version 2.1 of the License, or (at your option) any later version. |
| |
| The GNU C Library 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 |
| Lesser General Public License for more details. |
| |
| You should have received a copy of the GNU Lesser General Public |
| License along with the GNU C Library; if not, see |
| <http://www.gnu.org/licenses/>. */ |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <malloc.h> |
| #include <dlfcn.h> |
| |
| #define DSO "moddummy1.so" |
| #define FUNC "dummy1" |
| |
| #define DSO1 "moddummy2.so" |
| #define FUNC1 "dummy2" |
| |
| /* Result of the called function. */ |
| int func_result; |
| |
| /* Prototype for my hook. */ |
| void *custom_malloc_hook (size_t, const void *); |
| |
| /* Pointer to old malloc hooks. */ |
| void *(*old_malloc_hook) (size_t, const void *); |
| |
| /* Call function func_name in DSO dso_name via dlopen. */ |
| void |
| call_func (const char *dso_name, const char *func_name) |
| { |
| int ret; |
| void *dso; |
| int (*func) (void); |
| char *err; |
| |
| /* Open the DSO. */ |
| dso = dlopen (dso_name, RTLD_NOW|RTLD_GLOBAL); |
| if (dso == NULL) |
| { |
| err = dlerror (); |
| fprintf (stderr, "%s\n", err); |
| exit (1); |
| } |
| /* Clear any errors. */ |
| dlerror (); |
| |
| /* Lookup func. */ |
| func = (int (*) (void)) dlsym (dso, func_name); |
| if (func == NULL) |
| { |
| err = dlerror (); |
| if (err != NULL) |
| { |
| fprintf (stderr, "%s\n", err); |
| exit (1); |
| } |
| } |
| /* Call func. */ |
| func_result = (*func) (); |
| |
| /* Close the library and look for errors too. */ |
| ret = dlclose (dso); |
| if (ret != 0) |
| { |
| err = dlerror (); |
| fprintf (stderr, "%s\n", err); |
| exit (1); |
| } |
| |
| } |
| |
| /* Empty hook that does nothing. */ |
| void * |
| custom_malloc_hook (size_t size, const void *caller) |
| { |
| void *result; |
| /* Restore old hooks. */ |
| __malloc_hook = old_malloc_hook; |
| /* First call a function in another library via dlopen. */ |
| call_func (DSO1, FUNC1); |
| /* Called recursively. */ |
| result = malloc (size); |
| /* Restore new hooks. */ |
| __malloc_hook = custom_malloc_hook; |
| return result; |
| } |
| |
| static int |
| do_test (void) |
| { |
| /* Save old hook. */ |
| old_malloc_hook = __malloc_hook; |
| /* Install new hook. */ |
| __malloc_hook = custom_malloc_hook; |
| |
| /* Bug 17702 fixes two things: |
| * A recursive dlopen unmapping the ld.so.cache. |
| * An assertion that _r_debug is RT_CONSISTENT at entry to dlopen. |
| We can only test the latter. Testing the former requires modifying |
| ld.so.conf to cache the dummy libraries, then running ldconfig, |
| then run the test. If you do all of that (and glibc's test |
| infrastructure doesn't support that yet) then the test will |
| SEGFAULT without the fix. If you don't do that, then the test |
| will abort because of the assert described in detail below. */ |
| call_func (DSO, FUNC); |
| |
| /* Restore old hook. */ |
| __malloc_hook = old_malloc_hook; |
| |
| /* The function dummy2() is called by the malloc hook. Check to |
| see that it was called. This ensures the second recursive |
| dlopen happened and we called the function in that library. |
| Before the fix you either get a SIGSEGV when accessing mmap'd |
| ld.so.cache data or an assertion failure about _r_debug not |
| beint RT_CONSISTENT. We don't test for the SIGSEGV since it |
| would require finding moddummy1 or moddummy2 in the cache and |
| we don't have any infrastructure to test that, but the _r_debug |
| assertion triggers. */ |
| printf ("Returned result is %d\n", func_result); |
| if (func_result <= 0) |
| { |
| printf ("FAIL: Function call_func() not called.\n"); |
| exit (1); |
| } |
| |
| printf ("PASS: Function call_func() called more than once.\n"); |
| return 0; |
| } |
| |
| #define TEST_FUNCTION do_test () |
| #include "../test-skeleton.c" |