...

Source file src/runtime/vdso_linux.go

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

View as plain text