1// Copyright 2016 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 carchive_test
6
7import (
8	"bufio"
9	"bytes"
10	"debug/elf"
11	"flag"
12	"fmt"
13	"io/ioutil"
14	"log"
15	"os"
16	"os/exec"
17	"path/filepath"
18	"regexp"
19	"runtime"
20	"strings"
21	"syscall"
22	"testing"
23	"time"
24	"unicode"
25)
26
27// Program to run.
28var bin []string
29
30// C compiler with args (from $(go env CC) $(go env GOGCCFLAGS)).
31var cc []string
32
33// ".exe" on Windows.
34var exeSuffix string
35
36var GOOS, GOARCH, GOPATH string
37var libgodir string
38
39var testWork bool // If true, preserve temporary directories.
40
41func TestMain(m *testing.M) {
42	flag.BoolVar(&testWork, "testwork", false, "if true, log and preserve the test's temporary working directory")
43	flag.Parse()
44	if testing.Short() && os.Getenv("GO_BUILDER_NAME") == "" {
45		fmt.Printf("SKIP - short mode and $GO_BUILDER_NAME not set\n")
46		os.Exit(0)
47	}
48	log.SetFlags(log.Lshortfile)
49	os.Exit(testMain(m))
50}
51
52func testMain(m *testing.M) int {
53	// We need a writable GOPATH in which to run the tests.
54	// Construct one in a temporary directory.
55	var err error
56	GOPATH, err = ioutil.TempDir("", "carchive_test")
57	if err != nil {
58		log.Panic(err)
59	}
60	if testWork {
61		log.Println(GOPATH)
62	} else {
63		defer os.RemoveAll(GOPATH)
64	}
65	os.Setenv("GOPATH", GOPATH)
66
67	// Copy testdata into GOPATH/src/testarchive, along with a go.mod file
68	// declaring the same path.
69	modRoot := filepath.Join(GOPATH, "src", "testcarchive")
70	if err := overlayDir(modRoot, "testdata"); err != nil {
71		log.Panic(err)
72	}
73	if err := os.Chdir(modRoot); err != nil {
74		log.Panic(err)
75	}
76	os.Setenv("PWD", modRoot)
77	if err := ioutil.WriteFile("go.mod", []byte("module testcarchive\n"), 0666); err != nil {
78		log.Panic(err)
79	}
80
81	GOOS = goEnv("GOOS")
82	GOARCH = goEnv("GOARCH")
83	bin = cmdToRun("./testp")
84
85	ccOut := goEnv("CC")
86	cc = []string{string(ccOut)}
87
88	out := goEnv("GOGCCFLAGS")
89	quote := '\000'
90	start := 0
91	lastSpace := true
92	backslash := false
93	s := string(out)
94	for i, c := range s {
95		if quote == '\000' && unicode.IsSpace(c) {
96			if !lastSpace {
97				cc = append(cc, s[start:i])
98				lastSpace = true
99			}
100		} else {
101			if lastSpace {
102				start = i
103				lastSpace = false
104			}
105			if quote == '\000' && !backslash && (c == '"' || c == '\'') {
106				quote = c
107				backslash = false
108			} else if !backslash && quote == c {
109				quote = '\000'
110			} else if (quote == '\000' || quote == '"') && !backslash && c == '\\' {
111				backslash = true
112			} else {
113				backslash = false
114			}
115		}
116	}
117	if !lastSpace {
118		cc = append(cc, s[start:])
119	}
120
121	if GOOS == "darwin" {
122		// For Darwin/ARM.
123		// TODO(crawshaw): can we do better?
124		cc = append(cc, []string{"-framework", "CoreFoundation", "-framework", "Foundation"}...)
125	}
126	if GOOS == "aix" {
127		// -Wl,-bnoobjreorder is mandatory to keep the same layout
128		// in .text section.
129		cc = append(cc, "-Wl,-bnoobjreorder")
130	}
131	libbase := GOOS + "_" + GOARCH
132	if runtime.Compiler == "gccgo" {
133		libbase = "gccgo_" + libbase + "_fPIC"
134	} else {
135		switch GOOS {
136		case "darwin":
137			if GOARCH == "arm" || GOARCH == "arm64" {
138				libbase += "_shared"
139			}
140		case "dragonfly", "freebsd", "linux", "netbsd", "openbsd", "solaris", "illumos":
141			libbase += "_shared"
142		}
143	}
144	libgodir = filepath.Join(GOPATH, "pkg", libbase, "testcarchive")
145	cc = append(cc, "-I", libgodir)
146
147	if GOOS == "windows" {
148		exeSuffix = ".exe"
149	}
150
151	return m.Run()
152}
153
154func goEnv(key string) string {
155	out, err := exec.Command("go", "env", key).Output()
156	if err != nil {
157		if ee, ok := err.(*exec.ExitError); ok {
158			fmt.Fprintf(os.Stderr, "%s", ee.Stderr)
159		}
160		log.Panicf("go env %s failed:\n%s\n", key, err)
161	}
162	return strings.TrimSpace(string(out))
163}
164
165func cmdToRun(name string) []string {
166	execScript := "go_" + goEnv("GOOS") + "_" + goEnv("GOARCH") + "_exec"
167	executor, err := exec.LookPath(execScript)
168	if err != nil {
169		return []string{name}
170	}
171	return []string{executor, name}
172}
173
174// genHeader writes a C header file for the C-exported declarations found in .go
175// source files in dir.
176//
177// TODO(golang.org/issue/35715): This should be simpler.
178func genHeader(t *testing.T, header, dir string) {
179	t.Helper()
180
181	// The 'cgo' command generates a number of additional artifacts,
182	// but we're only interested in the header.
183	// Shunt the rest of the outputs to a temporary directory.
184	objDir, err := ioutil.TempDir(GOPATH, "_obj")
185	if err != nil {
186		t.Fatal(err)
187	}
188	defer os.RemoveAll(objDir)
189
190	files, err := filepath.Glob(filepath.Join(dir, "*.go"))
191	if err != nil {
192		t.Fatal(err)
193	}
194
195	cmd := exec.Command("go", "tool", "cgo",
196		"-objdir", objDir,
197		"-exportheader", header)
198	cmd.Args = append(cmd.Args, files...)
199	t.Log(cmd.Args)
200	if out, err := cmd.CombinedOutput(); err != nil {
201		t.Logf("%s", out)
202		t.Fatal(err)
203	}
204}
205
206func testInstall(t *testing.T, exe, libgoa, libgoh string, buildcmd ...string) {
207	t.Helper()
208	cmd := exec.Command(buildcmd[0], buildcmd[1:]...)
209	t.Log(buildcmd)
210	if out, err := cmd.CombinedOutput(); err != nil {
211		t.Logf("%s", out)
212		t.Fatal(err)
213	}
214	if !testWork {
215		defer func() {
216			os.Remove(libgoa)
217			os.Remove(libgoh)
218		}()
219	}
220
221	ccArgs := append(cc, "-o", exe, "main.c")
222	if GOOS == "windows" {
223		ccArgs = append(ccArgs, "main_windows.c", libgoa, "-lntdll", "-lws2_32", "-lwinmm")
224	} else {
225		ccArgs = append(ccArgs, "main_unix.c", libgoa)
226	}
227	if runtime.Compiler == "gccgo" {
228		ccArgs = append(ccArgs, "-lgo")
229	}
230	t.Log(ccArgs)
231	if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil {
232		t.Logf("%s", out)
233		t.Fatal(err)
234	}
235	if !testWork {
236		defer os.Remove(exe)
237	}
238
239	binArgs := append(cmdToRun(exe), "arg1", "arg2")
240	cmd = exec.Command(binArgs[0], binArgs[1:]...)
241	if runtime.Compiler == "gccgo" {
242		cmd.Env = append(os.Environ(), "GCCGO=1")
243	}
244	if out, err := cmd.CombinedOutput(); err != nil {
245		t.Logf("%s", out)
246		t.Fatal(err)
247	}
248
249	checkLineComments(t, libgoh)
250}
251
252var badLineRegexp = regexp.MustCompile(`(?m)^#line [0-9]+ "/.*$`)
253
254// checkLineComments checks that the export header generated by
255// -buildmode=c-archive doesn't have any absolute paths in the #line
256// comments. We don't want those paths because they are unhelpful for
257// the user and make the files change based on details of the location
258// of GOPATH.
259func checkLineComments(t *testing.T, hdrname string) {
260	hdr, err := ioutil.ReadFile(hdrname)
261	if err != nil {
262		if !os.IsNotExist(err) {
263			t.Error(err)
264		}
265		return
266	}
267	if line := badLineRegexp.Find(hdr); line != nil {
268		t.Errorf("bad #line directive with absolute path in %s: %q", hdrname, line)
269	}
270}
271
272func TestInstall(t *testing.T) {
273	if !testWork {
274		defer os.RemoveAll(filepath.Join(GOPATH, "pkg"))
275	}
276
277	libgoa := "libgo.a"
278	if runtime.Compiler == "gccgo" {
279		libgoa = "liblibgo.a"
280	}
281
282	// Generate the p.h header file.
283	//
284	// 'go install -i -buildmode=c-archive ./libgo' would do that too, but that
285	// would also attempt to install transitive standard-library dependencies to
286	// GOROOT, and we cannot assume that GOROOT is writable. (A non-root user may
287	// be running this test in a GOROOT owned by root.)
288	genHeader(t, "p.h", "./p")
289
290	testInstall(t, "./testp1"+exeSuffix,
291		filepath.Join(libgodir, libgoa),
292		filepath.Join(libgodir, "libgo.h"),
293		"go", "install", "-buildmode=c-archive", "./libgo")
294
295	// Test building libgo other than installing it.
296	// Header files are now present.
297	testInstall(t, "./testp2"+exeSuffix, "libgo.a", "libgo.h",
298		"go", "build", "-buildmode=c-archive", filepath.Join(".", "libgo", "libgo.go"))
299
300	testInstall(t, "./testp3"+exeSuffix, "libgo.a", "libgo.h",
301		"go", "build", "-buildmode=c-archive", "-o", "libgo.a", "./libgo")
302}
303
304func TestEarlySignalHandler(t *testing.T) {
305	switch GOOS {
306	case "darwin":
307		switch GOARCH {
308		case "arm", "arm64":
309			t.Skipf("skipping on %s/%s; see https://golang.org/issue/13701", GOOS, GOARCH)
310		}
311	case "windows":
312		t.Skip("skipping signal test on Windows")
313	}
314
315	if !testWork {
316		defer func() {
317			os.Remove("libgo2.a")
318			os.Remove("libgo2.h")
319			os.Remove("testp")
320			os.RemoveAll(filepath.Join(GOPATH, "pkg"))
321		}()
322	}
323
324	cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo2.a", "./libgo2")
325	if out, err := cmd.CombinedOutput(); err != nil {
326		t.Logf("%s", out)
327		t.Fatal(err)
328	}
329	checkLineComments(t, "libgo2.h")
330
331	ccArgs := append(cc, "-o", "testp"+exeSuffix, "main2.c", "libgo2.a")
332	if runtime.Compiler == "gccgo" {
333		ccArgs = append(ccArgs, "-lgo")
334	}
335	if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil {
336		t.Logf("%s", out)
337		t.Fatal(err)
338	}
339
340	darwin := "0"
341	if runtime.GOOS == "darwin" {
342		darwin = "1"
343	}
344	cmd = exec.Command(bin[0], append(bin[1:], darwin)...)
345
346	if out, err := cmd.CombinedOutput(); err != nil {
347		t.Logf("%s", out)
348		t.Fatal(err)
349	}
350}
351
352func TestSignalForwarding(t *testing.T) {
353	checkSignalForwardingTest(t)
354
355	if !testWork {
356		defer func() {
357			os.Remove("libgo2.a")
358			os.Remove("libgo2.h")
359			os.Remove("testp")
360			os.RemoveAll(filepath.Join(GOPATH, "pkg"))
361		}()
362	}
363
364	cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo2.a", "./libgo2")
365	if out, err := cmd.CombinedOutput(); err != nil {
366		t.Logf("%s", out)
367		t.Fatal(err)
368	}
369	checkLineComments(t, "libgo2.h")
370
371	ccArgs := append(cc, "-o", "testp"+exeSuffix, "main5.c", "libgo2.a")
372	if runtime.Compiler == "gccgo" {
373		ccArgs = append(ccArgs, "-lgo")
374	}
375	if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil {
376		t.Logf("%s", out)
377		t.Fatal(err)
378	}
379
380	cmd = exec.Command(bin[0], append(bin[1:], "1")...)
381
382	out, err := cmd.CombinedOutput()
383	t.Logf("%s", out)
384	expectSignal(t, err, syscall.SIGSEGV)
385
386	// SIGPIPE is never forwarded on darwin. See golang.org/issue/33384.
387	if runtime.GOOS != "darwin" {
388		// Test SIGPIPE forwarding
389		cmd = exec.Command(bin[0], append(bin[1:], "3")...)
390
391		out, err = cmd.CombinedOutput()
392		t.Logf("%s", out)
393		expectSignal(t, err, syscall.SIGPIPE)
394	}
395}
396
397func TestSignalForwardingExternal(t *testing.T) {
398	if GOOS == "freebsd" || GOOS == "aix" {
399		t.Skipf("skipping on %s/%s; signal always goes to the Go runtime", GOOS, GOARCH)
400	} else if GOOS == "darwin" && GOARCH == "amd64" {
401		t.Skipf("skipping on %s/%s: runtime does not permit SI_USER SIGSEGV", GOOS, GOARCH)
402	}
403	checkSignalForwardingTest(t)
404
405	if !testWork {
406		defer func() {
407			os.Remove("libgo2.a")
408			os.Remove("libgo2.h")
409			os.Remove("testp")
410			os.RemoveAll(filepath.Join(GOPATH, "pkg"))
411		}()
412	}
413
414	cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo2.a", "./libgo2")
415	if out, err := cmd.CombinedOutput(); err != nil {
416		t.Logf("%s", out)
417		t.Fatal(err)
418	}
419	checkLineComments(t, "libgo2.h")
420
421	ccArgs := append(cc, "-o", "testp"+exeSuffix, "main5.c", "libgo2.a")
422	if runtime.Compiler == "gccgo" {
423		ccArgs = append(ccArgs, "-lgo")
424	}
425	if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil {
426		t.Logf("%s", out)
427		t.Fatal(err)
428	}
429
430	// We want to send the process a signal and see if it dies.
431	// Normally the signal goes to the C thread, the Go signal
432	// handler picks it up, sees that it is running in a C thread,
433	// and the program dies. Unfortunately, occasionally the
434	// signal is delivered to a Go thread, which winds up
435	// discarding it because it was sent by another program and
436	// there is no Go handler for it. To avoid this, run the
437	// program several times in the hopes that it will eventually
438	// fail.
439	const tries = 20
440	for i := 0; i < tries; i++ {
441		cmd = exec.Command(bin[0], append(bin[1:], "2")...)
442
443		stderr, err := cmd.StderrPipe()
444		if err != nil {
445			t.Fatal(err)
446		}
447		defer stderr.Close()
448
449		r := bufio.NewReader(stderr)
450
451		err = cmd.Start()
452
453		if err != nil {
454			t.Fatal(err)
455		}
456
457		// Wait for trigger to ensure that the process is started.
458		ok, err := r.ReadString('\n')
459
460		// Verify trigger.
461		if err != nil || ok != "OK\n" {
462			t.Fatalf("Did not receive OK signal")
463		}
464
465		// Give the program a chance to enter the sleep function.
466		time.Sleep(time.Millisecond)
467
468		cmd.Process.Signal(syscall.SIGSEGV)
469
470		err = cmd.Wait()
471
472		if err == nil {
473			continue
474		}
475
476		if expectSignal(t, err, syscall.SIGSEGV) {
477			return
478		}
479	}
480
481	t.Errorf("program succeeded unexpectedly %d times", tries)
482}
483
484// checkSignalForwardingTest calls t.Skip if the SignalForwarding test
485// doesn't work on this platform.
486func checkSignalForwardingTest(t *testing.T) {
487	switch GOOS {
488	case "darwin":
489		switch GOARCH {
490		case "arm", "arm64":
491			t.Skipf("skipping on %s/%s; see https://golang.org/issue/13701", GOOS, GOARCH)
492		}
493	case "windows":
494		t.Skip("skipping signal test on Windows")
495	}
496}
497
498// expectSignal checks that err, the exit status of a test program,
499// shows a failure due to a specific signal. Returns whether we found
500// the expected signal.
501func expectSignal(t *testing.T, err error, sig syscall.Signal) bool {
502	if err == nil {
503		t.Error("test program succeeded unexpectedly")
504	} else if ee, ok := err.(*exec.ExitError); !ok {
505		t.Errorf("error (%v) has type %T; expected exec.ExitError", err, err)
506	} else if ws, ok := ee.Sys().(syscall.WaitStatus); !ok {
507		t.Errorf("error.Sys (%v) has type %T; expected syscall.WaitStatus", ee.Sys(), ee.Sys())
508	} else if !ws.Signaled() || ws.Signal() != sig {
509		t.Errorf("got %v; expected signal %v", ee, sig)
510	} else {
511		return true
512	}
513	return false
514}
515
516func TestOsSignal(t *testing.T) {
517	switch GOOS {
518	case "windows":
519		t.Skip("skipping signal test on Windows")
520	}
521
522	if !testWork {
523		defer func() {
524			os.Remove("libgo3.a")
525			os.Remove("libgo3.h")
526			os.Remove("testp")
527			os.RemoveAll(filepath.Join(GOPATH, "pkg"))
528		}()
529	}
530
531	cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo3.a", "./libgo3")
532	if out, err := cmd.CombinedOutput(); err != nil {
533		t.Logf("%s", out)
534		t.Fatal(err)
535	}
536	checkLineComments(t, "libgo3.h")
537
538	ccArgs := append(cc, "-o", "testp"+exeSuffix, "main3.c", "libgo3.a")
539	if runtime.Compiler == "gccgo" {
540		ccArgs = append(ccArgs, "-lgo")
541	}
542	if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil {
543		t.Logf("%s", out)
544		t.Fatal(err)
545	}
546
547	if out, err := exec.Command(bin[0], bin[1:]...).CombinedOutput(); err != nil {
548		t.Logf("%s", out)
549		t.Fatal(err)
550	}
551}
552
553func TestSigaltstack(t *testing.T) {
554	switch GOOS {
555	case "windows":
556		t.Skip("skipping signal test on Windows")
557	}
558
559	if !testWork {
560		defer func() {
561			os.Remove("libgo4.a")
562			os.Remove("libgo4.h")
563			os.Remove("testp")
564			os.RemoveAll(filepath.Join(GOPATH, "pkg"))
565		}()
566	}
567
568	cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo4.a", "./libgo4")
569	if out, err := cmd.CombinedOutput(); err != nil {
570		t.Logf("%s", out)
571		t.Fatal(err)
572	}
573	checkLineComments(t, "libgo4.h")
574
575	ccArgs := append(cc, "-o", "testp"+exeSuffix, "main4.c", "libgo4.a")
576	if runtime.Compiler == "gccgo" {
577		ccArgs = append(ccArgs, "-lgo")
578	}
579	if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil {
580		t.Logf("%s", out)
581		t.Fatal(err)
582	}
583
584	if out, err := exec.Command(bin[0], bin[1:]...).CombinedOutput(); err != nil {
585		t.Logf("%s", out)
586		t.Fatal(err)
587	}
588}
589
590const testar = `#!/usr/bin/env bash
591while [[ $1 == -* ]] >/dev/null; do
592  shift
593done
594echo "testar" > $1
595echo "testar" > PWD/testar.ran
596`
597
598func TestExtar(t *testing.T) {
599	switch GOOS {
600	case "windows":
601		t.Skip("skipping signal test on Windows")
602	}
603	if runtime.Compiler == "gccgo" {
604		t.Skip("skipping -extar test when using gccgo")
605	}
606	if runtime.GOOS == "darwin" && (runtime.GOARCH == "arm" || runtime.GOARCH == "arm64") {
607		t.Skip("shell scripts are not executable on iOS hosts")
608	}
609
610	if !testWork {
611		defer func() {
612			os.Remove("libgo4.a")
613			os.Remove("libgo4.h")
614			os.Remove("testar")
615			os.Remove("testar.ran")
616			os.RemoveAll(filepath.Join(GOPATH, "pkg"))
617		}()
618	}
619
620	os.Remove("testar")
621	dir, err := os.Getwd()
622	if err != nil {
623		t.Fatal(err)
624	}
625	s := strings.Replace(testar, "PWD", dir, 1)
626	if err := ioutil.WriteFile("testar", []byte(s), 0777); err != nil {
627		t.Fatal(err)
628	}
629
630	cmd := exec.Command("go", "build", "-buildmode=c-archive", "-ldflags=-extar="+filepath.Join(dir, "testar"), "-o", "libgo4.a", "./libgo4")
631	if out, err := cmd.CombinedOutput(); err != nil {
632		t.Logf("%s", out)
633		t.Fatal(err)
634	}
635	checkLineComments(t, "libgo4.h")
636
637	if _, err := os.Stat("testar.ran"); err != nil {
638		if os.IsNotExist(err) {
639			t.Error("testar does not exist after go build")
640		} else {
641			t.Errorf("error checking testar: %v", err)
642		}
643	}
644}
645
646func TestPIE(t *testing.T) {
647	switch GOOS {
648	case "windows", "darwin", "plan9":
649		t.Skipf("skipping PIE test on %s", GOOS)
650	}
651
652	if !testWork {
653		defer func() {
654			os.Remove("testp" + exeSuffix)
655			os.RemoveAll(filepath.Join(GOPATH, "pkg"))
656		}()
657	}
658
659	// Generate the p.h header file.
660	//
661	// 'go install -i -buildmode=c-archive ./libgo' would do that too, but that
662	// would also attempt to install transitive standard-library dependencies to
663	// GOROOT, and we cannot assume that GOROOT is writable. (A non-root user may
664	// be running this test in a GOROOT owned by root.)
665	genHeader(t, "p.h", "./p")
666
667	cmd := exec.Command("go", "install", "-buildmode=c-archive", "./libgo")
668	if out, err := cmd.CombinedOutput(); err != nil {
669		t.Logf("%s", out)
670		t.Fatal(err)
671	}
672
673	libgoa := "libgo.a"
674	if runtime.Compiler == "gccgo" {
675		libgoa = "liblibgo.a"
676	}
677
678	ccArgs := append(cc, "-fPIE", "-pie", "-o", "testp"+exeSuffix, "main.c", "main_unix.c", filepath.Join(libgodir, libgoa))
679	if runtime.Compiler == "gccgo" {
680		ccArgs = append(ccArgs, "-lgo")
681	}
682	if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil {
683		t.Logf("%s", out)
684		t.Fatal(err)
685	}
686
687	binArgs := append(bin, "arg1", "arg2")
688	cmd = exec.Command(binArgs[0], binArgs[1:]...)
689	if runtime.Compiler == "gccgo" {
690		cmd.Env = append(os.Environ(), "GCCGO=1")
691	}
692	if out, err := cmd.CombinedOutput(); err != nil {
693		t.Logf("%s", out)
694		t.Fatal(err)
695	}
696
697	if GOOS != "aix" {
698		f, err := elf.Open("testp" + exeSuffix)
699		if err != nil {
700			t.Fatal("elf.Open failed: ", err)
701		}
702		defer f.Close()
703		if hasDynTag(t, f, elf.DT_TEXTREL) {
704			t.Errorf("%s has DT_TEXTREL flag", "testp"+exeSuffix)
705		}
706	}
707}
708
709func hasDynTag(t *testing.T, f *elf.File, tag elf.DynTag) bool {
710	ds := f.SectionByType(elf.SHT_DYNAMIC)
711	if ds == nil {
712		t.Error("no SHT_DYNAMIC section")
713		return false
714	}
715	d, err := ds.Data()
716	if err != nil {
717		t.Errorf("can't read SHT_DYNAMIC contents: %v", err)
718		return false
719	}
720	for len(d) > 0 {
721		var t elf.DynTag
722		switch f.Class {
723		case elf.ELFCLASS32:
724			t = elf.DynTag(f.ByteOrder.Uint32(d[:4]))
725			d = d[8:]
726		case elf.ELFCLASS64:
727			t = elf.DynTag(f.ByteOrder.Uint64(d[:8]))
728			d = d[16:]
729		}
730		if t == tag {
731			return true
732		}
733	}
734	return false
735}
736
737func TestSIGPROF(t *testing.T) {
738	switch GOOS {
739	case "windows", "plan9":
740		t.Skipf("skipping SIGPROF test on %s", GOOS)
741	case "darwin":
742		t.Skipf("skipping SIGPROF test on %s; see https://golang.org/issue/19320", GOOS)
743	}
744
745	t.Parallel()
746
747	if !testWork {
748		defer func() {
749			os.Remove("testp6" + exeSuffix)
750			os.Remove("libgo6.a")
751			os.Remove("libgo6.h")
752		}()
753	}
754
755	cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo6.a", "./libgo6")
756	if out, err := cmd.CombinedOutput(); err != nil {
757		t.Logf("%s", out)
758		t.Fatal(err)
759	}
760	checkLineComments(t, "libgo6.h")
761
762	ccArgs := append(cc, "-o", "testp6"+exeSuffix, "main6.c", "libgo6.a")
763	if runtime.Compiler == "gccgo" {
764		ccArgs = append(ccArgs, "-lgo")
765	}
766	if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil {
767		t.Logf("%s", out)
768		t.Fatal(err)
769	}
770
771	argv := cmdToRun("./testp6")
772	cmd = exec.Command(argv[0], argv[1:]...)
773	if out, err := cmd.CombinedOutput(); err != nil {
774		t.Logf("%s", out)
775		t.Fatal(err)
776	}
777}
778
779// TestCompileWithoutShared tests that if we compile code without the
780// -shared option, we can put it into an archive. When we use the go
781// tool with -buildmode=c-archive, it passes -shared to the compiler,
782// so we override that. The go tool doesn't work this way, but Bazel
783// will likely do it in the future. And it ought to work. This test
784// was added because at one time it did not work on PPC GNU/Linux.
785func TestCompileWithoutShared(t *testing.T) {
786	// For simplicity, reuse the signal forwarding test.
787	checkSignalForwardingTest(t)
788
789	if !testWork {
790		defer func() {
791			os.Remove("libgo2.a")
792			os.Remove("libgo2.h")
793		}()
794	}
795
796	cmd := exec.Command("go", "build", "-buildmode=c-archive", "-gcflags=-shared=false", "-o", "libgo2.a", "./libgo2")
797	t.Log(cmd.Args)
798	out, err := cmd.CombinedOutput()
799	t.Logf("%s", out)
800	if err != nil {
801		t.Fatal(err)
802	}
803	checkLineComments(t, "libgo2.h")
804
805	exe := "./testnoshared" + exeSuffix
806
807	// In some cases, -no-pie is needed here, but not accepted everywhere. First try
808	// if -no-pie is accepted. See #22126.
809	ccArgs := append(cc, "-o", exe, "-no-pie", "main5.c", "libgo2.a")
810	if runtime.Compiler == "gccgo" {
811		ccArgs = append(ccArgs, "-lgo")
812	}
813	t.Log(ccArgs)
814	out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput()
815
816	// If -no-pie unrecognized, try -nopie if this is possibly clang
817	if err != nil && bytes.Contains(out, []byte("unknown")) && !strings.Contains(cc[0], "gcc") {
818		ccArgs = append(cc, "-o", exe, "-nopie", "main5.c", "libgo2.a")
819		t.Log(ccArgs)
820		out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput()
821	}
822
823	// Don't use either -no-pie or -nopie
824	if err != nil && bytes.Contains(out, []byte("unrecognized")) {
825		ccArgs := append(cc, "-o", exe, "main5.c", "libgo2.a")
826		t.Log(ccArgs)
827		out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput()
828	}
829	t.Logf("%s", out)
830	if err != nil {
831		t.Fatal(err)
832	}
833	if !testWork {
834		defer os.Remove(exe)
835	}
836
837	binArgs := append(cmdToRun(exe), "1")
838	t.Log(binArgs)
839	out, err = exec.Command(binArgs[0], binArgs[1:]...).CombinedOutput()
840	t.Logf("%s", out)
841	expectSignal(t, err, syscall.SIGSEGV)
842
843	// SIGPIPE is never forwarded on darwin. See golang.org/issue/33384.
844	if runtime.GOOS != "darwin" {
845		binArgs := append(cmdToRun(exe), "3")
846		t.Log(binArgs)
847		out, err = exec.Command(binArgs[0], binArgs[1:]...).CombinedOutput()
848		t.Logf("%s", out)
849		expectSignal(t, err, syscall.SIGPIPE)
850	}
851}
852
853// Test that installing a second time recreates the header file.
854func TestCachedInstall(t *testing.T) {
855	if !testWork {
856		defer os.RemoveAll(filepath.Join(GOPATH, "pkg"))
857	}
858
859	h := filepath.Join(libgodir, "libgo.h")
860
861	buildcmd := []string{"go", "install", "-buildmode=c-archive", "./libgo"}
862
863	cmd := exec.Command(buildcmd[0], buildcmd[1:]...)
864	t.Log(buildcmd)
865	if out, err := cmd.CombinedOutput(); err != nil {
866		t.Logf("%s", out)
867		t.Fatal(err)
868	}
869
870	if _, err := os.Stat(h); err != nil {
871		t.Errorf("libgo.h not installed: %v", err)
872	}
873
874	if err := os.Remove(h); err != nil {
875		t.Fatal(err)
876	}
877
878	cmd = exec.Command(buildcmd[0], buildcmd[1:]...)
879	t.Log(buildcmd)
880	if out, err := cmd.CombinedOutput(); err != nil {
881		t.Logf("%s", out)
882		t.Fatal(err)
883	}
884
885	if _, err := os.Stat(h); err != nil {
886		t.Errorf("libgo.h not installed in second run: %v", err)
887	}
888}
889
890// Issue 35294.
891func TestManyCalls(t *testing.T) {
892	t.Parallel()
893
894	if !testWork {
895		defer func() {
896			os.Remove("testp7" + exeSuffix)
897			os.Remove("libgo7.a")
898			os.Remove("libgo7.h")
899		}()
900	}
901
902	cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo7.a", "./libgo7")
903	if out, err := cmd.CombinedOutput(); err != nil {
904		t.Logf("%s", out)
905		t.Fatal(err)
906	}
907	checkLineComments(t, "libgo7.h")
908
909	ccArgs := append(cc, "-o", "testp7"+exeSuffix, "main7.c", "libgo7.a")
910	if runtime.Compiler == "gccgo" {
911		ccArgs = append(ccArgs, "-lgo")
912	}
913	if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil {
914		t.Logf("%s", out)
915		t.Fatal(err)
916	}
917
918	argv := cmdToRun("./testp7")
919	cmd = exec.Command(argv[0], argv[1:]...)
920	var sb strings.Builder
921	cmd.Stdout = &sb
922	cmd.Stderr = &sb
923	if err := cmd.Start(); err != nil {
924		t.Fatal(err)
925	}
926
927	timer := time.AfterFunc(time.Minute,
928		func() {
929			t.Error("test program timed out")
930			cmd.Process.Kill()
931		},
932	)
933	defer timer.Stop()
934
935	if err := cmd.Wait(); err != nil {
936		t.Log(sb.String())
937		t.Error(err)
938	}
939}
940