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:
committed by
Rich Felker
parent
149d872970
commit
1347154cdc
+26
-2
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user