Home | History | Annotate | Download | only in runtime
      1 // Copyright 2012 The Go Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style
      3 // license that can be found in the LICENSE file.
      4 
      5 package runtime
      6 
      7 import "unsafe"
      8 
      9 // Look up symbols in the Linux vDSO.
     10 
     11 // This code was originally based on the sample Linux vDSO parser at
     12 // https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/Documentation/vDSO/parse_vdso.c
     13 
     14 // This implements the ELF dynamic linking spec at
     15 // http://sco.com/developers/gabi/latest/ch5.dynamic.html
     16 
     17 // The version section is documented at
     18 // http://refspecs.linuxfoundation.org/LSB_3.2.0/LSB-Core-generic/LSB-Core-generic/symversion.html
     19 
     20 const (
     21 	_AT_RANDOM       = 25
     22 	_AT_SYSINFO_EHDR = 33
     23 	_AT_NULL         = 0 /* End of vector */
     24 
     25 	_PT_LOAD    = 1 /* Loadable program segment */
     26 	_PT_DYNAMIC = 2 /* Dynamic linking information */
     27 
     28 	_DT_NULL   = 0 /* Marks end of dynamic section */
     29 	_DT_HASH   = 4 /* Dynamic symbol hash table */
     30 	_DT_STRTAB = 5 /* Address of string table */
     31 	_DT_SYMTAB = 6 /* Address of symbol table */
     32 	_DT_VERSYM = 0x6ffffff0
     33 	_DT_VERDEF = 0x6ffffffc
     34 
     35 	_VER_FLG_BASE = 0x1 /* Version definition of file itself */
     36 
     37 	_SHN_UNDEF = 0 /* Undefined section */
     38 
     39 	_SHT_DYNSYM = 11 /* Dynamic linker symbol table */
     40 
     41 	_STT_FUNC = 2 /* Symbol is a code object */
     42 
     43 	_STB_GLOBAL = 1 /* Global symbol */
     44 	_STB_WEAK   = 2 /* Weak symbol */
     45 
     46 	_EI_NIDENT = 16
     47 )
     48 
     49 /* How to extract and insert information held in the st_info field.  */
     50 func _ELF64_ST_BIND(val byte) byte { return val >> 4 }
     51 func _ELF64_ST_TYPE(val byte) byte { return val & 0xf }
     52 
     53 type elf64Sym struct {
     54 	st_name  uint32
     55 	st_info  byte
     56 	st_other byte
     57 	st_shndx uint16
     58 	st_value uint64
     59 	st_size  uint64
     60 }
     61 
     62 type elf64Verdef struct {
     63 	vd_version uint16 /* Version revision */
     64 	vd_flags   uint16 /* Version information */
     65 	vd_ndx     uint16 /* Version Index */
     66 	vd_cnt     uint16 /* Number of associated aux entries */
     67 	vd_hash    uint32 /* Version name hash value */
     68 	vd_aux     uint32 /* Offset in bytes to verdaux array */
     69 	vd_next    uint32 /* Offset in bytes to next verdef entry */
     70 }
     71 
     72 type elf64Ehdr struct {
     73 	e_ident     [_EI_NIDENT]byte /* Magic number and other info */
     74 	e_type      uint16           /* Object file type */
     75 	e_machine   uint16           /* Architecture */
     76 	e_version   uint32           /* Object file version */
     77 	e_entry     uint64           /* Entry point virtual address */
     78 	e_phoff     uint64           /* Program header table file offset */
     79 	e_shoff     uint64           /* Section header table file offset */
     80 	e_flags     uint32           /* Processor-specific flags */
     81 	e_ehsize    uint16           /* ELF header size in bytes */
     82 	e_phentsize uint16           /* Program header table entry size */
     83 	e_phnum     uint16           /* Program header table entry count */
     84 	e_shentsize uint16           /* Section header table entry size */
     85 	e_shnum     uint16           /* Section header table entry count */
     86 	e_shstrndx  uint16           /* Section header string table index */
     87 }
     88 
     89 type elf64Phdr struct {
     90 	p_type   uint32 /* Segment type */
     91 	p_flags  uint32 /* Segment flags */
     92 	p_offset uint64 /* Segment file offset */
     93 	p_vaddr  uint64 /* Segment virtual address */
     94 	p_paddr  uint64 /* Segment physical address */
     95 	p_filesz uint64 /* Segment size in file */
     96 	p_memsz  uint64 /* Segment size in memory */
     97 	p_align  uint64 /* Segment alignment */
     98 }
     99 
    100 type elf64Shdr struct {
    101 	sh_name      uint32 /* Section name (string tbl index) */
    102 	sh_type      uint32 /* Section type */
    103 	sh_flags     uint64 /* Section flags */
    104 	sh_addr      uint64 /* Section virtual addr at execution */
    105 	sh_offset    uint64 /* Section file offset */
    106 	sh_size      uint64 /* Section size in bytes */
    107 	sh_link      uint32 /* Link to another section */
    108 	sh_info      uint32 /* Additional section information */
    109 	sh_addralign uint64 /* Section alignment */
    110 	sh_entsize   uint64 /* Entry size if section holds table */
    111 }
    112 
    113 type elf64Dyn struct {
    114 	d_tag int64  /* Dynamic entry type */
    115 	d_val uint64 /* Integer value */
    116 }
    117 
    118 type elf64Verdaux struct {
    119 	vda_name uint32 /* Version or dependency names */
    120 	vda_next uint32 /* Offset in bytes to next verdaux entry */
    121 }
    122 
    123 type elf64Auxv struct {
    124 	a_type uint64 /* Entry type */
    125 	a_val  uint64 /* Integer value */
    126 }
    127 
    128 type symbol_key struct {
    129 	name     string
    130 	sym_hash uint32
    131 	ptr      *uintptr
    132 }
    133 
    134 type version_key struct {
    135 	version  string
    136 	ver_hash uint32
    137 }
    138 
    139 type vdso_info struct {
    140 	valid bool
    141 
    142 	/* Load information */
    143 	load_addr   uintptr
    144 	load_offset uintptr /* load_addr - recorded vaddr */
    145 
    146 	/* Symbol table */
    147 	symtab     *[1 << 32]elf64Sym
    148 	symstrings *[1 << 32]byte
    149 	chain      []uint32
    150 	bucket     []uint32
    151 
    152 	/* Version table */
    153 	versym *[1 << 32]uint16
    154 	verdef *elf64Verdef
    155 }
    156 
    157 var linux26 = version_key{"LINUX_2.6", 0x3ae75f6}
    158 
    159 var sym_keys = []symbol_key{
    160 	{"__vdso_time", 0xa33c485, &__vdso_time_sym},
    161 	{"__vdso_gettimeofday", 0x315ca59, &__vdso_gettimeofday_sym},
    162 	{"__vdso_clock_gettime", 0xd35ec75, &__vdso_clock_gettime_sym},
    163 }
    164 
    165 // initialize with vsyscall fallbacks
    166 var (
    167 	__vdso_time_sym          uintptr = 0xffffffffff600400
    168 	__vdso_gettimeofday_sym  uintptr = 0xffffffffff600000
    169 	__vdso_clock_gettime_sym uintptr = 0
    170 )
    171 
    172 func vdso_init_from_sysinfo_ehdr(info *vdso_info, hdr *elf64Ehdr) {
    173 	info.valid = false
    174 	info.load_addr = uintptr(unsafe.Pointer(hdr))
    175 
    176 	pt := unsafe.Pointer(info.load_addr + uintptr(hdr.e_phoff))
    177 
    178 	// We need two things from the segment table: the load offset
    179 	// and the dynamic table.
    180 	var found_vaddr bool
    181 	var dyn *[1 << 20]elf64Dyn
    182 	for i := uint16(0); i < hdr.e_phnum; i++ {
    183 		pt := (*elf64Phdr)(add(pt, uintptr(i)*unsafe.Sizeof(elf64Phdr{})))
    184 		switch pt.p_type {
    185 		case _PT_LOAD:
    186 			if !found_vaddr {
    187 				found_vaddr = true
    188 				info.load_offset = info.load_addr + uintptr(pt.p_offset-pt.p_vaddr)
    189 			}
    190 
    191 		case _PT_DYNAMIC:
    192 			dyn = (*[1 << 20]elf64Dyn)(unsafe.Pointer(info.load_addr + uintptr(pt.p_offset)))
    193 		}
    194 	}
    195 
    196 	if !found_vaddr || dyn == nil {
    197 		return // Failed
    198 	}
    199 
    200 	// Fish out the useful bits of the dynamic table.
    201 
    202 	var hash *[1 << 30]uint32
    203 	hash = nil
    204 	info.symstrings = nil
    205 	info.symtab = nil
    206 	info.versym = nil
    207 	info.verdef = nil
    208 	for i := 0; dyn[i].d_tag != _DT_NULL; i++ {
    209 		dt := &dyn[i]
    210 		p := info.load_offset + uintptr(dt.d_val)
    211 		switch dt.d_tag {
    212 		case _DT_STRTAB:
    213 			info.symstrings = (*[1 << 32]byte)(unsafe.Pointer(p))
    214 		case _DT_SYMTAB:
    215 			info.symtab = (*[1 << 32]elf64Sym)(unsafe.Pointer(p))
    216 		case _DT_HASH:
    217 			hash = (*[1 << 30]uint32)(unsafe.Pointer(p))
    218 		case _DT_VERSYM:
    219 			info.versym = (*[1 << 32]uint16)(unsafe.Pointer(p))
    220 		case _DT_VERDEF:
    221 			info.verdef = (*elf64Verdef)(unsafe.Pointer(p))
    222 		}
    223 	}
    224 
    225 	if info.symstrings == nil || info.symtab == nil || hash == nil {
    226 		return // Failed
    227 	}
    228 
    229 	if info.verdef == nil {
    230 		info.versym = nil
    231 	}
    232 
    233 	// Parse the hash table header.
    234 	nbucket := hash[0]
    235 	nchain := hash[1]
    236 	info.bucket = hash[2 : 2+nbucket]
    237 	info.chain = hash[2+nbucket : 2+nbucket+nchain]
    238 
    239 	// That's all we need.
    240 	info.valid = true
    241 }
    242 
    243 func vdso_find_version(info *vdso_info, ver *version_key) int32 {
    244 	if !info.valid {
    245 		return 0
    246 	}
    247 
    248 	def := info.verdef
    249 	for {
    250 		if def.vd_flags&_VER_FLG_BASE == 0 {
    251 			aux := (*elf64Verdaux)(add(unsafe.Pointer(def), uintptr(def.vd_aux)))
    252 			if def.vd_hash == ver.ver_hash && ver.version == gostringnocopy(&info.symstrings[aux.vda_name]) {
    253 				return int32(def.vd_ndx & 0x7fff)
    254 			}
    255 		}
    256 
    257 		if def.vd_next == 0 {
    258 			break
    259 		}
    260 		def = (*elf64Verdef)(add(unsafe.Pointer(def), uintptr(def.vd_next)))
    261 	}
    262 
    263 	return -1 // can not match any version
    264 }
    265 
    266 func vdso_parse_symbols(info *vdso_info, version int32) {
    267 	if !info.valid {
    268 		return
    269 	}
    270 
    271 	for _, k := range sym_keys {
    272 		for chain := info.bucket[k.sym_hash%uint32(len(info.bucket))]; chain != 0; chain = info.chain[chain] {
    273 			sym := &info.symtab[chain]
    274 			typ := _ELF64_ST_TYPE(sym.st_info)
    275 			bind := _ELF64_ST_BIND(sym.st_info)
    276 			if typ != _STT_FUNC || bind != _STB_GLOBAL && bind != _STB_WEAK || sym.st_shndx == _SHN_UNDEF {
    277 				continue
    278 			}
    279 			if k.name != gostringnocopy(&info.symstrings[sym.st_name]) {
    280 				continue
    281 			}
    282 
    283 			// Check symbol version.
    284 			if info.versym != nil && version != 0 && int32(info.versym[chain]&0x7fff) != version {
    285 				continue
    286 			}
    287 
    288 			*k.ptr = info.load_offset + uintptr(sym.st_value)
    289 			break
    290 		}
    291 	}
    292 }
    293 
    294 func sysargs(argc int32, argv **byte) {
    295 	n := argc + 1
    296 
    297 	// skip envp to get to ELF auxiliary vector.
    298 	for argv_index(argv, n) != nil {
    299 		n++
    300 	}
    301 
    302 	// skip NULL separator
    303 	n++
    304 
    305 	// now argv+n is auxv
    306 	auxv := (*[1 << 32]elf64Auxv)(add(unsafe.Pointer(argv), uintptr(n)*ptrSize))
    307 
    308 	for i := 0; auxv[i].a_type != _AT_NULL; i++ {
    309 		av := &auxv[i]
    310 		switch av.a_type {
    311 		case _AT_SYSINFO_EHDR:
    312 			if av.a_val == 0 {
    313 				// Something went wrong
    314 				continue
    315 			}
    316 			var info vdso_info
    317 			// TODO(rsc): I don't understand why the compiler thinks info escapes
    318 			// when passed to the three functions below.
    319 			info1 := (*vdso_info)(noescape(unsafe.Pointer(&info)))
    320 			vdso_init_from_sysinfo_ehdr(info1, (*elf64Ehdr)(unsafe.Pointer(uintptr(av.a_val))))
    321 			vdso_parse_symbols(info1, vdso_find_version(info1, &linux26))
    322 
    323 		case _AT_RANDOM:
    324 			startupRandomData = (*[16]byte)(unsafe.Pointer(uintptr(av.a_val)))[:]
    325 		}
    326 	}
    327 }
    328