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 }