02d5e5d94a
https://sourceware.org/bugzilla/show_bug.cgi?id=17833 I've a shared library that contains both undefined and unique symbols. Then I try to call the following sequence of dlopen: 1. dlopen("./libfoo.so", RTLD_NOW) 2. dlopen("./libfoo.so", RTLD_LAZY | RTLD_GLOBAL) First dlopen call terminates with error because of undefined symbols, but STB_GNU_UNIQUE ones set DF_1_NODELETE flag and hence block library in the memory. The library goes into inconsistent state as several structures remain uninitialized. For instance, relocations for GOT table were not performed. By the time of second dlopen call this library looks like as it would be fully initialized but this is not true: any call through incorrect GOT table leads to segmentation fault. On some systems this inconsistency triggers assertions in the dynamic linker. This patch adds a parameter to _dl_close_worker to implement forced object deletion in case of dlopen() failure: 1. Clears DF_1_NODELETE bit if forced, to allow library to be removed from memory. 2. For each unique symbol that is defined in this object clears appropriate entry in _ns_unique_sym_table. [BZ #17833] * elf/Makefile (tests): Add tst-nodelete. (modules-names): Add tst-nodelete-uniquemod. (tst-nodelete-uniquemod.so-no-z-defs): New. (tst-nodelete-rtldmod.so-no-z-defs): Likewise. (tst-nodelete-zmod.so-no-z-defs): Likewise. ($(objpfx)tst-nodelete): Likewise. ($(objpfx)tst-nodelete.out): Likewise. (LDFLAGS-tst-nodelete): Likewise. (LDFLAGS-tst-nodelete-zmod.so): Likewise. * elf/dl-close.c (_dl_close_worker): Add a parameter to implement forced object deletion. (_dl_close): Pass false to _dl_close_worker. * elf/dl-open.c (_dl_open): Pass true to _dl_close_worker. * elf/tst-nodelete.cc: New file. * elf/tst-nodeletelib.cc: Likewise. * elf/tst-znodeletelib.cc: Likewise. * include/dlfcn.h (_dl_close_worker): Add a new parameter.
154 lines
5.4 KiB
C
154 lines
5.4 KiB
C
#ifndef _DLFCN_H
|
|
#include <dlfcn/dlfcn.h>
|
|
#ifndef _ISOMAC
|
|
#include <link.h> /* For ElfW. */
|
|
#include <stdbool.h>
|
|
|
|
/* Internally used flag. */
|
|
#define __RTLD_DLOPEN 0x80000000
|
|
#define __RTLD_SPROF 0x40000000
|
|
#define __RTLD_OPENEXEC 0x20000000
|
|
#define __RTLD_CALLMAP 0x10000000
|
|
#define __RTLD_AUDIT 0x08000000
|
|
#define __RTLD_SECURE 0x04000000 /* Apply additional security checks. */
|
|
#define __RTLD_NOIFUNC 0x02000000 /* Suppress calling ifunc functions. */
|
|
|
|
#define __LM_ID_CALLER -2
|
|
|
|
#ifdef SHARED
|
|
/* Locally stored program arguments. */
|
|
extern int __dlfcn_argc attribute_hidden;
|
|
extern char **__dlfcn_argv attribute_hidden;
|
|
#else
|
|
/* These variables are defined and initialized in the startup code. */
|
|
extern int __libc_argc attribute_hidden;
|
|
extern char **__libc_argv attribute_hidden;
|
|
|
|
# define __dlfcn_argc __libc_argc
|
|
# define __dlfcn_argv __libc_argv
|
|
#endif
|
|
|
|
|
|
/* Now define the internal interfaces. */
|
|
|
|
#define __libc_dlopen(name) \
|
|
__libc_dlopen_mode (name, RTLD_LAZY | __RTLD_DLOPEN)
|
|
extern void *__libc_dlopen_mode (const char *__name, int __mode);
|
|
extern void *__libc_dlsym (void *__map, const char *__name);
|
|
extern int __libc_dlclose (void *__map);
|
|
libc_hidden_proto (__libc_dlopen_mode)
|
|
libc_hidden_proto (__libc_dlsym)
|
|
libc_hidden_proto (__libc_dlclose)
|
|
|
|
/* Locate shared object containing the given address. */
|
|
#ifdef ElfW
|
|
extern int _dl_addr (const void *address, Dl_info *info,
|
|
struct link_map **mapp, const ElfW(Sym) **symbolp)
|
|
internal_function;
|
|
libc_hidden_proto (_dl_addr)
|
|
#endif
|
|
|
|
struct link_map;
|
|
|
|
/* Close an object previously opened by _dl_open. */
|
|
extern void _dl_close (void *map) attribute_hidden;
|
|
/* Same as above, but without locking and safety checks for user
|
|
provided map arguments. */
|
|
extern void _dl_close_worker (struct link_map *map, bool force)
|
|
attribute_hidden;
|
|
|
|
/* Look up NAME in shared object HANDLE (which may be RTLD_DEFAULT or
|
|
RTLD_NEXT). WHO is the calling function, for RTLD_NEXT. Returns
|
|
the symbol value, which may be NULL. */
|
|
extern void *_dl_sym (void *handle, const char *name, void *who)
|
|
internal_function;
|
|
|
|
/* Look up version VERSION of symbol NAME in shared object HANDLE
|
|
(which may be RTLD_DEFAULT or RTLD_NEXT). WHO is the calling
|
|
function, for RTLD_NEXT. Returns the symbol value, which may be
|
|
NULL. */
|
|
extern void *_dl_vsym (void *handle, const char *name, const char *version,
|
|
void *who)
|
|
internal_function;
|
|
|
|
/* Call OPERATE, catching errors from `dl_signal_error'. If there is no
|
|
error, *ERRSTRING is set to null. If there is an error, *ERRSTRING is
|
|
set to a string constructed from the strings passed to _dl_signal_error,
|
|
and the error code passed is the return value and *OBJNAME is set to
|
|
the object name which experienced the problems. ERRSTRING if nonzero
|
|
points to a malloc'ed string which the caller has to free after use.
|
|
ARGS is passed as argument to OPERATE. MALLOCEDP is set to true only
|
|
if the returned string is allocated using the libc's malloc. */
|
|
extern int _dl_catch_error (const char **objname, const char **errstring,
|
|
bool *mallocedp, void (*operate) (void *),
|
|
void *args)
|
|
internal_function;
|
|
|
|
/* Helper function for <dlfcn.h> functions. Runs the OPERATE function via
|
|
_dl_catch_error. Returns zero for success, nonzero for failure; and
|
|
arranges for `dlerror' to return the error details.
|
|
ARGS is passed as argument to OPERATE. */
|
|
extern int _dlerror_run (void (*operate) (void *), void *args)
|
|
internal_function;
|
|
|
|
#ifdef SHARED
|
|
# define DL_CALLER_DECL /* Nothing */
|
|
# define DL_CALLER RETURN_ADDRESS (0)
|
|
#else
|
|
# define DL_CALLER_DECL , void *dl_caller
|
|
# define DL_CALLER dl_caller
|
|
#endif
|
|
|
|
struct dlfcn_hook
|
|
{
|
|
void *(*dlopen) (const char *file, int mode, void *dl_caller);
|
|
int (*dlclose) (void *handle);
|
|
void *(*dlsym) (void *handle, const char *name, void *dl_caller);
|
|
void *(*dlvsym) (void *handle, const char *name, const char *version,
|
|
void *dl_caller);
|
|
char *(*dlerror) (void);
|
|
int (*dladdr) (const void *address, Dl_info *info);
|
|
int (*dladdr1) (const void *address, Dl_info *info,
|
|
void **extra_info, int flags);
|
|
int (*dlinfo) (void *handle, int request, void *arg, void *dl_caller);
|
|
void *(*dlmopen) (Lmid_t nsid, const char *file, int mode, void *dl_caller);
|
|
void *pad[4];
|
|
};
|
|
|
|
extern struct dlfcn_hook *_dlfcn_hook;
|
|
libdl_hidden_proto (_dlfcn_hook)
|
|
|
|
extern void *__dlopen (const char *file, int mode DL_CALLER_DECL)
|
|
attribute_hidden;
|
|
extern void *__dlmopen (Lmid_t nsid, const char *file, int mode DL_CALLER_DECL)
|
|
attribute_hidden;
|
|
extern int __dlclose (void *handle)
|
|
attribute_hidden;
|
|
extern void *__dlsym (void *handle, const char *name DL_CALLER_DECL)
|
|
attribute_hidden;
|
|
extern void *__dlvsym (void *handle, const char *name, const char *version
|
|
DL_CALLER_DECL)
|
|
attribute_hidden;
|
|
extern char *__dlerror (void)
|
|
attribute_hidden;
|
|
extern int __dladdr (const void *address, Dl_info *info)
|
|
attribute_hidden;
|
|
extern int __dladdr1 (const void *address, Dl_info *info,
|
|
void **extra_info, int flags)
|
|
attribute_hidden;
|
|
extern int __dlinfo (void *handle, int request, void *arg DL_CALLER_DECL)
|
|
attribute_hidden;
|
|
|
|
#ifndef SHARED
|
|
struct link_map;
|
|
extern void * __libc_dlsym_private (struct link_map *map, const char *name)
|
|
attribute_hidden;
|
|
extern void __libc_register_dl_open_hook (struct link_map *map)
|
|
attribute_hidden;
|
|
extern void __libc_register_dlfcn_hook (struct link_map *map)
|
|
attribute_hidden;
|
|
#endif
|
|
#endif
|
|
|
|
#endif
|