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