blob: 9e4c7f3d8d9db929ed110767c7511271c3b8eafd [file] [log] [blame]
// 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
}