...
Run Format

Source file src/runtime/vdso_linux_amd64.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	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	}
   307	

View as plain text