Browse Source

libsanitizer: ldso: track _dl_load_adds/_dl_load_subs counters

glibc populates dl_phdr_info::dlpi_adds and dlpi_subs with monotonic
counters of object load / unload events.  Callers of dl_iterate_phdr
use these to cache the iteration result across calls ("if both
counters are unchanged, the loaded-module list is unchanged too").
libgcc_s, libstdc++ exception unwinding, valgrind and similar tools
depend on this contract.

uClibc-ng never tracked these counters, so patch 0001 left them at
zero -- functionally fine for libsanitizer (which does not read
them on Linux) but degenerate for the rest of the ecosystem.

Add two unsigned-long-long globals next to _dl_loaded_modules in
dl-symbols.c.  Increment _dl_load_adds at the end of
_dl_add_elf_hash_table() (the central spot where every loaded
object hits the chain, both at startup and via dlopen) and
_dl_load_subs in do_dlclose() right after the chain unlink.  Wire
the values into dl_iterate_phdr's per-module callback.

For statically-linked programs the counters live in dl-symbols.c
included via libc/misc/elf/dl-core.c and stay at zero, since static
binaries never load or unload anything -- which matches the glibc
behaviour.

Signed-off-by: Ramin Moussavi <ramin.moussavi@yacoub.de>
Ramin Moussavi 3 weeks ago
parent
commit
6acef6952a
5 changed files with 14 additions and 2 deletions
  1. 2 0
      ldso/include/dl-hash.h
  2. 1 0
      ldso/ldso/dl-hash.c
  3. 8 0
      ldso/ldso/dl-symbols.c
  4. 1 0
      ldso/libdl/libdl.c
  5. 2 2
      libc/misc/elf/dl-iterate-phdr.c

+ 2 - 0
ldso/include/dl-hash.h

@@ -158,6 +158,8 @@ struct elf_resolve {
 extern struct dyn_elf     * _dl_symbol_tables;
 extern struct elf_resolve * _dl_loaded_modules;
 extern struct dyn_elf     * _dl_handles;
+extern unsigned long long   _dl_load_adds;
+extern unsigned long long   _dl_load_subs;
 
 extern struct elf_resolve * _dl_add_elf_hash_table(const char * libname,
 	DL_LOADADDR_TYPE loadaddr, unsigned long * dynamic_info,

+ 1 - 0
ldso/ldso/dl-hash.c

@@ -156,6 +156,7 @@ struct elf_resolve *_dl_add_elf_hash_table(const char *libname,
 	tpnt->loadaddr = loadaddr;
 	for (i = 0; i < DYNAMIC_SIZE; i++)
 		tpnt->dynamic_info[i] = dynamic_info[i];
+	++_dl_load_adds;
 	return tpnt;
 }
 

+ 8 - 0
ldso/ldso/dl-symbols.c

@@ -19,3 +19,11 @@
 
 struct elf_resolve *_dl_loaded_modules = NULL;
 
+/* Monotonic counters of load/unload events.  Incremented by the
+   dynamic loader whenever an object is added to or removed from
+   _dl_loaded_modules.  Exposed via dl_phdr_info::dlpi_adds /
+   dlpi_subs to let dl_iterate_phdr callers cache results across
+   invocations -- matches the glibc contract.  */
+unsigned long long _dl_load_adds = 0;
+unsigned long long _dl_load_subs = 0;
+

+ 1 - 0
ldso/libdl/libdl.c

@@ -1000,6 +1000,7 @@ static int do_dlclose(void *vhandle, int need_fini)
 					}
 				}
 			}
+			++_dl_load_subs;
 
 			/* Next, remove tpnt from the global symbol table list */
 			if (_dl_symbol_tables) {

+ 2 - 2
libc/misc/elf/dl-iterate-phdr.c

@@ -31,8 +31,8 @@ __dl_iterate_phdr (int (*callback) (struct dl_phdr_info *info, size_t size, void
 		info.dlpi_name = l->libname;
 		info.dlpi_phdr = l->ppnt;
 		info.dlpi_phnum = l->n_phent;
-		info.dlpi_adds = 0;
-		info.dlpi_subs = 0;
+		info.dlpi_adds = _dl_load_adds;
+		info.dlpi_subs = _dl_load_subs;
 #if defined(USE_TLS) && USE_TLS
 		info.dlpi_tls_modid = l->l_tls_modid;
 #else