...
Run Format

Source file src/runtime/vdso_linux_amd64.go

  // 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_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
  	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
  
  	/* Version table */
  	versym *[1 << 32]uint16
  	verdef *elf64Verdef
  }
  
  var linux26 = version_key{"LINUX_2.6", 0x3ae75f6}
  
  var sym_keys = []symbol_key{
  	{"__vdso_time", 0xa33c485, &__vdso_time_sym},
  	{"__vdso_gettimeofday", 0x315ca59, &__vdso_gettimeofday_sym},
  	{"__vdso_clock_gettime", 0xd35ec75, &__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 *[1 << 30]uint32
  	hash = nil
  	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_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 {
  		return // Failed
  	}
  
  	if info.verdef == nil {
  		info.versym = nil
  	}
  
  	// 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
  	}
  
  	for _, k := range sym_keys {
  		for chain := info.bucket[k.sym_hash%uint32(len(info.bucket))]; chain != 0; chain = info.chain[chain] {
  			sym := &info.symtab[chain]
  			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 {
  				continue
  			}
  			if k.name != gostringnocopy(&info.symstrings[sym.st_name]) {
  				continue
  			}
  
  			// Check symbol version.
  			if info.versym != nil && version != 0 && int32(info.versym[chain]&0x7fff) != version {
  				continue
  			}
  
  			*k.ptr = info.load_offset + uintptr(sym.st_value)
  			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))
  	}
  }
  

View as plain text