github.com/geraldss/go/src@v0.0.0-20210511222824-ac7d0ebfc235/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 arm arm64 mips64 mips64le ppc64 ppc64le 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 // https://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 _STT_NOTYPE = 0 /* Symbol type is not specified */ 46 47 _STB_GLOBAL = 1 /* Global symbol */ 48 _STB_WEAK = 2 /* Weak symbol */ 49 50 _EI_NIDENT = 16 51 52 // Maximum indices for the array types used when traversing the vDSO ELF structures. 53 // Computed from architecture-specific max provided by vdso_linux_*.go 54 vdsoSymTabSize = vdsoArrayMax / unsafe.Sizeof(elfSym{}) 55 vdsoDynSize = vdsoArrayMax / unsafe.Sizeof(elfDyn{}) 56 vdsoSymStringsSize = vdsoArrayMax // byte 57 vdsoVerSymSize = vdsoArrayMax / 2 // uint16 58 vdsoHashSize = vdsoArrayMax / 4 // uint32 59 60 // vdsoBloomSizeScale is a scaling factor for gnuhash tables which are uint32 indexed, 61 // but contain uintptrs 62 vdsoBloomSizeScale = unsafe.Sizeof(uintptr(0)) / 4 // uint32 63 ) 64 65 /* How to extract and insert information held in the st_info field. */ 66 func _ELF_ST_BIND(val byte) byte { return val >> 4 } 67 func _ELF_ST_TYPE(val byte) byte { return val & 0xf } 68 69 type vdsoSymbolKey struct { 70 name string 71 symHash uint32 72 gnuHash uint32 73 ptr *uintptr 74 } 75 76 type vdsoVersionKey struct { 77 version string 78 verHash uint32 79 } 80 81 type vdsoInfo struct { 82 valid bool 83 84 /* Load information */ 85 loadAddr uintptr 86 loadOffset uintptr /* loadAddr - recorded vaddr */ 87 88 /* Symbol table */ 89 symtab *[vdsoSymTabSize]elfSym 90 symstrings *[vdsoSymStringsSize]byte 91 chain []uint32 92 bucket []uint32 93 symOff uint32 94 isGNUHash bool 95 96 /* Version table */ 97 versym *[vdsoVerSymSize]uint16 98 verdef *elfVerdef 99 } 100 101 // see vdso_linux_*.go for vdsoSymbolKeys[] and vdso*Sym vars 102 103 func vdsoInitFromSysinfoEhdr(info *vdsoInfo, hdr *elfEhdr) { 104 info.valid = false 105 info.loadAddr = uintptr(unsafe.Pointer(hdr)) 106 107 pt := unsafe.Pointer(info.loadAddr + uintptr(hdr.e_phoff)) 108 109 // We need two things from the segment table: the load offset 110 // and the dynamic table. 111 var foundVaddr 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 !foundVaddr { 118 foundVaddr = true 119 info.loadOffset = info.loadAddr + uintptr(pt.p_offset-pt.p_vaddr) 120 } 121 122 case _PT_DYNAMIC: 123 dyn = (*[vdsoDynSize]elfDyn)(unsafe.Pointer(info.loadAddr + uintptr(pt.p_offset))) 124 } 125 } 126 127 if !foundVaddr || 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.loadOffset + 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 vdsoFindVersion(info *vdsoInfo, ver *vdsoVersionKey) 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.verHash && 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 vdsoParseSymbols(info *vdsoInfo, version int32) { 209 if !info.valid { 210 return 211 } 212 213 apply := func(symIndex uint32, k vdsoSymbolKey) bool { 214 sym := &info.symtab[symIndex] 215 typ := _ELF_ST_TYPE(sym.st_info) 216 bind := _ELF_ST_BIND(sym.st_info) 217 // On ppc64x, VDSO functions are of type _STT_NOTYPE. 218 if typ != _STT_FUNC && typ != _STT_NOTYPE || bind != _STB_GLOBAL && bind != _STB_WEAK || sym.st_shndx == _SHN_UNDEF { 219 return false 220 } 221 if k.name != gostringnocopy(&info.symstrings[sym.st_name]) { 222 return false 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.loadOffset + uintptr(sym.st_value) 230 return true 231 } 232 233 if !info.isGNUHash { 234 // Old-style DT_HASH table. 235 for _, k := range vdsoSymbolKeys { 236 for chain := info.bucket[k.symHash%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 vdsoSymbolKeys { 247 symIndex := info.bucket[k.gnuHash%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.gnuHash|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 vdsoauxv(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 vdsoInfo 275 // TODO(rsc): I don't understand why the compiler thinks info escapes 276 // when passed to the three functions below. 277 info1 := (*vdsoInfo)(noescape(unsafe.Pointer(&info))) 278 vdsoInitFromSysinfoEhdr(info1, (*elfEhdr)(unsafe.Pointer(val))) 279 vdsoParseSymbols(info1, vdsoFindVersion(info1, &vdsoLinuxVersion)) 280 } 281 } 282 283 // vdsoMarker reports whether PC is on the VDSO page. 284 //go:nosplit 285 func inVDSOPage(pc uintptr) bool { 286 for _, k := range vdsoSymbolKeys { 287 if *k.ptr != 0 { 288 page := *k.ptr &^ (physPageSize - 1) 289 return pc >= page && pc < page+physPageSize 290 } 291 } 292 return false 293 }