Source file
src/cmd/link/dwarf_test.go
1
2
3
4
5 package main
6
7 import (
8 "bytes"
9 cmddwarf "cmd/internal/dwarf"
10 "cmd/internal/objfile"
11 "cmd/internal/quoted"
12 "debug/dwarf"
13 "internal/testenv"
14 "os"
15 "os/exec"
16 "path"
17 "path/filepath"
18 "runtime"
19 "strings"
20 "testing"
21 )
22
23
24
25
26
27
28
29 func TestMain(m *testing.M) {
30 if os.Getenv("LINK_TEST_TOOLEXEC") == "" {
31
32 os.Exit(m.Run())
33 }
34
35 if strings.TrimSuffix(filepath.Base(os.Args[1]), ".exe") == "link" {
36
37
38 os.Args = os.Args[1:]
39 main()
40 os.Exit(0)
41 }
42
43 cmd := exec.Command(os.Args[1], os.Args[2:]...)
44 cmd.Stdin = os.Stdin
45 cmd.Stdout = os.Stdout
46 cmd.Stderr = os.Stderr
47 if err := cmd.Run(); err != nil {
48 os.Exit(1)
49 }
50 os.Exit(0)
51 }
52
53 func testDWARF(t *testing.T, buildmode string, expectDWARF bool, env ...string) {
54 testenv.MustHaveCGO(t)
55 testenv.MustHaveGoBuild(t)
56
57 if runtime.GOOS == "plan9" {
58 t.Skip("skipping on plan9; no DWARF symbol table in executables")
59 }
60
61 t.Parallel()
62
63 for _, prog := range []string{"testprog", "testprogcgo"} {
64 prog := prog
65 expectDWARF := expectDWARF
66 if runtime.GOOS == "aix" && prog == "testprogcgo" {
67 extld := os.Getenv("CC")
68 if extld == "" {
69 extld = "gcc"
70 }
71 extldArgs, err := quoted.Split(extld)
72 if err != nil {
73 t.Fatal(err)
74 }
75 expectDWARF, err = cmddwarf.IsDWARFEnabledOnAIXLd(extldArgs)
76 if err != nil {
77 t.Fatal(err)
78 }
79 }
80
81 t.Run(prog, func(t *testing.T) {
82 t.Parallel()
83
84 tmpDir := t.TempDir()
85
86 exe := filepath.Join(tmpDir, prog+".exe")
87 dir := "../../runtime/testdata/" + prog
88 cmd := exec.Command(testenv.GoToolPath(t), "build", "-toolexec", os.Args[0], "-o", exe)
89 if buildmode != "" {
90 cmd.Args = append(cmd.Args, "-buildmode", buildmode)
91 }
92 cmd.Args = append(cmd.Args, dir)
93 cmd.Env = append(os.Environ(), env...)
94 cmd.Env = append(cmd.Env, "CGO_CFLAGS=")
95 cmd.Env = append(cmd.Env, "LINK_TEST_TOOLEXEC=1")
96 out, err := cmd.CombinedOutput()
97 if err != nil {
98 t.Fatalf("go build -o %v %v: %v\n%s", exe, dir, err, out)
99 }
100
101 if buildmode == "c-archive" {
102
103 cmd := exec.Command("ar", "-x", exe)
104 cmd.Dir = tmpDir
105 if out, err := cmd.CombinedOutput(); err != nil {
106 t.Fatalf("ar -x %s: %v\n%s", exe, err, out)
107 }
108 exe = filepath.Join(tmpDir, "go.o")
109 }
110
111 darwinSymbolTestIsTooFlaky := true
112 if runtime.GOOS == "darwin" && !darwinSymbolTestIsTooFlaky {
113 if _, err = exec.LookPath("symbols"); err == nil {
114
115 out, err = exec.Command("symbols", exe).CombinedOutput()
116 if err != nil {
117 t.Fatalf("symbols %v: %v: %s", filepath.Base(exe), err, out)
118 } else {
119 if bytes.HasPrefix(out, []byte("Unable to find file")) {
120
121 t.Fatalf("symbols %v: failed to parse file", filepath.Base(exe))
122 } else if bytes.Contains(out, []byte(", Empty]")) {
123 t.Fatalf("symbols %v: parsed as empty", filepath.Base(exe))
124 }
125 }
126 }
127 }
128
129 f, err := objfile.Open(exe)
130 if err != nil {
131 t.Fatal(err)
132 }
133 defer f.Close()
134
135 syms, err := f.Symbols()
136 if err != nil {
137 t.Fatal(err)
138 }
139
140 var addr uint64
141 for _, sym := range syms {
142 if sym.Name == "main.main" {
143 addr = sym.Addr
144 break
145 }
146 }
147 if addr == 0 {
148 t.Fatal("cannot find main.main in symbols")
149 }
150
151 d, err := f.DWARF()
152 if err != nil {
153 if expectDWARF {
154 t.Fatal(err)
155 }
156 return
157 } else {
158 if !expectDWARF {
159 t.Fatal("unexpected DWARF section")
160 }
161 }
162
163
164
165 wantFile := path.Join(prog, "main.go")
166 wantLine := 24
167 r := d.Reader()
168 entry, err := r.SeekPC(addr)
169 if err != nil {
170 t.Fatal(err)
171 }
172 lr, err := d.LineReader(entry)
173 if err != nil {
174 t.Fatal(err)
175 }
176 var line dwarf.LineEntry
177 if err := lr.SeekPC(addr, &line); err == dwarf.ErrUnknownPC {
178 t.Fatalf("did not find file:line for %#x (main.main)", addr)
179 } else if err != nil {
180 t.Fatal(err)
181 }
182 if !strings.HasSuffix(line.File.Name, wantFile) || line.Line != wantLine {
183 t.Errorf("%#x is %s:%d, want %s:%d", addr, line.File.Name, line.Line, filepath.Join("...", wantFile), wantLine)
184 }
185 })
186 }
187 }
188
189 func TestDWARF(t *testing.T) {
190 testDWARF(t, "", true)
191 if !testing.Short() {
192 if runtime.GOOS == "windows" {
193 t.Skip("skipping Windows/c-archive; see Issue 35512 for more.")
194 }
195 t.Run("c-archive", func(t *testing.T) {
196 testDWARF(t, "c-archive", true)
197 })
198 }
199 }
200
201 func TestDWARFiOS(t *testing.T) {
202
203
204
205 if testing.Short() {
206 t.Skip("skipping in short mode")
207 }
208 if runtime.GOARCH != "amd64" || runtime.GOOS != "darwin" {
209 t.Skip("skipping on non-darwin/amd64 platform")
210 }
211 if err := exec.Command("xcrun", "--help").Run(); err != nil {
212 t.Skipf("error running xcrun, required for iOS cross build: %v", err)
213 }
214
215
216 if output, err := exec.Command("xcodebuild", "-showsdks").CombinedOutput(); err != nil {
217 t.Skipf("error running xcodebuild, required for iOS cross build: %v", err)
218 } else if !strings.Contains(string(output), "iOS SDK") {
219 t.Skipf("iOS SDK not detected.")
220 }
221 cc := "CC=" + runtime.GOROOT() + "/misc/ios/clangwrap.sh"
222
223 t.Run("exe", func(t *testing.T) {
224 testDWARF(t, "", false, cc, "CGO_ENABLED=1", "GOOS=ios", "GOARCH=arm64")
225 })
226
227 t.Run("c-archive", func(t *testing.T) {
228 testDWARF(t, "c-archive", true, cc, "CGO_ENABLED=1", "GOOS=ios", "GOARCH=arm64")
229 })
230 }
231
View as plain text