Source file
src/cmd/objdump/objdump_test.go
1
2
3
4
5 package main
6
7 import (
8 "cmd/internal/hash"
9 "flag"
10 "fmt"
11 "internal/platform"
12 "internal/testenv"
13 "os"
14 "path/filepath"
15 "runtime"
16 "strings"
17 "testing"
18 )
19
20
21
22 func TestMain(m *testing.M) {
23 if os.Getenv("GO_OBJDUMPTEST_IS_OBJDUMP") != "" {
24 main()
25 os.Exit(0)
26 }
27
28 os.Setenv("GO_OBJDUMPTEST_IS_OBJDUMP", "1")
29 os.Exit(m.Run())
30 }
31
32 var x86Need = []string{
33 "JMP main.main(SB)",
34 "CALL main.Println(SB)",
35 "RET",
36 }
37
38 var amd64GnuNeed = []string{
39 "jmp",
40 "callq",
41 "cmpb",
42 }
43
44 var i386GnuNeed = []string{
45 "jmp",
46 "call",
47 "cmp",
48 }
49
50 var armNeed = []string{
51 "B main.main(SB)",
52 "BL main.Println(SB)",
53 "RET",
54 }
55
56 var arm64Need = []string{
57 "JMP main.main(SB)",
58 "CALL main.Println(SB)",
59 "RET",
60 }
61
62 var armGnuNeed = []string{
63 "ldr",
64 "bl",
65 "cmp",
66 }
67
68 var loong64Need = []string{
69 "JMP main.main(SB)",
70 "CALL main.Println(SB)",
71 "RET",
72 }
73
74 var loong64GnuNeed = []string{
75 "ld.b",
76 "bl",
77 "beq",
78 }
79
80 var ppcNeed = []string{
81 "BR main.main(SB)",
82 "CALL main.Println(SB)",
83 "RET",
84 }
85
86 var ppcPIENeed = []string{
87 "BR",
88 "CALL",
89 "RET",
90 }
91
92 var ppcGnuNeed = []string{
93 "mflr",
94 "lbz",
95 "beq",
96 }
97
98 var s390xGnuNeed = []string{
99 "brasl",
100 "j",
101 "clije",
102 }
103
104 func mustHaveDisasm(t *testing.T) {
105 switch runtime.GOARCH {
106 case "mips", "mipsle", "mips64", "mips64le":
107 t.Skipf("skipping on %s, issue 12559", runtime.GOARCH)
108 }
109 }
110
111 var target = flag.String("target", "", "test disassembly of `goos/goarch` binary")
112
113
114
115
116
117
118
119
120
121
122 func testDisasm(t *testing.T, srcfname string, printCode bool, printGnuAsm bool, flags ...string) {
123 mustHaveDisasm(t)
124 goarch := runtime.GOARCH
125 if *target != "" {
126 f := strings.Split(*target, "/")
127 if len(f) != 2 {
128 t.Fatalf("-target argument must be goos/goarch")
129 }
130 defer os.Setenv("GOOS", os.Getenv("GOOS"))
131 defer os.Setenv("GOARCH", os.Getenv("GOARCH"))
132 os.Setenv("GOOS", f[0])
133 os.Setenv("GOARCH", f[1])
134 goarch = f[1]
135 }
136
137 hash := hash.Sum16([]byte(fmt.Sprintf("%v-%v-%v-%v", srcfname, flags, printCode, printGnuAsm)))
138 tmp := t.TempDir()
139 hello := filepath.Join(tmp, fmt.Sprintf("hello-%x.exe", hash))
140 args := []string{"build", "-o", hello}
141 args = append(args, flags...)
142 args = append(args, srcfname)
143 cmd := testenv.Command(t, testenv.GoToolPath(t), args...)
144
145 cmd.Dir = "testdata"
146 t.Logf("Running %v", cmd.Args)
147 out, err := cmd.CombinedOutput()
148 if err != nil {
149 t.Fatalf("go build %s: %v\n%s", srcfname, err, out)
150 }
151 need := []string{
152 "TEXT main.main(SB)",
153 }
154
155 if printCode {
156 need = append(need, ` Println("hello, world")`)
157 } else {
158 need = append(need, srcfname+":6")
159 }
160
161 switch goarch {
162 case "amd64", "386":
163 need = append(need, x86Need...)
164 case "arm":
165 need = append(need, armNeed...)
166 case "arm64":
167 need = append(need, arm64Need...)
168 case "loong64":
169 need = append(need, loong64Need...)
170 case "ppc64", "ppc64le":
171 var pie bool
172 for _, flag := range flags {
173 if flag == "-buildmode=pie" {
174 pie = true
175 break
176 }
177 }
178 if pie {
179
180
181
182 need = append(need, ppcPIENeed...)
183 } else {
184 need = append(need, ppcNeed...)
185 }
186 }
187
188 if printGnuAsm {
189 switch goarch {
190 case "amd64":
191 need = append(need, amd64GnuNeed...)
192 case "386":
193 need = append(need, i386GnuNeed...)
194 case "arm", "arm64":
195 need = append(need, armGnuNeed...)
196 case "loong64":
197 need = append(need, loong64GnuNeed...)
198 case "ppc64", "ppc64le":
199 need = append(need, ppcGnuNeed...)
200 case "s390x":
201 need = append(need, s390xGnuNeed...)
202 }
203 }
204 args = []string{
205 "-s", "main.main",
206 hello,
207 }
208
209 if printCode {
210 args = append([]string{"-S"}, args...)
211 }
212
213 if printGnuAsm {
214 args = append([]string{"-gnu"}, args...)
215 }
216 cmd = testenv.Command(t, testenv.Executable(t), args...)
217 cmd.Dir = "testdata"
218 out, err = cmd.CombinedOutput()
219 t.Logf("Running %v", cmd.Args)
220
221 if err != nil {
222 exename := srcfname[:len(srcfname)-len(filepath.Ext(srcfname))] + ".exe"
223 t.Fatalf("objdump %q: %v\n%s", exename, err, out)
224 }
225
226 text := string(out)
227 ok := true
228 for _, s := range need {
229 if !strings.Contains(text, s) {
230 t.Errorf("disassembly missing '%s'", s)
231 ok = false
232 }
233 }
234 if goarch == "386" {
235 if strings.Contains(text, "(IP)") {
236 t.Errorf("disassembly contains PC-Relative addressing on 386")
237 ok = false
238 }
239 }
240
241 if !ok || testing.Verbose() {
242 t.Logf("full disassembly:\n%s", text)
243 }
244 }
245
246 func testGoAndCgoDisasm(t *testing.T, printCode bool, printGnuAsm bool) {
247 t.Parallel()
248 testDisasm(t, "fmthello.go", printCode, printGnuAsm)
249 if testenv.HasCGO() {
250 testDisasm(t, "fmthellocgo.go", printCode, printGnuAsm)
251 }
252 }
253
254 func TestDisasm(t *testing.T) {
255 testGoAndCgoDisasm(t, false, false)
256 }
257
258 func TestDisasmCode(t *testing.T) {
259 testGoAndCgoDisasm(t, true, false)
260 }
261
262 func TestDisasmGnuAsm(t *testing.T) {
263 testGoAndCgoDisasm(t, false, true)
264 }
265
266 func TestDisasmExtld(t *testing.T) {
267 testenv.MustHaveCGO(t)
268 switch runtime.GOOS {
269 case "plan9":
270 t.Skipf("skipping on %s", runtime.GOOS)
271 }
272 t.Parallel()
273 testDisasm(t, "fmthello.go", false, false, "-ldflags=-linkmode=external")
274 }
275
276 func TestDisasmPIE(t *testing.T) {
277 if !platform.BuildModeSupported("gc", "pie", runtime.GOOS, runtime.GOARCH) {
278 t.Skipf("skipping on %s/%s, PIE buildmode not supported", runtime.GOOS, runtime.GOARCH)
279 }
280 if !platform.InternalLinkPIESupported(runtime.GOOS, runtime.GOARCH) {
281
282 testenv.MustHaveCGO(t)
283 }
284 t.Parallel()
285 testDisasm(t, "fmthello.go", false, false, "-buildmode=pie")
286 }
287
288 func TestDisasmGoobj(t *testing.T) {
289 mustHaveDisasm(t)
290 testenv.MustHaveGoBuild(t)
291
292 tmp := t.TempDir()
293
294 importcfgfile := filepath.Join(tmp, "hello.importcfg")
295 testenv.WriteImportcfg(t, importcfgfile, nil, "testdata/fmthello.go")
296
297 hello := filepath.Join(tmp, "hello.o")
298 args := []string{"tool", "compile", "-p=main", "-importcfg=" + importcfgfile, "-o", hello}
299 args = append(args, "testdata/fmthello.go")
300 out, err := testenv.Command(t, testenv.GoToolPath(t), args...).CombinedOutput()
301 if err != nil {
302 t.Fatalf("go tool compile fmthello.go: %v\n%s", err, out)
303 }
304 need := []string{
305 "main(SB)",
306 "fmthello.go:6",
307 }
308
309 args = []string{
310 "-s", "main",
311 hello,
312 }
313
314 out, err = testenv.Command(t, testenv.Executable(t), args...).CombinedOutput()
315 if err != nil {
316 t.Fatalf("objdump fmthello.o: %v\n%s", err, out)
317 }
318
319 text := string(out)
320 ok := true
321 for _, s := range need {
322 if !strings.Contains(text, s) {
323 t.Errorf("disassembly missing '%s'", s)
324 ok = false
325 }
326 }
327 if runtime.GOARCH == "386" {
328 if strings.Contains(text, "(IP)") {
329 t.Errorf("disassembly contains PC-Relative addressing on 386")
330 ok = false
331 }
332 }
333 if !ok {
334 t.Logf("full disassembly:\n%s", text)
335 }
336 }
337
338 func TestGoobjFileNumber(t *testing.T) {
339
340 testenv.MustHaveGoBuild(t)
341 mustHaveDisasm(t)
342
343 t.Parallel()
344
345 tmp := t.TempDir()
346
347 obj := filepath.Join(tmp, "p.a")
348 cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-o", obj)
349 cmd.Dir = filepath.Join("testdata/testfilenum")
350 out, err := cmd.CombinedOutput()
351 if err != nil {
352 t.Fatalf("build failed: %v\n%s", err, out)
353 }
354
355 cmd = testenv.Command(t, testenv.Executable(t), obj)
356 out, err = cmd.CombinedOutput()
357 if err != nil {
358 t.Fatalf("objdump failed: %v\n%s", err, out)
359 }
360
361 text := string(out)
362 for _, s := range []string{"a.go", "b.go", "c.go"} {
363 if !strings.Contains(text, s) {
364 t.Errorf("output missing '%s'", s)
365 }
366 }
367
368 if t.Failed() {
369 t.Logf("output:\n%s", text)
370 }
371 }
372
373 func TestGoObjOtherVersion(t *testing.T) {
374 t.Parallel()
375
376 obj := filepath.Join("testdata", "go116.o")
377 cmd := testenv.Command(t, testenv.Executable(t), obj)
378 out, err := cmd.CombinedOutput()
379 if err == nil {
380 t.Fatalf("objdump go116.o succeeded unexpectedly")
381 }
382 if !strings.Contains(string(out), "go object of a different version") {
383 t.Errorf("unexpected error message:\n%s", out)
384 }
385 }
386
View as plain text