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