This source file includes following definitions.
- vdso_init_from_sysinfo_ehdr
- vdso_find_version
- vdso_parse_symbols
- runtime·linux_setup_vdso
#include "runtime.h"
#define AT_RANDOM 25
#define AT_SYSINFO_EHDR 33
#define AT_NULL 0
#define PT_LOAD 1
#define PT_DYNAMIC 2
#define DT_NULL 0
#define DT_STRTAB 5
#define DT_SYMTAB 6
#define DT_VERSYM 0x6ffffff0
#define DT_VERDEF 0x6ffffffc
#define VER_FLG_BASE 0x1
#define SHN_UNDEF 0
#define SHT_DYNSYM 11
#define STT_FUNC 2
#define STB_GLOBAL 1
#define STB_WEAK 2
#define ELF64_ST_BIND(val) (((byte) (val)) >> 4)
#define ELF64_ST_TYPE(val) ((val) & 0xf)
#define EI_NIDENT (16)
typedef uint16 Elf64_Half;
typedef uint32 Elf64_Word;
typedef int32 Elf64_Sword;
typedef uint64 Elf64_Xword;
typedef int64 Elf64_Sxword;
typedef uint64 Elf64_Addr;
typedef uint64 Elf64_Off;
typedef uint16 Elf64_Section;
typedef Elf64_Half Elf64_Versym;
typedef struct
{
Elf64_Word st_name;
byte st_info;
byte st_other;
Elf64_Section st_shndx;
Elf64_Addr st_value;
Elf64_Xword st_size;
} Elf64_Sym;
typedef struct
{
Elf64_Half vd_version;
Elf64_Half vd_flags;
Elf64_Half vd_ndx;
Elf64_Half vd_cnt;
Elf64_Word vd_hash;
Elf64_Word vd_aux;
Elf64_Word vd_next;
} Elf64_Verdef;
typedef struct
{
byte e_ident[EI_NIDENT];
Elf64_Half e_type;
Elf64_Half e_machine;
Elf64_Word e_version;
Elf64_Addr e_entry;
Elf64_Off e_phoff;
Elf64_Off e_shoff;
Elf64_Word e_flags;
Elf64_Half e_ehsize;
Elf64_Half e_phentsize;
Elf64_Half e_phnum;
Elf64_Half e_shentsize;
Elf64_Half e_shnum;
Elf64_Half e_shstrndx;
} Elf64_Ehdr;
typedef struct
{
Elf64_Word p_type;
Elf64_Word p_flags;
Elf64_Off p_offset;
Elf64_Addr p_vaddr;
Elf64_Addr p_paddr;
Elf64_Xword p_filesz;
Elf64_Xword p_memsz;
Elf64_Xword p_align;
} Elf64_Phdr;
typedef struct
{
Elf64_Word sh_name;
Elf64_Word sh_type;
Elf64_Xword sh_flags;
Elf64_Addr sh_addr;
Elf64_Off sh_offset;
Elf64_Xword sh_size;
Elf64_Word sh_link;
Elf64_Word sh_info;
Elf64_Xword sh_addralign;
Elf64_Xword sh_entsize;
} Elf64_Shdr;
typedef struct
{
Elf64_Sxword d_tag;
union
{
Elf64_Xword d_val;
Elf64_Addr d_ptr;
} d_un;
} Elf64_Dyn;
typedef struct
{
Elf64_Word vda_name;
Elf64_Word vda_next;
} Elf64_Verdaux;
typedef struct
{
uint64 a_type;
union
{
uint64 a_val;
} a_un;
} Elf64_auxv_t;
typedef struct {
byte* name;
void** var_ptr;
} symbol_key;
typedef struct {
byte* version;
int32 ver_hash;
} version_key;
struct vdso_info {
bool valid;
uintptr load_addr;
uintptr load_offset;
int32 num_sym;
Elf64_Sym *symtab;
const byte *symstrings;
Elf64_Versym *versym;
Elf64_Verdef *verdef;
};
static version_key linux26 = { (byte*)"LINUX_2.6", 0x3ae75f6 };
void* runtime·__vdso_time_sym = (void*)0xffffffffff600400ULL;
void* runtime·__vdso_gettimeofday_sym = (void*)0xffffffffff600000ULL;
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 },
};
static void
vdso_init_from_sysinfo_ehdr(struct vdso_info *vdso_info, Elf64_Ehdr* hdr)
{
uint64 i;
bool found_vaddr = 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; i<hdr->e_shnum; i++) {
if(sh[i].sh_type == SHT_DYNSYM) {
vdso_info->num_sym = sh[i].sh_size / sizeof(Elf64_Sym);
}
}
for(i=0; i<hdr->e_phnum; i++) {
if(pt[i].p_type == PT_LOAD && found_vaddr == false) {
found_vaddr = true;
vdso_info->load_offset = (uintptr)hdr
+ (uintptr)pt[i].p_offset
- (uintptr)pt[i].p_vaddr;
} else if(pt[i].p_type == PT_DYNAMIC) {
dyn = (Elf64_Dyn*)((uintptr)hdr + pt[i].p_offset);
}
}
if(found_vaddr == false || dyn == nil)
return;
for(i=0; dyn[i].d_tag!=DT_NULL; i++) {
switch(dyn[i].d_tag) {
case DT_STRTAB:
vdso_info->symstrings = (const byte *)
((uintptr)dyn[i].d_un.d_ptr
+ vdso_info->load_offset);
break;
case DT_SYMTAB:
vdso_info->symtab = (Elf64_Sym *)
((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
+ vdso_info->load_offset);
break;
case DT_VERDEF:
vdso_info->verdef = (Elf64_Verdef *)
((uintptr)dyn[i].d_un.d_ptr
+ vdso_info->load_offset);
break;
}
}
if(vdso_info->symstrings == nil || vdso_info->symtab == nil)
return;
if(vdso_info->verdef == nil)
vdso_info->versym = 0;
vdso_info->valid = true;
}
static int32
vdso_find_version(struct vdso_info *vdso_info, version_key* ver)
{
if(vdso_info->valid == false) {
return 0;
}
Elf64_Verdef *def = vdso_info->verdef;
while(true) {
if((def->vd_flags & VER_FLG_BASE) == 0) {
Elf64_Verdaux *aux = (Elf64_Verdaux*)((byte *)def + def->vd_aux);
if(def->vd_hash == ver->ver_hash &&
runtime·strcmp(ver->version, vdso_info->symstrings + aux->vda_name) == 0) {
return def->vd_ndx & 0x7fff;
}
}
if(def->vd_next == 0) {
break;
}
def = (Elf64_Verdef *)((byte *)def + def->vd_next);
}
return 0;
}
static void
vdso_parse_symbols(struct vdso_info *vdso_info, int32 version)
{
int32 i, j;
if(vdso_info->valid == false)
return;
for(i=0; i<vdso_info->num_sym; i++) {
Elf64_Sym *sym = &vdso_info->symtab[i];
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; j<SYM_KEYS_COUNT; j++) {
if(runtime·strcmp(sym_keys[j].name, vdso_info->symstrings + sym->st_name) != 0)
continue;
if(vdso_info->versym != nil && version != 0
&& vdso_info->versym[i] & 0x7fff != version)
continue;
*sym_keys[j].var_ptr = (void *)(vdso_info->load_offset + sym->st_value);
}
}
}
static void
runtime·linux_setup_vdso(int32 argc, uint8** argv)
{
struct vdso_info vdso_info;
byte **p = argv;
p = &p[argc+1];
for(; *p!=0; p++) {}
p++;
Elf64_auxv_t *elf_auxv = (Elf64_auxv_t*) p;
for(int32 i=0; elf_auxv[i].a_type!=AT_NULL; i++) {
if(elf_auxv[i].a_type == AT_SYSINFO_EHDR) {
if(elf_auxv[i].a_un.a_val == 0) {
continue;
}
vdso_init_from_sysinfo_ehdr(&vdso_info, (Elf64_Ehdr*)elf_auxv[i].a_un.a_val);
vdso_parse_symbols(&vdso_info, vdso_find_version(&vdso_info, &linux26));
continue;
}
if(elf_auxv[i].a_type == AT_RANDOM) {
runtime·startup_random_data = (byte*)elf_auxv[i].a_un.a_val;
runtime·startup_random_data_len = 16;
continue;
}
}
}
void (*runtime·sysargs)(int32, uint8**) = runtime·linux_setup_vdso;