src

Go monorepo.
git clone git://code.dwrz.net/src
Log | Files | Refs

additions.go (5957B)


      1 // Copyright 2022 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 gosym
      6 
      7 import (
      8 	"encoding/binary"
      9 	"io"
     10 	"strings"
     11 
     12 	sv "golang.org/x/mod/semver"
     13 	"golang.org/x/vuln/internal/semver"
     14 )
     15 
     16 const (
     17 	funcSymNameGo119Lower string = "go.func.*"
     18 	funcSymNameGo120      string = "go:func.*"
     19 )
     20 
     21 // FuncSymName returns symbol name for Go functions used in binaries
     22 // based on Go version. Supported Go versions are 1.18 and greater.
     23 // If the go version is unreadable it assumes that it is a newer version
     24 // and returns the symbol name for go version 1.20 or greater.
     25 func FuncSymName(goVersion string) string {
     26 	// Support devel goX.Y...
     27 	v := strings.TrimPrefix(goVersion, "devel ")
     28 	v = semver.GoTagToSemver(v)
     29 	mm := sv.MajorMinor(v)
     30 	if sv.Compare(mm, "v1.20") >= 0 || mm == "" {
     31 		return funcSymNameGo120
     32 	} else if sv.Compare(mm, "v1.18") >= 0 {
     33 		return funcSymNameGo119Lower
     34 	}
     35 	return ""
     36 }
     37 
     38 // Additions to the original package from cmd/internal/objabi/funcdata.go
     39 const (
     40 	pcdata_InlTreeIndex = 2
     41 	funcdata_InlTree    = 3
     42 )
     43 
     44 // InlineTree returns the inline tree for Func f as a sequence of InlinedCalls.
     45 // goFuncValue is the value of the gosym.FuncSymName symbol.
     46 // baseAddr is the address of the memory region (ELF Prog) containing goFuncValue.
     47 // progReader is a ReaderAt positioned at the start of that region.
     48 func (t *LineTable) InlineTree(f *Func, goFuncValue, baseAddr uint64, progReader io.ReaderAt) ([]InlinedCall, error) {
     49 	if f.inlineTreeCount == 0 {
     50 		return nil, nil
     51 	}
     52 	if f.inlineTreeOffset == ^uint32(0) {
     53 		return nil, nil
     54 	}
     55 	var offset int64
     56 	if t.version >= ver118 {
     57 		offset = int64(goFuncValue - baseAddr + uint64(f.inlineTreeOffset))
     58 	} else {
     59 		offset = int64(uint64(f.inlineTreeOffset) - baseAddr)
     60 	}
     61 
     62 	r := io.NewSectionReader(progReader, offset, 1<<32) // pick a size larger than we need
     63 	ics := make([]InlinedCall, 0, f.inlineTreeCount)
     64 	for i := 0; i < f.inlineTreeCount; i++ {
     65 		if t.version >= ver120 {
     66 			var ric rawInlinedCall120
     67 			if err := binary.Read(r, t.binary, &ric); err != nil {
     68 				return nil, err
     69 			}
     70 			ics = append(ics, InlinedCall{
     71 				FuncID:   ric.FuncID,
     72 				Name:     t.funcName(uint32(ric.NameOff)),
     73 				ParentPC: ric.ParentPC,
     74 			})
     75 		} else {
     76 			var ric rawInlinedCall112
     77 			if err := binary.Read(r, t.binary, &ric); err != nil {
     78 				return nil, err
     79 			}
     80 			ics = append(ics, InlinedCall{
     81 				FuncID:   ric.FuncID,
     82 				Name:     t.funcName(uint32(ric.Func_)),
     83 				ParentPC: ric.ParentPC,
     84 			})
     85 		}
     86 	}
     87 	return ics, nil
     88 }
     89 
     90 // InlinedCall describes a call to an inlined function.
     91 type InlinedCall struct {
     92 	FuncID   uint8  // type of the called function
     93 	Name     string // name of called function
     94 	ParentPC int32  // position of an instruction whose source position is the call site (offset from entry)
     95 }
     96 
     97 // rawInlinedCall112 is the encoding of entries in the FUNCDATA_InlTree table
     98 // from Go 1.12 through 1.19. It is equivalent to runtime.inlinedCall.
     99 type rawInlinedCall112 struct {
    100 	Parent   int16 // index of parent in the inltree, or < 0
    101 	FuncID   uint8 // type of the called function
    102 	_        byte
    103 	File     int32 // perCU file index for inlined call. See cmd/link:pcln.go
    104 	Line     int32 // line number of the call site
    105 	Func_    int32 // offset into pclntab for name of called function
    106 	ParentPC int32 // position of an instruction whose source position is the call site (offset from entry)
    107 }
    108 
    109 // rawInlinedCall120 is the encoding of entries in the FUNCDATA_InlTree table
    110 // from Go 1.20. It is equivalent to runtime.inlinedCall.
    111 type rawInlinedCall120 struct {
    112 	FuncID    uint8 // type of the called function
    113 	_         [3]byte
    114 	NameOff   int32 // offset into pclntab for name of called function
    115 	ParentPC  int32 // position of an instruction whose source position is the call site (offset from entry)
    116 	StartLine int32 // line number of start of function (func keyword/TEXT directive)
    117 }
    118 
    119 func (f funcData) npcdata() uint32 { return f.field(7) }
    120 func (f funcData) nfuncdata(numFuncFields uint32) uint32 {
    121 	return uint32(f.data[f.fieldOffset(numFuncFields-1)+3])
    122 }
    123 
    124 func (f funcData) funcdataOffset(i uint8, numFuncFields uint32) uint32 {
    125 	if uint32(i) >= f.nfuncdata(numFuncFields) {
    126 		return ^uint32(0)
    127 	}
    128 	var off uint32
    129 	if f.t.version >= ver118 {
    130 		off = f.fieldOffset(numFuncFields) + // skip fixed part of _func
    131 			f.npcdata()*4 + // skip pcdata
    132 			uint32(i)*4 // index of i'th FUNCDATA
    133 	} else {
    134 		off = f.fieldOffset(numFuncFields) + // skip fixed part of _func
    135 			f.npcdata()*4
    136 		off += uint32(i) * f.t.ptrsize
    137 	}
    138 	return f.t.binary.Uint32(f.data[off:])
    139 }
    140 
    141 func (f funcData) fieldOffset(n uint32) uint32 {
    142 	// In Go 1.18, the first field of _func changed
    143 	// from a uintptr entry PC to a uint32 entry offset.
    144 	sz0 := f.t.ptrsize
    145 	if f.t.version >= ver118 {
    146 		sz0 = 4
    147 	}
    148 	return sz0 + (n-1)*4 // subsequent fields are 4 bytes each
    149 }
    150 
    151 func (f funcData) pcdataOffset(i uint8, numFuncFields uint32) uint32 {
    152 	if uint32(i) >= f.npcdata() {
    153 		return ^uint32(0)
    154 	}
    155 	off := f.fieldOffset(numFuncFields) + // skip fixed part of _func
    156 		uint32(i)*4 // index of i'th PCDATA
    157 	return f.t.binary.Uint32(f.data[off:])
    158 }
    159 
    160 // maxInlineTreeIndexValue returns the maximum value of the inline tree index
    161 // pc-value table in info. This is the only way to determine how many
    162 // IndexedCalls are in an inline tree, since the data of the tree itself is not
    163 // delimited in any way.
    164 func (t *LineTable) maxInlineTreeIndexValue(info funcData, numFuncFields uint32) int {
    165 	if info.npcdata() <= pcdata_InlTreeIndex {
    166 		return -1
    167 	}
    168 	off := info.pcdataOffset(pcdata_InlTreeIndex, numFuncFields)
    169 	p := t.pctab[off:]
    170 	val := int32(-1)
    171 	max := int32(-1)
    172 	var pc uint64
    173 	for t.step(&p, &pc, &val, pc == 0) {
    174 		if val > max {
    175 			max = val
    176 		}
    177 	}
    178 	return int(max)
    179 }
    180 
    181 type inlTree struct {
    182 	inlineTreeOffset uint32 // offset from go.func.* symbol
    183 	inlineTreeCount  int    // number of entries in inline tree
    184 }