github.com/ltltlt/go-source-code@v0.0.0-20190830023027-95be009773aa/runtime/vdso_linux.go (about) 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 // +build linux 6 // +build 386 amd64 7 8 package runtime 9 10 import "unsafe" 11 12 // Look up symbols in the Linux vDSO. 13 14 // This code was originally based on the sample Linux vDSO parser at 15 // https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/tools/testing/selftests/vDSO/parse_vdso.c 16 17 // This implements the ELF dynamic linking spec at 18 // http://sco.com/developers/gabi/latest/ch5.dynamic.html 19 20 // The version section is documented at 21 // http://refspecs.linuxfoundation.org/LSB_3.2.0/LSB-Core-generic/LSB-Core-generic/symversion.html 22 23 const ( 24 _AT_SYSINFO_EHDR = 33 25 26 _PT_LOAD = 1 /* Loadable program segment */ 27 _PT_DYNAMIC = 2 /* Dynamic linking information */ 28 29 _DT_NULL = 0 /* Marks end of dynamic section */ 30 _DT_HASH = 4 /* Dynamic symbol hash table */ 31 _DT_STRTAB = 5 /* Address of string table */ 32 _DT_SYMTAB = 6 /* Address of symbol table */ 33 _DT_GNU_HASH = 0x6ffffef5 /* GNU-style dynamic symbol hash table */ 34 _DT_VERSYM = 0x6ffffff0 35 _DT_VERDEF = 0x6ffffffc 36 37 _VER_FLG_BASE = 0x1 /* Version definition of file itself */ 38 39 _SHN_UNDEF = 0 /* Undefined section */ 40 41 _SHT_DYNSYM = 11 /* Dynamic linker symbol table */ 42 43 _STT_FUNC = 2 /* Symbol is a code object */ 44 45 _STB_GLOBAL = 1 /* Global symbol */ 46 _STB_WEAK = 2 /* Weak symbol */ 47 48 _EI_NIDENT = 16 49 50 // Maximum indices for the array types used when traversing the vDSO ELF structures. 51 // Computed from architecture-specific max provided by vdso_linux_*.go 52 vdsoSymTabSize = vdsoArrayMax / unsafe.Sizeof(elfSym{}) 53 vdsoDynSize = vdsoArrayMax / unsafe.Sizeof(elfDyn{}) 54 vdsoSymStringsSize = vdsoArrayMax // byte 55 vdsoVerSymSize = vdsoArrayMax / 2 // uint16 56 vdsoHashSize = vdsoArrayMax / 4 // uint32 57 58 // vdsoBloomSizeScale is a scaling factor for gnuhash tables which are uint32 indexed, 59 // but contain uintptrs 60 vdsoBloomSizeScale = unsafe.Sizeof(uintptr(0)) / 4 // uint32 61 ) 62 63 /* How to extract and insert information held in the st_info field. */ 64 func _ELF_ST_BIND(val byte) byte { return val >> 4 } 65 func _ELF_ST_TYPE(val byte) byte { return val & 0xf } 66 67 type symbol_key struct { 68 name string 69 sym_hash uint32 70 gnu_hash uint32 71 ptr *uintptr 72 } 73 74 type version_key struct { 75 version string 76 ver_hash uint32 77 } 78 79 type vdso_info struct { 80 valid bool 81 82 /* Load information */ 83 load_addr uintptr 84 load_offset uintptr /* load_addr - recorded vaddr */ 85 86 /* Symbol table */ 87 symtab *[vdsoSymTabSize]elfSym 88 symstrings *[vdsoSymStringsSize]byte 89 chain []uint32 90 bucket []uint32 91 symOff uint32 92 isGNUHash bool 93 94 /* Version table */ 95 versym *[vdsoVerSymSize]uint16 96 verdef *elfVerdef 97 } 98 99 var linux26 = version_key{"LINUX_2.6", 0x3ae75f6} 100 101 // see vdso_linux_*.go for sym_keys[] and __vdso_* vars 102 103 func vdso_init_from_sysinfo_ehdr(info *vdso_info, hdr *elfEhdr) { 104 info.valid = false 105 info.load_addr = uintptr(unsafe.Pointer(hdr)) 106 107 pt := unsafe.Pointer(info.load_addr + uintptr(hdr.e_phoff)) 108 109 // We need two things from the segment table: the load offset 110 // and the dynamic table. 111 var found_vaddr bool 112 var dyn *[vdsoDynSize]elfDyn 113 for i := uint16(0); i < hdr.e_phnum; i++ { 114 pt := (*elfPhdr)(add(pt, uintptr(i)*unsafe.Sizeof(elfPhdr{}))) 115 switch pt.p_type { 116 case _PT_LOAD: 117 if !found_vaddr { 118 found_vaddr = true 119 info.load_offset = info.load_addr + uintptr(pt.p_offset-pt.p_vaddr) 120 } 121 122 case _PT_DYNAMIC: 123 dyn = (*[vdsoDynSize]elfDyn)(unsafe.Pointer(info.load_addr + uintptr(pt.p_offset))) 124 } 125 } 126 127 if !found_vaddr || dyn == nil { 128 return // Failed 129 } 130 131 // Fish out the useful bits of the dynamic table. 132 133 var hash, gnuhash *[vdsoHashSize]uint32 134 info.symstrings = nil 135 info.symtab = nil 136 info.versym = nil 137 info.verdef = nil 138 for i := 0; dyn[i].d_tag != _DT_NULL; i++ { 139 dt := &dyn[i] 140 p := info.load_offset + uintptr(dt.d_val) 141 switch dt.d_tag { 142 case _DT_STRTAB: 143 info.symstrings = (*[vdsoSymStringsSize]byte)(unsafe.Pointer(p)) 144 case _DT_SYMTAB: 145 info.symtab = (*[vdsoSymTabSize]elfSym)(unsafe.Pointer(p)) 146 case _DT_HASH: 147 hash = (*[vdsoHashSize]uint32)(unsafe.Pointer(p)) 148 case _DT_GNU_HASH: 149 gnuhash = (*[vdsoHashSize]uint32)(unsafe.Pointer(p)) 150 case _DT_VERSYM: 151 info.versym = (*[vdsoVerSymSize]uint16)(unsafe.Pointer(p)) 152 case _DT_VERDEF: 153 info.verdef = (*elfVerdef)(unsafe.Pointer(p)) 154 } 155 } 156 157 if info.symstrings == nil || info.symtab == nil || (hash == nil && gnuhash == nil) { 158 return // Failed 159 } 160 161 if info.verdef == nil { 162 info.versym = nil 163 } 164 165 if gnuhash != nil { 166 // Parse the GNU hash table header. 167 nbucket := gnuhash[0] 168 info.symOff = gnuhash[1] 169 bloomSize := gnuhash[2] 170 info.bucket = gnuhash[4+bloomSize*uint32(vdsoBloomSizeScale):][:nbucket] 171 info.chain = gnuhash[4+bloomSize*uint32(vdsoBloomSizeScale)+nbucket:] 172 info.isGNUHash = true 173 } else { 174 // Parse the hash table header. 175 nbucket := hash[0] 176 nchain := hash[1] 177 info.bucket = hash[2 : 2+nbucket] 178 info.chain = hash[2+nbucket : 2+nbucket+nchain] 179 } 180 181 // That's all we need. 182 info.valid = true 183 } 184 185 func vdso_find_version(info *vdso_info, ver *version_key) int32 { 186 if !info.valid { 187 return 0 188 } 189 190 def := info.verdef 191 for { 192 if def.vd_flags&_VER_FLG_BASE == 0 { 193 aux := (*elfVerdaux)(add(unsafe.Pointer(def), uintptr(def.vd_aux))) 194 if def.vd_hash == ver.ver_hash && ver.version == gostringnocopy(&info.symstrings[aux.vda_name]) { 195 return int32(def.vd_ndx & 0x7fff) 196 } 197 } 198 199 if def.vd_next == 0 { 200 break 201 } 202 def = (*elfVerdef)(add(unsafe.Pointer(def), uintptr(def.vd_next))) 203 } 204 205 return -1 // cannot match any version 206 } 207 208 func vdso_parse_symbols(info *vdso_info, version int32) { 209 if !info.valid { 210 return 211 } 212 213 apply := func(symIndex uint32, k symbol_key) bool { 214 sym := &info.symtab[symIndex] 215 typ := _ELF_ST_TYPE(sym.st_info) 216 bind := _ELF_ST_BIND(sym.st_info) 217 if typ != _STT_FUNC || bind != _STB_GLOBAL && bind != _STB_WEAK || sym.st_shndx == _SHN_UNDEF { 218 return false 219 } 220 if k.name != gostringnocopy(&info.symstrings[sym.st_name]) { 221 return false 222 } 223 224 // Check symbol version. 225 if info.versym != nil && version != 0 && int32(info.versym[symIndex]&0x7fff) != version { 226 return false 227 } 228 229 *k.ptr = info.load_offset + uintptr(sym.st_value) 230 return true 231 } 232 233 if !info.isGNUHash { 234 // Old-style DT_HASH table. 235 for _, k := range sym_keys { 236 for chain := info.bucket[k.sym_hash%uint32(len(info.bucket))]; chain != 0; chain = info.chain[chain] { 237 if apply(chain, k) { 238 break 239 } 240 } 241 } 242 return 243 } 244 245 // New-style DT_GNU_HASH table. 246 for _, k := range sym_keys { 247 symIndex := info.bucket[k.gnu_hash%uint32(len(info.bucket))] 248 if symIndex < info.symOff { 249 continue 250 } 251 for ; ; symIndex++ { 252 hash := info.chain[symIndex-info.symOff] 253 if hash|1 == k.gnu_hash|1 { 254 // Found a hash match. 255 if apply(symIndex, k) { 256 break 257 } 258 } 259 if hash&1 != 0 { 260 // End of chain. 261 break 262 } 263 } 264 } 265 } 266 267 func archauxv(tag, val uintptr) { 268 switch tag { 269 case _AT_SYSINFO_EHDR: 270 if val == 0 { 271 // Something went wrong 272 return 273 } 274 var info vdso_info 275 // TODO(rsc): I don't understand why the compiler thinks info escapes 276 // when passed to the three functions below. 277 info1 := (*vdso_info)(noescape(unsafe.Pointer(&info))) 278 vdso_init_from_sysinfo_ehdr(info1, (*elfEhdr)(unsafe.Pointer(val))) 279 vdso_parse_symbols(info1, vdso_find_version(info1, &linux26)) 280 } 281 }