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