Follow @Openwall on Twitter for new release announcements and other news
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-ID: <20250822115732.818628-1-hulata@sysart.tech>
Date: Fri, 22 Aug 2025 13:57:32 +0200
From: tombo <hulata@...art.tech>
To: musl@...ts.openwall.com
Cc: tombo <hulata@...art.tech>
Subject: [PATCH v2] vdso: add support for GNU hash tables 

The previous submission had formatting issues, sorry.

On some kernels (e.g. AWS Ubuntu's Graviton official AMIs) the arm64 vDSO
exports symbols with DT_GNU_HASH but omits DT_HASH.  musl's
__vdsosym resolver only understands SysV hash, so vDSO symbols
fail to resolve and clock_gettime falls back to syscalls.

This patch adds a minimal GNU hash lookup implementation to
__vdsosym, fixing vDSO usage on affected systems.

---
 src/internal/vdso.c | 75 +++++++++++++++++++++++++++++++++++++++------
 1 file changed, 66 insertions(+), 9 deletions(-)

diff --git a/src/internal/vdso.c b/src/internal/vdso.c
index d46d3228..8fa19526 100644
--- a/src/internal/vdso.c
+++ b/src/internal/vdso.c
@@ -40,6 +40,53 @@ static int checkver(Verdef *def, int vsym, const char *vername, char *strings)
 #define OK_TYPES (1<<STT_NOTYPE | 1<<STT_OBJECT | 1<<STT_FUNC | 1<<STT_COMMON)
 #define OK_BINDS (1<<STB_GLOBAL | 1<<STB_WEAK | 1<<STB_GNU_UNIQUE)
 
+static uint32_t gnu_hash(const char *s0)
+{
+    const unsigned char *s = (const void *)s0;
+    uint_fast32_t h = 5381;
+    for (; *s; s++) h += h*32 + *s;
+    return h;
+}
+
+static Sym *gnu_lookup(uint32_t h1, uint32_t *hashtab,
+                       Sym *syms, char *strings, uint16_t *versym,
+                       Verdef *verdef, const char *vername, const char *s)
+{
+    uint32_t nbuckets  = hashtab[0];
+    uint32_t symoffset = hashtab[1];
+    uint32_t *buckets  = hashtab + 4 + hashtab[2]*(sizeof(size_t)/4);
+    uint32_t i = buckets[h1 % nbuckets];
+    if (!i) return 0;
+
+    uint32_t *hashval = buckets + nbuckets + (i - symoffset);
+    for (h1 |= 1; ; i++) {
+        uint32_t h2 = *hashval++;
+        if ((h1 == (h2|1))
+            && (1<<(syms[i].st_info&0xf) & OK_TYPES)
+            && (1<<(syms[i].st_info>>4) & OK_BINDS)
+            && syms[i].st_shndx
+            && !strcmp(s, strings + syms[i].st_name)
+            && (!versym || !vername || checkver(verdef, versym[i], vername, strings)))
+            return syms+i;
+        if (h2 & 1) break;
+    }
+    return 0;
+}
+
+static Sym *gnu_lookup_filtered(uint32_t h1, uint32_t *hashtab,
+                                Sym *syms, char *strings, uint16_t *versym,
+                                Verdef *verdef, const char *vername, const char *s)
+{
+    const size_t *bloomwords = (const void *)(hashtab+4);
+    size_t bloom_sz = hashtab[2];
+    size_t f = bloomwords[(h1 / (8*sizeof(size_t))) & (bloom_sz - 1)];
+    size_t fmask = 1ul << (h1 % (8*sizeof(size_t)));
+    if (!(f & fmask)) return 0;
+    f >>= (h1 >> hashtab[3]) % (8 * sizeof f);
+    if (!(f & 1)) return 0;
+    return gnu_lookup(h1, hashtab, syms, strings, versym, verdef, vername, s);
+}
+
 void *__vdsosym(const char *vername, const char *name)
 {
 	size_t i;
@@ -60,6 +107,7 @@ void *__vdsosym(const char *vername, const char *name)
 	char *strings = 0;
 	Sym *syms = 0;
 	Elf_Symndx *hashtab = 0;
+	uint32_t *ghashtab = 0; /* GNU hash */
 	uint16_t *versym = 0;
 	Verdef *verdef = 0;
 	
@@ -69,22 +117,31 @@ void *__vdsosym(const char *vername, const char *name)
 		case DT_STRTAB: strings = p; break;
 		case DT_SYMTAB: syms = p; break;
 		case DT_HASH: hashtab = p; break;
+		case DT_GNU_HASH: ghashtab = p; break;
 		case DT_VERSYM: versym = p; break;
 		case DT_VERDEF: verdef = p; break;
 		}
 	}	
 
-	if (!strings || !syms || !hashtab) return 0;
+	if (!strings || !syms) return 0;
 	if (!verdef) versym = 0;
 
-	for (i=0; i<hashtab[1]; i++) {
-		if (!(1<<(syms[i].st_info&0xf) & OK_TYPES)) continue;
-		if (!(1<<(syms[i].st_info>>4) & OK_BINDS)) continue;
-		if (!syms[i].st_shndx) continue;
-		if (strcmp(name, strings+syms[i].st_name)) continue;
-		if (versym && !checkver(verdef, versym[i], vername, strings))
-			continue;
-		return (void *)(base + syms[i].st_value);
+	if (ghashtab) {
+		uint32_t h = gnu_hash(name);
+		Sym *s = gnu_lookup_filtered(h, ghashtab, syms, strings, versym, verdef, vername, name);
+		if (s) return (void *)(base + s->st_value);
+	}
+
+	if (hashtab) {
+		for (i=0; i<hashtab[1]; i++) {
+			if (!(1<<(syms[i].st_info&0xf) & OK_TYPES)) continue;
+			if (!(1<<(syms[i].st_info>>4) & OK_BINDS)) continue;
+			if (!syms[i].st_shndx) continue;
+			if (strcmp(name, strings+syms[i].st_name)) continue;
+			if (versym && !checkver(verdef, versym[i], vername, strings))
+				continue;
+			return (void *)(base + syms[i].st_value);
+		}
 	}
 
 	return 0;
-- 

Powered by blists - more mailing lists

Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.