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