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