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.
26 // C compiler with args (from $(go env CC) $(go env GOGCCFLAGS)).
29 // An environment with GOPATH=$(pwd).
30 var gopathEnv
[]string
35 var GOOS
, GOARCH
string
40 GOARCH
= goEnv("GOARCH")
41 bin
= cmdToRun("./testp")
44 cc
= []string{string(ccOut
)}
46 out
:= goEnv("GOGCCFLAGS")
53 if quote
== '\000' && unicode
.IsSpace(c
) {
55 cc
= append(cc
, s
[start
:i
])
63 if quote
== '\000' && !backslash
&& (c
== '"' || c
== '\'') {
66 } else if !backslash
&& quote
== c
{
68 } else if (quote
== '\000' || quote
== '"') && !backslash
&& c
== '\\' {
76 cc
= append(cc
, s
[start
:])
81 // TODO(crawshaw): can we do better?
82 cc
= append(cc
, []string{"-framework", "CoreFoundation", "-framework", "Foundation"}...)
84 libgodir
= GOOS
+ "_" + GOARCH
85 if runtime
.Compiler
== "gccgo" {
86 libgodir
= "gccgo_" + libgodir
+ "_fPIC"
90 if GOARCH
== "arm" || GOARCH
== "arm64" {
93 case "dragonfly", "freebsd", "linux", "netbsd", "openbsd", "solaris":
97 cc
= append(cc
, "-I", filepath
.Join("pkg", libgodir
))
99 // Build an environment with GOPATH=$(pwd)
102 for _
, e
:= range env
{
103 if !strings
.HasPrefix(e
, "GOPATH=") {
107 dir
, err
:= os
.Getwd()
109 fmt
.Fprintln(os
.Stderr
, err
)
112 n
= append(n
, "GOPATH="+dir
)
115 if GOOS
== "windows" {
120 func goEnv(key
string) string {
121 out
, err
:= exec
.Command("go", "env", key
).Output()
123 fmt
.Fprintf(os
.Stderr
, "go env %s failed:\n%s", key
, err
)
124 fmt
.Fprintf(os
.Stderr
, "%s", err
.(*exec
.ExitError
).Stderr
)
127 return strings
.TrimSpace(string(out
))
130 func cmdToRun(name
string) []string {
131 execScript
:= "go_" + goEnv("GOOS") + "_" + goEnv("GOARCH") + "_exec"
132 executor
, err
:= exec
.LookPath(execScript
)
134 return []string{name
}
136 return []string{executor
, name
}
139 func testInstall(t
*testing
.T
, exe
, libgoa
, libgoh
string, buildcmd
...string) {
140 cmd
:= exec
.Command(buildcmd
[0], buildcmd
[1:]...)
142 if out
, err
:= cmd
.CombinedOutput(); err
!= nil {
151 ccArgs
:= append(cc
, "-o", exe
, "main.c")
152 if GOOS
== "windows" {
153 ccArgs
= append(ccArgs
, "main_windows.c", libgoa
, "-lntdll", "-lws2_32", "-lwinmm")
155 ccArgs
= append(ccArgs
, "main_unix.c", libgoa
)
157 if runtime
.Compiler
== "gccgo" {
158 ccArgs
= append(ccArgs
, "-lgo")
161 if out
, err
:= exec
.Command(ccArgs
[0], ccArgs
[1:]...).CombinedOutput(); err
!= nil {
167 binArgs
:= append(cmdToRun(exe
), "arg1", "arg2")
168 cmd
= exec
.Command(binArgs
[0], binArgs
[1:]...)
169 if runtime
.Compiler
== "gccgo" {
170 cmd
.Env
= append(os
.Environ(), "GCCGO=1")
172 if out
, err
:= cmd
.CombinedOutput(); err
!= nil {
178 func TestInstall(t
*testing
.T
) {
179 defer os
.RemoveAll("pkg")
182 if runtime
.Compiler
== "gccgo" {
183 libgoa
= "liblibgo.a"
186 testInstall(t
, "./testp1"+exeSuffix
,
187 filepath
.Join("pkg", libgodir
, libgoa
),
188 filepath
.Join("pkg", libgodir
, "libgo.h"),
189 "go", "install", "-buildmode=c-archive", "libgo")
191 // Test building libgo other than installing it.
192 // Header files are now present.
193 testInstall(t
, "./testp2"+exeSuffix
, "libgo.a", "libgo.h",
194 "go", "build", "-buildmode=c-archive", filepath
.Join("src", "libgo", "libgo.go"))
196 testInstall(t
, "./testp3"+exeSuffix
, "libgo.a", "libgo.h",
197 "go", "build", "-buildmode=c-archive", "-o", "libgo.a", "libgo")
200 func TestEarlySignalHandler(t
*testing
.T
) {
205 t
.Skipf("skipping on %s/%s; see https://golang.org/issue/13701", GOOS
, GOARCH
)
208 t
.Skip("skipping signal test on Windows")
212 os
.Remove("libgo2.a")
213 os
.Remove("libgo2.h")
218 cmd
:= exec
.Command("go", "build", "-buildmode=c-archive", "-o", "libgo2.a", "libgo2")
220 if out
, err
:= cmd
.CombinedOutput(); err
!= nil {
225 ccArgs
:= append(cc
, "-o", "testp"+exeSuffix
, "main2.c", "libgo2.a")
226 if runtime
.Compiler
== "gccgo" {
227 ccArgs
= append(ccArgs
, "-lgo")
229 if out
, err
:= exec
.Command(ccArgs
[0], ccArgs
[1:]...).CombinedOutput(); err
!= nil {
234 if out
, err
:= exec
.Command(bin
[0], bin
[1:]...).CombinedOutput(); err
!= nil {
240 func TestSignalForwarding(t
*testing
.T
) {
245 t
.Skipf("skipping on %s/%s; see https://golang.org/issue/13701", GOOS
, GOARCH
)
248 t
.Skip("skipping signal test on Windows")
252 os
.Remove("libgo2.a")
253 os
.Remove("libgo2.h")
258 cmd
:= exec
.Command("go", "build", "-buildmode=c-archive", "-o", "libgo2.a", "libgo2")
260 if out
, err
:= cmd
.CombinedOutput(); err
!= nil {
265 ccArgs
:= append(cc
, "-o", "testp"+exeSuffix
, "main5.c", "libgo2.a")
266 if runtime
.Compiler
== "gccgo" {
267 ccArgs
= append(ccArgs
, "-lgo")
269 if out
, err
:= exec
.Command(ccArgs
[0], ccArgs
[1:]...).CombinedOutput(); err
!= nil {
274 cmd
= exec
.Command(bin
[0], append(bin
[1:], "1")...)
276 out
, err
:= cmd
.CombinedOutput()
280 t
.Error("test program succeeded unexpectedly")
281 } else if ee
, ok
:= err
.(*exec
.ExitError
); !ok
{
283 t
.Errorf("error (%v) has type %T; expected exec.ExitError", err
, err
)
284 } else if ws
, ok
:= ee
.Sys().(syscall
.WaitStatus
); !ok
{
286 t
.Errorf("error.Sys (%v) has type %T; expected syscall.WaitStatus", ee
.Sys(), ee
.Sys())
287 } else if !ws
.Signaled() || ws
.Signal() != syscall
.SIGSEGV
{
289 t
.Errorf("got %v; expected SIGSEGV", ee
)
293 func TestSignalForwardingExternal(t
*testing
.T
) {
298 t
.Skipf("skipping on %s/%s; see https://golang.org/issue/13701", GOOS
, GOARCH
)
301 t
.Skip("skipping signal test on Windows")
305 os
.Remove("libgo2.a")
306 os
.Remove("libgo2.h")
311 cmd
:= exec
.Command("go", "build", "-buildmode=c-archive", "-o", "libgo2.a", "libgo2")
313 if out
, err
:= cmd
.CombinedOutput(); err
!= nil {
318 ccArgs
:= append(cc
, "-o", "testp"+exeSuffix
, "main5.c", "libgo2.a")
319 if runtime
.Compiler
== "gccgo" {
320 ccArgs
= append(ccArgs
, "-lgo")
322 if out
, err
:= exec
.Command(ccArgs
[0], ccArgs
[1:]...).CombinedOutput(); err
!= nil {
327 // We want to send the process a signal and see if it dies.
328 // Normally the signal goes to the C thread, the Go signal
329 // handler picks it up, sees that it is running in a C thread,
330 // and the program dies. Unfortunately, occasionally the
331 // signal is delivered to a Go thread, which winds up
332 // discarding it because it was sent by another program and
333 // there is no Go handler for it. To avoid this, run the
334 // program several times in the hopes that it will eventually
337 for i
:= 0; i
< tries
; i
++ {
338 cmd
= exec
.Command(bin
[0], append(bin
[1:], "2")...)
340 stderr
, err
:= cmd
.StderrPipe()
346 r
:= bufio
.NewReader(stderr
)
354 // Wait for trigger to ensure that the process is started.
355 ok
, err
:= r
.ReadString('\n')
358 if err
!= nil || ok
!= "OK\n" {
359 t
.Fatalf("Did not receive OK signal")
362 // Give the program a chance to enter the sleep function.
363 time
.Sleep(time
.Millisecond
)
365 cmd
.Process
.Signal(syscall
.SIGSEGV
)
373 if ee
, ok
:= err
.(*exec
.ExitError
); !ok
{
374 t
.Errorf("error (%v) has type %T; expected exec.ExitError", err
, err
)
375 } else if ws
, ok
:= ee
.Sys().(syscall
.WaitStatus
); !ok
{
376 t
.Errorf("error.Sys (%v) has type %T; expected syscall.WaitStatus", ee
.Sys(), ee
.Sys())
377 } else if !ws
.Signaled() || ws
.Signal() != syscall
.SIGSEGV
{
378 t
.Errorf("got %v; expected SIGSEGV", ee
)
380 // We got the error we expected.
385 t
.Errorf("program succeeded unexpectedly %d times", tries
)
388 func TestOsSignal(t
*testing
.T
) {
391 t
.Skip("skipping signal test on Windows")
395 os
.Remove("libgo3.a")
396 os
.Remove("libgo3.h")
401 cmd
:= exec
.Command("go", "build", "-buildmode=c-archive", "-o", "libgo3.a", "libgo3")
403 if out
, err
:= cmd
.CombinedOutput(); err
!= nil {
408 ccArgs
:= append(cc
, "-o", "testp"+exeSuffix
, "main3.c", "libgo3.a")
409 if runtime
.Compiler
== "gccgo" {
410 ccArgs
= append(ccArgs
, "-lgo")
412 if out
, err
:= exec
.Command(ccArgs
[0], ccArgs
[1:]...).CombinedOutput(); err
!= nil {
417 if out
, err
:= exec
.Command(bin
[0], bin
[1:]...).CombinedOutput(); err
!= nil {
423 func TestSigaltstack(t
*testing
.T
) {
426 t
.Skip("skipping signal test on Windows")
430 os
.Remove("libgo4.a")
431 os
.Remove("libgo4.h")
436 cmd
:= exec
.Command("go", "build", "-buildmode=c-archive", "-o", "libgo4.a", "libgo4")
438 if out
, err
:= cmd
.CombinedOutput(); err
!= nil {
443 ccArgs
:= append(cc
, "-o", "testp"+exeSuffix
, "main4.c", "libgo4.a")
444 if runtime
.Compiler
== "gccgo" {
445 ccArgs
= append(ccArgs
, "-lgo")
447 if out
, err
:= exec
.Command(ccArgs
[0], ccArgs
[1:]...).CombinedOutput(); err
!= nil {
452 if out
, err
:= exec
.Command(bin
[0], bin
[1:]...).CombinedOutput(); err
!= nil {
458 const testar
= `#!/usr/bin/env bash
459 while expr $1 : '[-]' >/dev/null; do
463 echo "testar" > PWD/testar.ran
466 func TestExtar(t
*testing
.T
) {
469 t
.Skip("skipping signal test on Windows")
471 if runtime
.Compiler
== "gccgo" {
472 t
.Skip("skipping -extar test when using gccgo")
476 os
.Remove("libgo4.a")
477 os
.Remove("libgo4.h")
479 os
.Remove("testar.ran")
484 dir
, err
:= os
.Getwd()
488 s
:= strings
.Replace(testar
, "PWD", dir
, 1)
489 if err
:= ioutil
.WriteFile("testar", []byte(s
), 0777); err
!= nil {
493 cmd
:= exec
.Command("go", "build", "-buildmode=c-archive", "-ldflags=-extar="+filepath
.Join(dir
, "testar"), "-o", "libgo4.a", "libgo4")
495 if out
, err
:= cmd
.CombinedOutput(); err
!= nil {
500 if _
, err
:= os
.Stat("testar.ran"); err
!= nil {
501 if os
.IsNotExist(err
) {
502 t
.Error("testar does not exist after go build")
504 t
.Errorf("error checking testar: %v", err
)
509 func TestPIE(t
*testing
.T
) {
511 case "windows", "darwin", "plan9":
512 t
.Skipf("skipping PIE test on %s", GOOS
)
516 os
.Remove("testp" + exeSuffix
)
520 cmd
:= exec
.Command("go", "install", "-buildmode=c-archive", "libgo")
522 if out
, err
:= cmd
.CombinedOutput(); err
!= nil {
528 if runtime
.Compiler
== "gccgo" {
529 libgoa
= "liblibgo.a"
532 ccArgs
:= append(cc
, "-fPIE", "-pie", "-o", "testp"+exeSuffix
, "main.c", "main_unix.c", filepath
.Join("pkg", libgodir
, libgoa
))
533 if runtime
.Compiler
== "gccgo" {
534 ccArgs
= append(ccArgs
, "-lgo")
536 if out
, err
:= exec
.Command(ccArgs
[0], ccArgs
[1:]...).CombinedOutput(); err
!= nil {
541 binArgs
:= append(bin
, "arg1", "arg2")
542 cmd
= exec
.Command(binArgs
[0], binArgs
[1:]...)
543 if runtime
.Compiler
== "gccgo" {
544 cmd
.Env
= append(os
.Environ(), "GCCGO=1")
546 if out
, err
:= cmd
.CombinedOutput(); err
!= nil {
551 f
, err
:= elf
.Open("testp" + exeSuffix
)
553 t
.Fatal("elf.Open failed: ", err
)
556 if hasDynTag(t
, f
, elf
.DT_TEXTREL
) {
557 t
.Errorf("%s has DT_TEXTREL flag", "testp"+exeSuffix
)
561 func hasDynTag(t
*testing
.T
, f
*elf
.File
, tag elf
.DynTag
) bool {
562 ds
:= f
.SectionByType(elf
.SHT_DYNAMIC
)
564 t
.Error("no SHT_DYNAMIC section")
569 t
.Errorf("can't read SHT_DYNAMIC contents: %v", err
)
576 t
= elf
.DynTag(f
.ByteOrder
.Uint32(d
[:4]))
579 t
= elf
.DynTag(f
.ByteOrder
.Uint64(d
[:8]))