Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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>

authored by

Fangrui Song and committed by
Shuah Khan
e0746bde eed8ecdf

+80 -26
+80 -26
tools/testing/selftests/vDSO/parse_vdso.c
··· 53 53 /* Symbol table */ 54 54 ELF(Sym) *symtab; 55 55 const char *symstrings; 56 + ELF(Word) *gnu_hash; 56 57 ELF_HASH_ENTRY *bucket, *chain; 57 58 ELF_HASH_ENTRY nbucket, nchain; 58 59 ··· 79 78 h ^= g >> 24; 80 79 h &= ~g; 81 80 } 81 + return h; 82 + } 83 + 84 + static uint32_t gnu_hash(const char *name) 85 + { 86 + const unsigned char *s = (void *)name; 87 + uint32_t h = 5381; 88 + 89 + for (; *s; s++) 90 + h += h * 32 + *s; 82 91 return h; 83 92 } 84 93 ··· 134 123 */ 135 124 ELF_HASH_ENTRY *hash = 0; 136 125 vdso_info.symstrings = 0; 126 + vdso_info.gnu_hash = 0; 137 127 vdso_info.symtab = 0; 138 128 vdso_info.versym = 0; 139 129 vdso_info.verdef = 0; ··· 155 143 ((uintptr_t)dyn[i].d_un.d_ptr 156 144 + vdso_info.load_offset); 157 145 break; 146 + case DT_GNU_HASH: 147 + vdso_info.gnu_hash = 148 + (ELF(Word) *)((uintptr_t)dyn[i].d_un.d_ptr + 149 + vdso_info.load_offset); 150 + break; 158 151 case DT_VERSYM: 159 152 vdso_info.versym = (ELF(Versym) *) 160 153 ((uintptr_t)dyn[i].d_un.d_ptr ··· 172 155 break; 173 156 } 174 157 } 175 - if (!vdso_info.symstrings || !vdso_info.symtab || !hash) 158 + if (!vdso_info.symstrings || !vdso_info.symtab || 159 + (!hash && !vdso_info.gnu_hash)) 176 160 return; /* Failed */ 177 161 178 162 if (!vdso_info.verdef) 179 163 vdso_info.versym = 0; 180 164 181 165 /* Parse the hash table header. */ 182 - vdso_info.nbucket = hash[0]; 183 - vdso_info.nchain = hash[1]; 184 - vdso_info.bucket = &hash[2]; 185 - vdso_info.chain = &hash[vdso_info.nbucket + 2]; 166 + if (vdso_info.gnu_hash) { 167 + vdso_info.nbucket = vdso_info.gnu_hash[0]; 168 + /* The bucket array is located after the header (4 uint32) and the bloom 169 + * filter (size_t array of gnu_hash[2] elements). 170 + */ 171 + vdso_info.bucket = vdso_info.gnu_hash + 4 + 172 + sizeof(size_t) / 4 * vdso_info.gnu_hash[2]; 173 + } else { 174 + vdso_info.nbucket = hash[0]; 175 + vdso_info.nchain = hash[1]; 176 + vdso_info.bucket = &hash[2]; 177 + vdso_info.chain = &hash[vdso_info.nbucket + 2]; 178 + } 186 179 187 180 /* That's all we need. */ 188 181 vdso_info.valid = true; ··· 236 209 && !strcmp(name, vdso_info.symstrings + aux->vda_name); 237 210 } 238 211 212 + static bool check_sym(ELF(Sym) *sym, ELF(Word) i, const char *name, 213 + const char *version, unsigned long ver_hash) 214 + { 215 + /* Check for a defined global or weak function w/ right name. */ 216 + if (ELF64_ST_TYPE(sym->st_info) != STT_FUNC) 217 + return false; 218 + if (ELF64_ST_BIND(sym->st_info) != STB_GLOBAL && 219 + ELF64_ST_BIND(sym->st_info) != STB_WEAK) 220 + return false; 221 + if (strcmp(name, vdso_info.symstrings + sym->st_name)) 222 + return false; 223 + 224 + /* Check symbol version. */ 225 + if (vdso_info.versym && 226 + !vdso_match_version(vdso_info.versym[i], version, ver_hash)) 227 + return false; 228 + 229 + return true; 230 + } 231 + 239 232 void *vdso_sym(const char *version, const char *name) 240 233 { 241 234 unsigned long ver_hash; ··· 263 216 return 0; 264 217 265 218 ver_hash = elf_hash(version); 266 - ELF(Word) chain = vdso_info.bucket[elf_hash(name) % vdso_info.nbucket]; 219 + ELF(Word) i; 267 220 268 - for (; chain != STN_UNDEF; chain = vdso_info.chain[chain]) { 269 - ELF(Sym) *sym = &vdso_info.symtab[chain]; 221 + if (vdso_info.gnu_hash) { 222 + uint32_t h1 = gnu_hash(name), h2, *hashval; 270 223 271 - /* Check for a defined global or weak function w/ right name. */ 272 - if (ELF64_ST_TYPE(sym->st_info) != STT_FUNC) 273 - continue; 274 - if (ELF64_ST_BIND(sym->st_info) != STB_GLOBAL && 275 - ELF64_ST_BIND(sym->st_info) != STB_WEAK) 276 - continue; 277 - if (sym->st_shndx == SHN_UNDEF) 278 - continue; 279 - if (strcmp(name, vdso_info.symstrings + sym->st_name)) 280 - continue; 281 - 282 - /* Check symbol version. */ 283 - if (vdso_info.versym 284 - && !vdso_match_version(vdso_info.versym[chain], 285 - version, ver_hash)) 286 - continue; 287 - 288 - return (void *)(vdso_info.load_offset + sym->st_value); 224 + i = vdso_info.bucket[h1 % vdso_info.nbucket]; 225 + if (i == 0) 226 + return 0; 227 + h1 |= 1; 228 + hashval = vdso_info.bucket + vdso_info.nbucket + 229 + (i - vdso_info.gnu_hash[1]); 230 + for (;; i++) { 231 + ELF(Sym) *sym = &vdso_info.symtab[i]; 232 + h2 = *hashval++; 233 + if (h1 == (h2 | 1) && 234 + check_sym(sym, i, name, version, ver_hash)) 235 + return (void *)(vdso_info.load_offset + 236 + sym->st_value); 237 + if (h2 & 1) 238 + break; 239 + } 240 + } else { 241 + i = vdso_info.bucket[elf_hash(name) % vdso_info.nbucket]; 242 + for (; i; i = vdso_info.chain[i]) { 243 + ELF(Sym) *sym = &vdso_info.symtab[i]; 244 + if (sym->st_shndx != SHN_UNDEF && 245 + check_sym(sym, i, name, version, ver_hash)) 246 + return (void *)(vdso_info.load_offset + 247 + sym->st_value); 248 + } 289 249 } 290 250 291 251 return 0;