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