mirror of
https://github.com/anchore/syft.git
synced 2026-08-19 16:48:27 +02:00
decode golang symbols (#5089)
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
This commit is contained in:
parent
29fd7d0dec
commit
c890e7f17f
@ -52,10 +52,7 @@ func getSymbols(r io.ReaderAt) (syms []binarySymbol, err error) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
seen[fn.Name] = struct{}{}
|
seen[fn.Name] = struct{}{}
|
||||||
syms = append(syms, binarySymbol{
|
syms = append(syms, makeBinarySymbol(fn.Name, fn.PackageName()))
|
||||||
packagePath: fn.PackageName(),
|
|
||||||
name: fn.Name,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// debug/gosym only exposes top-level functions; functions that the compiler inlined into their
|
// debug/gosym only exposes top-level functions; functions that the compiler inlined into their
|
||||||
@ -71,7 +68,7 @@ func getSymbols(r io.ReaderAt) (syms []binarySymbol, err error) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
seen[name] = struct{}{}
|
seen[name] = struct{}{}
|
||||||
syms = append(syms, binarySymbol{packagePath: pkgPath, name: name})
|
syms = append(syms, makeBinarySymbol(name, pkgPath))
|
||||||
}
|
}
|
||||||
|
|
||||||
return syms, nil
|
return syms, nil
|
||||||
@ -94,6 +91,55 @@ func packagePathFromSymbolName(name string) string {
|
|||||||
return name[:slash+1+dot]
|
return name[:slash+1+dot]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// makeBinarySymbol builds a binarySymbol, unescaping the %xx sequences the go linker introduces in the
|
||||||
|
// import-path portion of a symbol name (see unescapePackagePath). The local-symbol suffix (method and
|
||||||
|
// function names) is left untouched, so only the path prefix shared by packagePath and name is rewritten.
|
||||||
|
func makeBinarySymbol(name, pkgPath string) binarySymbol {
|
||||||
|
unescaped := unescapePackagePath(pkgPath)
|
||||||
|
if unescaped != pkgPath && strings.HasPrefix(name, pkgPath) {
|
||||||
|
name = unescaped + name[len(pkgPath):]
|
||||||
|
}
|
||||||
|
return binarySymbol{packagePath: unescaped, name: name}
|
||||||
|
}
|
||||||
|
|
||||||
|
// unescapePackagePath reverses the escaping cmd/internal/objabi.PathToPrefix applies to import paths in
|
||||||
|
// symbol names: bytes like '.' (at or after the final '/'), '%', '"', control bytes, and high bytes are
|
||||||
|
// written as lowercase "%xx". For example "gopkg.in/yaml.v2" is stored as "gopkg.in/yaml%2ev2", so this
|
||||||
|
// restores it before matching against the (unescaped) module paths from build info. A lone or malformed
|
||||||
|
// '%' sequence is left as-is.
|
||||||
|
func unescapePackagePath(path string) string {
|
||||||
|
if !strings.Contains(path, "%") {
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
var b strings.Builder
|
||||||
|
b.Grow(len(path))
|
||||||
|
for i := 0; i < len(path); i++ {
|
||||||
|
if path[i] == '%' && i+2 < len(path) {
|
||||||
|
if hi, ok1 := unhex(path[i+1]); ok1 {
|
||||||
|
if lo, ok2 := unhex(path[i+2]); ok2 {
|
||||||
|
b.WriteByte(hi<<4 | lo)
|
||||||
|
i += 2
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b.WriteByte(path[i])
|
||||||
|
}
|
||||||
|
return b.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func unhex(c byte) (byte, bool) {
|
||||||
|
switch {
|
||||||
|
case c >= '0' && c <= '9':
|
||||||
|
return c - '0', true
|
||||||
|
case c >= 'a' && c <= 'f':
|
||||||
|
return c - 'a' + 10, true
|
||||||
|
case c >= 'A' && c <= 'F':
|
||||||
|
return c - 'A' + 10, true
|
||||||
|
}
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
|
||||||
// nameWithoutTypeArgs strips the type-argument portion from an instantiated generic symbol name, e.g.
|
// nameWithoutTypeArgs strips the type-argument portion from an instantiated generic symbol name, e.g.
|
||||||
// "foo/bar.Do[net/url.Values]" -> "foo/bar.Do". The slashes and dots inside the brackets would otherwise
|
// "foo/bar.Do[net/url.Values]" -> "foo/bar.Do". The slashes and dots inside the brackets would otherwise
|
||||||
// corrupt package-path derivation (yielding "foo/bar.Do[net" for the example above). Mirrors
|
// corrupt package-path derivation (yielding "foo/bar.Do[net" for the example above). Mirrors
|
||||||
|
|||||||
@ -97,6 +97,58 @@ func Test_moduleSymbols(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_escapedImportPathAttribution(t *testing.T) {
|
||||||
|
// the go linker escapes the '.' in the last path element, so "gopkg.in/yaml.v2" appears in the symbol
|
||||||
|
// table as "gopkg.in/yaml%2ev2". attribution must still land these under the unescaped module path.
|
||||||
|
mainModule := &debug.Module{Path: "github.com/someorg/somecli"}
|
||||||
|
deps := []*debug.Module{{Path: "gopkg.in/yaml.v2"}}
|
||||||
|
|
||||||
|
symbols := []binarySymbol{
|
||||||
|
makeBinarySymbol("gopkg.in/yaml%2ev2.(*decoder).alias", "gopkg.in/yaml%2ev2"),
|
||||||
|
makeBinarySymbol("gopkg.in/yaml%2ev2.(*TypeError).Error", "gopkg.in/yaml%2ev2"),
|
||||||
|
}
|
||||||
|
|
||||||
|
gotByModule, gotStdlib := moduleSymbols(symbols, mainModule, deps)
|
||||||
|
assert.Nil(t, gotStdlib)
|
||||||
|
assert.Equal(t, map[string]map[string][]string{
|
||||||
|
"gopkg.in/yaml.v2": {
|
||||||
|
"gopkg.in/yaml.v2": {"(*TypeError).Error", "(*decoder).alias"},
|
||||||
|
},
|
||||||
|
}, gotByModule)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_unescapePackagePath(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
input string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{"gopkg.in/yaml%2ev2", "gopkg.in/yaml.v2"},
|
||||||
|
{"foo%25bar", "foo%bar"},
|
||||||
|
{"foo%22bar", "foo\"bar"},
|
||||||
|
// no escapes: unchanged
|
||||||
|
{"github.com/foo/bar", "github.com/foo/bar"},
|
||||||
|
{"", ""},
|
||||||
|
// malformed sequences are left as-is
|
||||||
|
{"foo%2", "foo%2"},
|
||||||
|
{"foo%zz", "foo%zz"},
|
||||||
|
{"foo%", "foo%"},
|
||||||
|
// uppercase hex is accepted too
|
||||||
|
{"foo%2Ebar", "foo.bar"},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.input, func(t *testing.T) {
|
||||||
|
assert.Equal(t, tt.expected, unescapePackagePath(tt.input))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_makeBinarySymbol(t *testing.T) {
|
||||||
|
// only the import-path prefix is unescaped; the local-symbol suffix is preserved verbatim
|
||||||
|
sym := makeBinarySymbol("gopkg.in/yaml%2ev2.(*decoder).alias", "gopkg.in/yaml%2ev2")
|
||||||
|
assert.Equal(t, "gopkg.in/yaml.v2", sym.packagePath)
|
||||||
|
assert.Equal(t, "gopkg.in/yaml.v2.(*decoder).alias", sym.name)
|
||||||
|
}
|
||||||
|
|
||||||
func Test_localSymbolName(t *testing.T) {
|
func Test_localSymbolName(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user