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