All of lore.kernel.org
 help / color / mirror / Atom feed
* [libevl][PATCH] lib/vdso: Update vdso parser to latest Linux version
@ 2025-09-13  9:47 Jan Kiszka
  2025-09-13 14:25 ` Philippe Gerum
  0 siblings, 1 reply; 2+ messages in thread
From: Jan Kiszka @ 2025-09-13  9:47 UTC (permalink / raw)
  To: Xenomai, Philippe Gerum

From: Jan Kiszka <jan.kiszka@siemens.com>

This widely aligns our copy of
linux/tools/testing/selftests/vDSO/parse_vdso.c with the version found
in 6.17-rc5+. We were primarily missing support for DT_GNU_HASH. This
becomes noticeable with the Debian 13 toolchain on arm64 which caused
DT_HASH to become absent from the vDSO.

Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
---
 lib/parse_vdso.c | 157 ++++++++++++++++++++++++++++++-----------------
 1 file changed, 99 insertions(+), 58 deletions(-)

diff --git a/lib/parse_vdso.c b/lib/parse_vdso.c
index fe6c77b..d11f1ba 100644
--- a/lib/parse_vdso.c
+++ b/lib/parse_vdso.c
@@ -28,26 +28,6 @@
 #include "parse_vdso.h"
 #include "internal.h"
 
-/*
- * To use this vDSO parser, first call one of the vdso_init_* functions.
- * If you've already parsed auxv, then pass the value of AT_SYSINFO_EHDR
- * to vdso_init_from_sysinfo_ehdr.  Otherwise pass auxv to vdso_init_from_auxv.
- * Then call lookup_vdso for each symbol you want.  For example, to look up
- * gettimeofday on x86_64, use:
- *
- *     <some pointer> = lookup_vdso("LINUX_2.6", "gettimeofday");
- * or
- *     <some pointer> = lookup_vdso("LINUX_2.6", "__vdso_gettimeofday");
- *
- * lookup_vdso will return 0 if the symbol doesn't exist or if the init function
- * failed or was not called.  lookup_vdso is a little slow, so its return value
- * should be cached.
- *
- * lookup_vdso is threadsafe; the init functions are not.
- */
-
-
-/* And here's the code. */
 #ifndef ELF_BITS
 # if ULONG_MAX > 0xffffffffUL
 #  define ELF_BITS 64
@@ -60,6 +40,8 @@
 #define ELF_BITS_XFORM(bits, x) ELF_BITS_XFORM2(bits, x)
 #define ELF(x) ELF_BITS_XFORM(ELF_BITS, x)
 
+#define ELF_HASH_ENTRY ELF(Word)
+
 static struct vdso_info
 {
 	bool valid;
@@ -71,29 +53,46 @@ static struct vdso_info
 	/* Symbol table */
 	ELF(Sym) *symtab;
 	const char *symstrings;
-	ELF(Word) *bucket, *chain;
-	ELF(Word) nbucket, nchain;
+	ELF(Word) *gnu_hash, *gnu_bucket;
+	ELF_HASH_ENTRY *bucket, *chain;
+	ELF_HASH_ENTRY nbucket, nchain;
 
 	/* Version table */
 	ELF(Versym) *versym;
 	ELF(Verdef) *verdef;
 } vdso_info;
 
-/* Straight from the ELF specification. */
+/*
+ * Straight from the ELF specification...and then tweaked slightly, in order to
+ * avoid a few clang warnings.
+ */
 static unsigned long elf_hash(const char *name)
 {
 	unsigned long h = 0, g;
-	while (*name)
+	const unsigned char *uch_name = (const unsigned char *)name;
+
+	while (*uch_name)
 	{
-		h = (h << 4) + *name++;
-		if ((g = h & 0xf0000000))
+		h = (h << 4) + *uch_name++;
+		g = h & 0xf0000000;
+		if (g)
 			h ^= g >> 24;
 		h &= ~g;
 	}
 	return h;
 }
 
-static __attribute__((unused)) void vdso_init_from_sysinfo_ehdr(uintptr_t base)
+static uint32_t gnu_hash(const char *name)
+{
+	const unsigned char *s = (void *)name;
+	uint32_t h = 5381;
+
+	for (; *s; s++)
+		h += h * 32 + *s;
+	return h;
+}
+
+static void vdso_init_from_sysinfo_ehdr(uintptr_t base)
 {
 	size_t i;
 	bool found_vaddr = false;
@@ -133,8 +132,9 @@ static __attribute__((unused)) void vdso_init_from_sysinfo_ehdr(uintptr_t base)
 	/*
 	 * Fish out the useful bits of the dynamic table.
 	 */
-	ELF(Word) *hash = 0;
+	ELF_HASH_ENTRY *hash = 0;
 	vdso_info.symstrings = 0;
+	vdso_info.gnu_hash = 0;
 	vdso_info.symtab = 0;
 	vdso_info.versym = 0;
 	vdso_info.verdef = 0;
@@ -151,10 +151,15 @@ static __attribute__((unused)) void vdso_init_from_sysinfo_ehdr(uintptr_t base)
 				 + vdso_info.load_offset);
 			break;
 		case DT_HASH:
-			hash = (ELF(Word) *)
+			hash = (ELF_HASH_ENTRY *)
 				((uintptr_t)dyn[i].d_un.d_ptr
 				 + vdso_info.load_offset);
 			break;
+		case DT_GNU_HASH:
+			vdso_info.gnu_hash =
+				(ELF(Word) *)((uintptr_t)dyn[i].d_un.d_ptr +
+					      vdso_info.load_offset);
+			break;
 		case DT_VERSYM:
 			vdso_info.versym = (ELF(Versym) *)
 				((uintptr_t)dyn[i].d_un.d_ptr
@@ -167,17 +172,27 @@ static __attribute__((unused)) void vdso_init_from_sysinfo_ehdr(uintptr_t base)
 			break;
 		}
 	}
-	if (!vdso_info.symstrings || !vdso_info.symtab || !hash)
+	if (!vdso_info.symstrings || !vdso_info.symtab ||
+	    (!hash && !vdso_info.gnu_hash))
 		return;  /* Failed */
 
 	if (!vdso_info.verdef)
 		vdso_info.versym = 0;
 
 	/* Parse the hash table header. */
-	vdso_info.nbucket = hash[0];
-	vdso_info.nchain = hash[1];
-	vdso_info.bucket = &hash[2];
-	vdso_info.chain = &hash[vdso_info.nbucket + 2];
+	if (vdso_info.gnu_hash) {
+		vdso_info.nbucket = vdso_info.gnu_hash[0];
+		/* The bucket array is located after the header (4 uint32) and the bloom
+		 * filter (size_t array of gnu_hash[2] elements).
+		 */
+		vdso_info.gnu_bucket = vdso_info.gnu_hash + 4 +
+				       sizeof(size_t) / 4 * vdso_info.gnu_hash[2];
+	} else {
+		vdso_info.nbucket = hash[0];
+		vdso_info.nchain = hash[1];
+		vdso_info.bucket = &hash[2];
+		vdso_info.chain = &hash[vdso_info.nbucket + 2];
+	}
 
 	/* That's all we need. */
 	vdso_info.valid = true;
@@ -221,37 +236,63 @@ static bool vdso_match_version(ELF(Versym) ver,
 		&& !strcmp(name, vdso_info.symstrings + aux->vda_name);
 }
 
+static bool check_sym(ELF(Sym) *sym, ELF(Word) i, const char *name,
+		      const char *version, unsigned long ver_hash)
+{
+	/* Check for a defined global or weak function w/ right name. */
+	if (ELF64_ST_TYPE(sym->st_info) != STT_FUNC)
+		return false;
+	if (ELF64_ST_BIND(sym->st_info) != STB_GLOBAL &&
+	    ELF64_ST_BIND(sym->st_info) != STB_WEAK)
+		return false;
+	if (strcmp(name, vdso_info.symstrings + sym->st_name))
+		return false;
+
+	/* Check symbol version. */
+	if (vdso_info.versym &&
+	    !vdso_match_version(vdso_info.versym[i], version, ver_hash))
+		return false;
+
+	return true;
+}
+
 void *evl_lookup_vdso(const char *version, const char *name)
 {
 	unsigned long ver_hash;
-
 	if (!vdso_info.valid)
 		return 0;
 
 	ver_hash = elf_hash(version);
-	ELF(Word) chain = vdso_info.bucket[elf_hash(name) % vdso_info.nbucket];
-
-	for (; chain != STN_UNDEF; chain = vdso_info.chain[chain]) {
-		ELF(Sym) *sym = &vdso_info.symtab[chain];
-
-		/* Check for a defined global or weak function w/ right name. */
-		if (ELF64_ST_TYPE(sym->st_info) != STT_FUNC)
-			continue;
-		if (ELF64_ST_BIND(sym->st_info) != STB_GLOBAL &&
-		    ELF64_ST_BIND(sym->st_info) != STB_WEAK)
-			continue;
-		if (sym->st_shndx == SHN_UNDEF)
-			continue;
-		if (strcmp(name, vdso_info.symstrings + sym->st_name))
-			continue;
-
-		/* Check symbol version. */
-		if (vdso_info.versym
-		    && !vdso_match_version(vdso_info.versym[chain],
-					   version, ver_hash))
-			continue;
-
-		return (void *)(vdso_info.load_offset + sym->st_value);
+	ELF(Word) i;
+
+	if (vdso_info.gnu_hash) {
+		uint32_t h1 = gnu_hash(name), h2, *hashval;
+
+		i = vdso_info.gnu_bucket[h1 % vdso_info.nbucket];
+		if (i == 0)
+			return 0;
+		h1 |= 1;
+		hashval = vdso_info.gnu_bucket + vdso_info.nbucket +
+			  (i - vdso_info.gnu_hash[1]);
+		for (;; i++) {
+			ELF(Sym) *sym = &vdso_info.symtab[i];
+			h2 = *hashval++;
+			if (h1 == (h2 | 1) &&
+			    check_sym(sym, i, name, version, ver_hash))
+				return (void *)(vdso_info.load_offset +
+						sym->st_value);
+			if (h2 & 1)
+				break;
+		}
+	} else {
+		i = vdso_info.bucket[elf_hash(name) % vdso_info.nbucket];
+		for (; i; i = vdso_info.chain[i]) {
+			ELF(Sym) *sym = &vdso_info.symtab[i];
+			if (sym->st_shndx != SHN_UNDEF &&
+			    check_sym(sym, i, name, version, ver_hash))
+				return (void *)(vdso_info.load_offset +
+						sym->st_value);
+		}
 	}
 
 	return 0;
-- 
2.51.0

^ permalink raw reply related	[flat|nested] 2+ messages in thread

* Re: [libevl][PATCH] lib/vdso: Update vdso parser to latest Linux version
  2025-09-13  9:47 [libevl][PATCH] lib/vdso: Update vdso parser to latest Linux version Jan Kiszka
@ 2025-09-13 14:25 ` Philippe Gerum
  0 siblings, 0 replies; 2+ messages in thread
From: Philippe Gerum @ 2025-09-13 14:25 UTC (permalink / raw)
  To: Jan Kiszka; +Cc: Xenomai

Jan Kiszka <jan.kiszka@siemens.com> writes:

> From: Jan Kiszka <jan.kiszka@siemens.com>
>
> This widely aligns our copy of
> linux/tools/testing/selftests/vDSO/parse_vdso.c with the version found
> in 6.17-rc5+. We were primarily missing support for DT_GNU_HASH. This
> becomes noticeable with the Debian 13 toolchain on arm64 which caused
> DT_HASH to become absent from the vDSO.
>
> Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
> ---
>  lib/parse_vdso.c | 157 ++++++++++++++++++++++++++++++-----------------
>  1 file changed, 99 insertions(+), 58 deletions(-)
>

Merged, thanks.

-- 
Philippe.

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2025-09-13 14:25 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-09-13  9:47 [libevl][PATCH] lib/vdso: Update vdso parser to latest Linux version Jan Kiszka
2025-09-13 14:25 ` Philippe Gerum

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.