package runtime
import "unsafe"
const (
_AT_SYSINFO_EHDR = 33
_PT_LOAD = 1
_PT_DYNAMIC = 2
_DT_NULL = 0
_DT_HASH = 4
_DT_STRTAB = 5
_DT_SYMTAB = 6
_DT_GNU_HASH = 0x6ffffef5
_DT_VERSYM = 0x6ffffff0
_DT_VERDEF = 0x6ffffffc
_VER_FLG_BASE = 0x1
_SHN_UNDEF = 0
_SHT_DYNSYM = 11
_STT_FUNC = 2
_STT_NOTYPE = 0
_STB_GLOBAL = 1
_STB_WEAK = 2
_EI_NIDENT = 16
vdsoSymTabSize = vdsoArrayMax / unsafe .Sizeof (elfSym {})
vdsoDynSize = vdsoArrayMax / unsafe .Sizeof (elfDyn {})
vdsoSymStringsSize = vdsoArrayMax
vdsoVerSymSize = vdsoArrayMax / 2
vdsoHashSize = vdsoArrayMax / 4
vdsoBloomSizeScale = unsafe .Sizeof (uintptr (0 )) / 4
)
func _ELF_ST_BIND(val byte ) byte { return val >> 4 }
func _ELF_ST_TYPE(val byte ) byte { return val & 0xf }
type vdsoSymbolKey struct {
name string
symHash uint32
gnuHash uint32
ptr *uintptr
}
type vdsoVersionKey struct {
version string
verHash uint32
}
type vdsoInfo struct {
valid bool
loadAddr uintptr
loadOffset uintptr
symtab *[vdsoSymTabSize ]elfSym
symstrings *[vdsoSymStringsSize ]byte
chain []uint32
bucket []uint32
symOff uint32
isGNUHash bool
versym *[vdsoVerSymSize ]uint16
verdef *elfVerdef
}
func vdsoInitFromSysinfoEhdr(info *vdsoInfo , hdr *elfEhdr ) {
info .valid = false
info .loadAddr = uintptr (unsafe .Pointer (hdr ))
pt := unsafe .Pointer (info .loadAddr + uintptr (hdr .e_phoff ))
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 {
foundVaddr = true
info .loadOffset = info .loadAddr + uintptr (pt .p_offset -pt .p_vaddr )
}
case _PT_DYNAMIC :
dyn = (*[vdsoDynSize ]elfDyn )(unsafe .Pointer (info .loadAddr + uintptr (pt .p_offset )))
}
}
if !foundVaddr || dyn == nil {
return
}
var hash , gnuhash *[vdsoHashSize ]uint32
info .symstrings = nil
info .symtab = nil
info .versym = nil
info .verdef = nil
for i := 0 ; dyn [i ].d_tag != _DT_NULL ; i ++ {
dt := &dyn [i ]
p := info .loadOffset + 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_HASH :
hash = (*[vdsoHashSize ]uint32 )(unsafe .Pointer (p ))
case _DT_GNU_HASH :
gnuhash = (*[vdsoHashSize ]uint32 )(unsafe .Pointer (p ))
case _DT_VERSYM :
info .versym = (*[vdsoVerSymSize ]uint16 )(unsafe .Pointer (p ))
case _DT_VERDEF :
info .verdef = (*elfVerdef )(unsafe .Pointer (p ))
}
}
if info .symstrings == nil || info .symtab == nil || (hash == nil && gnuhash == nil ) {
return
}
if info .verdef == nil {
info .versym = nil
}
if gnuhash != nil {
nbucket := gnuhash [0 ]
info .symOff = gnuhash [1 ]
bloomSize := gnuhash [2 ]
info .bucket = gnuhash [4 +bloomSize *uint32 (vdsoBloomSizeScale ):][:nbucket ]
info .chain = gnuhash [4 +bloomSize *uint32 (vdsoBloomSizeScale )+nbucket :]
info .isGNUHash = true
} else {
nbucket := hash [0 ]
nchain := hash [1 ]
info .bucket = hash [2 : 2 +nbucket ]
info .chain = hash [2 +nbucket : 2 +nbucket +nchain ]
}
info .valid = true
}
func vdsoFindVersion(info *vdsoInfo , ver *vdsoVersionKey ) int32 {
if !info .valid {
return 0
}
def := info .verdef
for {
if def .vd_flags &_VER_FLG_BASE == 0 {
aux := (*elfVerdaux )(add (unsafe .Pointer (def ), uintptr (def .vd_aux )))
if def .vd_hash == ver .verHash && ver .version == gostringnocopy (&info .symstrings [aux .vda_name ]) {
return int32 (def .vd_ndx & 0x7fff )
}
}
if def .vd_next == 0 {
break
}
def = (*elfVerdef )(add (unsafe .Pointer (def ), uintptr (def .vd_next )))
}
return -1
}
func vdsoParseSymbols(info *vdsoInfo , version int32 ) {
if !info .valid {
return
}
apply := func (symIndex uint32 , k vdsoSymbolKey ) bool {
sym := &info .symtab [symIndex ]
typ := _ELF_ST_TYPE (sym .st_info )
bind := _ELF_ST_BIND (sym .st_info )
if typ != _STT_FUNC && typ != _STT_NOTYPE || bind != _STB_GLOBAL && bind != _STB_WEAK || sym .st_shndx == _SHN_UNDEF {
return false
}
if k .name != gostringnocopy (&info .symstrings [sym .st_name ]) {
return false
}
if info .versym != nil && version != 0 && int32 (info .versym [symIndex ]&0x7fff ) != version {
return false
}
*k .ptr = info .loadOffset + uintptr (sym .st_value )
return true
}
if !info .isGNUHash {
for _ , k := range vdsoSymbolKeys {
if len (info .bucket ) > 0 {
for chain := info .bucket [k .symHash %uint32 (len (info .bucket ))]; chain != 0 ; chain = info .chain [chain ] {
if apply (chain , k ) {
break
}
}
}
}
return
}
for _ , k := range vdsoSymbolKeys {
symIndex := info .bucket [k .gnuHash %uint32 (len (info .bucket ))]
if symIndex < info .symOff {
continue
}
for ; ; symIndex ++ {
hash := info .chain [symIndex -info .symOff ]
if hash |1 == k .gnuHash |1 {
if apply (symIndex , k ) {
break
}
}
if hash &1 != 0 {
break
}
}
}
}
func vdsoauxv(tag , val uintptr ) {
switch tag {
case _AT_SYSINFO_EHDR :
if val == 0 {
return
}
var info vdsoInfo
info1 := (*vdsoInfo )(noescape (unsafe .Pointer (&info )))
vdsoInitFromSysinfoEhdr (info1 , (*elfEhdr )(unsafe .Pointer (val )))
vdsoParseSymbols (info1 , vdsoFindVersion (info1 , &vdsoLinuxVersion ))
}
}
func inVDSOPage(pc uintptr ) bool {
for _ , k := range vdsoSymbolKeys {
if *k .ptr != 0 {
page := *k .ptr &^ (physPageSize - 1 )
return pc >= page && pc < page +physPageSize
}
}
return false
}
The pages are generated with Golds v0.7.0-preview . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .