Skip to content

Commit e0746bd

Browse files
MaskRayshuahkh
authored andcommitted
selftests/vDSO: support DT_GNU_HASH
glibc added support for DT_GNU_HASH in 2006 and DT_HASH has been obsoleted for more than one decade in many Linux distributions. Many vDSOs support DT_GNU_HASH. This patch adds selftests support. Link: https://lore.kernel.org/r/20241206130724.7944-2-xry111@xry111.site Signed-off-by: Fangrui Song <i@maskray.me> Tested-by: Xi Ruoyao <xry111@xry111.site> Signed-off-by: Xi Ruoyao <xry111@xry111.site> # rebase Signed-off-by: Shuah Khan <skhan@linuxfoundation.org>
1 parent eed8ecd commit e0746bd

1 file changed

Lines changed: 82 additions & 28 deletions

File tree

tools/testing/selftests/vDSO/parse_vdso.c

Lines changed: 82 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ static struct vdso_info
5353
/* Symbol table */
5454
ELF(Sym) *symtab;
5555
const char *symstrings;
56+
ELF(Word) *gnu_hash;
5657
ELF_HASH_ENTRY *bucket, *chain;
5758
ELF_HASH_ENTRY nbucket, nchain;
5859

@@ -81,6 +82,16 @@ static unsigned long elf_hash(const char *name)
8182
return h;
8283
}
8384

85+
static uint32_t gnu_hash(const char *name)
86+
{
87+
const unsigned char *s = (void *)name;
88+
uint32_t h = 5381;
89+
90+
for (; *s; s++)
91+
h += h * 32 + *s;
92+
return h;
93+
}
94+
8495
void vdso_init_from_sysinfo_ehdr(uintptr_t base)
8596
{
8697
size_t i;
@@ -123,6 +134,7 @@ void vdso_init_from_sysinfo_ehdr(uintptr_t base)
123134
*/
124135
ELF_HASH_ENTRY *hash = 0;
125136
vdso_info.symstrings = 0;
137+
vdso_info.gnu_hash = 0;
126138
vdso_info.symtab = 0;
127139
vdso_info.versym = 0;
128140
vdso_info.verdef = 0;
@@ -143,6 +155,11 @@ void vdso_init_from_sysinfo_ehdr(uintptr_t base)
143155
((uintptr_t)dyn[i].d_un.d_ptr
144156
+ vdso_info.load_offset);
145157
break;
158+
case DT_GNU_HASH:
159+
vdso_info.gnu_hash =
160+
(ELF(Word) *)((uintptr_t)dyn[i].d_un.d_ptr +
161+
vdso_info.load_offset);
162+
break;
146163
case DT_VERSYM:
147164
vdso_info.versym = (ELF(Versym) *)
148165
((uintptr_t)dyn[i].d_un.d_ptr
@@ -155,17 +172,27 @@ void vdso_init_from_sysinfo_ehdr(uintptr_t base)
155172
break;
156173
}
157174
}
158-
if (!vdso_info.symstrings || !vdso_info.symtab || !hash)
175+
if (!vdso_info.symstrings || !vdso_info.symtab ||
176+
(!hash && !vdso_info.gnu_hash))
159177
return; /* Failed */
160178

161179
if (!vdso_info.verdef)
162180
vdso_info.versym = 0;
163181

164182
/* Parse the hash table header. */
165-
vdso_info.nbucket = hash[0];
166-
vdso_info.nchain = hash[1];
167-
vdso_info.bucket = &hash[2];
168-
vdso_info.chain = &hash[vdso_info.nbucket + 2];
183+
if (vdso_info.gnu_hash) {
184+
vdso_info.nbucket = vdso_info.gnu_hash[0];
185+
/* The bucket array is located after the header (4 uint32) and the bloom
186+
* filter (size_t array of gnu_hash[2] elements).
187+
*/
188+
vdso_info.bucket = vdso_info.gnu_hash + 4 +
189+
sizeof(size_t) / 4 * vdso_info.gnu_hash[2];
190+
} else {
191+
vdso_info.nbucket = hash[0];
192+
vdso_info.nchain = hash[1];
193+
vdso_info.bucket = &hash[2];
194+
vdso_info.chain = &hash[vdso_info.nbucket + 2];
195+
}
169196

