| // Copyright 2012 The Go Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style | 
 | // license that can be found in the LICENSE file. | 
 |  | 
 | package runtime | 
 |  | 
 | import "unsafe" | 
 |  | 
 | // 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 | 
 |  | 
 | const ( | 
 | 	_AT_SYSINFO_EHDR = 33 | 
 |  | 
 | 	_PT_LOAD    = 1 /* Loadable program segment */ | 
 | 	_PT_DYNAMIC = 2 /* Dynamic linking information */ | 
 |  | 
 | 	_DT_NULL     = 0          /* Marks end of dynamic section */ | 
 | 	_DT_HASH     = 4          /* Dynamic symbol hash table */ | 
 | 	_DT_STRTAB   = 5          /* Address of string table */ | 
 | 	_DT_SYMTAB   = 6          /* Address of symbol table */ | 
 | 	_DT_GNU_HASH = 0x6ffffef5 /* GNU-style dynamic symbol hash table */ | 
 | 	_DT_VERSYM   = 0x6ffffff0 | 
 | 	_DT_VERDEF   = 0x6ffffffc | 
 |  | 
 | 	_VER_FLG_BASE = 0x1 /* Version definition of file itself */ | 
 |  | 
 | 	_SHN_UNDEF = 0 /* Undefined section */ | 
 |  | 
 | 	_SHT_DYNSYM = 11 /* Dynamic linker symbol table */ | 
 |  | 
 | 	_STT_FUNC = 2 /* Symbol is a code object */ | 
 |  | 
 | 	_STB_GLOBAL = 1 /* Global symbol */ | 
 | 	_STB_WEAK   = 2 /* Weak symbol */ | 
 |  | 
 | 	_EI_NIDENT = 16 | 
 | ) | 
 |  | 
 | /* How to extract and insert information held in the st_info field.  */ | 
 | func _ELF64_ST_BIND(val byte) byte { return val >> 4 } | 
 | func _ELF64_ST_TYPE(val byte) byte { return val & 0xf } | 
 |  | 
 | type elf64Sym struct { | 
 | 	st_name  uint32 | 
 | 	st_info  byte | 
 | 	st_other byte | 
 | 	st_shndx uint16 | 
 | 	st_value uint64 | 
 | 	st_size  uint64 | 
 | } | 
 |  | 
 | type elf64Verdef struct { | 
 | 	vd_version uint16 /* Version revision */ | 
 | 	vd_flags   uint16 /* Version information */ | 
 | 	vd_ndx     uint16 /* Version Index */ | 
 | 	vd_cnt     uint16 /* Number of associated aux entries */ | 
 | 	vd_hash    uint32 /* Version name hash value */ | 
 | 	vd_aux     uint32 /* Offset in bytes to verdaux array */ | 
 | 	vd_next    uint32 /* Offset in bytes to next verdef entry */ | 
 | } | 
 |  | 
 | type elf64Ehdr struct { | 
 | 	e_ident     [_EI_NIDENT]byte /* Magic number and other info */ | 
 | 	e_type      uint16           /* Object file type */ | 
 | 	e_machine   uint16           /* Architecture */ | 
 | 	e_version   uint32           /* Object file version */ | 
 | 	e_entry     uint64           /* Entry point virtual address */ | 
 | 	e_phoff     uint64           /* Program header table file offset */ | 
 | 	e_shoff     uint64           /* Section header table file offset */ | 
 | 	e_flags     uint32           /* Processor-specific flags */ | 
 | 	e_ehsize    uint16           /* ELF header size in bytes */ | 
 | 	e_phentsize uint16           /* Program header table entry size */ | 
 | 	e_phnum     uint16           /* Program header table entry count */ | 
 | 	e_shentsize uint16           /* Section header table entry size */ | 
 | 	e_shnum     uint16           /* Section header table entry count */ | 
 | 	e_shstrndx  uint16           /* Section header string table index */ | 
 | } | 
 |  | 
 | type elf64Phdr struct { | 
 | 	p_type   uint32 /* Segment type */ | 
 | 	p_flags  uint32 /* Segment flags */ | 
 | 	p_offset uint64 /* Segment file offset */ | 
 | 	p_vaddr  uint64 /* Segment virtual address */ | 
 | 	p_paddr  uint64 /* Segment physical address */ | 
 | 	p_filesz uint64 /* Segment size in file */ | 
 | 	p_memsz  uint64 /* Segment size in memory */ | 
 | 	p_align  uint64 /* Segment alignment */ | 
 | } | 
 |  | 
 | type elf64Shdr struct { | 
 | 	sh_name      uint32 /* Section name (string tbl index) */ | 
 | 	sh_type      uint32 /* Section type */ | 
 | 	sh_flags     uint64 /* Section flags */ | 
 | 	sh_addr      uint64 /* Section virtual addr at execution */ | 
 | 	sh_offset    uint64 /* Section file offset */ | 
 | 	sh_size      uint64 /* Section size in bytes */ | 
 | 	sh_link      uint32 /* Link to another section */ | 
 | 	sh_info      uint32 /* Additional section information */ | 
 | 	sh_addralign uint64 /* Section alignment */ | 
 | 	sh_entsize   uint64 /* Entry size if section holds table */ | 
 | } | 
 |  | 
 | type elf64Dyn struct { | 
 | 	d_tag int64  /* Dynamic entry type */ | 
 | 	d_val uint64 /* Integer value */ | 
 | } | 
 |  | 
 | type elf64Verdaux struct { | 
 | 	vda_name uint32 /* Version or dependency names */ | 
 | 	vda_next uint32 /* Offset in bytes to next verdaux entry */ | 
 | } | 
 |  | 
 | type elf64Auxv struct { | 
 | 	a_type uint64 /* Entry type */ | 
 | 	a_val  uint64 /* Integer value */ | 
 | } | 
 |  | 
 | type symbol_key struct { | 
 | 	name     string | 
 | 	sym_hash uint32 | 
 | 	gnu_hash uint32 | 
 | 	ptr      *uintptr | 
 | } | 
 |  | 
 | type version_key struct { | 
 | 	version  string | 
 | 	ver_hash uint32 | 
 | } | 
 |  | 
 | type vdso_info struct { | 
 | 	valid bool | 
 |  | 
 | 	/* Load information */ | 
 | 	load_addr   uintptr | 
 | 	load_offset uintptr /* load_addr - recorded vaddr */ | 
 |  | 
 | 	/* Symbol table */ | 
 | 	symtab     *[1 << 32]elf64Sym | 
 | 	symstrings *[1 << 32]byte | 
 | 	chain      []uint32 | 
 | 	bucket     []uint32 | 
 | 	symOff     uint32 | 
 | 	isGNUHash  bool | 
 |  | 
 | 	/* Version table */ | 
 | 	versym *[1 << 32]uint16 | 
 | 	verdef *elf64Verdef | 
 | } | 
 |  | 
 | var linux26 = version_key{"LINUX_2.6", 0x3ae75f6} | 
 |  | 
 | var sym_keys = []symbol_key{ | 
 | 	{"__vdso_time", 0xa33c485, 0x821e8e0d, &__vdso_time_sym}, | 
 | 	{"__vdso_gettimeofday", 0x315ca59, 0xb01bca00, &__vdso_gettimeofday_sym}, | 
 | 	{"__vdso_clock_gettime", 0xd35ec75, 0x6e43a318, &__vdso_clock_gettime_sym}, | 
 | } | 
 |  | 
 | // initialize with vsyscall fallbacks | 
 | var ( | 
 | 	__vdso_time_sym          uintptr = 0xffffffffff600400 | 
 | 	__vdso_gettimeofday_sym  uintptr = 0xffffffffff600000 | 
 | 	__vdso_clock_gettime_sym uintptr = 0 | 
 | ) | 
 |  | 
 | func vdso_init_from_sysinfo_ehdr(info *vdso_info, hdr *elf64Ehdr) { | 
 | 	info.valid = false | 
 | 	info.load_addr = uintptr(unsafe.Pointer(hdr)) | 
 |  | 
 | 	pt := unsafe.Pointer(info.load_addr + uintptr(hdr.e_phoff)) | 
 |  | 
 | 	// We need two things from the segment table: the load offset | 
 | 	// and the dynamic table. | 
 | 	var found_vaddr bool | 
 | 	var dyn *[1 << 20]elf64Dyn | 
 | 	for i := uint16(0); i < hdr.e_phnum; i++ { | 
 | 		pt := (*elf64Phdr)(add(pt, uintptr(i)*unsafe.Sizeof(elf64Phdr{}))) | 
 | 		switch pt.p_type { | 
 | 		case _PT_LOAD: | 
 | 			if !found_vaddr { | 
 | 				found_vaddr = true | 
 | 				info.load_offset = info.load_addr + uintptr(pt.p_offset-pt.p_vaddr) | 
 | 			} | 
 |  | 
 | 		case _PT_DYNAMIC: | 
 | 			dyn = (*[1 << 20]elf64Dyn)(unsafe.Pointer(info.load_addr + uintptr(pt.p_offset))) | 
 | 		} | 
 | 	} | 
 |  | 
 | 	if !found_vaddr || dyn == nil { | 
 | 		return // Failed | 
 | 	} | 
 |  | 
 | 	// Fish out the useful bits of the dynamic table. | 
 |  | 
 | 	var hash, gnuhash *[1 << 30]uint32 | 
 | 	info.symstrings = nil | 
 | 	info.symtab = nil | 
 | 	info.versym = nil | 
 | 	info.verdef = nil | 
 | 	for i := 0; dyn[i].d_tag != _DT_NULL; i++ { | 
 | 		dt := &dyn[i] | 
 | 		p := info.load_offset + uintptr(dt.d_val) | 
 | 		switch dt.d_tag { | 
 | 		case _DT_STRTAB: | 
 | 			info.symstrings = (*[1 << 32]byte)(unsafe.Pointer(p)) | 
 | 		case _DT_SYMTAB: | 
 | 			info.symtab = (*[1 << 32]elf64Sym)(unsafe.Pointer(p)) | 
 | 		case _DT_HASH: | 
 | 			hash = (*[1 << 30]uint32)(unsafe.Pointer(p)) | 
 | 		case _DT_GNU_HASH: | 
 | 			gnuhash = (*[1 << 30]uint32)(unsafe.Pointer(p)) | 
 | 		case _DT_VERSYM: | 
 | 			info.versym = (*[1 << 32]uint16)(unsafe.Pointer(p)) | 
 | 		case _DT_VERDEF: | 
 | 			info.verdef = (*elf64Verdef)(unsafe.Pointer(p)) | 
 | 		} | 
 | 	} | 
 |  | 
 | 	if info.symstrings == nil || info.symtab == nil || (hash == nil && gnuhash == nil) { | 
 | 		return // Failed | 
 | 	} | 
 |  | 
 | 	if info.verdef == nil { | 
 | 		info.versym = nil | 
 | 	} | 
 |  | 
 | 	if gnuhash != nil { | 
 | 		// Parse the GNU hash table header. | 
 | 		nbucket := gnuhash[0] | 
 | 		info.symOff = gnuhash[1] | 
 | 		bloomSize := gnuhash[2] | 
 | 		info.bucket = gnuhash[4+bloomSize*2:][:nbucket] | 
 | 		info.chain = gnuhash[4+bloomSize*2+nbucket:] | 
 | 		info.isGNUHash = true | 
 | 	} else { | 
 | 		// Parse the hash table header. | 
 | 		nbucket := hash[0] | 
 | 		nchain := hash[1] | 
 | 		info.bucket = hash[2 : 2+nbucket] | 
 | 		info.chain = hash[2+nbucket : 2+nbucket+nchain] | 
 | 	} | 
 |  | 
 | 	// That's all we need. | 
 | 	info.valid = true | 
 | } | 
 |  | 
 | func vdso_find_version(info *vdso_info, ver *version_key) int32 { | 
 | 	if !info.valid { | 
 | 		return 0 | 
 | 	} | 
 |  | 
 | 	def := info.verdef | 
 | 	for { | 
 | 		if def.vd_flags&_VER_FLG_BASE == 0 { | 
 | 			aux := (*elf64Verdaux)(add(unsafe.Pointer(def), uintptr(def.vd_aux))) | 
 | 			if def.vd_hash == ver.ver_hash && ver.version == gostringnocopy(&info.symstrings[aux.vda_name]) { | 
 | 				return int32(def.vd_ndx & 0x7fff) | 
 | 			} | 
 | 		} | 
 |  | 
 | 		if def.vd_next == 0 { | 
 | 			break | 
 | 		} | 
 | 		def = (*elf64Verdef)(add(unsafe.Pointer(def), uintptr(def.vd_next))) | 
 | 	} | 
 |  | 
 | 	return -1 // cannot match any version | 
 | } | 
 |  | 
 | func vdso_parse_symbols(info *vdso_info, version int32) { | 
 | 	if !info.valid { | 
 | 		return | 
 | 	} | 
 |  | 
 | 	apply := func(symIndex uint32, k symbol_key) bool { | 
 | 		sym := &info.symtab[symIndex] | 
 | 		typ := _ELF64_ST_TYPE(sym.st_info) | 
 | 		bind := _ELF64_ST_BIND(sym.st_info) | 
 | 		if typ != _STT_FUNC || bind != _STB_GLOBAL && bind != _STB_WEAK || sym.st_shndx == _SHN_UNDEF { | 
 | 			return false | 
 | 		} | 
 | 		if k.name != gostringnocopy(&info.symstrings[sym.st_name]) { | 
 | 			return false | 
 | 		} | 
 |  | 
 | 		// Check symbol version. | 
 | 		if info.versym != nil && version != 0 && int32(info.versym[symIndex]&0x7fff) != version { | 
 | 			return false | 
 | 		} | 
 |  | 
 | 		*k.ptr = info.load_offset + uintptr(sym.st_value) | 
 | 		return true | 
 | 	} | 
 |  | 
 | 	if !info.isGNUHash { | 
 | 		// Old-style DT_HASH table. | 
 | 		for _, k := range sym_keys { | 
 | 			for chain := info.bucket[k.sym_hash%uint32(len(info.bucket))]; chain != 0; chain = info.chain[chain] { | 
 | 				if apply(chain, k) { | 
 | 					break | 
 | 				} | 
 | 			} | 
 | 		} | 
 | 		return | 
 | 	} | 
 |  | 
 | 	// New-style DT_GNU_HASH table. | 
 | 	for _, k := range sym_keys { | 
 | 		symIndex := info.bucket[k.gnu_hash%uint32(len(info.bucket))] | 
 | 		if symIndex < info.symOff { | 
 | 			continue | 
 | 		} | 
 | 		for ; ; symIndex++ { | 
 | 			hash := info.chain[symIndex-info.symOff] | 
 | 			if hash|1 == k.gnu_hash|1 { | 
 | 				// Found a hash match. | 
 | 				if apply(symIndex, k) { | 
 | 					break | 
 | 				} | 
 | 			} | 
 | 			if hash&1 != 0 { | 
 | 				// End of chain. | 
 | 				break | 
 | 			} | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | func archauxv(tag, val uintptr) { | 
 | 	switch tag { | 
 | 	case _AT_SYSINFO_EHDR: | 
 | 		if val == 0 { | 
 | 			// Something went wrong | 
 | 			return | 
 | 		} | 
 | 		var info vdso_info | 
 | 		// TODO(rsc): I don't understand why the compiler thinks info escapes | 
 | 		// when passed to the three functions below. | 
 | 		info1 := (*vdso_info)(noescape(unsafe.Pointer(&info))) | 
 | 		vdso_init_from_sysinfo_ehdr(info1, (*elf64Ehdr)(unsafe.Pointer(val))) | 
 | 		vdso_parse_symbols(info1, vdso_find_version(info1, &linux26)) | 
 | 	} | 
 | } |