| // Copyright 2012 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package runtime |
| |
| import "unsafe" |
| |
| // Look up symbols in the Fuchsia vDSO. |
| // |
| // When processes are spawned in Fuchsia, they are provided with a handle to a channel |
| // and a pointer to where the vDSO is loaded. Syscalls may never be called directly; |
| // on syscall entry, the kernel checks to see that the syscall was entered from the |
| // vDSO. Because reading from the channel requires performing a syscall, we must |
| // resolve symbols from the vDSO first. |
| // |
| // This is only performed when not using cgo. When a Go binary is built with cgo, libc |
| // handles all of this. |
| |
| type vdsoSymbolKey struct { |
| name string |
| gnuHash uint32 |
| ptr *uintptr |
| } |
| |
| type vdsoInfo struct { |
| /* Load information */ |
| loadAddr uintptr |
| loadBase uintptr /* loadAddr + p_vaddr */ |
| |
| /* Symbol table */ |
| symtab *[vdsoSymTabSize]elfSym |
| symstrings *[vdsoSymStringsSize]byte |
| chain []uint32 |
| bucket []uint32 |
| symOff uint32 |
| nBuckets uint32 |
| } |
| |
| var ( |
| vdsoLoadBase uintptr |
| vdsoMemSize uintptr |
| ) |
| |
| // see vdso_keys_fuchsia.go for vdsoSymbolKeys[] and vdso*Sym vars |
| |
| func vdsoInit(info *vdsoInfo, hdr *elfEhdr) { |
| info.loadAddr = uintptr(unsafe.Pointer(hdr)) |
| |
| pt := unsafe.Pointer(info.loadAddr + uintptr(hdr.e_phoff)) |
| |
| // We need two things from the segment table: the load offset |
| // and the dynamic table. |
| var foundVaddr bool |
| var dyn *[vdsoDynSize]elfDyn |
| for i := uint16(0); i < hdr.e_phnum; i++ { |
| pt := (*elfPhdr)(add(pt, uintptr(i)*unsafe.Sizeof(elfPhdr{}))) |
| switch pt.p_type { |
| case _PT_LOAD: |
| if !foundVaddr && pt.p_flags&_PF_X == _PF_X { |
| foundVaddr = true |
| info.loadBase = info.loadAddr + uintptr(pt.p_vaddr) |
| vdsoLoadBase = info.loadBase |
| vdsoMemSize = uintptr(pt.p_memsz) |
| } |
| |
| case _PT_DYNAMIC: |
| dyn = (*[vdsoDynSize]elfDyn)(unsafe.Pointer(info.loadAddr + uintptr(pt.p_vaddr))) |
| } |
| } |
| |
| if !foundVaddr || dyn == nil { |
| crash() |
| } |
| |
| // Fish out the useful bits of the dynamic table. |
| var gnuhash *[vdsoHashSize]uint32 |
| info.symstrings = nil |
| info.symtab = nil |
| for i := 0; dyn[i].d_tag != _DT_NULL; i++ { |
| dt := &dyn[i] |
| p := info.loadAddr + uintptr(dt.d_val) |
| switch dt.d_tag { |
| case _DT_STRTAB: |
| info.symstrings = (*[vdsoSymStringsSize]byte)(unsafe.Pointer(p)) |
| case _DT_SYMTAB: |
| info.symtab = (*[vdsoSymTabSize]elfSym)(unsafe.Pointer(p)) |
| case _DT_GNU_HASH: |
| gnuhash = (*[vdsoHashSize]uint32)(unsafe.Pointer(p)) |
| } |
| } |
| |
| if info.symstrings == nil || info.symtab == nil || gnuhash == nil { |
| crash() |
| } |
| |
| // Parse the GNU hash table header. |
| info.nBuckets = gnuhash[0] |
| info.symOff = gnuhash[1] |
| bloomSize := gnuhash[2] |
| info.bucket = gnuhash[4+bloomSize*uint32(vdsoBloomSizeScale):][:info.nBuckets] |
| info.chain = gnuhash[4+bloomSize*uint32(vdsoBloomSizeScale)+info.nBuckets:] |
| } |
| |
| func vdsoResolveSymbols(info *vdsoInfo) { |
| // Loop over the keys we wish to resolve and assign the symbol's offset to the |
| // pointer in the key table. We assume that all symbols we want to resolve |
| // exist, and crash if we are unable to resolve any of them. |
| for _, k := range vdsoSymbolKeys { |
| symIndex := info.bucket[k.gnuHash%info.nBuckets] |
| |
| // Anything less than info.symOff is e.g. STN_UNDEF and will not be |
| // resolvable. Since that means we wouldn't be able to resolve this |
| // symbol we definitely need, crash. |
| if symIndex < info.symOff { |
| crash() |
| } |
| |
| // For each symbol in this bucket, compare hashes and the name. If the |
| // symbol is not found, crash. |
| for ; ; symIndex++ { |
| hash := info.chain[symIndex-info.symOff] |
| sym := &info.symtab[symIndex] |
| if hash|1 == k.gnuHash|1 && k.name == gostringnocopy(&info.symstrings[sym.st_name]) { |
| typ := _ELF_ST_TYPE(sym.st_info) |
| bind := _ELF_ST_BIND(sym.st_info) |
| // If this isn't a function, doesn't have the appropriate binding, or |
| // has an undefined index, crash. |
| if typ != _STT_FUNC || (bind != _STB_GLOBAL && bind != _STB_WEAK) || sym.st_shndx == _SHN_UNDEF { |
| crash() |
| } |
| |
| // We've found our symbol, assign its address and continue on to the |
| // next symbol. |
| *k.ptr = info.loadAddr + uintptr(sym.st_value) |
| break |
| } |
| |
| if hash&1 != 0 { |
| // We made it to the end of this chain without finding |
| // our symbol. We won't find it. Crash. |
| crash() |
| } |
| } |
| } |
| } |
| |
| func loadVDSO(val uintptr) { |
| var info vdsoInfo |
| // TODO(dhobsd): rsc says in vdso_linux.go that the compiler thinks info |
| // escapes. Remove/fix/investigate this comment at some point. |
| info1 := (*vdsoInfo)(noescape(unsafe.Pointer(&info))) |
| vdsoInit(info1, (*elfEhdr)(unsafe.Pointer(val))) |
| vdsoResolveSymbols(info1) |
| } |
| |
| // inVDSOPage returns whether an observed PC falls within the VDSO mapping. |
| func inVDSOPage(pc uintptr) bool { |
| return pc >= vdsoLoadBase && pc < vdsoLoadBase+vdsoMemSize |
| } |