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