1
2
3
4
5 package errorstest
6
7 import (
8 "bytes"
9 "cmd/internal/quoted"
10 "internal/testenv"
11 "os"
12 "os/exec"
13 "path/filepath"
14 "strings"
15 "testing"
16 "unicode"
17 )
18
19
20
21
22 const magicInput = "abcdefghijklmnopqrstuvwxyz0123"
23 const magicReplace = "\n//go:cgo_ldflag \"-badflag\"\n//"
24
25 const cSymbol = "BadSymbol" + magicInput + "Name"
26 const cDefSource = "int " + cSymbol + " = 1;"
27 const cRefSource = "extern int " + cSymbol + "; int F() { return " + cSymbol + "; }"
28
29
30
31 const goSource = `
32 package main
33
34 // #cgo LDFLAGS: TMPDIR/cbad.o TMPDIR/cbad.so
35 // extern int F();
36 import "C"
37
38 func main() {
39 println(C.F())
40 }
41 `
42
43 func TestBadSymbol(t *testing.T) {
44 testenv.MustHaveGoBuild(t)
45 testenv.MustHaveCGO(t)
46
47 dir := t.TempDir()
48
49 mkdir := func(base string) string {
50 ret := filepath.Join(dir, base)
51 if err := os.Mkdir(ret, 0755); err != nil {
52 t.Fatal(err)
53 }
54 return ret
55 }
56
57 cdir := mkdir("c")
58 godir := mkdir("go")
59
60 makeFile := func(mdir, base, source string) string {
61 ret := filepath.Join(mdir, base)
62 if err := os.WriteFile(ret, []byte(source), 0644); err != nil {
63 t.Fatal(err)
64 }
65 return ret
66 }
67
68 cDefFile := makeFile(cdir, "cdef.c", cDefSource)
69 cRefFile := makeFile(cdir, "cref.c", cRefSource)
70
71 ccCmd := cCompilerCmd(t)
72
73 cCompile := func(arg, base, src string) string {
74 out := filepath.Join(cdir, base)
75 run := append(ccCmd, arg, "-o", out, src)
76 output, err := exec.Command(run[0], run[1:]...).CombinedOutput()
77 if err != nil {
78 t.Log(run)
79 t.Logf("%s", output)
80 t.Fatal(err)
81 }
82 if err := os.Remove(src); err != nil {
83 t.Fatal(err)
84 }
85 return out
86 }
87
88
89
90
91 cShared := cCompile("-shared", "c.so", cDefFile)
92
93
94
95
96 cObj := cCompile("-c", "c.o", cRefFile)
97
98
99
100
101
102
103
104
105
106 rewrite := func(from, to string) {
107 obj, err := os.ReadFile(from)
108 if err != nil {
109 t.Fatal(err)
110 }
111
112 if bytes.Count(obj, []byte(magicInput)) == 0 {
113 t.Fatalf("%s: did not find magic string", from)
114 }
115
116 if len(magicInput) != len(magicReplace) {
117 t.Fatalf("internal test error: different magic lengths: %d != %d", len(magicInput), len(magicReplace))
118 }
119
120 obj = bytes.ReplaceAll(obj, []byte(magicInput), []byte(magicReplace))
121
122 if err := os.WriteFile(to, obj, 0644); err != nil {
123 t.Fatal(err)
124 }
125 }
126
127 cBadShared := filepath.Join(godir, "cbad.so")
128 rewrite(cShared, cBadShared)
129
130 cBadObj := filepath.Join(godir, "cbad.o")
131 rewrite(cObj, cBadObj)
132
133 goSourceBadObject := strings.ReplaceAll(goSource, "TMPDIR", godir)
134 makeFile(godir, "go.go", goSourceBadObject)
135
136 makeFile(godir, "go.mod", "module badsym")
137
138
139 cmd := exec.Command("go", "build", "-ldflags=-v")
140 cmd.Dir = godir
141 output, err := cmd.CombinedOutput()
142
143
144
145
146
147 if err == nil {
148 t.Errorf("go build succeeded unexpectedly")
149 }
150
151 t.Logf("%s", output)
152
153 for _, line := range bytes.Split(output, []byte("\n")) {
154 if bytes.Contains(line, []byte("dynamic symbol")) && bytes.Contains(line, []byte("contains unsupported character")) {
155
156 continue
157 }
158
159
160
161 if bytes.Contains(line, []byte("-badflag")) {
162 t.Error("output should not mention -badflag")
163 }
164
165
166
167
168 if bytes.Contains(line, []byte("unrecognized")) || bytes.Contains(output, []byte("unknown")) {
169 t.Error("problem should have been caught before invoking C linker")
170 }
171 }
172 }
173
174 func cCompilerCmd(t *testing.T) []string {
175 cc, err := quoted.Split(goEnv(t, "CC"))
176 if err != nil {
177 t.Skipf("parsing go env CC: %s", err)
178 }
179 if len(cc) == 0 {
180 t.Skipf("no C compiler")
181 }
182 testenv.MustHaveExecPath(t, cc[0])
183
184 out := goEnv(t, "GOGCCFLAGS")
185 quote := '\000'
186 start := 0
187 lastSpace := true
188 backslash := false
189 s := string(out)
190 for i, c := range s {
191 if quote == '\000' && unicode.IsSpace(c) {
192 if !lastSpace {
193 cc = append(cc, s[start:i])
194 lastSpace = true
195 }
196 } else {
197 if lastSpace {
198 start = i
199 lastSpace = false
200 }
201 if quote == '\000' && !backslash && (c == '"' || c == '\'') {
202 quote = c
203 backslash = false
204 } else if !backslash && quote == c {
205 quote = '\000'
206 } else if (quote == '\000' || quote == '"') && !backslash && c == '\\' {
207 backslash = true
208 } else {
209 backslash = false
210 }
211 }
212 }
213 if !lastSpace {
214 cc = append(cc, s[start:])
215 }
216
217
218 cc = cc[:len(cc):len(cc)]
219
220 return cc
221 }
222
223 func goEnv(t *testing.T, key string) string {
224 out, err := exec.Command("go", "env", key).CombinedOutput()
225 if err != nil {
226 t.Logf("go env %s\n", key)
227 t.Logf("%s", out)
228 t.Fatal(err)
229 }
230 return strings.TrimSpace(string(out))
231 }
232
View as plain text