gitlab.com/Raven-IO/raven-delve@v1.22.4/pkg/internal/gosym/additions.go (about)

     1  // Copyright 2023 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  	"fmt"
    10  	"io"
    11  	"regexp"
    12  	"strings"
    13  )
    14  
    15  const (
    16  	funcSymNameGo119Lower string = "go.func.*"
    17  	funcSymNameGo120      string = "go:func.*"
    18  )
    19  
    20  // Additions to the original package from cmd/internal/objabi/funcdata.go
    21  const (
    22  	pcdata_InlTreeIndex = 2
    23  	funcdata_InlTree    = 3
    24  )
    25  
    26  var (
    27  	// Regexp for matching go tags. The groups are:
    28  	// 1  the major.minor version
    29  	// 2  the patch version, or empty if none
    30  	// 3  the entire prerelease, if present
    31  	// 4  the prerelease type ("beta" or "rc")
    32  	// 5  the prerelease number
    33  	tagRegexp = regexp.MustCompile(`^go(\d+\.\d+)(\.\d+|)((beta|rc|-pre)(\d+))?$`)
    34  )
    35  
    36  // parsed returns the parsed form of a semantic version string.
    37  type parsed struct {
    38  	major      string
    39  	minor      string
    40  	patch      string
    41  	short      string
    42  	prerelease string
    43  	build      string
    44  }
    45  
    46  func parsePrerelease(v string) (t, rest string, ok bool) {
    47  	// "A pre-release version MAY be denoted by appending a hyphen and
    48  	// a series of dot separated identifiers immediately following the patch version.
    49  	// Identifiers MUST comprise only ASCII alphanumerics and hyphen [0-9A-Za-z-].
    50  	// Identifiers MUST NOT be empty. Numeric identifiers MUST NOT include leading zeroes."
    51  	if v == "" || v[0] != '-' {
    52  		return
    53  	}
    54  	i := 1
    55  	start := 1
    56  	for i < len(v) && v[i] != '+' {
    57  		if !isIdentChar(v[i]) && v[i] != '.' {
    58  			return
    59  		}
    60  		if v[i] == '.' {
    61  			if start == i || isBadNum(v[start:i]) {
    62  				return
    63  			}
    64  			start = i + 1
    65  		}
    66  		i++
    67  	}
    68  	if start == i || isBadNum(v[start:i]) {
    69  		return
    70  	}
    71  	return v[:i], v[i:], true
    72  }
    73  
    74  // GoTagToSemver is a modified copy of pkgsite/internal/stdlib:VersionForTag.
    75  func GoTagToSemver(tag string) string {
    76  	if tag == "" {
    77  		return ""
    78  	}
    79  	tag = strings.Fields(tag)[0]
    80  	// Special cases for go1.
    81  	if tag == "go1" {
    82  		return "v1.0.0"
    83  	}
    84  	if tag == "go1.0" {
    85  		return ""
    86  	}
    87  	m := tagRegexp.FindStringSubmatch(tag)
    88  	if m == nil {
    89  		return ""
    90  	}
    91  	version := "v" + m[1]
    92  	if m[2] != "" {
    93  		version += m[2]
    94  	} else {
    95  		version += ".0"
    96  	}
    97  	if m[3] != "" {
    98  		if !strings.HasPrefix(m[4], "-") {
    99  			version += "-"
   100  		}
   101  		version += m[4] + "." + m[5]
   102  	}
   103  	return version
   104  }
   105  
   106  func parseBuild(v string) (t, rest string, ok bool) {
   107  	if v == "" || v[0] != '+' {
   108  		return
   109  	}
   110  	i := 1
   111  	start := 1
   112  	for i < len(v) {
   113  		if !isIdentChar(v[i]) && v[i] != '.' {
   114  			return
   115  		}
   116  		if v[i] == '.' {
   117  			if start == i {
   118  				return
   119  			}
   120  			start = i + 1
   121  		}
   122  		i++
   123  	}
   124  	if start == i {
   125  		return
   126  	}
   127  	return v[:i], v[i:], true
   128  }
   129  func parseInt(v string) (t, rest string, ok bool) {
   130  	if v == "" {
   131  		return
   132  	}
   133  	if v[0] < '0' || '9' < v[0] {
   134  		return
   135  	}
   136  	i := 1
   137  	for i < len(v) && '0' <= v[i] && v[i] <= '9' {
   138  		i++
   139  	}
   140  	if v[0] == '0' && i != 1 {
   141  		return
   142  	}
   143  	return v[:i], v[i:], true
   144  }
   145  func isIdentChar(c byte) bool {
   146  	return 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' || c == '-'
   147  }
   148  
   149  func parse(v string) (p parsed, ok bool) {
   150  	if v == "" || v[0] != 'v' {
   151  		return
   152  	}
   153  	p.major, v, ok = parseInt(v[1:])
   154  	if !ok {
   155  		return
   156  	}
   157  	if v == "" {
   158  		p.minor = "0"
   159  		p.patch = "0"
   160  		p.short = ".0.0"
   161  		return
   162  	}
   163  	if v[0] != '.' {
   164  		ok = false
   165  		return
   166  	}
   167  	p.minor, v, ok = parseInt(v[1:])
   168  	if !ok {
   169  		return
   170  	}
   171  	if v == "" {
   172  		p.patch = "0"
   173  		p.short = ".0"
   174  		return
   175  	}
   176  	if v[0] != '.' {
   177  		ok = false
   178  		return
   179  	}
   180  	p.patch, v, ok = parseInt(v[1:])
   181  	if !ok {
   182  		return
   183  	}
   184  	if len(v) > 0 && v[0] == '-' {
   185  		p.prerelease, v, ok = parsePrerelease(v)
   186  		if !ok {
   187  			return
   188  		}
   189  	}
   190  	if len(v) > 0 && v[0] == '+' {
   191  		p.build, v, ok = parseBuild(v)
   192  		if !ok {
   193  			return
   194  		}
   195  	}
   196  	if v != "" {
   197  		ok = false
   198  		return
   199  	}
   200  	ok = true
   201  	return
   202  }
   203  
   204  func isBadNum(v string) bool {
   205  	i := 0
   206  	for i < len(v) && '0' <= v[i] && v[i] <= '9' {
   207  		i++
   208  	}
   209  	return i == len(v) && i > 1 && v[0] == '0'
   210  }
   211  func nextIdent(x string) (dx, rest string) {
   212  	i := 0
   213  	for i < len(x) && x[i] != '.' {
   214  		i++
   215  	}
   216  	return x[:i], x[i:]
   217  }
   218  func isNum(v string) bool {
   219  	i := 0
   220  	for i < len(v) && '0' <= v[i] && v[i] <= '9' {
   221  		i++
   222  	}
   223  	return i == len(v)
   224  }
   225  
   226  // MajorMinor returns the major.minor version prefix of the semantic version v.
   227  // For example, MajorMinor("v2.1.0") == "v2.1".
   228  // If v is an invalid semantic version string, MajorMinor returns the empty string.
   229  func MajorMinor(v string) string {
   230  	pv, ok := parse(v)
   231  	if !ok {
   232  		return ""
   233  	}
   234  	i := 1 + len(pv.major)
   235  	if j := i + 1 + len(pv.minor); j <= len(v) && v[i] == '.' && v[i+1:j] == pv.minor {
   236  		return v[:j]
   237  	}
   238  	return v[:i] + "." + pv.minor
   239  }
   240  
   241  func compareInt(x, y string) int {
   242  	if x == y {
   243  		return 0
   244  	}
   245  	if len(x) < len(y) {
   246  		return -1
   247  	}
   248  	if len(x) > len(y) {
   249  		return +1
   250  	}
   251  	if x < y {
   252  		return -1
   253  	} else {
   254  		return +1
   255  	}
   256  }
   257  func comparePrerelease(x, y string) int {
   258  	// "When major, minor, and patch are equal, a pre-release version has
   259  	// lower precedence than a normal version.
   260  	// Example: 1.0.0-alpha < 1.0.0.
   261  	// Precedence for two pre-release versions with the same major, minor,
   262  	// and patch version MUST be determined by comparing each dot separated
   263  	// identifier from left to right until a difference is found as follows:
   264  	// identifiers consisting of only digits are compared numerically and
   265  	// identifiers with letters or hyphens are compared lexically in ASCII
   266  	// sort order. Numeric identifiers always have lower precedence than
   267  	// non-numeric identifiers. A larger set of pre-release fields has a
   268  	// higher precedence than a smaller set, if all of the preceding
   269  	// identifiers are equal.
   270  	// Example: 1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-alpha.beta <
   271  	// 1.0.0-beta < 1.0.0-beta.2 < 1.0.0-beta.11 < 1.0.0-rc.1 < 1.0.0."
   272  	if x == y {
   273  		return 0
   274  	}
   275  	if x == "" {
   276  		return +1
   277  	}
   278  	if y == "" {
   279  		return -1
   280  	}
   281  	for x != "" && y != "" {
   282  		x = x[1:] // skip - or .
   283  		y = y[1:] // skip - or .
   284  		var dx, dy string
   285  		dx, x = nextIdent(x)
   286  		dy, y = nextIdent(y)
   287  		if dx != dy {
   288  			ix := isNum(dx)
   289  			iy := isNum(dy)
   290  			if ix != iy {
   291  				if ix {
   292  					return -1
   293  				} else {
   294  					return +1
   295  				}
   296  			}
   297  			if ix {
   298  				if len(dx) < len(dy) {
   299  					return -1
   300  				}
   301  				if len(dx) > len(dy) {
   302  					return +1
   303  				}
   304  			}
   305  			if dx < dy {
   306  				return -1
   307  			} else {
   308  				return +1
   309  			}
   310  		}
   311  	}
   312  	if x == "" {
   313  		return -1
   314  	} else {
   315  		return +1
   316  	}
   317  }
   318  
   319  // Compare returns an integer comparing two versions according to
   320  // semantic version precedence.
   321  // The result will be 0 if v == w, -1 if v < w, or +1 if v > w.
   322  //
   323  // An invalid semantic version string is considered less than a valid one.
   324  // All invalid semantic version strings compare equal to each other.
   325  func Compare(v, w string) int {
   326  	pv, ok1 := parse(v)
   327  	pw, ok2 := parse(w)
   328  	if !ok1 && !ok2 {
   329  		return 0
   330  	}
   331  	if !ok1 {
   332  		return -1
   333  	}
   334  	if !ok2 {
   335  		return +1
   336  	}
   337  	if c := compareInt(pv.major, pw.major); c != 0 {
   338  		return c
   339  	}
   340  	if c := compareInt(pv.minor, pw.minor); c != 0 {
   341  		return c
   342  	}
   343  	if c := compareInt(pv.patch, pw.patch); c != 0 {
   344  		return c
   345  	}
   346  	return comparePrerelease(pv.prerelease, pw.prerelease)
   347  }
   348  
   349  // InlineTree returns the inline tree for Func f as a sequence of InlinedCalls.
   350  // goFuncValue is the value of the gosym.FuncSymName symbol.
   351  // baseAddr is the address of the memory region (ELF Prog) containing goFuncValue.
   352  // progReader is a ReaderAt positioned at the start of that region.
   353  func (t *LineTable) InlineTree(f *Func, goFuncValue, baseAddr uint64, progReader io.ReaderAt) ([]InlinedCall, error) {
   354  	if f.inlineTreeCount == 0 {
   355  		return nil, nil
   356  	}
   357  	if f.inlineTreeOffset == ^uint32(0) {
   358  		return nil, nil
   359  	}
   360  	var offset int64
   361  	if t.version >= ver118 {
   362  		offset = int64(goFuncValue - baseAddr + uint64(f.inlineTreeOffset))
   363  	} else {
   364  		offset = int64(uint64(f.inlineTreeOffset) - baseAddr)
   365  	}
   366  	r := io.NewSectionReader(progReader, offset, 1<<32) // pick a size larger than we need
   367  	var ics []InlinedCall
   368  	for i := 0; i < f.inlineTreeCount; i++ {
   369  		if t.version >= ver120 {
   370  			var ric rawInlinedCall120
   371  			if err := binary.Read(r, t.binary, &ric); err != nil {
   372  				return nil, fmt.Errorf("error reading into rawInlinedCall: %#v", err)
   373  			}
   374  			ics = append(ics, InlinedCall{
   375  				FuncID:   ric.FuncID,
   376  				Name:     t.funcName(uint32(ric.NameOff)),
   377  				ParentPC: ric.ParentPC,
   378  			})
   379  		} else {
   380  			var ric rawInlinedCall112
   381  			if err := binary.Read(r, t.binary, &ric); err != nil {
   382  				return nil, err
   383  			}
   384  			ics = append(ics, InlinedCall{
   385  				FuncID:   ric.FuncID,
   386  				Name:     t.funcName(uint32(ric.Func_)),
   387  				ParentPC: ric.ParentPC,
   388  			})
   389  		}
   390  	}
   391  	return ics, nil
   392  }
   393  
   394  // FuncSymName returns symbol name for Go functions used in binaries
   395  // based on Go version. Supported Go versions are 1.18 and greater.
   396  // If the go version is unreadable it assumes that it is a newer version
   397  // and returns the symbol name for go version 1.20 or greater.
   398  func FuncSymName(goVersion string) string {
   399  	// Support devel goX.Y...
   400  	v := strings.TrimPrefix(goVersion, "devel ")
   401  	v = GoTagToSemver(v)
   402  	mm := MajorMinor(v)
   403  	if Compare(mm, "v1.20") >= 0 || mm == "" {
   404  		return funcSymNameGo120
   405  	} else if Compare(mm, "v1.18") >= 0 {
   406  		return funcSymNameGo119Lower
   407  	}
   408  	return ""
   409  }
   410  
   411  func GetFuncSymName() string {
   412  	return funcSymNameGo120
   413  }
   414  
   415  // InlinedCall describes a call to an inlined function.
   416  type InlinedCall struct {
   417  	FuncID   uint8  // type of the called function
   418  	Name     string // name of called function
   419  	ParentPC int32  // position of an instruction whose source position is the call site (offset from entry)
   420  }
   421  
   422  // rawInlinedCall112 is the encoding of entries in the FUNCDATA_InlTree table
   423  // from Go 1.12 through 1.19. It is equivalent to runtime.inlinedCall.
   424  type rawInlinedCall112 struct {
   425  	Parent   int16 // index of parent in the inltree, or < 0
   426  	FuncID   uint8 // type of the called function
   427  	_        byte
   428  	File     int32 // perCU file index for inlined call. See cmd/link:pcln.go
   429  	Line     int32 // line number of the call site
   430  	Func_    int32 // offset into pclntab for name of called function
   431  	ParentPC int32 // position of an instruction whose source position is the call site (offset from entry)
   432  }
   433  
   434  // rawInlinedCall120 is the encoding of entries in the FUNCDATA_InlTree table
   435  // from Go 1.20. It is equivalent to runtime.inlinedCall.
   436  type rawInlinedCall120 struct {
   437  	FuncID    uint8 // type of the called function
   438  	_         [3]byte
   439  	NameOff   int32 // offset into pclntab for name of called function
   440  	ParentPC  int32 // position of an instruction whose source position is the call site (offset from entry)
   441  	StartLine int32 // line number of start of function (func keyword/TEXT directive)
   442  }
   443  
   444  func (f funcData) npcdata() uint32 { return f.field(7) }
   445  func (f funcData) nfuncdata(numFuncFields uint32) uint32 {
   446  	return uint32(f.data[f.fieldOffset(numFuncFields-1)+3])
   447  }
   448  
   449  func (f funcData) funcdataOffset(i uint8, numFuncFields uint32) uint32 {
   450  	if uint32(i) >= f.nfuncdata(numFuncFields) {
   451  		return ^uint32(0)
   452  	}
   453  	var off uint32
   454  	if f.t.version >= ver118 {
   455  		off = f.fieldOffset(numFuncFields) + // skip fixed part of _func
   456  			f.npcdata()*4 + // skip pcdata
   457  			uint32(i)*4 // index of i'th FUNCDATA
   458  	} else {
   459  		off = f.fieldOffset(numFuncFields) + // skip fixed part of _func
   460  			f.npcdata()*4
   461  		off += uint32(i) * f.t.ptrsize
   462  	}
   463  	return f.t.binary.Uint32(f.data[off:])
   464  }
   465  
   466  func (f funcData) fieldOffset(n uint32) uint32 {
   467  	// In Go 1.18, the first field of _func changed
   468  	// from a uintptr entry PC to a uint32 entry offset.
   469  	sz0 := f.t.ptrsize
   470  	if f.t.version >= ver118 {
   471  		sz0 = 4
   472  	}
   473  	return sz0 + (n-1)*4 // subsequent fields are 4 bytes each
   474  }
   475  
   476  func (f funcData) pcdataOffset(i uint8, numFuncFields uint32) uint32 {
   477  	if uint32(i) >= f.npcdata() {
   478  		return ^uint32(0)
   479  	}
   480  	off := f.fieldOffset(numFuncFields) + // skip fixed part of _func
   481  		uint32(i)*4 // index of i'th PCDATA
   482  	return f.t.binary.Uint32(f.data[off:])
   483  }
   484  
   485  // maxInlineTreeIndexValue returns the maximum value of the inline tree index
   486  // pc-value table in info. This is the only way to determine how many
   487  // IndexedCalls are in an inline tree, since the data of the tree itself is not
   488  // delimited in any way.
   489  func (t *LineTable) maxInlineTreeIndexValue(info funcData, numFuncFields uint32) int {
   490  	if info.npcdata() <= pcdata_InlTreeIndex {
   491  		return -1
   492  	}
   493  	off := info.pcdataOffset(pcdata_InlTreeIndex, numFuncFields)
   494  	p := t.pctab[off:]
   495  	val := int32(-1)
   496  	max := int32(-1)
   497  	var pc uint64
   498  	for t.step(&p, &pc, &val, pc == 0) {
   499  		if val > max {
   500  			max = val
   501  		}
   502  	}
   503  	return int(max)
   504  }
   505  
   506  type inlTree struct {
   507  	inlineTreeOffset uint32 // offset from go.func.* symbol
   508  	inlineTreeCount  int    // number of entries in inline tree
   509  }