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 "runtime.bss": "B",
120 "runtime.noptrbss": "B",
121 }
122
123 out, err = testenv.Command(t, testenv.Executable(t), exe).CombinedOutput()
124 if err != nil {
125 t.Fatalf("go tool nm: %v\n%s", err, string(out))
126 }
127
128 relocated := func(code string) bool {
129 if runtime.GOOS == "aix" {
130
131
132
133
134 switch code {
135 case "T", "t", "R", "r":
136 return iscgo
137 case "D", "d", "B", "b":
138 return true
139 }
140 }
141 if platform.DefaultPIE(runtime.GOOS, runtime.GOARCH, false) {
142
143 return true
144 }
145 return false
146 }
147
148 dups := make(map[string]bool)
149 for _, line := range strings.Split(string(out), "\n") {
150 f := strings.Fields(line)
151 if len(f) < 3 {
152 continue
153 }
154 name := f[2]
155 if addr, found := names[name]; found {
156 if want, have := addr, "0x"+f[0]; have != want {
157 if !relocated(f[1]) {
158 t.Errorf("want %s address for %s symbol, but have %s", want, name, have)
159 }
160 }
161 delete(names, name)
162 }
163 if _, found := dups[name]; found {
164 t.Errorf("duplicate name of %q is found", name)
165 }
166 if stype, found := runtimeSyms[name]; found {
167 if runtime.GOOS == "plan9" && stype == "R" {
168
169 stype = "D"
170 }
171 if want, have := stype, strings.ToUpper(f[1]); have != want {
172 if runtime.GOOS == "android" && name == "runtime.epclntab" && have == "D" {
173
174 t.Logf("(ignoring on %s) want %s type for %s symbol, but have %s", runtime.GOOS, want, name, have)
175 } else {
176 t.Errorf("want %s type for %s symbol, but have %s", want, name, have)
177 }
178 }
179 delete(runtimeSyms, name)
180 }
181 }
182 if len(names) > 0 {
183 t.Errorf("executable is missing %v symbols", names)
184 }
185 if len(runtimeSyms) > 0 {
186 t.Errorf("executable is missing %v symbols", runtimeSyms)
187 }
188 }
189
190 func TestGoExec(t *testing.T) {
191 testGoExec(t, false, false)
192 }
193
194 func testGoLib(t *testing.T, iscgo bool) {
195 t.Parallel()
196 tmpdir := t.TempDir()
197
198 gopath := filepath.Join(tmpdir, "gopath")
199 libpath := filepath.Join(gopath, "src", "mylib")
200
201 err := os.MkdirAll(libpath, 0777)
202 if err != nil {
203 t.Fatal(err)
204 }
205 src := filepath.Join(libpath, "a.go")
206 file, err := os.Create(src)
207 if err != nil {
208 t.Fatal(err)
209 }
210 err = template.Must(template.New("mylib").Parse(testlib)).Execute(file, iscgo)
211 if e := file.Close(); err == nil {
212 err = e
213 }
214 if err == nil {
215 err = os.WriteFile(filepath.Join(libpath, "go.mod"), []byte("module mylib\n"), 0666)
216 }
217 if err != nil {
218 t.Fatal(err)
219 }
220
221 cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-buildmode=archive", "-o", "mylib.a", ".")
222 cmd.Dir = libpath
223 cmd.Env = append(os.Environ(), "GOPATH="+gopath)
224 out, err := cmd.CombinedOutput()
225 if err != nil {
226 t.Fatalf("building test lib failed: %s %s", err, out)
227 }
228 mylib := filepath.Join(libpath, "mylib.a")
229
230 out, err = testenv.Command(t, testenv.Executable(t), mylib).CombinedOutput()
231 if err != nil {
232 t.Fatalf("go tool nm: %v\n%s", err, string(out))
233 }
234 type symType struct {
235 Type string
236 Name string
237 CSym bool
238 Found bool
239 }
240 var syms = []symType{
241 {"B", "mylib.Testdata", false, false},
242 {"T", "mylib.Testfunc", false, false},
243 }
244 if iscgo {
245 syms = append(syms, symType{"B", "mylib.TestCgodata", false, false})
246 syms = append(syms, symType{"T", "mylib.TestCgofunc", false, false})
247 if runtime.GOOS == "darwin" || runtime.GOOS == "ios" || (runtime.GOOS == "windows" && runtime.GOARCH == "386") {
248 syms = append(syms, symType{"D", "_cgodata", true, false})
249 syms = append(syms, symType{"T", "_cgofunc", true, false})
250 } else if runtime.GOOS == "aix" {
251 syms = append(syms, symType{"D", "cgodata", true, false})
252 syms = append(syms, symType{"T", ".cgofunc", true, false})
253 } else {
254 syms = append(syms, symType{"D", "cgodata", true, false})
255 syms = append(syms, symType{"T", "cgofunc", true, false})
256 }
257 }
258
259 for _, line := range strings.Split(string(out), "\n") {
260 f := strings.Fields(line)
261 var typ, name string
262 var csym bool
263 if iscgo {
264 if len(f) < 4 {
265 continue
266 }
267 csym = !strings.Contains(f[0], "_go_.o")
268 typ = f[2]
269 name = f[3]
270 } else {
271 if len(f) < 3 {
272 continue
273 }
274 typ = f[1]
275 name = f[2]
276 }
277 for i := range syms {
278 sym := &syms[i]
279 if sym.Type == typ && sym.Name == name && sym.CSym == csym {
280 if sym.Found {
281 t.Fatalf("duplicate symbol %s %s", sym.Type, sym.Name)
282 }
283 sym.Found = true
284 }
285 }
286 }
287 for _, sym := range syms {
288 if !sym.Found {
289 t.Errorf("cannot found symbol %s %s", sym.Type, sym.Name)
290 }
291 }
292 }
293
294 func TestGoLib(t *testing.T) {
295 testGoLib(t, false)
296 }
297
298 const testexec = `
299 package main
300
301 import "fmt"
302 {{if .}}import "C"
303 {{end}}
304
305 func main() {
306 testfunc()
307 }
308
309 var testdata uint32
310
311 func testfunc() {
312 fmt.Printf("main=%p\n", main)
313 fmt.Printf("testfunc=%p\n", testfunc)
314 fmt.Printf("testdata=%p\n", &testdata)
315 }
316 `
317
318 const testlib = `
319 package mylib
320
321 {{if .}}
322 // int cgodata = 5;
323 // void cgofunc(void) {}
324 import "C"
325
326 var TestCgodata = C.cgodata
327
328 func TestCgofunc() {
329 C.cgofunc()
330 }
331 {{end}}
332
333 var Testdata uint32
334
335 func Testfunc() {}
336 `
337
View as plain text