170197
/* That's all we need. */
171198
vdso_info.valid = true;
@@ -209,36 +236,63 @@ static bool vdso_match_version(ELF(Versym) ver,
209236
&& !strcmp(name, vdso_info.symstrings + aux->vda_name);
210237
}
211238

239+
static bool check_sym(ELF(Sym) *sym, ELF(Word) i, const char *name,
240+
const char *version, unsigned long ver_hash)
241+
{
242+
/* Check for a defined global or weak function w/ right name. */
243+
if (ELF64_ST_TYPE(sym->st_info) != STT_FUNC)
244+
return false;
245+
if (ELF64_ST_BIND(sym->st_info) != STB_GLOBAL &&
246+
ELF64_ST_BIND(sym->st_info) != STB_WEAK)
247+
return false;
248+
if (strcmp(name, vdso_info.symstrings + sym->st_name))
249+
return false;
250+
251+
/* Check symbol version. */
252+
if (vdso_info.versym &&
253+
!vdso_match_version(vdso_info.versym[i], version, ver_hash))
254+
return false;
255+
256+
return true;
257+
}
258+
212259
void *vdso_sym(const char *version, const char *name)
213260
{
214261
unsigned long ver_hash;
215262
if (!vdso_info.valid)
216263
return 0;
217264

218265
ver_hash = elf_hash(version);
219-
ELF(Word) chain = vdso_info.bucket[elf_hash(name) % vdso_info.nbucket];
220-
221-
for (; chain != STN_UNDEF; chain = vdso_info.chain[chain]) {
222-
ELF(Sym) *sym = &vdso_info.symtab[chain];
223-
224-
/* Check for a defined global or weak function w/ right name. */
225-
if (ELF64_ST_TYPE(sym->st_info) != STT_FUNC)
226-
continue;
227-
if (ELF64_ST_BIND(sym->st_info) != STB_GLOBAL &&
228-
ELF64_ST_BIND(sym->st_info) != STB_WEAK)
229-
continue;
230-
if (sym->st_shndx == SHN_UNDEF)
231-
continue;
232-
if (strcmp(name, vdso_info.symstrings + sym->st_name))
233-
continue;
234-
235-
/* Check symbol version. */
236-
if (vdso_info.versym
237-
&& !vdso_match_version(vdso_info.versym[chain],
238-
version, ver_hash))
239-
continue;
240-
241-
return (void *)(vdso_info.load_offset + sym->st_value);
266+
ELF(Word) i;
267+
268+
if (vdso_info.gnu_hash) {
269+
uint32_t h1 = gnu_hash(name), h2, *hashval;
270+
271+
i = vdso_info.bucket[h1 % vdso_info.nbucket];
272+
if (i == 0)
273+
return 0;
274+
h1 |= 1;
275+
hashval = vdso_info.bucket + vdso_info.nbucket +
276+
(i - vdso_info.gnu_hash[1]);
277+
for (;; i++) {
278+
ELF(Sym) *sym = &vdso_info.symtab[i];
279+
h2 = *hashval++;
280+
if (h1 == (h2 | 1) &&
281+
check_sym(sym, i, name, version, ver_hash))
282+
return (void *)(vdso_info.load_offset +
283+
sym->st_value);
284+
if (h2 & 1)
285+
break;
286+
}
287+
} else {
288+
i = vdso_info.bucket[elf_hash(name) % vdso_info.nbucket];
289+
for (; i; i = vdso_info.chain[i]) {
290+
ELF(Sym) *sym = &vdso_info.symtab[i];
291+
if (sym->st_shndx != SHN_UNDEF &&
292+
check_sym(sym, i, name, version, ver_hash))
293+
return (void *)(vdso_info.load_offset +
294+
sym->st_value);
295+
}
242296
}
243297

244298
return 0;

0 commit comments

Comments
 (0)