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