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