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