mirror of
https://sourceware.org/git/glibc.git
synced 2024-11-23 09:43:32 +08:00
elf: Add _dl_find_object function
It can be used to speed up the libgcc unwinder, and the internal _dl_find_dso_for_object function (which is used for caller identification in dlopen and related functions, and in dladdr). _dl_find_object is in the internal namespace due to bug 28503. If libgcc switches to _dl_find_object, this namespace issue will be fixed. It is located in libc for two reasons: it is necessary to forward the call to the static libc after static dlopen, and there is a link ordering issue with -static-libgcc and libgcc_eh.a because libc.so is not a linker script that includes ld.so in the glibc build tree (so that GCC's internal -lc after libgcc_eh.a does not pick up ld.so). It is necessary to do the i386 customization in the sysdeps/x86/bits/dl_find_object.h header shared with x86-64 because otherwise, multilib installations are broken. The implementation uses software transactional memory, as suggested by Torvald Riegel. Two copies of the supporting data structures are used, also achieving full async-signal-safety. Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
This commit is contained in:
parent
83b8d5027d
commit
5d28a8962d
4
NEWS
4
NEWS
@ -102,6 +102,10 @@ Major new features:
|
||||
* The printf family of functions now handles the flagged %#m conversion
|
||||
specifier, printing errno as an error constant (similar to strerrorname_np).
|
||||
|
||||
* The function _dl_find_object has been added. In-process unwinders
|
||||
can use it to efficiently locate unwinding information for a code
|
||||
address.
|
||||
|
||||
Deprecated and removed features, and other changes affecting compatibility:
|
||||
|
||||
* On x86-64, the LD_PREFER_MAP_32BIT_EXEC environment variable support
|
||||
|
32
bits/dl_find_object.h
Normal file
32
bits/dl_find_object.h
Normal file
@ -0,0 +1,32 @@
|
||||
/* System dependent definitions for finding objects by address.
|
||||
Copyright (C) 2021 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
|
||||
<https://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef _DLFCN_H
|
||||
# error "Never use <bits/dl_find_object.h> directly; include <dlfcn.h> instead."
|
||||
#endif
|
||||
|
||||
/* This implementation does not have a dlfo_eh_dbase member in struct
|
||||
dl_find_object. */
|
||||
#define DLFO_STRUCT_HAS_EH_DBASE 0
|
||||
|
||||
/* This implementation does not have a dlfo_eh_count member in struct
|
||||
dl_find_object. */
|
||||
#define DLFO_STRUCT_HAS_EH_COUNT 0
|
||||
|
||||
/* The ELF segment which contains the exception handling data. */
|
||||
#define DLFO_EH_SEGMENT_TYPE PT_GNU_EH_FRAME
|
@ -19,7 +19,7 @@ subdir := dlfcn
|
||||
|
||||
include ../Makeconfig
|
||||
|
||||
headers := bits/dlfcn.h dlfcn.h
|
||||
headers := bits/dlfcn.h bits/dl_find_object.h dlfcn.h
|
||||
extra-libs := libdl
|
||||
libdl-routines := libdl-compat
|
||||
routines = \
|
||||
|
@ -28,6 +28,8 @@
|
||||
|
||||
|
||||
#ifdef __USE_GNU
|
||||
#include <bits/dl_find_object.h>
|
||||
|
||||
/* If the first argument of `dlsym' or `dlvsym' is set to RTLD_NEXT
|
||||
the run-time address of the symbol called NAME in the next shared
|
||||
object is returned. The "next" relation is defined by the order
|
||||
@ -194,6 +196,31 @@ typedef struct
|
||||
Dl_serpath dls_serpath[1]; /* Actually longer, dls_cnt elements. */
|
||||
# endif
|
||||
} Dl_serinfo;
|
||||
|
||||
struct dl_find_object
|
||||
{
|
||||
__extension__ unsigned long long int dlfo_flags;
|
||||
void *dlfo_map_start; /* Beginning of mapping containing address. */
|
||||
void *dlfo_map_end; /* End of mapping. */
|
||||
struct link_map *dlfo_link_map;
|
||||
void *dlfo_eh_frame; /* Exception handling data of the object. */
|
||||
# if DLFO_STRUCT_HAS_EH_DBASE
|
||||
void *dlfo_eh_dbase; /* Base address for DW_EH_PE_datarel. */
|
||||
# if __WORDSIZE == 32
|
||||
unsigned int __dlfo_eh_dbase_pad;
|
||||
# endif
|
||||
# endif
|
||||
# if DLFO_STRUCT_HAS_EH_COUNT
|
||||
int dlfo_eh_count; /* Number of exception handling entries. */
|
||||
unsigned int __dlfo_eh_count_pad;
|
||||
# endif
|
||||
__extension__ unsigned long long int __dflo_reserved[7];
|
||||
};
|
||||
|
||||
/* If ADDRESS is found in an object, fill in *RESULT and return 0.
|
||||
Otherwise, return -1. */
|
||||
int _dl_find_object (void *__address, struct dl_find_object *__result) __THROW;
|
||||
|
||||
#endif /* __USE_GNU */
|
||||
|
||||
|
||||
|
55
elf/Makefile
55
elf/Makefile
@ -25,7 +25,8 @@ headers = elf.h bits/elfclass.h link.h bits/link.h bits/link_lavcurrent.h
|
||||
routines = $(all-dl-routines) dl-support dl-iteratephdr \
|
||||
dl-addr dl-addr-obj enbl-secure dl-profstub \
|
||||
dl-origin dl-libc dl-sym dl-sysdep dl-error \
|
||||
dl-reloc-static-pie libc_early_init rtld_static_init
|
||||
dl-reloc-static-pie libc_early_init rtld_static_init \
|
||||
libc-dl_find_object
|
||||
|
||||
# The core dynamic linking functions are in libc for the static and
|
||||
# profiled libraries.
|
||||
@ -36,7 +37,8 @@ dl-routines = $(addprefix dl-,load lookup object reloc deps \
|
||||
exception sort-maps lookup-direct \
|
||||
call-libc-early-init write \
|
||||
thread_gscope_wait tls_init_tp \
|
||||
debug-symbols minimal-malloc)
|
||||
debug-symbols minimal-malloc \
|
||||
find_object)
|
||||
ifeq (yes,$(use-ldconfig))
|
||||
dl-routines += dl-cache
|
||||
endif
|
||||
@ -63,6 +65,9 @@ elide-routines.os = $(all-dl-routines) dl-support enbl-secure dl-origin \
|
||||
dl-sysdep dl-exception dl-reloc-static-pie \
|
||||
thread_gscope_wait rtld_static_init
|
||||
|
||||
# These object files are only included in the dynamically-linked libc.
|
||||
shared-only-routines = libc-dl_find_object
|
||||
|
||||
# ld.so uses those routines, plus some special stuff for being the program
|
||||
# interpreter and operating independent of libc.
|
||||
rtld-routines = rtld $(all-dl-routines) dl-sysdep dl-environ dl-minimal \
|
||||
@ -171,7 +176,8 @@ tests-static-normal := tst-array1-static tst-array5-static \
|
||||
|
||||
tests-static-internal := tst-tls1-static \
|
||||
tst-ptrguard1-static tst-stackguard1-static \
|
||||
tst-tls1-static-non-pie
|
||||
tst-tls1-static-non-pie \
|
||||
tst-dl_find_object-static
|
||||
|
||||
CRT-tst-tls1-static-non-pie := $(csu-objpfx)crt1.o
|
||||
tst-tls1-static-non-pie-no-pie = yes
|
||||
@ -241,7 +247,8 @@ tests-internal += loadtest unload unload2 circleload1 \
|
||||
tst-tls3 tst-tls6 tst-tls7 tst-tls8 tst-dlmopen2 \
|
||||
tst-ptrguard1 tst-stackguard1 \
|
||||
tst-create_format1 tst-tls-surplus tst-dl-hwcaps_split \
|
||||
tst-audit19a
|
||||
tst-audit19a \
|
||||
tst-dl_find_object tst-dl_find_object-threads
|
||||
tests-container += tst-pldd tst-dlopen-tlsmodid-container \
|
||||
tst-dlopen-self-container tst-preload-pthread-libc
|
||||
test-srcs = tst-pathopt
|
||||
@ -386,6 +393,15 @@ modules-names = testobj1 testobj2 testobj3 testobj4 testobj5 testobj6 \
|
||||
tst-audit19bmod \
|
||||
tst-auditmod20 \
|
||||
tst-auditmod22 \
|
||||
tst-dl_find_object-mod1 \
|
||||
tst-dl_find_object-mod2 \
|
||||
tst-dl_find_object-mod3 \
|
||||
tst-dl_find_object-mod4 \
|
||||
tst-dl_find_object-mod5 \
|
||||
tst-dl_find_object-mod6 \
|
||||
tst-dl_find_object-mod7 \
|
||||
tst-dl_find_object-mod8 \
|
||||
tst-dl_find_object-mod9 \
|
||||
|
||||
# Most modules build with _ISOMAC defined, but those filtered out
|
||||
# depend on internal headers.
|
||||
@ -2033,3 +2049,34 @@ $(objpfx)tst-ro-dynamic-mod.so: $(objpfx)tst-ro-dynamic-mod.os \
|
||||
$(objpfx)tst-ro-dynamic-mod.os
|
||||
|
||||
$(objpfx)tst-rtld-run-static.out: $(objpfx)/ldconfig
|
||||
|
||||
$(objpfx)tst-dl_find_object.out: \
|
||||
$(objpfx)tst-dl_find_object-mod1.so $(objpfx)tst-dl_find_object-mod2.so
|
||||
$(objpfx)tst-dl_find_object-static.out: \
|
||||
$(objpfx)tst-dl_find_object-mod1.so $(objpfx)tst-dl_find_object-mod2.so
|
||||
tst-dl_find_object-static-ENV = $(static-dlopen-environment)
|
||||
CFLAGS-tst-dl_find_object.c += -funwind-tables
|
||||
CFLAGS-tst-dl_find_object-static.c += -funwind-tables
|
||||
LDFLAGS-tst-dl_find_object-static += -Wl,--eh-frame-hdr
|
||||
CFLAGS-tst-dl_find_object-mod1.c += -funwind-tables
|
||||
CFLAGS-tst-dl_find_object-mod2.c += -funwind-tables
|
||||
LDFLAGS-tst-dl_find_object-mod2.so += -Wl,--enable-new-dtags,-z,nodelete
|
||||
$(objpfx)tst-dl_find_object-threads: $(shared-thread-library)
|
||||
CFLAGS-tst-dl_find_object-threads.c += -funwind-tables
|
||||
$(objpfx)tst-dl_find_object-threads.out: \
|
||||
$(objpfx)tst-dl_find_object-mod1.so \
|
||||
$(objpfx)tst-dl_find_object-mod2.so \
|
||||
$(objpfx)tst-dl_find_object-mod3.so \
|
||||
$(objpfx)tst-dl_find_object-mod4.so \
|
||||
$(objpfx)tst-dl_find_object-mod5.so \
|
||||
$(objpfx)tst-dl_find_object-mod6.so \
|
||||
$(objpfx)tst-dl_find_object-mod7.so \
|
||||
$(objpfx)tst-dl_find_object-mod8.so \
|
||||
$(objpfx)tst-dl_find_object-mod9.so
|
||||
CFLAGS-tst-dl_find_object-mod3.c += -funwind-tables
|
||||
CFLAGS-tst-dl_find_object-mod4.c += -funwind-tables
|
||||
CFLAGS-tst-dl_find_object-mod5.c += -funwind-tables
|
||||
CFLAGS-tst-dl_find_object-mod6.c += -funwind-tables
|
||||
CFLAGS-tst-dl_find_object-mod7.c += -funwind-tables
|
||||
CFLAGS-tst-dl_find_object-mod8.c += -funwind-tables
|
||||
CFLAGS-tst-dl_find_object-mod9.c += -funwind-tables
|
||||
|
@ -20,6 +20,9 @@ libc {
|
||||
__register_frame_info_table_bases; _Unwind_Find_FDE;
|
||||
}
|
||||
%endif
|
||||
GLIBC_2.35 {
|
||||
_dl_find_object;
|
||||
}
|
||||
GLIBC_PRIVATE {
|
||||
# functions used in other libraries
|
||||
__libc_early_init;
|
||||
|
@ -32,6 +32,7 @@
|
||||
#include <sysdep-cancel.h>
|
||||
#include <tls.h>
|
||||
#include <stap-probe.h>
|
||||
#include <dl-find_object.h>
|
||||
|
||||
#include <dl-unmap-segments.h>
|
||||
|
||||
@ -682,6 +683,9 @@ _dl_close_worker (struct link_map *map, bool force)
|
||||
if (imap->l_next != NULL)
|
||||
imap->l_next->l_prev = imap->l_prev;
|
||||
|
||||
/* Update the data used by _dl_find_object. */
|
||||
_dl_find_object_dlclose (imap);
|
||||
|
||||
free (imap->l_versions);
|
||||
if (imap->l_origin != (char *) -1)
|
||||
free ((char *) imap->l_origin);
|
||||
|
842
elf/dl-find_object.c
Normal file
842
elf/dl-find_object.c
Normal file
@ -0,0 +1,842 @@
|
||||
/* Locating objects in the process image. ld.so implementation.
|
||||
Copyright (C) 2021 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
|
||||
<https://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <assert.h>
|
||||
#include <atomic_wide_counter.h>
|
||||
#include <dl-find_object.h>
|
||||
#include <dlfcn.h>
|
||||
#include <ldsodefs.h>
|
||||
#include <link.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/* Fallback implementation of _dl_find_object. It uses a linear
|
||||
search, needs locking, and is not async-signal-safe. It is used in
|
||||
_dl_find_object prior to initialization, when called from audit
|
||||
modules. It also serves as the reference implementation for
|
||||
_dl_find_object. */
|
||||
static int
|
||||
_dl_find_object_slow (void *pc, struct dl_find_object *result)
|
||||
{
|
||||
ElfW(Addr) addr = (ElfW(Addr)) pc;
|
||||
for (Lmid_t ns = 0; ns < GL(dl_nns); ++ns)
|
||||
for (struct link_map *l = GL(dl_ns)[ns]._ns_loaded; l != NULL;
|
||||
l = l->l_next)
|
||||
if (addr >= l->l_map_start && addr < l->l_map_end
|
||||
&& (l->l_contiguous || _dl_addr_inside_object (l, addr)))
|
||||
{
|
||||
assert (ns == l->l_ns);
|
||||
struct dl_find_object_internal internal;
|
||||
_dl_find_object_from_map (l, &internal);
|
||||
_dl_find_object_to_external (&internal, result);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Object not found. */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Data for the main executable. There is usually a large gap between
|
||||
the main executable and initially loaded shared objects. Record
|
||||
the main executable separately, to increase the chance that the
|
||||
range for the non-closeable mappings below covers only the shared
|
||||
objects (and not also the gap between main executable and shared
|
||||
objects). */
|
||||
static struct dl_find_object_internal _dlfo_main attribute_relro;
|
||||
|
||||
/* Data for initially loaded shared objects that cannot be unloaded.
|
||||
(This may also contain non-contiguous mappings from the main
|
||||
executable.) The mappings are stored in address order in the
|
||||
_dlfo_nodelete_mappings array (containing
|
||||
_dlfo_nodelete_mappings_size elements). It is not modified after
|
||||
initialization. */
|
||||
static uintptr_t _dlfo_nodelete_mappings_end attribute_relro;
|
||||
static size_t _dlfo_nodelete_mappings_size attribute_relro;
|
||||
static struct dl_find_object_internal *_dlfo_nodelete_mappings
|
||||
attribute_relro;
|
||||
|
||||
/* Mappings created by dlopen can go away with dlclose, so a dynamic
|
||||
data structure with some synchronization is needed. Individual
|
||||
segments are similar to the _dlfo_nodelete_mappings array above.
|
||||
The previous segment contains lower addresses and is at most half
|
||||
as long. Checking the address of the base address of the first
|
||||
element during a lookup can therefore approximate a binary search
|
||||
over all segments, even though the data is not stored in one
|
||||
contiguous array.
|
||||
|
||||
During updates, the segments are overwritten in place, and a
|
||||
software transactional memory construct (involving the
|
||||
_dlfo_loaded_mappings_version variable) is used to detect
|
||||
concurrent modification, and retry as necessary. The memory
|
||||
allocations are never deallocated, but slots used for objects that
|
||||
have been dlclose'd can be reused by dlopen. The memory can live
|
||||
in the regular C malloc heap.
|
||||
|
||||
The segments are populated from the start of the list, with the
|
||||
mappings with the highest address. Only if this segment is full,
|
||||
previous segments are used for mappings at lower addresses. The
|
||||
remaining segments are populated as needed, but after allocating
|
||||
further segments, some of the initial segments (at the end of the
|
||||
linked list) can be empty (with size 0).
|
||||
|
||||
Adding new elements to this data structure is another source of
|
||||
quadratic behavior for dlopen. If the other causes of quadratic
|
||||
behavior are eliminated, a more complicated data structure will be
|
||||
needed. */
|
||||
struct dlfo_mappings_segment
|
||||
{
|
||||
/* The previous segment has lower base addresses. */
|
||||
struct dlfo_mappings_segment *previous;
|
||||
|
||||
/* Used by __libc_freeres to deallocate malloc'ed memory. */
|
||||
void *to_free;
|
||||
|
||||
/* Count of array elements in use and allocated. */
|
||||
size_t size;
|
||||
size_t allocated;
|
||||
|
||||
struct dl_find_object_internal objects[];
|
||||
};
|
||||
|
||||
/* To achieve async-signal-safety, two copies of the data structure
|
||||
are used, so that a signal handler can still use this data even if
|
||||
dlopen or dlclose modify the other copy. The the MSB in
|
||||
_dlfo_loaded_mappings_version determines which array element is the
|
||||
currently active region. */
|
||||
static struct dlfo_mappings_segment *_dlfo_loaded_mappings[2];
|
||||
|
||||
/* Returns the number of actually used elements in all segments
|
||||
starting at SEG. */
|
||||
static inline size_t
|
||||
_dlfo_mappings_segment_count_used (struct dlfo_mappings_segment *seg)
|
||||
{
|
||||
size_t count = 0;
|
||||
for (; seg != NULL && seg->size > 0; seg = seg->previous)
|
||||
for (size_t i = 0; i < seg->size; ++i)
|
||||
/* Exclude elements which have been dlclose'd. */
|
||||
count += seg->objects[i].map != NULL;
|
||||
return count;
|
||||
}
|
||||
|
||||
/* Compute the total number of available allocated segments linked
|
||||
from SEG. */
|
||||
static inline size_t
|
||||
_dlfo_mappings_segment_count_allocated (struct dlfo_mappings_segment *seg)
|
||||
{
|
||||
size_t count = 0;
|
||||
for (; seg != NULL; seg = seg->previous)
|
||||
count += seg->allocated;
|
||||
return count;
|
||||
}
|
||||
|
||||
/* This is essentially an arbitrary value. dlopen allocates plenty of
|
||||
memory anyway, so over-allocated a bit does not hurt. Not having
|
||||
many small-ish segments helps to avoid many small binary searches.
|
||||
Not using a power of 2 means that we do not waste an extra page
|
||||
just for the malloc header if a mapped allocation is used in the
|
||||
glibc allocator. */
|
||||
enum { dlfo_mappings_initial_segment_size = 63 };
|
||||
|
||||
/* Allocate an empty segment. This used for the first ever
|
||||
allocation. */
|
||||
static struct dlfo_mappings_segment *
|
||||
_dlfo_mappings_segment_allocate_unpadded (size_t size)
|
||||
{
|
||||
if (size < dlfo_mappings_initial_segment_size)
|
||||
size = dlfo_mappings_initial_segment_size;
|
||||
/* No overflow checks here because the size is a mapping count, and
|
||||
struct link_map is larger than what we allocate here. */
|
||||
enum
|
||||
{
|
||||
element_size = sizeof ((struct dlfo_mappings_segment) {}.objects[0])
|
||||
};
|
||||
size_t to_allocate = (sizeof (struct dlfo_mappings_segment)
|
||||
+ size * element_size);
|
||||
struct dlfo_mappings_segment *result = malloc (to_allocate);
|
||||
if (result != NULL)
|
||||
{
|
||||
result->previous = NULL;
|
||||
result->to_free = NULL; /* Minimal malloc memory cannot be freed. */
|
||||
result->size = 0;
|
||||
result->allocated = size;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Allocate an empty segment that is at least SIZE large. PREVIOUS
|
||||
points to the chain of previously allocated segments and can be
|
||||
NULL. */
|
||||
static struct dlfo_mappings_segment *
|
||||
_dlfo_mappings_segment_allocate (size_t size,
|
||||
struct dlfo_mappings_segment * previous)
|
||||
{
|
||||
/* Exponential sizing policies, so that lookup approximates a binary
|
||||
search. */
|
||||
{
|
||||
size_t minimum_growth;
|
||||
if (previous == NULL)
|
||||
minimum_growth = dlfo_mappings_initial_segment_size;
|
||||
else
|
||||
minimum_growth = 2* previous->allocated;
|
||||
if (size < minimum_growth)
|
||||
size = minimum_growth;
|
||||
}
|
||||
enum { cache_line_size_estimate = 128 };
|
||||
/* No overflow checks here because the size is a mapping count, and
|
||||
struct link_map is larger than what we allocate here. */
|
||||
enum
|
||||
{
|
||||
element_size = sizeof ((struct dlfo_mappings_segment) {}.objects[0])
|
||||
};
|
||||
size_t to_allocate = (sizeof (struct dlfo_mappings_segment)
|
||||
+ size * element_size
|
||||
+ 2 * cache_line_size_estimate);
|
||||
char *ptr = malloc (to_allocate);
|
||||
if (ptr == NULL)
|
||||
return NULL;
|
||||
char *original_ptr = ptr;
|
||||
/* Start and end at a (conservative) 128-byte cache line boundary.
|
||||
Do not use memalign for compatibility with partially interposing
|
||||
malloc implementations. */
|
||||
char *end = PTR_ALIGN_DOWN (ptr + to_allocate, cache_line_size_estimate);
|
||||
ptr = PTR_ALIGN_UP (ptr, cache_line_size_estimate);
|
||||
struct dlfo_mappings_segment *result
|
||||
= (struct dlfo_mappings_segment *) ptr;
|
||||
result->previous = previous;
|
||||
result->to_free = original_ptr;
|
||||
result->size = 0;
|
||||
/* We may have obtained slightly more space if malloc happened
|
||||
to provide an over-aligned pointer. */
|
||||
result->allocated = (((uintptr_t) (end - ptr)
|
||||
- sizeof (struct dlfo_mappings_segment))
|
||||
/ element_size);
|
||||
assert (result->allocated >= size);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Monotonic counter for software transactional memory. The lowest
|
||||
bit indicates which element of the _dlfo_loaded_mappings contains
|
||||
up-to-date data. */
|
||||
static __atomic_wide_counter _dlfo_loaded_mappings_version;
|
||||
|
||||
/* TM version at the start of the read operation. */
|
||||
static inline uint64_t
|
||||
_dlfo_read_start_version (void)
|
||||
{
|
||||
/* Acquire MO load synchronizes with the fences at the beginning and
|
||||
end of the TM update region. */
|
||||
return __atomic_wide_counter_load_acquire (&_dlfo_loaded_mappings_version);
|
||||
}
|
||||
|
||||
/* Optimized variant of _dlfo_read_start_version which can be called
|
||||
when the loader is write-locked. */
|
||||
static inline uint64_t
|
||||
_dlfo_read_version_locked (void)
|
||||
{
|
||||
return __atomic_wide_counter_load_relaxed (&_dlfo_loaded_mappings_version);
|
||||
}
|
||||
|
||||
/* Update the version to reflect that an update is happening. This
|
||||
does not change the bit that controls the active segment chain.
|
||||
Returns the index of the currently active segment chain. */
|
||||
static inline unsigned int
|
||||
_dlfo_mappings_begin_update (void)
|
||||
{
|
||||
unsigned int v
|
||||
= __atomic_wide_counter_fetch_add_relaxed (&_dlfo_loaded_mappings_version,
|
||||
2);
|
||||
/* Subsequent stores to the TM data must not be reordered before the
|
||||
store above with the version update. */
|
||||
atomic_thread_fence_release ();
|
||||
return v & 1;
|
||||
}
|
||||
|
||||
/* Installs the just-updated version as the active version. */
|
||||
static inline void
|
||||
_dlfo_mappings_end_update (void)
|
||||
{
|
||||
/* The previous writes to the TM data must not be reordered after
|
||||
the version update below. */
|
||||
atomic_thread_fence_release ();
|
||||
__atomic_wide_counter_fetch_add_relaxed (&_dlfo_loaded_mappings_version,
|
||||
1);
|
||||
}
|
||||
/* Completes an in-place update without switching versions. */
|
||||
static inline void
|
||||
_dlfo_mappings_end_update_no_switch (void)
|
||||
{
|
||||
/* The previous writes to the TM data must not be reordered after
|
||||
the version update below. */
|
||||
atomic_thread_fence_release ();
|
||||
__atomic_wide_counter_fetch_add_relaxed (&_dlfo_loaded_mappings_version,
|
||||
2);
|
||||
}
|
||||
|
||||
/* Return true if the read was successful, given the start
|
||||
version. */
|
||||
static inline bool
|
||||
_dlfo_read_success (uint64_t start_version)
|
||||
{
|
||||
return _dlfo_read_start_version () == start_version;
|
||||
}
|
||||
|
||||
/* Returns the active segment identified by the specified start
|
||||
version. */
|
||||
static struct dlfo_mappings_segment *
|
||||
_dlfo_mappings_active_segment (uint64_t start_version)
|
||||
{
|
||||
return _dlfo_loaded_mappings[start_version & 1];
|
||||
}
|
||||
|
||||
/* Searches PC amoung the address-sorted array [FIRST1, FIRST1 +
|
||||
SIZE). Assumes PC >= FIRST1->map_start. Returns a pointer to the
|
||||
element that contains PC, or NULL if there is no such element. */
|
||||
static inline struct dl_find_object_internal *
|
||||
_dlfo_lookup (uintptr_t pc, struct dl_find_object_internal *first1, size_t size)
|
||||
{
|
||||
struct dl_find_object_internal *end = first1 + size;
|
||||
|
||||
/* Search for a lower bound in first. */
|
||||
struct dl_find_object_internal *first = first1;
|
||||
while (size > 0)
|
||||
{
|
||||
size_t half = size >> 1;
|
||||
struct dl_find_object_internal *middle = first + half;
|
||||
if (middle->map_start < pc)
|
||||
{
|
||||
first = middle + 1;
|
||||
size -= half + 1;
|
||||
}
|
||||
else
|
||||
size = half;
|
||||
}
|
||||
|
||||
if (first != end && pc == first->map_start)
|
||||
{
|
||||
if (pc < first->map_end)
|
||||
return first;
|
||||
else
|
||||
/* Zero-length mapping after dlclose. */
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Check to see if PC is in the previous mapping. */
|
||||
--first;
|
||||
if (pc < first->map_end)
|
||||
/* pc >= first->map_start implied by the search above. */
|
||||
return first;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
_dl_find_object (void *pc1, struct dl_find_object *result)
|
||||
{
|
||||
uintptr_t pc = (uintptr_t) pc1;
|
||||
|
||||
if (__glibc_unlikely (_dlfo_main.map_end == 0))
|
||||
{
|
||||
/* Not initialized. No locking is needed here because this can
|
||||
only be called from audit modules, which cannot create
|
||||
threads. */
|
||||
return _dl_find_object_slow (pc1, result);
|
||||
}
|
||||
|
||||
/* Main executable. */
|
||||
if (pc >= _dlfo_main.map_start && pc < _dlfo_main.map_end)
|
||||
{
|
||||
_dl_find_object_to_external (&_dlfo_main, result);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Other initially loaded objects. */
|
||||
if (pc >= _dlfo_nodelete_mappings->map_start
|
||||
&& pc < _dlfo_nodelete_mappings_end)
|
||||
{
|
||||
struct dl_find_object_internal *obj
|
||||
= _dlfo_lookup (pc, _dlfo_nodelete_mappings,
|
||||
_dlfo_nodelete_mappings_size);
|
||||
if (obj != NULL)
|
||||
{
|
||||
_dl_find_object_to_external (obj, result);
|
||||
return 0;
|
||||
}
|
||||
/* Fall through to the full search. The kernel may have mapped
|
||||
the initial mappings with gaps that are later filled by
|
||||
dlopen with other mappings. */
|
||||
}
|
||||
|
||||
/* Handle audit modules, dlopen, dlopen objects. This uses software
|
||||
transactional memory, with a retry loop in case the version
|
||||
changes during execution. */
|
||||
while (true)
|
||||
{
|
||||
retry:
|
||||
;
|
||||
uint64_t start_version = _dlfo_read_start_version ();
|
||||
|
||||
/* The read through seg->previous assumes that the CPU
|
||||
recognizes the load dependency, so that no invalid size
|
||||
values is read. Furthermore, the code assumes that no
|
||||
out-of-thin-air value for seg->size is observed. Together,
|
||||
this ensures that the observed seg->size value is always less
|
||||
than seg->allocated, so that _dlfo_mappings_index does not
|
||||
read out-of-bounds. (This avoids intermediate TM version
|
||||
verification. A concurrent version update will lead to
|
||||
invalid lookup results, but not to out-of-memory access.)
|
||||
|
||||
Either seg == NULL or seg->size == 0 terminates the segment
|
||||
list. _dl_find_object_update does not bother to clear the
|
||||
size on earlier unused segments. */
|
||||
for (struct dlfo_mappings_segment *seg
|
||||
= _dlfo_mappings_active_segment (start_version);
|
||||
seg != NULL && seg->size > 0; seg = seg->previous)
|
||||
if (pc >= seg->objects[0].map_start)
|
||||
{
|
||||
/* PC may lie within this segment. If it is less than the
|
||||
segment start address, it can only lie in a previous
|
||||
segment, due to the base address sorting. */
|
||||
struct dl_find_object_internal *obj
|
||||
= _dlfo_lookup (pc, seg->objects, seg->size);
|
||||
|
||||
if (obj != NULL)
|
||||
{
|
||||
/* Found the right mapping. Copy out the data prior to
|
||||
checking if the read transaction was successful. */
|
||||
struct dl_find_object_internal copy = *obj;
|
||||
if (_dlfo_read_success (start_version))
|
||||
{
|
||||
_dl_find_object_to_external (©, result);
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
/* Read transaction failure. */
|
||||
goto retry;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* PC is not covered by this mapping. */
|
||||
if (_dlfo_read_success (start_version))
|
||||
return -1;
|
||||
else
|
||||
/* Read transaction failure. */
|
||||
goto retry;
|
||||
}
|
||||
} /* if: PC might lie within the current seg. */
|
||||
|
||||
/* PC is not covered by any segment. */
|
||||
if (_dlfo_read_success (start_version))
|
||||
return -1;
|
||||
} /* Transaction retry loop. */
|
||||
}
|
||||
rtld_hidden_def (_dl_find_object)
|
||||
|
||||
/* _dlfo_process_initial is called twice. First to compute the array
|
||||
sizes from the initial loaded mappings. Second to fill in the
|
||||
bases and infos arrays with the (still unsorted) data. Returns the
|
||||
number of loaded (non-nodelete) mappings. */
|
||||
static size_t
|
||||
_dlfo_process_initial (void)
|
||||
{
|
||||
struct link_map *main_map = GL(dl_ns)[LM_ID_BASE]._ns_loaded;
|
||||
|
||||
size_t nodelete = 0;
|
||||
if (!main_map->l_contiguous)
|
||||
{
|
||||
struct dl_find_object_internal dlfo;
|
||||
_dl_find_object_from_map (main_map, &dlfo);
|
||||
|
||||
/* PT_LOAD segments for a non-contiguous are added to the
|
||||
non-closeable mappings. */
|
||||
for (const ElfW(Phdr) *ph = main_map->l_phdr,
|
||||
*ph_end = main_map->l_phdr + main_map->l_phnum;
|
||||
ph < ph_end; ++ph)
|
||||
if (ph->p_type == PT_LOAD)
|
||||
{
|
||||
if (_dlfo_nodelete_mappings != NULL)
|
||||
{
|
||||
/* Second pass only. */
|
||||
_dlfo_nodelete_mappings[nodelete] = dlfo;
|
||||
_dlfo_nodelete_mappings[nodelete].map_start
|
||||
= ph->p_vaddr + main_map->l_addr;
|
||||
_dlfo_nodelete_mappings[nodelete].map_end
|
||||
= _dlfo_nodelete_mappings[nodelete].map_start + ph->p_memsz;
|
||||
}
|
||||
++nodelete;
|
||||
}
|
||||
}
|
||||
|
||||
size_t loaded = 0;
|
||||
for (Lmid_t ns = 0; ns < GL(dl_nns); ++ns)
|
||||
for (struct link_map *l = GL(dl_ns)[ns]._ns_loaded; l != NULL;
|
||||
l = l->l_next)
|
||||
/* Skip the main map processed above, and proxy maps. */
|
||||
if (l != main_map && l == l->l_real)
|
||||
{
|
||||
/* lt_library link maps are implicitly NODELETE. */
|
||||
if (l->l_type == lt_library || l->l_nodelete_active)
|
||||
{
|
||||
if (_dlfo_nodelete_mappings != NULL)
|
||||
/* Second pass only. */
|
||||
_dl_find_object_from_map
|
||||
(l, _dlfo_nodelete_mappings + nodelete);
|
||||
++nodelete;
|
||||
}
|
||||
else if (l->l_type == lt_loaded)
|
||||
{
|
||||
if (_dlfo_loaded_mappings[0] != NULL)
|
||||
/* Second pass only. */
|
||||
_dl_find_object_from_map
|
||||
(l, &_dlfo_loaded_mappings[0]->objects[loaded]);
|
||||
++loaded;
|
||||
}
|
||||
}
|
||||
|
||||
_dlfo_nodelete_mappings_size = nodelete;
|
||||
return loaded;
|
||||
}
|
||||
|
||||
/* Selection sort based on mapping start address. */
|
||||
void
|
||||
_dlfo_sort_mappings (struct dl_find_object_internal *objects, size_t size)
|
||||
{
|
||||
if (size < 2)
|
||||
return;
|
||||
|
||||
for (size_t i = 0; i < size - 1; ++i)
|
||||
{
|
||||
/* Find minimum. */
|
||||
size_t min_idx = i;
|
||||
uintptr_t min_val = objects[i].map_start;
|
||||
for (size_t j = i + 1; j < size; ++j)
|
||||
if (objects[j].map_start < min_val)
|
||||
{
|
||||
min_idx = j;
|
||||
min_val = objects[j].map_start;
|
||||
}
|
||||
|
||||
/* Swap into place. */
|
||||
struct dl_find_object_internal tmp = objects[min_idx];
|
||||
objects[min_idx] = objects[i];
|
||||
objects[i] = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
_dl_find_object_init (void)
|
||||
{
|
||||
/* Cover the main mapping. */
|
||||
{
|
||||
struct link_map *main_map = GL(dl_ns)[LM_ID_BASE]._ns_loaded;
|
||||
|
||||
if (main_map->l_contiguous)
|
||||
_dl_find_object_from_map (main_map, &_dlfo_main);
|
||||
else
|
||||
{
|
||||
/* Non-contiguous main maps are handled in
|
||||
_dlfo_process_initial. Mark as initialized, but not
|
||||
coverying any valid PC. */
|
||||
_dlfo_main.map_start = -1;
|
||||
_dlfo_main.map_end = -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Allocate the data structures. */
|
||||
size_t loaded_size = _dlfo_process_initial ();
|
||||
_dlfo_nodelete_mappings = malloc (_dlfo_nodelete_mappings_size
|
||||
* sizeof (*_dlfo_nodelete_mappings));
|
||||
if (loaded_size > 0)
|
||||
_dlfo_loaded_mappings[0]
|
||||
= _dlfo_mappings_segment_allocate_unpadded (loaded_size);
|
||||
if (_dlfo_nodelete_mappings == NULL
|
||||
|| (loaded_size > 0 && _dlfo_loaded_mappings[0] == NULL))
|
||||
_dl_fatal_printf ("\
|
||||
Fatal glibc error: cannot allocate memory for find-object data\n");
|
||||
/* Fill in the data with the second call. */
|
||||
_dlfo_nodelete_mappings_size = 0;
|
||||
_dlfo_process_initial ();
|
||||
|
||||
/* Sort both arrays. */
|
||||
if (_dlfo_nodelete_mappings_size > 0)
|
||||
{
|
||||
_dlfo_sort_mappings (_dlfo_nodelete_mappings,
|
||||
_dlfo_nodelete_mappings_size);
|
||||
size_t last_idx = _dlfo_nodelete_mappings_size - 1;
|
||||
_dlfo_nodelete_mappings_end = _dlfo_nodelete_mappings[last_idx].map_end;
|
||||
}
|
||||
if (loaded_size > 0)
|
||||
_dlfo_sort_mappings (_dlfo_loaded_mappings[0]->objects,
|
||||
_dlfo_loaded_mappings[0]->size);
|
||||
}
|
||||
|
||||
static void
|
||||
_dl_find_object_link_map_sort (struct link_map **loaded, size_t size)
|
||||
{
|
||||
/* Selection sort based on map_start. */
|
||||
if (size < 2)
|
||||
return;
|
||||
for (size_t i = 0; i < size - 1; ++i)
|
||||
{
|
||||
/* Find minimum. */
|
||||
size_t min_idx = i;
|
||||
ElfW(Addr) min_val = loaded[i]->l_map_start;
|
||||
for (size_t j = i + 1; j < size; ++j)
|
||||
if (loaded[j]->l_map_start < min_val)
|
||||
{
|
||||
min_idx = j;
|
||||
min_val = loaded[j]->l_map_start;
|
||||
}
|
||||
|
||||
/* Swap into place. */
|
||||
struct link_map *tmp = loaded[min_idx];
|
||||
loaded[min_idx] = loaded[i];
|
||||
loaded[i] = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
/* Initializes the segment for writing. Returns the target write
|
||||
index (plus 1) in this segment. The index is chosen so that a
|
||||
partially filled segment still has data at index 0. */
|
||||
static inline size_t
|
||||
_dlfo_update_init_seg (struct dlfo_mappings_segment *seg,
|
||||
size_t remaining_to_add)
|
||||
{
|
||||
if (remaining_to_add < seg->allocated)
|
||||
/* Partially filled segment. */
|
||||
seg->size = remaining_to_add;
|
||||
else
|
||||
seg->size = seg->allocated;
|
||||
return seg->size;
|
||||
}
|
||||
|
||||
/* Invoked from _dl_find_object_update after sorting. */
|
||||
static bool
|
||||
_dl_find_object_update_1 (struct link_map **loaded, size_t count)
|
||||
{
|
||||
int active_idx = _dlfo_read_version_locked () & 1;
|
||||
|
||||
struct dlfo_mappings_segment *current_seg
|
||||
= _dlfo_loaded_mappings[active_idx];
|
||||
size_t current_used = _dlfo_mappings_segment_count_used (current_seg);
|
||||
|
||||
struct dlfo_mappings_segment *target_seg
|
||||
= _dlfo_loaded_mappings[!active_idx];
|
||||
size_t remaining_to_add = current_used + count;
|
||||
|
||||
/* Ensure that the new segment chain has enough space. */
|
||||
{
|
||||
size_t new_allocated
|
||||
= _dlfo_mappings_segment_count_allocated (target_seg);
|
||||
if (new_allocated < remaining_to_add)
|
||||
{
|
||||
size_t more = remaining_to_add - new_allocated;
|
||||
target_seg = _dlfo_mappings_segment_allocate (more, target_seg);
|
||||
if (target_seg == NULL)
|
||||
/* Out of memory. Do not end the update and keep the
|
||||
current version unchanged. */
|
||||
return false;
|
||||
|
||||
/* Start update cycle. */
|
||||
_dlfo_mappings_begin_update ();
|
||||
|
||||
/* The barrier ensures that a concurrent TM read or fork does
|
||||
not see a partially initialized segment. */
|
||||
atomic_store_release (&_dlfo_loaded_mappings[!active_idx], target_seg);
|
||||
}
|
||||
else
|
||||
/* Start update cycle without allocation. */
|
||||
_dlfo_mappings_begin_update ();
|
||||
}
|
||||
|
||||
size_t target_seg_index1 = _dlfo_update_init_seg (target_seg,
|
||||
remaining_to_add);
|
||||
|
||||
/* Merge the current_seg segment list with the loaded array into the
|
||||
target_set. Merging occurs backwards, in decreasing l_map_start
|
||||
order. */
|
||||
size_t loaded_index1 = count;
|
||||
size_t current_seg_index1;
|
||||
if (current_seg == NULL)
|
||||
current_seg_index1 = 0;
|
||||
else
|
||||
current_seg_index1 = current_seg->size;
|
||||
while (true)
|
||||
{
|
||||
if (current_seg_index1 == 0)
|
||||
{
|
||||
/* Switch to the previous segment. */
|
||||
if (current_seg != NULL)
|
||||
current_seg = current_seg->previous;
|
||||
if (current_seg != NULL)
|
||||
{
|
||||
current_seg_index1 = current_seg->size;
|
||||
if (current_seg_index1 == 0)
|
||||
/* No more data in previous segments. */
|
||||
current_seg = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (current_seg != NULL
|
||||
&& (current_seg->objects[current_seg_index1 - 1].map == NULL))
|
||||
{
|
||||
/* This mapping has been dlclose'd. Do not copy it. */
|
||||
--current_seg_index1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (loaded_index1 == 0 && current_seg == NULL)
|
||||
/* No more data in either source. */
|
||||
break;
|
||||
|
||||
/* Make room for another mapping. */
|
||||
assert (remaining_to_add > 0);
|
||||
if (target_seg_index1 == 0)
|
||||
{
|
||||
/* Switch segments and set the size of the segment. */
|
||||
target_seg = target_seg->previous;
|
||||
target_seg_index1 = _dlfo_update_init_seg (target_seg,
|
||||
remaining_to_add);
|
||||
}
|
||||
|
||||
/* Determine where to store the data. */
|
||||
struct dl_find_object_internal *dlfo
|
||||
= &target_seg->objects[target_seg_index1 - 1];
|
||||
|
||||
if (loaded_index1 == 0
|
||||
|| (current_seg != NULL
|
||||
&& (loaded[loaded_index1 - 1]->l_map_start
|
||||
< current_seg->objects[current_seg_index1 - 1].map_start)))
|
||||
{
|
||||
/* Prefer mapping in current_seg. */
|
||||
assert (current_seg_index1 > 0);
|
||||
*dlfo = current_seg->objects[current_seg_index1 - 1];
|
||||
--current_seg_index1;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Prefer newly loaded link map. */
|
||||
assert (loaded_index1 > 0);
|
||||
_dl_find_object_from_map (loaded[loaded_index1 - 1], dlfo);
|
||||
loaded[loaded_index1 - 1]->l_find_object_processed = 1;
|
||||
--loaded_index1;
|
||||
}
|
||||
|
||||
/* Consume space in target segment. */
|
||||
--target_seg_index1;
|
||||
|
||||
--remaining_to_add;
|
||||
}
|
||||
|
||||
/* Everything has been added. */
|
||||
assert (remaining_to_add == 0);
|
||||
|
||||
/* The segment must have been filled up to the beginning. */
|
||||
assert (target_seg_index1 == 0);
|
||||
|
||||
/* Prevent searching further into unused segments. */
|
||||
if (target_seg->previous != NULL)
|
||||
target_seg->previous->size = 0;
|
||||
|
||||
_dlfo_mappings_end_update ();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
_dl_find_object_update (struct link_map *new_map)
|
||||
{
|
||||
/* Copy the newly-loaded link maps into an array for sorting. */
|
||||
size_t count = 0;
|
||||
for (struct link_map *l = new_map; l != NULL; l = l->l_next)
|
||||
/* Skip proxy maps and already-processed maps. */
|
||||
count += l == l->l_real && !l->l_find_object_processed;
|
||||
struct link_map **map_array = malloc (count * sizeof (*map_array));
|
||||
if (map_array == NULL)
|
||||
return false;
|
||||
{
|
||||
size_t i = 0;
|
||||
for (struct link_map *l = new_map; l != NULL; l = l->l_next)
|
||||
if (l == l->l_real && !l->l_find_object_processed)
|
||||
map_array[i++] = l;
|
||||
}
|
||||
if (count == 0)
|
||||
return true;
|
||||
|
||||
_dl_find_object_link_map_sort (map_array, count);
|
||||
bool ok = _dl_find_object_update_1 (map_array, count);
|
||||
free (map_array);
|
||||
return ok;
|
||||
}
|
||||
|
||||
void
|
||||
_dl_find_object_dlclose (struct link_map *map)
|
||||
{
|
||||
uint64_t start_version = _dlfo_read_version_locked ();
|
||||
uintptr_t map_start = map->l_map_start;
|
||||
|
||||
|
||||
/* Directly patch the size information in the mapping to mark it as
|
||||
unused. See the parallel lookup logic in _dl_find_object. Do
|
||||
not check for previous dlclose at the same mapping address
|
||||
because that cannot happen (there would have to be an
|
||||
intermediate dlopen, which drops size-zero mappings). */
|
||||
for (struct dlfo_mappings_segment *seg
|
||||
= _dlfo_mappings_active_segment (start_version);
|
||||
seg != NULL && seg->size > 0; seg = seg->previous)
|
||||
if (map_start >= seg->objects[0].map_start)
|
||||
{
|
||||
struct dl_find_object_internal *obj
|
||||
= _dlfo_lookup (map_start, seg->objects, seg->size);
|
||||
if (obj == NULL)
|
||||
/* Ignore missing link maps because of potential shutdown
|
||||
issues around __libc_freeres. */
|
||||
return;
|
||||
|
||||
/* The update happens in-place, but given that we do not use
|
||||
atomic accesses on the read side, update the version around
|
||||
the update to trigger re-validation in concurrent
|
||||
readers. */
|
||||
_dlfo_mappings_begin_update ();
|
||||
|
||||
/* Mark as closed. */
|
||||
obj->map_end = obj->map_start;
|
||||
obj->map = NULL;
|
||||
|
||||
_dlfo_mappings_end_update_no_switch ();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
_dl_find_object_freeres (void)
|
||||
{
|
||||
for (int idx = 0; idx < 2; ++idx)
|
||||
{
|
||||
for (struct dlfo_mappings_segment *seg = _dlfo_loaded_mappings[idx];
|
||||
seg != NULL; )
|
||||
{
|
||||
struct dlfo_mappings_segment *previous = seg->previous;
|
||||
free (seg->to_free);
|
||||
seg = previous;
|
||||
}
|
||||
/* Stop searching in shared objects. */
|
||||
_dlfo_loaded_mappings[idx] = 0;
|
||||
}
|
||||
}
|
115
elf/dl-find_object.h
Normal file
115
elf/dl-find_object.h
Normal file
@ -0,0 +1,115 @@
|
||||
/* Locating objects in the process image. ld.so implementation.
|
||||
Copyright (C) 2021 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
|
||||
<https://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef _DL_FIND_EH_FRAME_H
|
||||
#define _DL_FIND_EH_FRAME_H
|
||||
|
||||
#include <assert.h>
|
||||
#include <dlfcn.h>
|
||||
#include <ldsodefs.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/* Internal version of struct dl_find_object. Does not include the
|
||||
(yet unused) flags member. We need to make a copy of data also in
|
||||
struct link_map to support non-contiguous mappings, and to support
|
||||
software transactional memory (the link map is not covered by
|
||||
transactions). */
|
||||
struct dl_find_object_internal
|
||||
{
|
||||
uintptr_t map_start;
|
||||
uintptr_t map_end; /* Set to map_start by dlclose. */
|
||||
struct link_map *map; /* Set to NULL by dlclose. */
|
||||
void *eh_frame;
|
||||
#if DLFO_STRUCT_HAS_EH_DBASE
|
||||
void *eh_dbase;
|
||||
#endif
|
||||
#if DLFO_STRUCT_HAS_EH_COUNT
|
||||
int eh_count;
|
||||
#endif
|
||||
};
|
||||
|
||||
static inline void
|
||||
_dl_find_object_to_external (struct dl_find_object_internal *internal,
|
||||
struct dl_find_object *external)
|
||||
{
|
||||
external->dlfo_flags = 0;
|
||||
external->dlfo_map_start = (void *) internal->map_start;
|
||||
external->dlfo_map_end = (void *) internal->map_end;
|
||||
external->dlfo_link_map = internal->map;
|
||||
external->dlfo_eh_frame = internal->eh_frame;
|
||||
# if DLFO_STRUCT_HAS_EH_DBASE
|
||||
external->dlfo_eh_dbase = internal->eh_dbase;
|
||||
# endif
|
||||
# if DLFO_STRUCT_HAS_EH_COUNT
|
||||
external->dlfo_eh_count = internal->eh_count;
|
||||
# endif
|
||||
}
|
||||
|
||||
/* Extract the object location data from a link map and writes it to
|
||||
*RESULT. */
|
||||
static void __attribute__ ((unused))
|
||||
_dl_find_object_from_map (struct link_map *l,
|
||||
struct dl_find_object_internal *result)
|
||||
{
|
||||
result->map_start = (uintptr_t) l->l_map_start;
|
||||
result->map_end = (uintptr_t) l->l_map_end;
|
||||
result->map = l;
|
||||
|
||||
#if DLFO_STRUCT_HAS_EH_DBASE
|
||||
result->eh_dbase = (void *) l->l_info[DT_PLTGOT];
|
||||
#endif
|
||||
|
||||
for (const ElfW(Phdr) *ph = l->l_phdr, *ph_end = l->l_phdr + l->l_phnum;
|
||||
ph < ph_end; ++ph)
|
||||
if (ph->p_type == DLFO_EH_SEGMENT_TYPE)
|
||||
{
|
||||
result->eh_frame = (void *) (ph->p_vaddr + l->l_addr);
|
||||
#if DLFO_STRUCT_HAS_EH_COUNT
|
||||
result->eh_count = ph->p_memsz / 8;
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
/* Object has no exception handling segment. */
|
||||
result->eh_frame = NULL;
|
||||
#if DLFO_STRUCT_HAS_EH_COUNT
|
||||
result->eh_count = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Called by the dynamic linker to set up the data structures for the
|
||||
initially loaded objects. This creates a few persistent
|
||||
allocations, so it should be called with the minimal malloc. */
|
||||
void _dl_find_object_init (void) attribute_hidden;
|
||||
|
||||
/* Called by dlopen/dlmopen to add new objects to the DWARF EH frame
|
||||
data structures. NEW_MAP is the dlopen'ed link map. Link maps on
|
||||
the l_next list are added if l_object_processed is 0. Needs to
|
||||
be protected by loader write lock. Returns true on success, false
|
||||
on malloc failure. */
|
||||
bool _dl_find_object_update (struct link_map *new_map) attribute_hidden;
|
||||
|
||||
/* Called by dlclose to remove the link map from the DWARF EH frame
|
||||
data structures. Needs to be protected by loader write lock. */
|
||||
void _dl_find_object_dlclose (struct link_map *l) attribute_hidden;
|
||||
|
||||
/* Called from __libc_freeres to deallocate malloc'ed memory. */
|
||||
void _dl_find_object_freeres (void) attribute_hidden;
|
||||
|
||||
#endif /* _DL_FIND_OBJECT_H */
|
@ -17,8 +17,10 @@
|
||||
<https://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <ldsodefs.h>
|
||||
#include <dl-find_object.h>
|
||||
|
||||
void
|
||||
__rtld_libc_freeres (void)
|
||||
{
|
||||
_dl_find_object_freeres ();
|
||||
}
|
||||
|
@ -36,6 +36,7 @@
|
||||
#include <array_length.h>
|
||||
#include <libc-early-init.h>
|
||||
#include <gnu/lib-names.h>
|
||||
#include <dl-find_object.h>
|
||||
|
||||
#include <dl-dst.h>
|
||||
#include <dl-prop.h>
|
||||
@ -731,6 +732,10 @@ dl_open_worker_begin (void *a)
|
||||
objects. */
|
||||
update_scopes (new);
|
||||
|
||||
if (!_dl_find_object_update (new))
|
||||
_dl_signal_error (ENOMEM, new->l_libname->name, NULL,
|
||||
N_ ("cannot allocate address lookup data"));
|
||||
|
||||
/* FIXME: It is unclear whether the order here is correct.
|
||||
Shouldn't new objects be made available for binding (and thus
|
||||
execution) only after there TLS data has been set up fully?
|
||||
|
@ -43,6 +43,7 @@
|
||||
#include <dl-vdso.h>
|
||||
#include <dl-vdso-setup.h>
|
||||
#include <dl-auxv.h>
|
||||
#include <dl-find_object.h>
|
||||
|
||||
extern char *__progname;
|
||||
char **_dl_argv = &__progname; /* This is checked for some error messages. */
|
||||
@ -417,6 +418,8 @@ _dl_non_dynamic_init (void)
|
||||
break;
|
||||
}
|
||||
|
||||
call_function_static_weak (_dl_find_object_init);
|
||||
|
||||
/* Setup relro on the binary itself. */
|
||||
if (_dl_main_map.l_relro_size != 0)
|
||||
_dl_protect_relro (&_dl_main_map);
|
||||
|
26
elf/libc-dl_find_object.c
Normal file
26
elf/libc-dl_find_object.c
Normal file
@ -0,0 +1,26 @@
|
||||
/* Locating objects in the process image. libc forwarder.
|
||||
Copyright (C) 2021 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
|
||||
<https://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <ldsodefs.h>
|
||||
#include <dlfcn.h>
|
||||
|
||||
int
|
||||
_dl_find_object (void *address, struct dl_find_object *result)
|
||||
{
|
||||
return GLRO (dl_find_object) (address, result);
|
||||
}
|
11
elf/rtld.c
11
elf/rtld.c
@ -51,6 +51,7 @@
|
||||
#include <dl-tunables.h>
|
||||
#include <get-dynamic-info.h>
|
||||
#include <dl-execve.h>
|
||||
#include <dl-find_object.h>
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
@ -582,6 +583,10 @@ _dl_start (void *arg)
|
||||
|
||||
__rtld_malloc_init_stubs ();
|
||||
|
||||
/* Do not use an initializer for these members because it would
|
||||
intefere with __rtld_static_init. */
|
||||
GLRO (dl_find_object) = &_dl_find_object;
|
||||
|
||||
{
|
||||
#ifdef DONT_USE_BOOTSTRAP_MAP
|
||||
ElfW(Addr) entry = _dl_start_final (arg);
|
||||
@ -2335,6 +2340,9 @@ dl_main (const ElfW(Phdr) *phdr,
|
||||
rtld_timer_stop (&relocate_time, start);
|
||||
}
|
||||
|
||||
/* Set up the object lookup structures. */
|
||||
_dl_find_object_init ();
|
||||
|
||||
/* The library defining malloc has already been relocated due to
|
||||
prelinking. Resolve the malloc symbols for the dynamic
|
||||
loader. */
|
||||
@ -2443,6 +2451,9 @@ dl_main (const ElfW(Phdr) *phdr,
|
||||
re-relocation, we might call a user-supplied function
|
||||
(e.g. calloc from _dl_relocate_object) that uses TLS data. */
|
||||
|
||||
/* Set up the object lookup structures. */
|
||||
_dl_find_object_init ();
|
||||
|
||||
/* The malloc implementation has been relocated, so resolving
|
||||
its symbols (and potentially calling IFUNC resolvers) is safe
|
||||
at this point. */
|
||||
|
@ -78,6 +78,7 @@ __rtld_static_init (struct link_map *map)
|
||||
extern __typeof (dl->_dl_tls_static_size) _dl_tls_static_size
|
||||
attribute_hidden;
|
||||
dl->_dl_tls_static_size = _dl_tls_static_size;
|
||||
dl->_dl_find_object = _dl_find_object;
|
||||
|
||||
__rtld_static_init_arch (map, dl);
|
||||
}
|
||||
|
10
elf/tst-dl_find_object-mod1.c
Normal file
10
elf/tst-dl_find_object-mod1.c
Normal file
@ -0,0 +1,10 @@
|
||||
char mod1_data;
|
||||
|
||||
void
|
||||
mod1_function (void (*f) (void))
|
||||
{
|
||||
/* Make sure this is not a tail call and unwind information is
|
||||
therefore needed. */
|
||||
f ();
|
||||
f ();
|
||||
}
|
15
elf/tst-dl_find_object-mod2.c
Normal file
15
elf/tst-dl_find_object-mod2.c
Normal file
@ -0,0 +1,15 @@
|
||||
#include <dlfcn.h>
|
||||
|
||||
char mod2_data;
|
||||
|
||||
void
|
||||
mod2_function (void (*f) (void))
|
||||
{
|
||||
/* Make sure this is not a tail call and unwind information is
|
||||
therefore needed. */
|
||||
f ();
|
||||
f ();
|
||||
}
|
||||
|
||||
/* Used to verify that _dl_find_object after static dlopen works. */
|
||||
void *find_object = _dl_find_object;
|
10
elf/tst-dl_find_object-mod3.c
Normal file
10
elf/tst-dl_find_object-mod3.c
Normal file
@ -0,0 +1,10 @@
|
||||
char mod3_data[4096];
|
||||
|
||||
void
|
||||
mod3_function (void (*f) (void))
|
||||
{
|
||||
/* Make sure this is not a tail call and unwind information is
|
||||
therefore needed. */
|
||||
f ();
|
||||
f ();
|
||||
}
|
10
elf/tst-dl_find_object-mod4.c
Normal file
10
elf/tst-dl_find_object-mod4.c
Normal file
@ -0,0 +1,10 @@
|
||||
char mod4_data;
|
||||
|
||||
void
|
||||
mod4_function (void (*f) (void))
|
||||
{
|
||||
/* Make sure this is not a tail call and unwind information is
|
||||
therefore needed. */
|
||||
f ();
|
||||
f ();
|
||||
}
|
11
elf/tst-dl_find_object-mod5.c
Normal file
11
elf/tst-dl_find_object-mod5.c
Normal file
@ -0,0 +1,11 @@
|
||||
/* Slightly larger to get different layouts. */
|
||||
char mod5_data[4096];
|
||||
|
||||
void
|
||||
mod5_function (void (*f) (void))
|
||||
{
|
||||
/* Make sure this is not a tail call and unwind information is
|
||||
therefore needed. */
|
||||
f ();
|
||||
f ();
|
||||
}
|
11
elf/tst-dl_find_object-mod6.c
Normal file
11
elf/tst-dl_find_object-mod6.c
Normal file
@ -0,0 +1,11 @@
|
||||
/* Large to get different layouts. */
|
||||
char mod6_data[4096];
|
||||
|
||||
void
|
||||
mod6_function (void (*f) (void))
|
||||
{
|
||||
/* Make sure this is not a tail call and unwind information is
|
||||
therefore needed. */
|
||||
f ();
|
||||
f ();
|
||||
}
|
10
elf/tst-dl_find_object-mod7.c
Normal file
10
elf/tst-dl_find_object-mod7.c
Normal file
@ -0,0 +1,10 @@
|
||||
char mod7_data;
|
||||
|
||||
void
|
||||
mod7_function (void (*f) (void))
|
||||
{
|
||||
/* Make sure this is not a tail call and unwind information is
|
||||
therefore needed. */
|
||||
f ();
|
||||
f ();
|
||||
}
|
10
elf/tst-dl_find_object-mod8.c
Normal file
10
elf/tst-dl_find_object-mod8.c
Normal file
@ -0,0 +1,10 @@
|
||||
char mod8_data;
|
||||
|
||||
void
|
||||
mod8_function (void (*f) (void))
|
||||
{
|
||||
/* Make sure this is not a tail call and unwind information is
|
||||
therefore needed. */
|
||||
f ();
|
||||
f ();
|
||||
}
|
10
elf/tst-dl_find_object-mod9.c
Normal file
10
elf/tst-dl_find_object-mod9.c
Normal file
@ -0,0 +1,10 @@
|
||||
char mod9_data;
|
||||
|
||||
void
|
||||
mod9_function (void (*f) (void))
|
||||
{
|
||||
/* Make sure this is not a tail call and unwind information is
|
||||
therefore needed. */
|
||||
f ();
|
||||
f ();
|
||||
}
|
22
elf/tst-dl_find_object-static.c
Normal file
22
elf/tst-dl_find_object-static.c
Normal file
@ -0,0 +1,22 @@
|
||||
/* Basic tests for _dl_find_object. Static version.
|
||||
Copyright (C) 2021 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
|
||||
<https://www.gnu.org/licenses/>. */
|
||||
|
||||
/* Disable tests around _r_debug and libc symbols that do not work in
|
||||
the static case. */
|
||||
#define FOR_STATIC
|
||||
#include "tst-dl_find_object.c"
|
275
elf/tst-dl_find_object-threads.c
Normal file
275
elf/tst-dl_find_object-threads.c
Normal file
@ -0,0 +1,275 @@
|
||||
/* _dl_find_object test with parallelism.
|
||||
Copyright (C) 2021 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
|
||||
<https://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <array_length.h>
|
||||
#include <dlfcn.h>
|
||||
#include <elf/dl-find_object.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <support/check.h>
|
||||
#include <support/support.h>
|
||||
#include <support/xdlfcn.h>
|
||||
#include <support/xthread.h>
|
||||
#include <support/xunistd.h>
|
||||
|
||||
/* Computes the expected _dl_find_object result directly from the
|
||||
map. */
|
||||
static void
|
||||
from_map (struct link_map *l, struct dl_find_object *expected)
|
||||
{
|
||||
struct dl_find_object_internal internal;
|
||||
_dl_find_object_from_map (l, &internal);
|
||||
_dl_find_object_to_external (&internal, expected);
|
||||
}
|
||||
|
||||
/* Returns the soname for the test object NUMBER. */
|
||||
static char *
|
||||
soname (int number)
|
||||
{
|
||||
return xasprintf ("tst-dl_find_object-mod%d.so", number);
|
||||
}
|
||||
|
||||
/* Returns the data symbol name for the test object NUMBER. */
|
||||
static char *
|
||||
symbol (int number)
|
||||
{
|
||||
return xasprintf ("mod%d_data", number);
|
||||
}
|
||||
|
||||
struct verify_data
|
||||
{
|
||||
char *soname;
|
||||
void *address; /* Address in the shared object. */
|
||||
struct dl_find_object dlfo;
|
||||
pthread_t thr;
|
||||
};
|
||||
|
||||
/* Compare _dl_find_object result at ADDRESS with *EXPECTED. */
|
||||
static void
|
||||
check (void *address, struct dl_find_object *expected, int line)
|
||||
{
|
||||
struct dl_find_object actual;
|
||||
int ret = _dl_find_object (address, &actual);
|
||||
if (expected == NULL)
|
||||
{
|
||||
if (ret != -1)
|
||||
{
|
||||
support_record_failure ();
|
||||
printf ("%s:%d: unexpected success for %p\n",
|
||||
__FILE__, line, address);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (ret != 0)
|
||||
{
|
||||
support_record_failure ();
|
||||
printf ("%s:%d: unexpected failure for %p\n",
|
||||
__FILE__, line, address);
|
||||
return;
|
||||
}
|
||||
|
||||
if (actual.dlfo_flags != expected->dlfo_flags)
|
||||
{
|
||||
support_record_failure ();
|
||||
printf ("%s:%d: error: %p: flags is %llu, expected %llu\n",
|
||||
__FILE__, line, address,
|
||||
actual.dlfo_flags, expected->dlfo_flags);
|
||||
}
|
||||
if (actual.dlfo_flags != expected->dlfo_flags)
|
||||
{
|
||||
support_record_failure ();
|
||||
printf ("%s:%d: error: %p: map start is %p, expected %p\n",
|
||||
__FILE__, line,
|
||||
address, actual.dlfo_map_start, expected->dlfo_map_start);
|
||||
}
|
||||
if (actual.dlfo_map_end != expected->dlfo_map_end)
|
||||
{
|
||||
support_record_failure ();
|
||||
printf ("%s:%d: error: %p: map end is %p, expected %p\n",
|
||||
__FILE__, line,
|
||||
address, actual.dlfo_map_end, expected->dlfo_map_end);
|
||||
}
|
||||
if (actual.dlfo_link_map != expected->dlfo_link_map)
|
||||
{
|
||||
support_record_failure ();
|
||||
printf ("%s:%d: error: %p: link map is %p, expected %p\n",
|
||||
__FILE__, line,
|
||||
address, actual.dlfo_link_map, expected->dlfo_link_map);
|
||||
}
|
||||
if (actual.dlfo_eh_frame != expected->dlfo_eh_frame)
|
||||
{
|
||||
support_record_failure ();
|
||||
printf ("%s:%d: error: %p: EH frame is %p, expected %p\n",
|
||||
__FILE__, line,
|
||||
address, actual.dlfo_eh_frame, expected->dlfo_eh_frame);
|
||||
}
|
||||
#if DLFO_STRUCT_HAS_EH_DBASE
|
||||
if (actual.dlfo_eh_dbase != expected->dlfo_eh_dbase)
|
||||
{
|
||||
support_record_failure ();
|
||||
printf ("%s:%d: error: %p: data base is %p, expected %p\n",
|
||||
__FILE__, line,
|
||||
address, actual.dlfo_eh_dbase, expected->dlfo_eh_dbase);
|
||||
}
|
||||
#endif
|
||||
#if DLFO_STRUCT_HAS_EH_COUNT
|
||||
if (actual.dlfo_eh_count != expected->dlfo_eh_count)
|
||||
{
|
||||
support_record_failure ();
|
||||
printf ("%s:%d: error: %p: count is %d, expected %d\n",
|
||||
__FILE__, line,
|
||||
address, actual.dlfo_eh_count, expected->dlfo_eh_count);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Request process termination after 3 seconds. */
|
||||
static bool exit_requested;
|
||||
static void *
|
||||
exit_thread (void *ignored)
|
||||
{
|
||||
usleep (3 * 100 * 1000);
|
||||
__atomic_store_n (&exit_requested, true, __ATOMIC_RELAXED);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void *
|
||||
verify_thread (void *closure)
|
||||
{
|
||||
struct verify_data *data = closure;
|
||||
|
||||
while (!__atomic_load_n (&exit_requested, __ATOMIC_RELAXED))
|
||||
{
|
||||
check (data->address, &data->dlfo, __LINE__);
|
||||
check (data->dlfo.dlfo_map_start, &data->dlfo, __LINE__);
|
||||
check (data->dlfo.dlfo_map_end - 1, &data->dlfo, __LINE__);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Sets up the verification data, dlopen'ing shared object NUMBER, and
|
||||
launches a verification thread. */
|
||||
static void
|
||||
start_verify (int number, struct verify_data *data)
|
||||
{
|
||||
data->soname = soname (number);
|
||||
struct link_map *l = xdlopen (data->soname, RTLD_NOW);
|
||||
from_map (l, &data->dlfo);
|
||||
TEST_VERIFY_EXIT (data->dlfo.dlfo_link_map == l);
|
||||
char *sym = symbol (number);
|
||||
data->address = xdlsym (data->dlfo.dlfo_link_map, sym);
|
||||
free (sym);
|
||||
data->thr = xpthread_create (NULL, verify_thread, data);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
struct verify_data data_mod2;
|
||||
struct verify_data data_mod4;
|
||||
struct verify_data data_mod7;
|
||||
|
||||
/* Load the modules with gaps. */
|
||||
{
|
||||
void *mod1 = xdlopen ("tst-dl_find_object-mod1.so", RTLD_NOW);
|
||||
start_verify (2, &data_mod2);
|
||||
void *mod3 = xdlopen ("tst-dl_find_object-mod3.so", RTLD_NOW);
|
||||
start_verify (4, &data_mod4);
|
||||
void *mod5 = xdlopen ("tst-dl_find_object-mod5.so", RTLD_NOW);
|
||||
void *mod6 = xdlopen ("tst-dl_find_object-mod6.so", RTLD_NOW);
|
||||
start_verify (7, &data_mod7);
|
||||
xdlclose (mod6);
|
||||
xdlclose (mod5);
|
||||
xdlclose (mod3);
|
||||
xdlclose (mod1);
|
||||
}
|
||||
|
||||
/* Objects that continuously opened and closed. */
|
||||
struct temp_object
|
||||
{
|
||||
char *soname;
|
||||
char *symbol;
|
||||
struct link_map *link_map;
|
||||
void *address;
|
||||
} temp_objects[] =
|
||||
{
|
||||
{ soname (1), symbol (1), },
|
||||
{ soname (3), symbol (3), },
|
||||
{ soname (5), symbol (5), },
|
||||
{ soname (6), symbol (6), },
|
||||
{ soname (8), symbol (8), },
|
||||
{ soname (9), symbol (9), },
|
||||
};
|
||||
|
||||
pthread_t exit_thr = xpthread_create (NULL, exit_thread, NULL);
|
||||
|
||||
struct drand48_data state;
|
||||
srand48_r (1, &state);
|
||||
while (!__atomic_load_n (&exit_requested, __ATOMIC_RELAXED))
|
||||
{
|
||||
long int idx;
|
||||
lrand48_r (&state, &idx);
|
||||
idx %= array_length (temp_objects);
|
||||
if (temp_objects[idx].link_map == NULL)
|
||||
{
|
||||
temp_objects[idx].link_map = xdlopen (temp_objects[idx].soname,
|
||||
RTLD_NOW);
|
||||
temp_objects[idx].address = xdlsym (temp_objects[idx].link_map,
|
||||
temp_objects[idx].symbol);
|
||||
}
|
||||
else
|
||||
{
|
||||
xdlclose (temp_objects[idx].link_map);
|
||||
temp_objects[idx].link_map = NULL;
|
||||
struct dl_find_object dlfo;
|
||||
int ret = _dl_find_object (temp_objects[idx].address, &dlfo);
|
||||
if (ret != -1)
|
||||
{
|
||||
TEST_VERIFY_EXIT (ret == 0);
|
||||
support_record_failure ();
|
||||
printf ("%s: error: %s EH found after dlclose, link map %p\n",
|
||||
__FILE__, temp_objects[idx].soname, dlfo.dlfo_link_map);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
xpthread_join (data_mod2.thr);
|
||||
xpthread_join (data_mod4.thr);
|
||||
xpthread_join (data_mod7.thr);
|
||||
xpthread_join (exit_thr);
|
||||
|
||||
for (size_t i = 0; i < array_length (temp_objects); ++i)
|
||||
{
|
||||
free (temp_objects[i].soname);
|
||||
free (temp_objects[i].symbol);
|
||||
if (temp_objects[i].link_map != NULL)
|
||||
xdlclose (temp_objects[i].link_map);
|
||||
}
|
||||
|
||||
free (data_mod2.soname);
|
||||
free (data_mod4.soname);
|
||||
xdlclose (data_mod4.dlfo.dlfo_link_map);
|
||||
free (data_mod7.soname);
|
||||
xdlclose (data_mod7.dlfo.dlfo_link_map);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include <support/test-driver.c>
|
240
elf/tst-dl_find_object.c
Normal file
240
elf/tst-dl_find_object.c
Normal file
@ -0,0 +1,240 @@
|
||||
/* Basic tests for _dl_find_object.
|
||||
Copyright (C) 2021 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
|
||||
<https://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <dl-find_object.h>
|
||||
#include <dlfcn.h>
|
||||
#include <gnu/lib-names.h>
|
||||
#include <ldsodefs.h>
|
||||
#include <link.h>
|
||||
#include <stdio.h>
|
||||
#include <support/check.h>
|
||||
#include <support/xdlfcn.h>
|
||||
|
||||
/* Use data objects for testing, so that it is not necessary to decode
|
||||
function descriptors on architectures that have them. */
|
||||
static char main_program_data;
|
||||
|
||||
/* Computes the expected _dl_find_object result directly from the
|
||||
map. */
|
||||
static void
|
||||
from_map (struct link_map *l, struct dl_find_object *expected)
|
||||
{
|
||||
struct dl_find_object_internal internal;
|
||||
_dl_find_object_from_map (l, &internal);
|
||||
_dl_find_object_to_external (&internal, expected);
|
||||
}
|
||||
|
||||
/* Compare _dl_find_object result at ADDRESS with *EXPECTED. */
|
||||
static void
|
||||
check (void *address,
|
||||
struct dl_find_object *expected, int line)
|
||||
{
|
||||
struct dl_find_object actual;
|
||||
int ret = _dl_find_object (address, &actual);
|
||||
if (expected == NULL)
|
||||
{
|
||||
if (ret != -1)
|
||||
{
|
||||
support_record_failure ();
|
||||
printf ("%s:%d: unexpected success for %p\n",
|
||||
__FILE__, line, address);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (ret != 0)
|
||||
{
|
||||
support_record_failure ();
|
||||
printf ("%s:%d: unexpected failure for %p\n",
|
||||
__FILE__, line, address);
|
||||
return;
|
||||
}
|
||||
|
||||
if (actual.dlfo_flags != expected->dlfo_flags)
|
||||
{
|
||||
support_record_failure ();
|
||||
printf ("%s:%d: error: %p: flags is %llu, expected %llu\n",
|
||||
__FILE__, line, address,
|
||||
actual.dlfo_flags, expected->dlfo_flags);
|
||||
}
|
||||
if (actual.dlfo_flags != expected->dlfo_flags)
|
||||
{
|
||||
support_record_failure ();
|
||||
printf ("%s:%d: error: %p: map start is %p, expected %p\n",
|
||||
__FILE__, line,
|
||||
address, actual.dlfo_map_start, expected->dlfo_map_start);
|
||||
}
|
||||
if (actual.dlfo_map_end != expected->dlfo_map_end)
|
||||
{
|
||||
support_record_failure ();
|
||||
printf ("%s:%d: error: %p: map end is %p, expected %p\n",
|
||||
__FILE__, line,
|
||||
address, actual.dlfo_map_end, expected->dlfo_map_end);
|
||||
}
|
||||
if (actual.dlfo_link_map != expected->dlfo_link_map)
|
||||
{
|
||||
support_record_failure ();
|
||||
printf ("%s:%d: error: %p: link map is %p, expected %p\n",
|
||||
__FILE__, line,
|
||||
address, actual.dlfo_link_map, expected->dlfo_link_map);
|
||||
}
|
||||
if (actual.dlfo_eh_frame != expected->dlfo_eh_frame)
|
||||
{
|
||||
support_record_failure ();
|
||||
printf ("%s:%d: error: %p: EH frame is %p, expected %p\n",
|
||||
__FILE__, line,
|
||||
address, actual.dlfo_eh_frame, expected->dlfo_eh_frame);
|
||||
}
|
||||
#if DLFO_STRUCT_HAS_EH_DBASE
|
||||
if (actual.dlfo_eh_dbase != expected->dlfo_eh_dbase)
|
||||
{
|
||||
support_record_failure ();
|
||||
printf ("%s:%d: error: %p: data base is %p, expected %p\n",
|
||||
__FILE__, line,
|
||||
address, actual.dlfo_eh_dbase, expected->dlfo_eh_dbase);
|
||||
}
|
||||
#endif
|
||||
#if DLFO_STRUCT_HAS_EH_COUNT
|
||||
if (actual.dlfo_eh_count != expected->dlfo_eh_count)
|
||||
{
|
||||
support_record_failure ();
|
||||
printf ("%s:%d: error: %p: count is %d, expected %d\n",
|
||||
__FILE__, line,
|
||||
address, actual.dlfo_eh_count, expected->dlfo_eh_count);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Check that unwind data for the main executable and the dynamic
|
||||
linker can be found. */
|
||||
static void
|
||||
check_initial (void)
|
||||
{
|
||||
#ifndef FOR_STATIC
|
||||
/* Avoid direct reference, which could lead to copy relocations. */
|
||||
struct r_debug *debug = xdlsym (NULL, "_r_debug");
|
||||
TEST_VERIFY_EXIT (debug != NULL);
|
||||
char **tzname = xdlsym (NULL, "tzname");
|
||||
|
||||
/* The main executable has an unnamed link map. */
|
||||
struct link_map *main_map = (struct link_map *) debug->r_map;
|
||||
TEST_COMPARE_STRING (main_map->l_name, "");
|
||||
|
||||
/* The link map of the dynamic linker. */
|
||||
struct link_map *rtld_map = xdlopen (LD_SO, RTLD_LAZY | RTLD_NOLOAD);
|
||||
TEST_VERIFY_EXIT (rtld_map != NULL);
|
||||
|
||||
/* The link map of libc.so. */
|
||||
struct link_map *libc_map = xdlopen (LIBC_SO, RTLD_LAZY | RTLD_NOLOAD);
|
||||
TEST_VERIFY_EXIT (libc_map != NULL);
|
||||
|
||||
struct dl_find_object expected;
|
||||
|
||||
/* Data in the main program. */
|
||||
from_map (main_map, &expected);
|
||||
check (&main_program_data, &expected, __LINE__);
|
||||
/* Corner cases for the mapping. */
|
||||
check ((void *) main_map->l_map_start, &expected, __LINE__);
|
||||
check ((void *) (main_map->l_map_end - 1), &expected, __LINE__);
|
||||
|
||||
/* Data in the dynamic loader. */
|
||||
from_map (rtld_map, &expected);
|
||||
check (debug, &expected, __LINE__);
|
||||
check ((void *) rtld_map->l_map_start, &expected, __LINE__);
|
||||
check ((void *) (rtld_map->l_map_end - 1), &expected, __LINE__);
|
||||
|
||||
/* Data in libc. */
|
||||
from_map (libc_map, &expected);
|
||||
check (tzname, &expected, __LINE__);
|
||||
check ((void *) libc_map->l_map_start, &expected, __LINE__);
|
||||
check ((void *) (libc_map->l_map_end - 1), &expected, __LINE__);
|
||||
#endif
|
||||
}
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
{
|
||||
struct dl_find_object dlfo = { };
|
||||
int ret = _dl_find_object (&main_program_data, &dlfo);
|
||||
printf ("info: main program unwind data: %p (%d)\n",
|
||||
dlfo.dlfo_eh_frame, ret);
|
||||
TEST_COMPARE (ret, 0);
|
||||
TEST_VERIFY (dlfo.dlfo_eh_frame != NULL);
|
||||
}
|
||||
|
||||
check_initial ();
|
||||
|
||||
/* dlopen-based test. First an object that can be dlclosed. */
|
||||
struct link_map *mod1 = xdlopen ("tst-dl_find_object-mod1.so", RTLD_NOW);
|
||||
void *mod1_data = xdlsym (mod1, "mod1_data");
|
||||
void *map_start = (void *) mod1->l_map_start;
|
||||
void *map_end = (void *) (mod1->l_map_end - 1);
|
||||
check_initial ();
|
||||
|
||||
struct dl_find_object expected;
|
||||
from_map (mod1, &expected);
|
||||
check (mod1_data, &expected, __LINE__);
|
||||
check (map_start, &expected, __LINE__);
|
||||
check (map_end, &expected, __LINE__);
|
||||
|
||||
/* Unloading must make the data unavailable. */
|
||||
xdlclose (mod1);
|
||||
check_initial ();
|
||||
check (mod1_data, NULL, __LINE__);
|
||||
check (map_start, NULL, __LINE__);
|
||||
check (map_end, NULL, __LINE__);
|
||||
|
||||
/* Now try a NODELETE load. */
|
||||
struct link_map *mod2 = xdlopen ("tst-dl_find_object-mod2.so", RTLD_NOW);
|
||||
void *mod2_data = xdlsym (mod1, "mod2_data");
|
||||
map_start = (void *) mod2->l_map_start;
|
||||
map_end = (void *) (mod2->l_map_end - 1);
|
||||
check_initial ();
|
||||
from_map (mod2, &expected);
|
||||
check (mod2_data, &expected, __LINE__);
|
||||
check (map_start, &expected, __LINE__);
|
||||
check (map_end, &expected, __LINE__);
|
||||
dlclose (mod2); /* Does nothing due to NODELETE. */
|
||||
check_initial ();
|
||||
check (mod2_data, &expected, __LINE__);
|
||||
check (map_start, &expected, __LINE__);
|
||||
check (map_end, &expected, __LINE__);
|
||||
|
||||
/* Now load again the first module. */
|
||||
mod1 = xdlopen ("tst-dl_find_object-mod1.so", RTLD_NOW);
|
||||
mod1_data = xdlsym (mod1, "mod1_data");
|
||||
map_start = (void *) mod1->l_map_start;
|
||||
map_end = (void *) (mod1->l_map_end - 1);
|
||||
check_initial ();
|
||||
from_map (mod1, &expected);
|
||||
check (mod1_data, &expected, __LINE__);
|
||||
check (map_start, &expected, __LINE__);
|
||||
check (map_end, &expected, __LINE__);
|
||||
|
||||
/* Check that _dl_find_object works from a shared object (mostly for
|
||||
static dlopen). */
|
||||
__typeof (_dl_find_object) *find_object
|
||||
= *(void **) xdlsym (mod2, "find_object");
|
||||
struct dl_find_object actual;
|
||||
TEST_COMPARE (find_object (&main_program_data, &actual), 0);
|
||||
check (&main_program_data, &actual, __LINE__); /* Reversed check. */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include <support/test-driver.c>
|
@ -30,6 +30,12 @@ __atomic_wide_counter_load_relaxed (__atomic_wide_counter *c)
|
||||
return atomic_load_relaxed (&c->__value64);
|
||||
}
|
||||
|
||||
static inline uint64_t
|
||||
__atomic_wide_counter_load_acquire (__atomic_wide_counter *c)
|
||||
{
|
||||
return atomic_load_acquire (&c->__value64);
|
||||
}
|
||||
|
||||
static inline uint64_t
|
||||
__atomic_wide_counter_fetch_add_relaxed (__atomic_wide_counter *c,
|
||||
unsigned int val)
|
||||
@ -64,6 +70,14 @@ __atomic_wide_counter_fetch_xor_release (__atomic_wide_counter *c,
|
||||
uint64_t __atomic_wide_counter_load_relaxed (__atomic_wide_counter *c)
|
||||
attribute_hidden;
|
||||
|
||||
static inline uint64_t
|
||||
__atomic_wide_counter_load_acquire (__atomic_wide_counter *c)
|
||||
{
|
||||
uint64_t r = __atomic_wide_counter_load_relaxed (c);
|
||||
atomic_thread_fence_acquire ();
|
||||
return r;
|
||||
}
|
||||
|
||||
uint64_t __atomic_wide_counter_fetch_add_relaxed (__atomic_wide_counter *c,
|
||||
unsigned int op)
|
||||
attribute_hidden;
|
||||
|
1
include/bits/dl_find_object.h
Normal file
1
include/bits/dl_find_object.h
Normal file
@ -0,0 +1 @@
|
||||
#include_next <bits/dl_find_object.h>
|
@ -4,6 +4,8 @@
|
||||
#include <link.h> /* For ElfW. */
|
||||
#include <stdbool.h>
|
||||
|
||||
rtld_hidden_proto (_dl_find_object)
|
||||
|
||||
/* Internally used flag. */
|
||||
#define __RTLD_DLOPEN 0x80000000
|
||||
#define __RTLD_SPROF 0x40000000
|
||||
|
@ -211,6 +211,9 @@ struct link_map
|
||||
freed, ie. not allocated with
|
||||
the dummy malloc in ld.so. */
|
||||
unsigned int l_ld_readonly:1; /* Nonzero if dynamic section is readonly. */
|
||||
unsigned int l_find_object_processed:1; /* Zero if _dl_find_object_update
|
||||
needs to process this
|
||||
lt_library map. */
|
||||
|
||||
/* NODELETE status of the map. Only valid for maps of type
|
||||
lt_loaded. Lazy binding sets l_nodelete_active directly,
|
||||
|
@ -39,7 +39,7 @@ chapters = $(addsuffix .texi, \
|
||||
pipe socket terminal syslog math arith time \
|
||||
resource setjmp signal startup process ipc job \
|
||||
nss users sysinfo conf crypt debug threads \
|
||||
probes tunables)
|
||||
dynlink probes tunables)
|
||||
appendices = lang.texi header.texi install.texi maint.texi platform.texi \
|
||||
contrib.texi
|
||||
licenses = freemanuals.texi lgpl-2.1.texi fdl-1.3.texi
|
||||
|
137
manual/dynlink.texi
Normal file
137
manual/dynlink.texi
Normal file
@ -0,0 +1,137 @@
|
||||
@node Dynamic Linker
|
||||
@c @node Dynamic Linker, Internal Probes, Threads, Top
|
||||
@c %MENU% Loading programs and shared objects.
|
||||
@chapter Dynamic Linker
|
||||
@cindex dynamic linker
|
||||
@cindex dynamic loader
|
||||
|
||||
The @dfn{dynamic linker} is responsible for loading dynamically linked
|
||||
programs and their dependencies (in the form of shared objects). The
|
||||
dynamic linker in @theglibc{} also supports loading shared objects (such
|
||||
as plugins) later at run time.
|
||||
|
||||
Dynamic linkers are sometimes called @dfn{dynamic loaders}.
|
||||
|
||||
@menu
|
||||
* Dynamic Linker Introspection:: Interfaces for querying mapping information.
|
||||
@end menu
|
||||
|
||||
@node Dynamic Linker Introspection
|
||||
@section Dynamic Linker Introspection
|
||||
|
||||
@Theglibc{} provides various functions for querying information from the
|
||||
dynamic linker.
|
||||
|
||||
@deftp {Data Type} {struct dl_find_object}
|
||||
@standards{GNU, dlfcn.h}
|
||||
This structure contains information about a main program or loaded
|
||||
object. The @code{_dl_find_object} function uses it to return
|
||||
result data to the caller.
|
||||
|
||||
@table @code
|
||||
@item unsigned long long int dlfo_flags
|
||||
Currently unused and always 0.
|
||||
|
||||
@item void *dlfo_map_start
|
||||
The start address of the inspected mapping. This information comes from
|
||||
the program header, so it follows its convention, and the address is not
|
||||
necessarily page-aligned.
|
||||
|
||||
@item void *dlfo_map_end
|
||||
The end address of the mapping.
|
||||
|
||||
@item struct link_map *dlf_link_map
|
||||
This member contains a pointer to the link map of the object.
|
||||
|
||||
@item struct link_map *dlf_link_map
|
||||
This member contains a pointer to the exception handling data of the
|
||||
object. See @code{DLFO_EH_SEGMENT_TYPE} below.
|
||||
|
||||
@end table
|
||||
|
||||
This structure is a GNU extension.
|
||||
@end deftp
|
||||
|
||||
@deftypevr Macro int DLFO_STRUCT_HAS_EH_DBASE
|
||||
@standards{GNU, dlfcn.h}
|
||||
On most targets, this macro is defined as @code{0}. If it is defined to
|
||||
@code{1}, @code{struct dl_find_object} contains an additional member
|
||||
@code{dlfo_eh_dbase} of type @code{void *}. It is the base address for
|
||||
@code{DW_EH_PE_datarel} DWARF encodings to this location.
|
||||
|
||||
This macro is a GNU extension.
|
||||
@end deftypevr
|
||||
|
||||
@deftypevr Macro int DLFO_STRUCT_HAS_EH_COUNT
|
||||
@standards{GNU, dlfcn.h}
|
||||
On most targets, this macro is defined as @code{0}. If it is defined to
|
||||
@code{1}, @code{struct dl_find_object} contains an additional member
|
||||
@code{dlfo_eh_count} of type @code{int}. It is the number of exception
|
||||
handling entries in the EH frame segment identified by the
|
||||
@code{dlfo_eh_frame} member.
|
||||
|
||||
This macro is a GNU extension.
|
||||
@end deftypevr
|
||||
|
||||
@deftypevr Macro int DLFO_EH_SEGMENT_TYPE
|
||||
@standards{GNU, dlfcn.h}
|
||||
On targets using DWARF-based exception unwinding, this macro expands to
|
||||
@code{PT_GNU_EH_FRAME}. This indicates that @code{dlfo_eh_frame} in
|
||||
@code{struct dl_find_object} points to the @code{PT_GNU_EH_FRAME}
|
||||
segment of the object. On targets that use other unwinding formats, the
|
||||
macro expands to the program header type for the unwinding data.
|
||||
|
||||
This macro is a GNU extension.
|
||||
@end deftypevr
|
||||
|
||||
@deftypefun {int} _dl_find_object (void *@var{address}, struct dl_find_object *@var{result})
|
||||
@standards{GNU, dlfcn.h}
|
||||
@safety{@mtsafe{}@assafe{}@acsafe{}}
|
||||
On success, this function returns 0 and writes about the object
|
||||
surrounding the address to @code{*@var{result}}. On failure, -1 is
|
||||
returned.
|
||||
|
||||
The @var{address} can be a code address or data address. On
|
||||
architectures using function descriptors, no attempt is made to decode
|
||||
the function descriptor. Depending on how these descriptors are
|
||||
implemented, @code{_dl_find_object} may return the object that defines
|
||||
the function descriptor (and not the object that contains the code
|
||||
implementing the function), or fail to find any object at all.
|
||||
|
||||
On success @var{address} is greater than or equal to
|
||||
@code{@var{result}->dlfo_map_start} and less than
|
||||
@code{@var{result}->dlfo_map_end}, that is, the supplied code address is
|
||||
located within the reported mapping.
|
||||
|
||||
This function returns a pointer to the unwinding information for the
|
||||
object that contains the program code @var{address} in
|
||||
@code{@var{result}->dlfo_eh_frame}. If the platform uses DWARF
|
||||
unwinding information, this is the in-memory address of the
|
||||
@code{PT_GNU_EH_FRAME} segment. See @code{DLFO_EH_SEGMENT_TYPE} above.
|
||||
In case @var{address} resides in an object that lacks unwinding information,
|
||||
the function still returns 0, but sets @code{@var{result}->dlfo_eh_frame}
|
||||
to a null pointer.
|
||||
|
||||
@code{_dl_find_object} itself is thread-safe. However, if the
|
||||
application invokes @code{dlclose} for the object that contains
|
||||
@var{address} concurrently with @code{_dl_find_object} or after the call
|
||||
returns, accessing the unwinding data for that object or the link map
|
||||
(through @code{@var{result}->dlfo_link_map}) is not safe. Therefore, the
|
||||
application needs to ensure by other means (e.g., by convention) that
|
||||
@var{address} remains a valid code address while the unwinding
|
||||
information is processed.
|
||||
|
||||
This function is a GNU extension.
|
||||
@end deftypefun
|
||||
|
||||
|
||||
@c FIXME these are undocumented:
|
||||
@c dladdr
|
||||
@c dladdr1
|
||||
@c dlclose
|
||||
@c dlerror
|
||||
@c dlinfo
|
||||
@c dlmopen
|
||||
@c dlopen
|
||||
@c dlsym
|
||||
@c dlvsym
|
@ -1,10 +0,0 @@
|
||||
@c FIXME these are undocumented:
|
||||
@c dladdr
|
||||
@c dladdr1
|
||||
@c dlclose
|
||||
@c dlerror
|
||||
@c dlinfo
|
||||
@c dlmopen
|
||||
@c dlopen
|
||||
@c dlsym
|
||||
@c dlvsym
|
@ -1,5 +1,5 @@
|
||||
@node Internal Probes
|
||||
@c @node Internal Probes, Tunables, Threads, Top
|
||||
@c @node Internal Probes, Tunables, Dynamic Linker, Top
|
||||
@c %MENU% Probes to monitor libc internal behavior
|
||||
@chapter Internal probes
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
@node Threads
|
||||
@c @node Threads, Internal Probes, Debugging Support, Top
|
||||
@c @node Threads, Dynamic Linker, Debugging Support, Top
|
||||
@c %MENU% Functions, constants, and data types for working with threads
|
||||
@chapter Threads
|
||||
@cindex threads
|
||||
|
25
sysdeps/arm/bits/dl_find_object.h
Normal file
25
sysdeps/arm/bits/dl_find_object.h
Normal file
@ -0,0 +1,25 @@
|
||||
/* arm definitions for finding objects.
|
||||
Copyright (C) 2021 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
|
||||
<https://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef _DLFCN_H
|
||||
# error "Never use <bits/dl_find_object.h> directly; include <dlfcn.h> instead."
|
||||
#endif
|
||||
|
||||
#define DLFO_STRUCT_HAS_EH_DBASE 0
|
||||
#define DLFO_STRUCT_HAS_EH_COUNT 1
|
||||
#define DLFO_EH_SEGMENT_TYPE PT_ARM_EXIDX
|
@ -717,6 +717,11 @@ struct rtld_global_ro
|
||||
/* Called from __libc_shared to deallocate malloc'ed memory. */
|
||||
void (*_dl_libc_freeres) (void);
|
||||
|
||||
/* Implementation of _dl_find_object. The public entry point is in
|
||||
libc, and this is patched by __rtld_static_init to support static
|
||||
dlopen. */
|
||||
int (*_dl_find_object) (void *, struct dl_find_object *);
|
||||
|
||||
#ifdef HAVE_DL_DISCOVER_OSVERSION
|
||||
int (*_dl_discover_osversion) (void);
|
||||
#endif
|
||||
|
@ -2286,6 +2286,7 @@ GLIBC_2.34 shm_open F
|
||||
GLIBC_2.34 shm_unlink F
|
||||
GLIBC_2.34 timespec_getres F
|
||||
GLIBC_2.35 __memcmpeq F
|
||||
GLIBC_2.35 _dl_find_object F
|
||||
GLIBC_2.35 close_range F
|
||||
GLIBC_2.4 __confstr_chk F
|
||||
GLIBC_2.4 __fgets_chk F
|
||||
|
25
sysdeps/nios2/bits/dl_find_object.h
Normal file
25
sysdeps/nios2/bits/dl_find_object.h
Normal file
@ -0,0 +1,25 @@
|
||||
/* nios2 definitions for finding objects.
|
||||
Copyright (C) 2021 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
|
||||
<https://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef _DLFCN_H
|
||||
# error "Never use <bits/dl_find_object.h> directly; include <dlfcn.h> instead."
|
||||
#endif
|
||||
|
||||
#define DLFO_STRUCT_HAS_EH_DBASE 1
|
||||
#define DLFO_STRUCT_HAS_EH_COUNT 0
|
||||
#define DLFO_EH_SEGMENT_TYPE PT_GNU_EH_FRAME
|
@ -2613,3 +2613,4 @@ GLIBC_2.34 tss_delete F
|
||||
GLIBC_2.34 tss_get F
|
||||
GLIBC_2.34 tss_set F
|
||||
GLIBC_2.35 __memcmpeq F
|
||||
GLIBC_2.35 _dl_find_object F
|
||||
|
@ -2710,6 +2710,7 @@ GLIBC_2.34 tss_delete F
|
||||
GLIBC_2.34 tss_get F
|
||||
GLIBC_2.34 tss_set F
|
||||
GLIBC_2.35 __memcmpeq F
|
||||
GLIBC_2.35 _dl_find_object F
|
||||
GLIBC_2.4 _IO_fprintf F
|
||||
GLIBC_2.4 _IO_printf F
|
||||
GLIBC_2.4 _IO_sprintf F
|
||||
|
@ -2374,3 +2374,4 @@ GLIBC_2.34 tss_delete F
|
||||
GLIBC_2.34 tss_get F
|
||||
GLIBC_2.34 tss_set F
|
||||
GLIBC_2.35 __memcmpeq F
|
||||
GLIBC_2.35 _dl_find_object F
|
||||
|
@ -492,6 +492,7 @@ GLIBC_2.34 tss_delete F
|
||||
GLIBC_2.34 tss_get F
|
||||
GLIBC_2.34 tss_set F
|
||||
GLIBC_2.35 __memcmpeq F
|
||||
GLIBC_2.35 _dl_find_object F
|
||||
GLIBC_2.4 _Exit F
|
||||
GLIBC_2.4 _IO_2_1_stderr_ D 0xa0
|
||||
GLIBC_2.4 _IO_2_1_stdin_ D 0xa0
|
||||
|
@ -489,6 +489,7 @@ GLIBC_2.34 tss_delete F
|
||||
GLIBC_2.34 tss_get F
|
||||
GLIBC_2.34 tss_set F
|
||||
GLIBC_2.35 __memcmpeq F
|
||||
GLIBC_2.35 _dl_find_object F
|
||||
GLIBC_2.4 _Exit F
|
||||
GLIBC_2.4 _IO_2_1_stderr_ D 0xa0
|
||||
GLIBC_2.4 _IO_2_1_stdin_ D 0xa0
|
||||
|
@ -2648,3 +2648,4 @@ GLIBC_2.34 tss_delete F
|
||||
GLIBC_2.34 tss_get F
|
||||
GLIBC_2.34 tss_set F
|
||||
GLIBC_2.35 __memcmpeq F
|
||||
GLIBC_2.35 _dl_find_object F
|
||||
|
@ -2597,6 +2597,7 @@ GLIBC_2.34 tss_delete F
|
||||
GLIBC_2.34 tss_get F
|
||||
GLIBC_2.34 tss_set F
|
||||
GLIBC_2.35 __memcmpeq F
|
||||
GLIBC_2.35 _dl_find_object F
|
||||
GLIBC_2.4 __confstr_chk F
|
||||
GLIBC_2.4 __fgets_chk F
|
||||
GLIBC_2.4 __fgets_unlocked_chk F
|
||||
|
@ -2781,6 +2781,7 @@ GLIBC_2.34 tss_delete F
|
||||
GLIBC_2.34 tss_get F
|
||||
GLIBC_2.34 tss_set F
|
||||
GLIBC_2.35 __memcmpeq F
|
||||
GLIBC_2.35 _dl_find_object F
|
||||
GLIBC_2.4 __confstr_chk F
|
||||
GLIBC_2.4 __fgets_chk F
|
||||
GLIBC_2.4 __fgets_unlocked_chk F
|
||||
|
@ -2548,6 +2548,7 @@ GLIBC_2.34 tss_delete F
|
||||
GLIBC_2.34 tss_get F
|
||||
GLIBC_2.34 tss_set F
|
||||
GLIBC_2.35 __memcmpeq F
|
||||
GLIBC_2.35 _dl_find_object F
|
||||
GLIBC_2.4 __confstr_chk F
|
||||
GLIBC_2.4 __fgets_chk F
|
||||
GLIBC_2.4 __fgets_unlocked_chk F
|
||||
|
@ -493,6 +493,7 @@ GLIBC_2.34 tss_delete F
|
||||
GLIBC_2.34 tss_get F
|
||||
GLIBC_2.34 tss_set F
|
||||
GLIBC_2.35 __memcmpeq F
|
||||
GLIBC_2.35 _dl_find_object F
|
||||
GLIBC_2.4 _Exit F
|
||||
GLIBC_2.4 _IO_2_1_stderr_ D 0x98
|
||||
GLIBC_2.4 _IO_2_1_stdin_ D 0x98
|
||||
|
@ -2724,6 +2724,7 @@ GLIBC_2.34 tss_delete F
|
||||
GLIBC_2.34 tss_get F
|
||||
GLIBC_2.34 tss_set F
|
||||
GLIBC_2.35 __memcmpeq F
|
||||
GLIBC_2.35 _dl_find_object F
|
||||
GLIBC_2.4 __confstr_chk F
|
||||
GLIBC_2.4 __fgets_chk F
|
||||
GLIBC_2.4 __fgets_unlocked_chk F
|
||||
|
@ -2697,3 +2697,4 @@ GLIBC_2.34 tss_delete F
|
||||
GLIBC_2.34 tss_get F
|
||||
GLIBC_2.34 tss_set F
|
||||
GLIBC_2.35 __memcmpeq F
|
||||
GLIBC_2.35 _dl_find_object F
|
||||
|
@ -2694,3 +2694,4 @@ GLIBC_2.34 tss_delete F
|
||||
GLIBC_2.34 tss_get F
|
||||
GLIBC_2.34 tss_set F
|
||||
GLIBC_2.35 __memcmpeq F
|
||||
GLIBC_2.35 _dl_find_object F
|
||||
|
@ -2689,6 +2689,7 @@ GLIBC_2.34 tss_delete F
|
||||
GLIBC_2.34 tss_get F
|
||||
GLIBC_2.34 tss_set F
|
||||
GLIBC_2.35 __memcmpeq F
|
||||
GLIBC_2.35 _dl_find_object F
|
||||
GLIBC_2.4 __confstr_chk F
|
||||
GLIBC_2.4 __fgets_chk F
|
||||
GLIBC_2.4 __fgets_unlocked_chk F
|
||||
|
@ -2687,6 +2687,7 @@ GLIBC_2.34 tss_delete F
|
||||
GLIBC_2.34 tss_get F
|
||||
GLIBC_2.34 tss_set F
|
||||
GLIBC_2.35 __memcmpeq F
|
||||
GLIBC_2.35 _dl_find_object F
|
||||
GLIBC_2.4 __confstr_chk F
|
||||
GLIBC_2.4 __fgets_chk F
|
||||
GLIBC_2.4 __fgets_unlocked_chk F
|
||||
|
@ -2695,6 +2695,7 @@ GLIBC_2.34 tss_delete F
|
||||
GLIBC_2.34 tss_get F
|
||||
GLIBC_2.34 tss_set F
|
||||
GLIBC_2.35 __memcmpeq F
|
||||
GLIBC_2.35 _dl_find_object F
|
||||
GLIBC_2.4 __confstr_chk F
|
||||
GLIBC_2.4 __fgets_chk F
|
||||
GLIBC_2.4 __fgets_unlocked_chk F
|
||||
|
@ -2599,6 +2599,7 @@ GLIBC_2.34 tss_delete F
|
||||
GLIBC_2.34 tss_get F
|
||||
GLIBC_2.34 tss_set F
|
||||
GLIBC_2.35 __memcmpeq F
|
||||
GLIBC_2.35 _dl_find_object F
|
||||
GLIBC_2.4 __confstr_chk F
|
||||
GLIBC_2.4 __fgets_chk F
|
||||
GLIBC_2.4 __fgets_unlocked_chk F
|
||||
|
@ -2736,3 +2736,4 @@ GLIBC_2.34 tss_delete F
|
||||
GLIBC_2.34 tss_get F
|
||||
GLIBC_2.34 tss_set F
|
||||
GLIBC_2.35 __memcmpeq F
|
||||
GLIBC_2.35 _dl_find_object F
|
||||
|
@ -2751,6 +2751,7 @@ GLIBC_2.34 tss_delete F
|
||||
GLIBC_2.34 tss_get F
|
||||
GLIBC_2.34 tss_set F
|
||||
GLIBC_2.35 __memcmpeq F
|
||||
GLIBC_2.35 _dl_find_object F
|
||||
GLIBC_2.4 _IO_fprintf F
|
||||
GLIBC_2.4 _IO_printf F
|
||||
GLIBC_2.4 _IO_sprintf F
|
||||
|
@ -2784,6 +2784,7 @@ GLIBC_2.34 tss_delete F
|
||||
GLIBC_2.34 tss_get F
|
||||
GLIBC_2.34 tss_set F
|
||||
GLIBC_2.35 __memcmpeq F
|
||||
GLIBC_2.35 _dl_find_object F
|
||||
GLIBC_2.4 _IO_fprintf F
|
||||
GLIBC_2.4 _IO_printf F
|
||||
GLIBC_2.4 _IO_sprintf F
|
||||
|
@ -2507,6 +2507,7 @@ GLIBC_2.34 tss_delete F
|
||||
GLIBC_2.34 tss_get F
|
||||
GLIBC_2.34 tss_set F
|
||||
GLIBC_2.35 __memcmpeq F
|
||||
GLIBC_2.35 _dl_find_object F
|
||||
GLIBC_2.4 _IO_fprintf F
|
||||
GLIBC_2.4 _IO_printf F
|
||||
GLIBC_2.4 _IO_sprintf F
|
||||
|
@ -2809,3 +2809,4 @@ GLIBC_2.34 tss_delete F
|
||||
GLIBC_2.34 tss_get F
|
||||
GLIBC_2.34 tss_set F
|
||||
GLIBC_2.35 __memcmpeq F
|
||||
GLIBC_2.35 _dl_find_object F
|
||||
|
@ -2376,3 +2376,4 @@ GLIBC_2.34 tss_delete F
|
||||
GLIBC_2.34 tss_get F
|
||||
GLIBC_2.34 tss_set F
|
||||
GLIBC_2.35 __memcmpeq F
|
||||
GLIBC_2.35 _dl_find_object F
|
||||
|
@ -2576,3 +2576,4 @@ GLIBC_2.34 tss_delete F
|
||||
GLIBC_2.34 tss_get F
|
||||
GLIBC_2.34 tss_set F
|
||||
GLIBC_2.35 __memcmpeq F
|
||||
GLIBC_2.35 _dl_find_object F
|
||||
|
@ -2749,6 +2749,7 @@ GLIBC_2.34 tss_delete F
|
||||
GLIBC_2.34 tss_get F
|
||||
GLIBC_2.34 tss_set F
|
||||
GLIBC_2.35 __memcmpeq F
|
||||
GLIBC_2.35 _dl_find_object F
|
||||
GLIBC_2.4 _IO_fprintf F
|
||||
GLIBC_2.4 _IO_printf F
|
||||
GLIBC_2.4 _IO_sprintf F
|
||||
|
@ -2544,6 +2544,7 @@ GLIBC_2.34 tss_delete F
|
||||
GLIBC_2.34 tss_get F
|
||||
GLIBC_2.34 tss_set F
|
||||
GLIBC_2.35 __memcmpeq F
|
||||
GLIBC_2.35 _dl_find_object F
|
||||
GLIBC_2.4 _IO_fprintf F
|
||||
GLIBC_2.4 _IO_printf F
|
||||
GLIBC_2.4 _IO_sprintf F
|
||||
|
@ -2604,6 +2604,7 @@ GLIBC_2.34 tss_delete F
|
||||
GLIBC_2.34 tss_get F
|
||||
GLIBC_2.34 tss_set F
|
||||
GLIBC_2.35 __memcmpeq F
|
||||
GLIBC_2.35 _dl_find_object F
|
||||
GLIBC_2.4 __confstr_chk F
|
||||
GLIBC_2.4 __fgets_chk F
|
||||
GLIBC_2.4 __fgets_unlocked_chk F
|
||||
|
@ -2601,6 +2601,7 @@ GLIBC_2.34 tss_delete F
|
||||
GLIBC_2.34 tss_get F
|
||||
GLIBC_2.34 tss_set F
|
||||
GLIBC_2.35 __memcmpeq F
|
||||
GLIBC_2.35 _dl_find_object F
|
||||
GLIBC_2.4 __confstr_chk F
|
||||
GLIBC_2.4 __fgets_chk F
|
||||
GLIBC_2.4 __fgets_unlocked_chk F
|
||||
|
@ -2744,6 +2744,7 @@ GLIBC_2.34 tss_delete F
|
||||
GLIBC_2.34 tss_get F
|
||||
GLIBC_2.34 tss_set F
|
||||
GLIBC_2.35 __memcmpeq F
|
||||
GLIBC_2.35 _dl_find_object F
|
||||
GLIBC_2.4 _IO_fprintf F
|
||||
GLIBC_2.4 _IO_printf F
|
||||
GLIBC_2.4 _IO_sprintf F
|
||||
|
@ -2571,6 +2571,7 @@ GLIBC_2.34 tss_delete F
|
||||
GLIBC_2.34 tss_get F
|
||||
GLIBC_2.34 tss_set F
|
||||
GLIBC_2.35 __memcmpeq F
|
||||
GLIBC_2.35 _dl_find_object F
|
||||
GLIBC_2.4 __confstr_chk F
|
||||
GLIBC_2.4 __fgets_chk F
|
||||
GLIBC_2.4 __fgets_unlocked_chk F
|
||||
|
@ -2522,6 +2522,7 @@ GLIBC_2.34 tss_delete F
|
||||
GLIBC_2.34 tss_get F
|
||||
GLIBC_2.34 tss_set F
|
||||
GLIBC_2.35 __memcmpeq F
|
||||
GLIBC_2.35 _dl_find_object F
|
||||
GLIBC_2.4 __confstr_chk F
|
||||
GLIBC_2.4 __fgets_chk F
|
||||
GLIBC_2.4 __fgets_unlocked_chk F
|
||||
|
@ -2628,3 +2628,4 @@ GLIBC_2.34 tss_delete F
|
||||
GLIBC_2.34 tss_get F
|
||||
GLIBC_2.34 tss_set F
|
||||
GLIBC_2.35 __memcmpeq F
|
||||
GLIBC_2.35 _dl_find_object F
|
||||
|
29
sysdeps/x86/bits/dl_find_object.h
Normal file
29
sysdeps/x86/bits/dl_find_object.h
Normal file
@ -0,0 +1,29 @@
|
||||
/* x86 definitions for finding objects.
|
||||
Copyright (C) 2021 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
|
||||
<https://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef _DLFCN_H
|
||||
# error "Never use <bits/dl_find_object.h> directly; include <dlfcn.h> instead."
|
||||
#endif
|
||||
|
||||
#ifdef __x86_64__
|
||||
# define DLFO_STRUCT_HAS_EH_DBASE 0
|
||||
#else
|
||||
# define DLFO_STRUCT_HAS_EH_DBASE 1
|
||||
#endif
|
||||
#define DLFO_STRUCT_HAS_EH_COUNT 0
|
||||
#define DLFO_EH_SEGMENT_TYPE PT_GNU_EH_FRAME
|
Loading…
Reference in New Issue
Block a user