1// names-1 is a change detector for Go symbol names.  We don't want
2// the name mangling to change silently.
3package main
4
5import (
6	"bytes"
7	"debug/elf"
8	"debug/macho"
9	"debug/pe"
10	"fmt"
11	"os"
12	"runtime"
13	"strings"
14)
15
16type Type int
17type Alias = int
18
19//go:noinline
20func Function1(out *bytes.Buffer) int {
21	var f2 func(int) int
22	f1 := func(i int) int {
23		if i == 0 {
24			return 0
25		}
26		type NestedType struct { a int }
27		t := NestedType{f2(i-1)}
28		fmt.Fprint(out, t)
29		return t.a
30	}
31	f2 = func(i int) int {
32		if i == 0 {
33			return 0
34		}
35		type NestedType struct { a int }
36		t := NestedType{f1(i-1)}
37		fmt.Fprint(out, t)
38		return t.a
39	}
40	return f1(10) + f2(10)
41}
42
43//go:noinline
44func Function2(out *bytes.Buffer) {
45	{
46		type T struct { b int }
47		fmt.Fprint(out, T{1})
48	}
49	{
50		type T struct { b int }
51		fmt.Fprint(out, T{2})
52	}
53}
54
55func (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) {
56}
57
58//go:noinline
59func Function3(out *bytes.Buffer) {
60	fmt.Fprintf(out, "%T", Type(0))
61}
62
63func main() {
64	if runtime.GOOS == "aix" {
65		// Not supported on AIX until there is an externally
66		// visible version of internal/xcoff.
67		return
68	}
69
70	var b bytes.Buffer
71	Function1(&b)
72	Function2(&b)
73	Function3(&b)
74	_ = len(b.String())
75
76	for _, n := range []string{"/proc/self/exe", os.Args[0]} {
77		if f, err := os.Open(n); err == nil {
78			checkFile(f)
79			return
80		}
81	}
82	fmt.Println("checksyms: could not find executable")
83	fmt.Println("UNSUPPORTED: checksyms")
84}
85
86func checkFile(f *os.File) {
87	var syms []string
88	if ef, err := elf.NewFile(f); err == nil {
89		esyms, err := ef.Symbols()
90		if err != nil {
91			panic(err)
92		}
93		for _, esym := range esyms {
94			syms = append(syms, esym.Name)
95		}
96	} else if mf, err := macho.NewFile(f); err == nil {
97		for _, msym := range mf.Symtab.Syms {
98			syms = append(syms, msym.Name)
99		}
100	} else if pf, err := pe.NewFile(f); err == nil {
101		for _, psym := range pf.Symbols {
102			syms = append(syms, psym.Name)
103		}
104	} else {
105		fmt.Println("checksyms: could not parse executable")
106		fmt.Println("UNSUPPORTED: checksyms")
107		return
108	}
109	checkSyms(syms)
110}
111
112var want = []string{
113	"main.Function1",
114	"main.Function1..f",
115	"main.Function1..func1",
116	"main.Function1..func1.main.NestedType..d",
117	"main.Function1..func2",
118	"main.Function1..func2.main.NestedType..d",
119	"main.Function2",
120	"main.Function2..f",
121	"main.Function2.main.T..d",
122	"main.Function2.main.T..i1..d",
123	"main.Function3",
124	"main.Function3..f",
125	"main.Type..d",
126	"main.Type.M",
127	"main.main",
128	"main.want",
129	"type...1.1main.Type",  // Why is this here?
130	"type...1main.Function1..func1.NestedType",
131	"type...1main.Function1..func2.NestedType",
132	"type...1main.Function2.T",
133	"type...1main.Function2.T..i1",
134	"type...1main.Type",
135	"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",
136	"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",
137	"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",
138	"type..struct.4.main.f.0int.4tag.x20.x23.x24.x25.x5e.x26.x7b.x7d.x3a.x20..u4e16..u754c.5.5",
139}
140
141func checkSyms(syms []string) {
142	m := make(map[string]bool)
143	for _, sym := range syms {
144		if strings.Contains(sym, ".") {
145			m[sym] = true
146		}
147	}
148
149	ok := true
150	for _, w := range want {
151		if m[w] {
152			delete(m, w)
153		} else {
154			fmt.Printf("checksyms: missing expected symbol %q\n", w)
155			ok = false
156		}
157	}
158
159	for sym := range m {
160		if !strings.Contains(sym, "main") {
161			continue
162		}
163
164		// Skip some symbols we may see but know are unimportant.
165		if sym == "go-main.c" {
166			continue
167		}
168		if strings.HasPrefix(sym, "runtime.") {
169			continue
170		}
171
172		// We can see a lot of spurious .eq and .hash
173		// functions for types defined in other packages.
174		// This is a bug but skip them for now.
175		if strings.Contains(sym, "..eq") || strings.Contains(sym, "..hash") {
176			continue
177		}
178
179		// Skip closure types by skipping incomparable structs.
180		// This may be a bug, not sure.
181		if strings.Contains(sym, ".4x.5") {
182			continue
183		}
184
185		// These functions may be inlined.
186		if sym == "main.checkFile" || sym == "main.checkSyms" {
187			continue
188		}
189
190		fmt.Printf("checksyms: found unexpected symbol %q\n", sym)
191		ok = false
192	}
193
194	if !ok  {
195		fmt.Println("FAIL: checksyms")
196	}
197}
198