* [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.