mirror of https://github.com/golang/go.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
This commit is contained in:
parent
9f08c5c383
commit
1db4c8dc41
|
|
@ -4,12 +4,24 @@
|
||||||
|
|
||||||
#include "runtime.h"
|
#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_RANDOM 25
|
||||||
#define AT_SYSINFO_EHDR 33
|
#define AT_SYSINFO_EHDR 33
|
||||||
#define AT_NULL 0 /* End of vector */
|
#define AT_NULL 0 /* End of vector */
|
||||||
#define PT_LOAD 1 /* Loadable program segment */
|
#define PT_LOAD 1 /* Loadable program segment */
|
||||||
#define PT_DYNAMIC 2 /* Dynamic linking information */
|
#define PT_DYNAMIC 2 /* Dynamic linking information */
|
||||||
#define DT_NULL 0 /* Marks end of dynamic section */
|
#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_STRTAB 5 /* Address of string table */
|
||||||
#define DT_SYMTAB 6 /* Address of symbol table */
|
#define DT_SYMTAB 6 /* Address of symbol table */
|
||||||
#define DT_VERSYM 0x6ffffff0
|
#define DT_VERSYM 0x6ffffff0
|
||||||
|
|
@ -132,6 +144,7 @@ typedef struct
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
byte* name;
|
byte* name;
|
||||||
|
int32 sym_hash;
|
||||||
void** var_ptr;
|
void** var_ptr;
|
||||||
} symbol_key;
|
} symbol_key;
|
||||||
|
|
||||||
|
|
@ -148,9 +161,10 @@ struct vdso_info {
|
||||||
uintptr load_offset; /* load_addr - recorded vaddr */
|
uintptr load_offset; /* load_addr - recorded vaddr */
|
||||||
|
|
||||||
/* Symbol table */
|
/* Symbol table */
|
||||||
int32 num_sym;
|
|
||||||
Elf64_Sym *symtab;
|
Elf64_Sym *symtab;
|
||||||
const byte *symstrings;
|
const byte *symstrings;
|
||||||
|
Elf64_Word *bucket, *chain;
|
||||||
|
Elf64_Word nbucket, nchain;
|
||||||
|
|
||||||
/* Version table */
|
/* Version table */
|
||||||
Elf64_Versym *versym;
|
Elf64_Versym *versym;
|
||||||
|
|
@ -166,9 +180,9 @@ void* runtime·__vdso_clock_gettime_sym = (void*)0;
|
||||||
|
|
||||||
#define SYM_KEYS_COUNT 3
|
#define SYM_KEYS_COUNT 3
|
||||||
static symbol_key sym_keys[] = {
|
static symbol_key sym_keys[] = {
|
||||||
{ (byte*)"__vdso_time", &runtime·__vdso_time_sym },
|
{ (byte*)"__vdso_time", 0xa33c485, &runtime·__vdso_time_sym },
|
||||||
{ (byte*)"__vdso_gettimeofday", &runtime·__vdso_gettimeofday_sym },
|
{ (byte*)"__vdso_gettimeofday", 0x315ca59, &runtime·__vdso_gettimeofday_sym },
|
||||||
{ (byte*)"__vdso_clock_gettime", &runtime·__vdso_clock_gettime_sym },
|
{ (byte*)"__vdso_clock_gettime", 0xd35ec75, &runtime·__vdso_clock_gettime_sym },
|
||||||
};
|
};
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|
@ -176,18 +190,15 @@ vdso_init_from_sysinfo_ehdr(struct vdso_info *vdso_info, Elf64_Ehdr* hdr)
|
||||||
{
|
{
|
||||||
uint64 i;
|
uint64 i;
|
||||||
bool found_vaddr = false;
|
bool found_vaddr = false;
|
||||||
|
Elf64_Phdr *pt;
|
||||||
|
Elf64_Dyn *dyn;
|
||||||
|
Elf64_Word *hash;
|
||||||
|
|
||||||
|
vdso_info->valid = false;
|
||||||
vdso_info->load_addr = (uintptr) hdr;
|
vdso_info->load_addr = (uintptr) hdr;
|
||||||
|
|
||||||
Elf64_Phdr *pt = (Elf64_Phdr*)(vdso_info->load_addr + hdr->e_phoff);
|
pt = (Elf64_Phdr*)(vdso_info->load_addr + hdr->e_phoff);
|
||||||
Elf64_Shdr *sh = (Elf64_Shdr*)(vdso_info->load_addr + hdr->e_shoff);
|
dyn = nil;
|
||||||
Elf64_Dyn *dyn = 0;
|
|
||||||
|
|
||||||
for(i=0; i<hdr->e_shnum; i++) {
|
|
||||||
if(sh[i].sh_type == SHT_DYNSYM) {
|
|
||||||
vdso_info->num_sym = sh[i].sh_size / sizeof(Elf64_Sym);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// We need two things from the segment table: the load offset
|
// We need two things from the segment table: the load offset
|
||||||
// and the dynamic table.
|
// and the dynamic table.
|
||||||
|
|
@ -206,6 +217,11 @@ vdso_init_from_sysinfo_ehdr(struct vdso_info *vdso_info, Elf64_Ehdr* hdr)
|
||||||
return; // Failed
|
return; // Failed
|
||||||
|
|
||||||
// Fish out the useful bits of the dynamic table.
|
// 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++) {
|
for(i=0; dyn[i].d_tag!=DT_NULL; i++) {
|
||||||
switch(dyn[i].d_tag) {
|
switch(dyn[i].d_tag) {
|
||||||
case DT_STRTAB:
|
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
|
((uintptr)dyn[i].d_un.d_ptr
|
||||||
+ vdso_info->load_offset);
|
+ vdso_info->load_offset);
|
||||||
break;
|
break;
|
||||||
|
case DT_HASH:
|
||||||
|
hash = (Elf64_Word *)
|
||||||
|
((uintptr)dyn[i].d_un.d_ptr
|
||||||
|
+ vdso_info->load_offset);
|
||||||
|
break;
|
||||||
case DT_VERSYM:
|
case DT_VERSYM:
|
||||||
vdso_info->versym = (Elf64_Versym *)
|
vdso_info->versym = (Elf64_Versym *)
|
||||||
((uintptr)dyn[i].d_un.d_ptr
|
((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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(vdso_info->symstrings == nil || vdso_info->symtab == nil)
|
if(vdso_info->symstrings == nil || vdso_info->symtab == nil || hash == nil)
|
||||||
return; // Failed
|
return; // Failed
|
||||||
|
|
||||||
if(vdso_info->verdef == nil)
|
if(vdso_info->verdef == nil)
|
||||||
vdso_info->versym = 0;
|
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.
|
// That's all we need.
|
||||||
vdso_info->valid = true;
|
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);
|
def = (Elf64_Verdef *)((byte *)def + def->vd_next);
|
||||||
}
|
}
|
||||||
return 0;
|
return -1; // can not match any version
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
vdso_parse_symbols(struct vdso_info *vdso_info, int32 version)
|
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)
|
if(vdso_info->valid == false)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for(i=0; i<vdso_info->num_sym; i++) {
|
for(i=0; i<SYM_KEYS_COUNT; i++) {
|
||||||
Elf64_Sym *sym = &vdso_info->symtab[i];
|
for(chain = vdso_info->bucket[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.
|
sym = &vdso_info->symtab[chain];
|
||||||
if(ELF64_ST_TYPE(sym->st_info) != STT_FUNC)
|
if(ELF64_ST_TYPE(sym->st_info) != STT_FUNC)
|
||||||
continue;
|
continue;
|
||||||
if(ELF64_ST_BIND(sym->st_info) != STB_GLOBAL &&
|
if(ELF64_ST_BIND(sym->st_info) != STB_GLOBAL &&
|
||||||
ELF64_ST_BIND(sym->st_info) != STB_WEAK)
|
ELF64_ST_BIND(sym->st_info) != STB_WEAK)
|
||||||
continue;
|
continue;
|
||||||
if(sym->st_shndx == SHN_UNDEF)
|
if(sym->st_shndx == SHN_UNDEF)
|
||||||
continue;
|
continue;
|
||||||
|
if(runtime·strcmp(sym_keys[i].name, vdso_info->symstrings + sym->st_name) != 0)
|
||||||
for(j=0; j<SYM_KEYS_COUNT; j++) {
|
|
||||||
if(runtime·strcmp(sym_keys[j].name, vdso_info->symstrings + sym->st_name) != 0)
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Check symbol version.
|
// Check symbol version.
|
||||||
if(vdso_info->versym != nil && version != 0
|
if(vdso_info->versym != nil && version != 0
|
||||||
&& vdso_info->versym[i] & 0x7fff != version)
|
&& vdso_info->versym[chain] & 0x7fff != version)
|
||||||
continue;
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue