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