Source file
src/cmd/nm/nm_test.go
1
2
3
4
5 package main
6
7 import (
8 "internal/obscuretestdata"
9 "internal/platform"
10 "internal/testenv"
11 "os"
12 "path/filepath"
13 "runtime"
14 "strings"
15 "testing"
16 "text/template"
17 )
18
19
20
21 func TestMain(m *testing.M) {
22 if os.Getenv("GO_NMTEST_IS_NM") != "" {
23 main()
24 os.Exit(0)
25 }
26
27 os.Setenv("GO_NMTEST_IS_NM", "1")
28 os.Exit(m.Run())
29 }
30
31 func TestNonGoExecs(t *testing.T) {
32 t.Parallel()
33 testfiles := []string{
34 "debug/elf/testdata/gcc-386-freebsd-exec",
35 "debug/elf/testdata/gcc-amd64-linux-exec",
36 "debug/macho/testdata/gcc-386-darwin-exec.base64",
37 "debug/macho/testdata/gcc-amd64-darwin-exec.base64",
38
39 "debug/pe/testdata/gcc-386-mingw-exec",
40 "debug/plan9obj/testdata/amd64-plan9-exec",
41 "debug/plan9obj/testdata/386-plan9-exec",
42 "internal/xcoff/testdata/gcc-ppc64-aix-dwarf2-exec",
43 }
44 for _, f := range testfiles {
45 exepath := filepath.Join(testenv.GOROOT(t), "src", f)
46 if strings.HasSuffix(f, ".base64") {
47 tf, err := obscuretestdata.DecodeToTempFile(exepath)
48 if err != nil {
49 t.Errorf("obscuretestdata.DecodeToTempFile(%s): %v", exepath, err)
50 continue
51 }
52 defer os.Remove(tf)
53 exepath = tf
54 }
55
56 cmd := testenv.Command(t, testenv.Executable(t), exepath)
57 out, err := cmd.CombinedOutput()
58 if err != nil {
59 t.Errorf("go tool nm %v: %v\n%s", exepath, err, string(out))
60 }
61 }
62 }
63
64 func testGoExec(t *testing.T, iscgo, isexternallinker bool) {
65 t.Parallel()
66 tmpdir := t.TempDir()
67
68 src := filepath.Join(tmpdir, "a.go")
69 file, err := os.Create(src)
70 if err != nil {
71 t.Fatal(err)
72 }
73 err = template.Must(template.New("main").Parse(testexec)).Execute(file, iscgo)
74 if e := file.Close(); err == nil {
75 err = e
76 }
77 if err != nil {
78 t.Fatal(err)
79 }
80
81 exe := filepath.Join(tmpdir, "a.exe")
82 args := []string{"build", "-o", exe}
83 if iscgo {
84 linkmode := "internal"
85 if isexternallinker {
86 linkmode = "external"
87 }
88 args = append(args, "-ldflags", "-linkmode="+linkmode)
89 }
90 args = append(args, src)
91 out, err := testenv.Command(t, testenv.GoToolPath(t), args...).CombinedOutput()
92 if err != nil {
93 t.Fatalf("building test executable failed: %s %s", err, out)
94 }
95
96 out, err = testenv.Command(t, exe).CombinedOutput()
97 if err != nil {
98 t.Fatalf("running test executable failed: %s %s", err, out)
99 }
100 names := make(map[string]string)
101 for _, line := range strings.Split(string(out), "\n") {
102 if line == "" {
103 continue
104 }
105 f := strings.Split(line, "=")
106 if len(f) != 2 {
107 t.Fatalf("unexpected output line: %q", line)
108 }
109 names["main."+f[0]] = f[1]
110 }
111
112 runtimeSyms := map[string]string{
113 "runtime.text": "T",
114 "runtime.etext": "T",
115 "runtime.rodata": "R",
116 "runtime.erodata": "R",
117 "runtime.epclntab": "R",
118 "runtime.noptrdata": "D",
119 }
120
121 out, err = testenv.Command(t, testenv.Executable(t), exe).CombinedOutput()
122 if err != nil {
123 t.Fatalf("go tool nm: %v\n%s", err, string(out))
124 }
125
126 relocated := func(code string) bool {
127 if runtime.GOOS == "aix" {
128
129
130
131
132 switch code {
133 case "T", "t", "R", "r":
134 return iscgo
135 case "D", "d", "B", "b":
136 return true
137 }
138 }
139 if platform.DefaultPIE(runtime.GOOS, runtime.GOARCH, false) {
140
141 return true
142 }
143 return false
144 }
145
146 dups := make(map[string]bool)
147 for _, line := range strings.Split(string(out), "\n") {
148 f := strings.Fields(line)
149 if len(f) < 3 {
150 continue
151 }
152 name := f[2]
153 if addr, found := names[name]; found {
154 if want, have := addr, "0x"+f[0]; have != want {
155 if !relocated(f[1]) {
156 t.Errorf("want %s address for %s symbol, but have %s", want, name, have)
157 }
158 }
159 delete(names, name)
160 }
161 if _, found := dups[name]; found {
162 t.Errorf("duplicate name of %q is found", name)
163 }
164 if stype, found := runtimeSyms[name]; found {
165 if runtime.GOOS == "plan9" && stype == "R" {
166
167 stype = "D"
168 }
169 if want, have := stype, strings.ToUpper(f[1]); have != want {
170 if runtime.GOOS == "android" && name == "runtime.epclntab" && have == "D" {
171
172 t.Logf("(ignoring on %s) want %s type for %s symbol, but have %s", runtime.GOOS, want, name, have)
173 } else {
174 t.Errorf("want %s type for %s symbol, but have %s", want, name, have)
175 }
176 }
177 delete(runtimeSyms, name)
178 }
179 }
180 if len(names) > 0 {
181 t.Errorf("executable is missing %v symbols", names)
182 }
183 if len(runtimeSyms) > 0 {
184 t.Errorf("executable is missing %v symbols", runtimeSyms)
185 }
186 }
187
188 func TestGoExec(t *testing.T) {
189 testGoExec(t, false, false)
190 }
191
192 func testGoLib(t *testing.T, iscgo bool) {
193 t.Parallel()
194 tmpdir := t.TempDir()
195
196 gopath := filepath.Join(tmpdir, "gopath")
197 libpath := filepath.Join(gopath, "src", "mylib")
198
199 err := os.MkdirAll(libpath, 0777)
200 if err != nil {
201 t.Fatal(err)
202 }
203 src := filepath.Join(libpath, "a.go")
204 file, err := os.Create(src)
205 if err != nil {
206 t.Fatal(err)
207 }
208 err = template.Must(template.New("mylib").Parse(testlib)).Execute(file, iscgo)
209 if e := file.Close(); err == nil {
210 err = e
211 }
212 if err == nil {
213 err = os.WriteFile(filepath.Join(libpath, "go.mod"), []byte("module mylib\n"), 0666)
214 }
215 if err != nil {
216 t.Fatal(err)
217 }
218
219 cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-buildmode=archive", "-o", "mylib.a", ".")
220 cmd.Dir = libpath
221 cmd.Env = append(os.Environ(), "GOPATH="+gopath)
222 out, err := cmd.CombinedOutput()
223 if err != nil {
224 t.Fatalf("building test lib failed: %s %s", err, out)
225 }
226 mylib := filepath.Join(libpath, "mylib.a")
227
228 out, err = testenv.Command(t, testenv.Executable(t), mylib).CombinedOutput()
229 if err != nil {
230 t.Fatalf("go tool nm: %v\n%s", err, string(out))
231 }
232 type symType struct {
233 Type string
234 Name string
235 CSym bool
236 Found bool
237 }
238 var syms = []symType{
239 {"B", "mylib.Testdata", false, false},
240 {"T", "mylib.Testfunc", false, false},
241 }
242 if iscgo {
243 syms = append(syms, symType{"B", "mylib.TestCgodata", false, false})
244 syms = append(syms, symType{"T", "mylib.TestCgofunc", false, false})
245 if runtime.GOOS == "darwin" || runtime.GOOS == "ios" || (runtime.GOOS == "windows" && runtime.GOARCH == "386") {
246 syms = append(syms, symType{"D", "_cgodata", true, false})
247 syms = append(syms, symType{"T", "_cgofunc", true, false})
248 } else if runtime.GOOS == "aix" {
249 syms = append(syms, symType{"D", "cgodata", true, false})
250 syms = append(syms, symType{"T", ".cgofunc", true, false})
251 } else {
252 syms = append(syms, symType{"D", "cgodata", true, false})
253 syms = append(syms, symType{"T", "cgofunc", true, false})
254 }
255 }
256
257 for _, line := range strings.Split(string(out), "\n") {
258 f := strings.Fields(line)
259 var typ, name string
260 var csym bool
261 if iscgo {
262 if len(f) < 4 {
263 continue
264 }
265 csym = !strings.Contains(f[0], "_go_.o")
266 typ = f[2]
267 name = f[3]
268 } else {
269 if len(f) < 3 {
270 continue
271 }
272 typ = f[1]
273 name = f[2]
274 }
275 for i := range syms {
276 sym := &syms[i]
277 if sym.Type == typ && sym.Name == name && sym.CSym == csym {
278 if sym.Found {
279 t.Fatalf("duplicate symbol %s %s", sym.Type, sym.Name)
280 }
281 sym.Found = true
282 }
283 }
284 }
285 for _, sym := range syms {
286 if !sym.Found {
287 t.Errorf("cannot found symbol %s %s", sym.Type, sym.Name)
288 }
289 }
290 }
291
292 func TestGoLib(t *testing.T) {
293 testGoLib(t, false)
294 }
295
296 const testexec = `
297 package main
298
299 import "fmt"
300 {{if .}}import "C"
301 {{end}}
302
303 func main() {
304 testfunc()
305 }
306
307 var testdata uint32
308
309 func testfunc() {
310 fmt.Printf("main=%p\n", main)
311 fmt.Printf("testfunc=%p\n", testfunc)
312 fmt.Printf("testdata=%p\n", &testdata)
313 }
314 `
315
316 const testlib = `
317 package mylib
318
319 {{if .}}
320 // int cgodata = 5;
321 // void cgofunc(void) {}
322 import "C"
323
324 var TestCgodata = C.cgodata
325
326 func TestCgofunc() {
327 C.cgofunc()
328 }
329 {{end}}
330
331 var Testdata uint32
332
333 func Testfunc() {}
334 `
335
View as plain text