blob: 88399f36cee1f2d26a00c78f7afd358747201888 [file] [log] [blame]
// names-1 is a change detector for Go symbol names. We don't want
// the name mangling to change silently.
package main
import (
"bytes"
"debug/elf"
"debug/macho"
"debug/pe"
"fmt"
"os"
"runtime"
"strings"
)
type Type int
type Alias = int
//go:noinline
func Function1(out *bytes.Buffer) int {
var f2 func(int) int
f1 := func(i int) int {
if i == 0 {
return 0
}
type NestedType struct { a int }
t := NestedType{f2(i-1)}
fmt.Fprint(out, t)
return t.a
}
f2 = func(i int) int {
if i == 0 {
return 0
}
type NestedType struct { a int }
t := NestedType{f1(i-1)}
fmt.Fprint(out, t)
return t.a
}
return f1(10) + f2(10)
}
//go:noinline
func Function2(out *bytes.Buffer) {
{
type T struct { b int }
fmt.Fprint(out, T{1})
}
{
type T struct { b int }
fmt.Fprint(out, T{2})
}
}
func (t Type) M(bool, int8, float32, complex64, string, func(), func(int16) (float64, complex128), *byte, struct { f int "tag #$%^&{}: 世界" }, []int32, [24]int64, map[uint8]uint16, chan uint32, <-chan uint64, chan <- uintptr, Type, Alias) {
}
//go:noinline
func Function3(out *bytes.Buffer) {
fmt.Fprintf(out, "%T", Type(0))
}
func main() {
if runtime.GOOS == "aix" {
// Not supported on AIX until there is an externally
// visible version of internal/xcoff.
return
}
var b bytes.Buffer
Function1(&b)
Function2(&b)
Function3(&b)
_ = len(b.String())
for _, n := range []string{"/proc/self/exe", os.Args[0]} {
if f, err := os.Open(n); err == nil {
checkFile(f)
return
}
}
fmt.Println("checksyms: could not find executable")
fmt.Println("UNSUPPORTED: checksyms")
}
func checkFile(f *os.File) {
var syms []string
if ef, err := elf.NewFile(f); err == nil {
esyms, err := ef.Symbols()
if err != nil {
panic(err)
}
for _, esym := range esyms {
syms = append(syms, esym.Name)
}
} else if mf, err := macho.NewFile(f); err == nil {
for _, msym := range mf.Symtab.Syms {
syms = append(syms, msym.Name)
}
} else if pf, err := pe.NewFile(f); err == nil {
for _, psym := range pf.Symbols {
syms = append(syms, psym.Name)
}
} else {
fmt.Println("checksyms: could not parse executable")
fmt.Println("UNSUPPORTED: checksyms")
return
}
checkSyms(syms)
}
var want = []string{
"main.Function1",
"main.Function1..f",
"main.Function1..func1",
"main.Function1..func1.main.NestedType..d",
"main.Function1..func2",
"main.Function1..func2.main.NestedType..d",
"main.Function2",
"main.Function2..f",
"main.Function2.main.T..d",
"main.Function2.main.T..i1..d",
"main.Function3",
"main.Function3..f",
"main.Type..d",
"main.Type.M",
"main.main",
"main.want",
"type...1.1main.Type", // Why is this here?
"type...1main.Function1..func1.NestedType",
"type...1main.Function1..func2.NestedType",
"type...1main.Function2.T",
"type...1main.Function2.T..i1",
"type...1main.Type",
"type..func.8.1main.Type.3bool.3int8.3float32.3complex64.3string.3func.8.9.8.9.3func.8int16.9.8float64.3complex128.9.3.1uint8.3struct.4.main.f.0int.4tag.x20.x23.x24.x25.x5e.x26.x7b.x7d.x3a.x20..u4e16..u754c.5.5.3.6.7int32.3.624.7int64.3map.6uint8.7uint16.3chan.0uint32.3.4.5chan.0uint64.3chan.4.5.0uintptr.3main.Type.3int.9.8.9",
"type..func.8bool.3int8.3float32.3complex64.3string.3func.8.9.8.9.3func.8int16.9.8float64.3complex128.9.3.1uint8.3struct.4.main.f.0int.4tag.x20.x23.x24.x25.x5e.x26.x7b.x7d.x3a.x20..u4e16..u754c.5.5.3.6.7int32.3.624.7int64.3map.6uint8.7uint16.3chan.0uint32.3.4.5chan.0uint64.3chan.4.5.0uintptr.3main.Type.3int.9.8.9",
"type..func.8main.Type.3bool.3int8.3float32.3complex64.3string.3func.8.9.8.9.3func.8int16.9.8float64.3complex128.9.3.1uint8.3struct.4.main.f.0int.4tag.x20.x23.x24.x25.x5e.x26.x7b.x7d.x3a.x20..u4e16..u754c.5.5.3.6.7int32.3.624.7int64.3map.6uint8.7uint16.3chan.0uint32.3.4.5chan.0uint64.3chan.4.5.0uintptr.3main.Type.3int.9.8.9",
"type..struct.4.main.f.0int.4tag.x20.x23.x24.x25.x5e.x26.x7b.x7d.x3a.x20..u4e16..u754c.5.5",
}
func checkSyms(syms []string) {
m := make(map[string]bool)
for _, sym := range syms {
if strings.Contains(sym, ".") {
m[sym] = true
}
}
ok := true
for _, w := range want {
if m[w] {
delete(m, w)
} else {
fmt.Printf("checksyms: missing expected symbol %q\n", w)
ok = false
}
}
for sym := range m {
if !strings.Contains(sym, "main") {
continue
}
// Skip some symbols we may see but know are unimportant.
if sym == "go-main.c" {
continue
}
if strings.HasPrefix(sym, "runtime.") {
continue
}
// We can see a lot of spurious .eq and .hash
// functions for types defined in other packages.
// This is a bug but skip them for now.
if strings.Contains(sym, "..eq") || strings.Contains(sym, "..hash") {
continue
}
// Skip closure types by skipping incomparable structs.
// This may be a bug, not sure.
if strings.Contains(sym, ".4x.5") {
continue
}
// These functions may be inlined.
if sym == "main.checkFile" || sym == "main.checkSyms" {
continue
}
fmt.Printf("checksyms: found unexpected symbol %q\n", sym)
ok = false
}
if !ok {
fmt.Println("FAIL: checksyms")
}
}