vdso: add support for GNU hash tables

on some kernel builds, the vdso exports symbols with DT_GNU_HASH but
omits DT_HASH, causing the vdso not to get used and for clock_gettime
to fall back to using a syscall.

our vdso resolver does not use the hash table anyway, but does need
the symbol count from the standard sysv hash table. if it's missing,
use the GNU hashtable to calculate the number of symbols.
This commit is contained in:
Tomáš Hulata
2026-03-19 09:57:35 -04:00
committed by Rich Felker
parent 149d872970
commit 1347154cdc
+26 -2
View File
@@ -40,6 +40,24 @@ 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 size_t count_syms_gnu(uint32_t *gh)
{
size_t nsym, i;
uint32_t *buckets = gh + 4 + (gh[2]*sizeof(size_t)/4);
uint32_t *hashval;
for (i = nsym = 0; i < gh[0]; i++) {
if (buckets[i] > nsym)
nsym = buckets[i];
}
if (nsym) {
hashval = buckets + gh[0] + (nsym - gh[1]);
do nsym++;
while (!(*hashval++ & 1));
}
return nsym;
}
void *__vdsosym(const char *vername, const char *name)
{
size_t i;
@@ -60,6 +78,7 @@ void *__vdsosym(const char *vername, const char *name)
char *strings = 0;
Sym *syms = 0;
Elf_Symndx *hashtab = 0;
uint32_t *ghashtab = 0;
uint16_t *versym = 0;
Verdef *verdef = 0;
@@ -69,15 +88,20 @@ 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;
size_t nsym = 0;
for (i=0; i<hashtab[1]; i++) {
if (hashtab) nsym = hashtab[1];
else if (ghashtab) nsym = count_syms_gnu(ghashtab);
for (i=0; i<nsym; 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;