1// Copyright 2018 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package work
6
7import (
8	"os"
9	"strings"
10	"testing"
11)
12
13var goodCompilerFlags = [][]string{
14	{"-DFOO"},
15	{"-Dfoo=bar"},
16	{"-Ufoo"},
17	{"-Ufoo1"},
18	{"-F/Qt"},
19	{"-F", "/Qt"},
20	{"-I/"},
21	{"-I/etc/passwd"},
22	{"-I."},
23	{"-O"},
24	{"-O2"},
25	{"-Osmall"},
26	{"-W"},
27	{"-Wall"},
28	{"-Wp,-Dfoo=bar"},
29	{"-Wp,-Ufoo"},
30	{"-Wp,-Dfoo1"},
31	{"-Wp,-Ufoo1"},
32	{"-flto"},
33	{"-fobjc-arc"},
34	{"-fno-objc-arc"},
35	{"-fomit-frame-pointer"},
36	{"-fno-omit-frame-pointer"},
37	{"-fpic"},
38	{"-fno-pic"},
39	{"-fPIC"},
40	{"-fno-PIC"},
41	{"-fpie"},
42	{"-fno-pie"},
43	{"-fPIE"},
44	{"-fno-PIE"},
45	{"-fsplit-stack"},
46	{"-fno-split-stack"},
47	{"-fstack-xxx"},
48	{"-fno-stack-xxx"},
49	{"-fsanitize=hands"},
50	{"-g"},
51	{"-ggdb"},
52	{"-march=souza"},
53	{"-mcmodel=medium"},
54	{"-mcpu=123"},
55	{"-mfpu=123"},
56	{"-mlarge-data-threshold=16"},
57	{"-mtune=happybirthday"},
58	{"-mstack-overflow"},
59	{"-mno-stack-overflow"},
60	{"-mmacosx-version"},
61	{"-mnop-fun-dllimport"},
62	{"-pthread"},
63	{"-std=c99"},
64	{"-xc"},
65	{"-D", "FOO"},
66	{"-D", "foo=bar"},
67	{"-I", "."},
68	{"-I", "/etc/passwd"},
69	{"-I", "世界"},
70	{"-I", "=/usr/include/libxml2"},
71	{"-I", "dir"},
72	{"-I", "$SYSROOT/dir"},
73	{"-isystem", "/usr/include/mozjs-68"},
74	{"-include", "/usr/include/mozjs-68/RequiredDefines.h"},
75	{"-framework", "Chocolate"},
76	{"-x", "c"},
77	{"-v"},
78}
79
80var badCompilerFlags = [][]string{
81	{"-D@X"},
82	{"-D-X"},
83	{"-Ufoo=bar"},
84	{"-F@dir"},
85	{"-F-dir"},
86	{"-I@dir"},
87	{"-I-dir"},
88	{"-O@1"},
89	{"-Wa,-foo"},
90	{"-W@foo"},
91	{"-Wp,-DX,-D@X"},
92	{"-Wp,-UX,-U@X"},
93	{"-g@gdb"},
94	{"-g-gdb"},
95	{"-march=@dawn"},
96	{"-march=-dawn"},
97	{"-mcmodel=@model"},
98	{"-mlarge-data-threshold=@12"},
99	{"-std=@c99"},
100	{"-std=-c99"},
101	{"-x@c"},
102	{"-x-c"},
103	{"-D", "@foo"},
104	{"-D", "-foo"},
105	{"-I", "@foo"},
106	{"-I", "-foo"},
107	{"-I", "=@obj"},
108	{"-include", "@foo"},
109	{"-framework", "-Caffeine"},
110	{"-framework", "@Home"},
111	{"-x", "--c"},
112	{"-x", "@obj"},
113}
114
115func TestCheckCompilerFlags(t *testing.T) {
116	for _, f := range goodCompilerFlags {
117		if err := checkCompilerFlags("test", "test", f); err != nil {
118			t.Errorf("unexpected error for %q: %v", f, err)
119		}
120	}
121	for _, f := range badCompilerFlags {
122		if err := checkCompilerFlags("test", "test", f); err == nil {
123			t.Errorf("missing error for %q", f)
124		}
125	}
126}
127
128var goodLinkerFlags = [][]string{
129	{"-Fbar"},
130	{"-lbar"},
131	{"-Lbar"},
132	{"-fpic"},
133	{"-fno-pic"},
134	{"-fPIC"},
135	{"-fno-PIC"},
136	{"-fpie"},
137	{"-fno-pie"},
138	{"-fPIE"},
139	{"-fno-PIE"},
140	{"-fsanitize=hands"},
141	{"-g"},
142	{"-ggdb"},
143	{"-march=souza"},
144	{"-mcpu=123"},
145	{"-mfpu=123"},
146	{"-mtune=happybirthday"},
147	{"-pic"},
148	{"-pthread"},
149	{"-Wl,--hash-style=both"},
150	{"-Wl,-rpath,foo"},
151	{"-Wl,-rpath,$ORIGIN/foo"},
152	{"-Wl,-R", "/foo"},
153	{"-Wl,-R", "foo"},
154	{"-Wl,-R,foo"},
155	{"-Wl,--just-symbols=foo"},
156	{"-Wl,--just-symbols,foo"},
157	{"-Wl,--warn-error"},
158	{"-Wl,--no-warn-error"},
159	{"foo.so"},
160	{"_世界.dll"},
161	{"./x.o"},
162	{"libcgosotest.dylib"},
163	{"-F", "framework"},
164	{"-l", "."},
165	{"-l", "/etc/passwd"},
166	{"-l", "世界"},
167	{"-L", "framework"},
168	{"-framework", "Chocolate"},
169	{"-v"},
170	{"-Wl,-sectcreate,__TEXT,__info_plist,${SRCDIR}/Info.plist"},
171	{"-Wl,-framework", "-Wl,Chocolate"},
172	{"-Wl,-framework,Chocolate"},
173	{"-Wl,-unresolved-symbols=ignore-all"},
174	{"-Wl,-z,relro"},
175	{"-Wl,-z,relro,-z,now"},
176	{"-Wl,-z,now"},
177	{"-Wl,-z,noexecstack"},
178	{"libcgotbdtest.tbd"},
179	{"./libcgotbdtest.tbd"},
180}
181
182var badLinkerFlags = [][]string{
183	{"-DFOO"},
184	{"-Dfoo=bar"},
185	{"-W"},
186	{"-Wall"},
187	{"-fobjc-arc"},
188	{"-fno-objc-arc"},
189	{"-fomit-frame-pointer"},
190	{"-fno-omit-frame-pointer"},
191	{"-fsplit-stack"},
192	{"-fno-split-stack"},
193	{"-fstack-xxx"},
194	{"-fno-stack-xxx"},
195	{"-mstack-overflow"},
196	{"-mno-stack-overflow"},
197	{"-mnop-fun-dllimport"},
198	{"-std=c99"},
199	{"-xc"},
200	{"-D", "FOO"},
201	{"-D", "foo=bar"},
202	{"-I", "FOO"},
203	{"-L", "@foo"},
204	{"-L", "-foo"},
205	{"-x", "c"},
206	{"-D@X"},
207	{"-D-X"},
208	{"-I@dir"},
209	{"-I-dir"},
210	{"-O@1"},
211	{"-Wa,-foo"},
212	{"-W@foo"},
213	{"-g@gdb"},
214	{"-g-gdb"},
215	{"-march=@dawn"},
216	{"-march=-dawn"},
217	{"-std=@c99"},
218	{"-std=-c99"},
219	{"-x@c"},
220	{"-x-c"},
221	{"-D", "@foo"},
222	{"-D", "-foo"},
223	{"-I", "@foo"},
224	{"-I", "-foo"},
225	{"-l", "@foo"},
226	{"-l", "-foo"},
227	{"-framework", "-Caffeine"},
228	{"-framework", "@Home"},
229	{"-Wl,-framework,-Caffeine"},
230	{"-Wl,-framework", "-Wl,@Home"},
231	{"-Wl,-framework", "@Home"},
232	{"-Wl,-framework,Chocolate,@Home"},
233	{"-Wl,--hash-style=foo"},
234	{"-x", "--c"},
235	{"-x", "@obj"},
236	{"-Wl,-rpath,@foo"},
237	{"-Wl,-R,foo,bar"},
238	{"-Wl,-R,@foo"},
239	{"-Wl,--just-symbols,@foo"},
240	{"../x.o"},
241	{"-Wl,-R,"},
242	{"-Wl,-O"},
243	{"-Wl,-e="},
244	{"-Wl,-e,"},
245	{"-Wl,-R,-flag"},
246}
247
248func TestCheckLinkerFlags(t *testing.T) {
249	for _, f := range goodLinkerFlags {
250		if err := checkLinkerFlags("test", "test", f); err != nil {
251			t.Errorf("unexpected error for %q: %v", f, err)
252		}
253	}
254	for _, f := range badLinkerFlags {
255		if err := checkLinkerFlags("test", "test", f); err == nil {
256			t.Errorf("missing error for %q", f)
257		}
258	}
259}
260
261func TestCheckFlagAllowDisallow(t *testing.T) {
262	if err := checkCompilerFlags("TEST", "test", []string{"-disallow"}); err == nil {
263		t.Fatalf("missing error for -disallow")
264	}
265	os.Setenv("CGO_TEST_ALLOW", "-disallo")
266	if err := checkCompilerFlags("TEST", "test", []string{"-disallow"}); err == nil {
267		t.Fatalf("missing error for -disallow with CGO_TEST_ALLOW=-disallo")
268	}
269	os.Setenv("CGO_TEST_ALLOW", "-disallow")
270	if err := checkCompilerFlags("TEST", "test", []string{"-disallow"}); err != nil {
271		t.Fatalf("unexpected error for -disallow with CGO_TEST_ALLOW=-disallow: %v", err)
272	}
273	os.Unsetenv("CGO_TEST_ALLOW")
274
275	if err := checkCompilerFlags("TEST", "test", []string{"-Wall"}); err != nil {
276		t.Fatalf("unexpected error for -Wall: %v", err)
277	}
278	os.Setenv("CGO_TEST_DISALLOW", "-Wall")
279	if err := checkCompilerFlags("TEST", "test", []string{"-Wall"}); err == nil {
280		t.Fatalf("missing error for -Wall with CGO_TEST_DISALLOW=-Wall")
281	}
282	os.Setenv("CGO_TEST_ALLOW", "-Wall") // disallow wins
283	if err := checkCompilerFlags("TEST", "test", []string{"-Wall"}); err == nil {
284		t.Fatalf("missing error for -Wall with CGO_TEST_DISALLOW=-Wall and CGO_TEST_ALLOW=-Wall")
285	}
286
287	os.Setenv("CGO_TEST_ALLOW", "-fplugin.*")
288	os.Setenv("CGO_TEST_DISALLOW", "-fplugin=lint.so")
289	if err := checkCompilerFlags("TEST", "test", []string{"-fplugin=faster.so"}); err != nil {
290		t.Fatalf("unexpected error for -fplugin=faster.so: %v", err)
291	}
292	if err := checkCompilerFlags("TEST", "test", []string{"-fplugin=lint.so"}); err == nil {
293		t.Fatalf("missing error for -fplugin=lint.so: %v", err)
294	}
295}
296
297func TestCheckCompilerFlagsForInternalLink(t *testing.T) {
298	// Any "bad" compiler flag should trigger external linking.
299	for _, f := range badCompilerFlags {
300		if err := checkCompilerFlagsForInternalLink("test", "test", f); err == nil {
301			t.Errorf("missing error for %q", f)
302		}
303	}
304
305	// All "good" compiler flags should not trigger external linking,
306	// except for anything that begins with "-flto".
307	for _, f := range goodCompilerFlags {
308		foundLTO := false
309		for _, s := range f {
310			if strings.Contains(s, "-flto") {
311				foundLTO = true
312			}
313		}
314		if err := checkCompilerFlagsForInternalLink("test", "test", f); err != nil {
315			// expect error for -flto
316			if !foundLTO {
317				t.Errorf("unexpected error for %q: %v", f, err)
318			}
319		} else {
320			// expect no error for everything else
321			if foundLTO {
322				t.Errorf("missing error for %q: %v", f, err)
323			}
324		}
325	}
326}
327