From: Ian Lance Taylor Date: Fri, 13 Jun 2014 20:29:26 +0000 (-0700) Subject: runtime: fix VDSO lookup to use dynamic hash table X-Git-Tag: go1.4beta1~1290 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=1db4c8dc413b588668851eddf05426dabb08c95a;p=gostls13.git runtime: fix VDSO lookup to use dynamic hash table Reportedly in the Linux 3.16 kernel the VDSO will not have section headers or a normal symbol table. Too late for 1.3 but perhaps for 1.3.1, if there is one. Fixes #8197. LGTM=rsc R=golang-codereviews, mattn.jp, rsc CC=golang-codereviews https://golang.org/cl/101260044 --- diff --git a/src/pkg/runtime/vdso_linux_amd64.c b/src/pkg/runtime/vdso_linux_amd64.c index f55d312a0a..38e1152438 100644 --- a/src/pkg/runtime/vdso_linux_amd64.c +++ b/src/pkg/runtime/vdso_linux_amd64.c @@ -4,12 +4,24 @@ #include "runtime.h" +// Look up symbols in the Linux vDSO. + +// This code was originally based on the sample Linux vDSO parser at +// https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/Documentation/vDSO/parse_vdso.c + +// This implements the ELF dynamic linking spec at +// http://sco.com/developers/gabi/latest/ch5.dynamic.html + +// The version section is documented at +// http://refspecs.linuxfoundation.org/LSB_3.2.0/LSB-Core-generic/LSB-Core-generic/symversion.html + #define AT_RANDOM 25 #define AT_SYSINFO_EHDR 33 #define AT_NULL 0 /* End of vector */ #define PT_LOAD 1 /* Loadable program segment */ #define PT_DYNAMIC 2 /* Dynamic linking information */ #define DT_NULL 0 /* Marks end of dynamic section */ +#define DT_HASH 4 /* Dynamic symbol hash table */ #define DT_STRTAB 5 /* Address of string table */ #define DT_SYMTAB 6 /* Address of symbol table */ #define DT_VERSYM 0x6ffffff0 @@ -132,6 +144,7 @@ typedef struct typedef struct { byte* name; + int32 sym_hash; void** var_ptr; } symbol_key; @@ -148,9 +161,10 @@ struct vdso_info { uintptr load_offset; /* load_addr - recorded vaddr */ /* Symbol table */ - int32 num_sym; Elf64_Sym *symtab; const byte *symstrings; + Elf64_Word *bucket, *chain; + Elf64_Word nbucket, nchain; /* Version table */ Elf64_Versym *versym; @@ -166,9 +180,9 @@ void* runtime·__vdso_clock_gettime_sym = (void*)0; #define SYM_KEYS_COUNT 3 static symbol_key sym_keys[] = { - { (byte*)"__vdso_time", &runtime·__vdso_time_sym }, - { (byte*)"__vdso_gettimeofday", &runtime·__vdso_gettimeofday_sym }, - { (byte*)"__vdso_clock_gettime", &runtime·__vdso_clock_gettime_sym }, + { (byte*)"__vdso_time", 0xa33c485, &runtime·__vdso_time_sym }, + { (byte*)"__vdso_gettimeofday", 0x315ca59, &runtime·__vdso_gettimeofday_sym }, + { (byte*)"__vdso_clock_gettime", 0xd35ec75, &runtime·__vdso_clock_gettime_sym }, }; static void @@ -176,18 +190,15 @@ vdso_init_from_sysinfo_ehdr(struct vdso_info *vdso_info, Elf64_Ehdr* hdr) { uint64 i; bool found_vaddr = false; + Elf64_Phdr *pt; + Elf64_Dyn *dyn; + Elf64_Word *hash; + vdso_info->valid = false; vdso_info->load_addr = (uintptr) hdr; - Elf64_Phdr *pt = (Elf64_Phdr*)(vdso_info->load_addr + hdr->e_phoff); - Elf64_Shdr *sh = (Elf64_Shdr*)(vdso_info->load_addr + hdr->e_shoff); - Elf64_Dyn *dyn = 0; - - for(i=0; ie_shnum; i++) { - if(sh[i].sh_type == SHT_DYNSYM) { - vdso_info->num_sym = sh[i].sh_size / sizeof(Elf64_Sym); - } - } + pt = (Elf64_Phdr*)(vdso_info->load_addr + hdr->e_phoff); + dyn = nil; // We need two things from the segment table: the load offset // and the dynamic table. @@ -206,6 +217,11 @@ vdso_init_from_sysinfo_ehdr(struct vdso_info *vdso_info, Elf64_Ehdr* hdr) return; // Failed // Fish out the useful bits of the dynamic table. + hash = nil; + vdso_info->symstrings = nil; + vdso_info->symtab = nil; + vdso_info->versym = nil; + vdso_info->verdef = nil; for(i=0; dyn[i].d_tag!=DT_NULL; i++) { switch(dyn[i].d_tag) { case DT_STRTAB: @@ -218,6 +234,11 @@ vdso_init_from_sysinfo_ehdr(struct vdso_info *vdso_info, Elf64_Ehdr* hdr) ((uintptr)dyn[i].d_un.d_ptr + vdso_info->load_offset); break; + case DT_HASH: + hash = (Elf64_Word *) + ((uintptr)dyn[i].d_un.d_ptr + + vdso_info->load_offset); + break; case DT_VERSYM: vdso_info->versym = (Elf64_Versym *) ((uintptr)dyn[i].d_un.d_ptr @@ -230,12 +251,18 @@ vdso_init_from_sysinfo_ehdr(struct vdso_info *vdso_info, Elf64_Ehdr* hdr) break; } } - if(vdso_info->symstrings == nil || vdso_info->symtab == nil) + if(vdso_info->symstrings == nil || vdso_info->symtab == nil || hash == nil) return; // Failed if(vdso_info->verdef == nil) vdso_info->versym = 0; + // Parse the hash table header. + vdso_info->nbucket = hash[0]; + vdso_info->nchain = hash[1]; + vdso_info->bucket = &hash[2]; + vdso_info->chain = &hash[vdso_info->nbucket + 2]; + // That's all we need. vdso_info->valid = true; } @@ -261,39 +288,41 @@ vdso_find_version(struct vdso_info *vdso_info, version_key* ver) } def = (Elf64_Verdef *)((byte *)def + def->vd_next); } - return 0; + return -1; // can not match any version } static void vdso_parse_symbols(struct vdso_info *vdso_info, int32 version) { - int32 i, j; + int32 i; + Elf64_Word chain; + Elf64_Sym *sym; if(vdso_info->valid == false) return; - for(i=0; inum_sym; i++) { - Elf64_Sym *sym = &vdso_info->symtab[i]; + for(i=0; ibucket[sym_keys[i].sym_hash % vdso_info->nbucket]; + chain != 0; chain = vdso_info->chain[chain]) { - // Check for a defined global or weak function w/ right name. - if(ELF64_ST_TYPE(sym->st_info) != STT_FUNC) - continue; - if(ELF64_ST_BIND(sym->st_info) != STB_GLOBAL && - ELF64_ST_BIND(sym->st_info) != STB_WEAK) - continue; - if(sym->st_shndx == SHN_UNDEF) - continue; - - for(j=0; jsymstrings + sym->st_name) != 0) + sym = &vdso_info->symtab[chain]; + if(ELF64_ST_TYPE(sym->st_info) != STT_FUNC) + continue; + if(ELF64_ST_BIND(sym->st_info) != STB_GLOBAL && + ELF64_ST_BIND(sym->st_info) != STB_WEAK) + continue; + if(sym->st_shndx == SHN_UNDEF) + continue; + if(runtime·strcmp(sym_keys[i].name, vdso_info->symstrings + sym->st_name) != 0) continue; // Check symbol version. if(vdso_info->versym != nil && version != 0 - && vdso_info->versym[i] & 0x7fff != version) + && vdso_info->versym[chain] & 0x7fff != version) continue; - *sym_keys[j].var_ptr = (void *)(vdso_info->load_offset + sym->st_value); + *sym_keys[i].var_ptr = (void *)(vdso_info->load_offset + sym->st_value); + break; } } }