cshared_test.go 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856
  1. // Copyright 2017 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. package cshared_test
  5. import (
  6. "bytes"
  7. "debug/elf"
  8. "debug/pe"
  9. "encoding/binary"
  10. "flag"
  11. "fmt"
  12. "log"
  13. "os"
  14. "os/exec"
  15. "path/filepath"
  16. "runtime"
  17. "strings"
  18. "sync"
  19. "testing"
  20. "unicode"
  21. )
  22. // C compiler with args (from $(go env CC) $(go env GOGCCFLAGS)).
  23. var cc []string
  24. // ".exe" on Windows.
  25. var exeSuffix string
  26. var GOOS, GOARCH, GOROOT string
  27. var installdir, androiddir string
  28. var libSuffix, libgoname string
  29. func TestMain(m *testing.M) {
  30. os.Exit(testMain(m))
  31. }
  32. func testMain(m *testing.M) int {
  33. log.SetFlags(log.Lshortfile)
  34. flag.Parse()
  35. if testing.Short() && os.Getenv("GO_BUILDER_NAME") == "" {
  36. fmt.Printf("SKIP - short mode and $GO_BUILDER_NAME not set\n")
  37. os.Exit(0)
  38. }
  39. GOOS = goEnv("GOOS")
  40. GOARCH = goEnv("GOARCH")
  41. GOROOT = goEnv("GOROOT")
  42. if _, err := os.Stat(GOROOT); os.IsNotExist(err) {
  43. log.Fatalf("Unable able to find GOROOT at '%s'", GOROOT)
  44. }
  45. androiddir = fmt.Sprintf("/data/local/tmp/testcshared-%d", os.Getpid())
  46. if runtime.GOOS != GOOS && GOOS == "android" {
  47. args := append(adbCmd(), "exec-out", "mkdir", "-p", androiddir)
  48. cmd := exec.Command(args[0], args[1:]...)
  49. out, err := cmd.CombinedOutput()
  50. if err != nil {
  51. log.Fatalf("setupAndroid failed: %v\n%s\n", err, out)
  52. }
  53. defer cleanupAndroid()
  54. }
  55. cc = []string{goEnv("CC")}
  56. out := goEnv("GOGCCFLAGS")
  57. quote := '\000'
  58. start := 0
  59. lastSpace := true
  60. backslash := false
  61. s := string(out)
  62. for i, c := range s {
  63. if quote == '\000' && unicode.IsSpace(c) {
  64. if !lastSpace {
  65. cc = append(cc, s[start:i])
  66. lastSpace = true
  67. }
  68. } else {
  69. if lastSpace {
  70. start = i
  71. lastSpace = false
  72. }
  73. if quote == '\000' && !backslash && (c == '"' || c == '\'') {
  74. quote = c
  75. backslash = false
  76. } else if !backslash && quote == c {
  77. quote = '\000'
  78. } else if (quote == '\000' || quote == '"') && !backslash && c == '\\' {
  79. backslash = true
  80. } else {
  81. backslash = false
  82. }
  83. }
  84. }
  85. if !lastSpace {
  86. cc = append(cc, s[start:])
  87. }
  88. switch GOOS {
  89. case "darwin", "ios":
  90. // For Darwin/ARM.
  91. // TODO(crawshaw): can we do better?
  92. cc = append(cc, []string{"-framework", "CoreFoundation", "-framework", "Foundation"}...)
  93. case "android":
  94. cc = append(cc, "-pie")
  95. }
  96. libgodir := GOOS + "_" + GOARCH
  97. switch GOOS {
  98. case "darwin", "ios":
  99. if GOARCH == "arm64" {
  100. libgodir += "_shared"
  101. }
  102. case "dragonfly", "freebsd", "linux", "netbsd", "openbsd", "solaris", "illumos":
  103. libgodir += "_shared"
  104. }
  105. cc = append(cc, "-I", filepath.Join("pkg", libgodir))
  106. // Force reallocation (and avoid aliasing bugs) for parallel tests that append to cc.
  107. cc = cc[:len(cc):len(cc)]
  108. if GOOS == "windows" {
  109. exeSuffix = ".exe"
  110. }
  111. // Copy testdata into GOPATH/src/testcshared, along with a go.mod file
  112. // declaring the same path.
  113. GOPATH, err := os.MkdirTemp("", "cshared_test")
  114. if err != nil {
  115. log.Panic(err)
  116. }
  117. defer os.RemoveAll(GOPATH)
  118. os.Setenv("GOPATH", GOPATH)
  119. modRoot := filepath.Join(GOPATH, "src", "testcshared")
  120. if err := overlayDir(modRoot, "testdata"); err != nil {
  121. log.Panic(err)
  122. }
  123. if err := os.Chdir(modRoot); err != nil {
  124. log.Panic(err)
  125. }
  126. os.Setenv("PWD", modRoot)
  127. if err := os.WriteFile("go.mod", []byte("module testcshared\n"), 0666); err != nil {
  128. log.Panic(err)
  129. }
  130. // Directory where cgo headers and outputs will be installed.
  131. // The installation directory format varies depending on the platform.
  132. output, err := exec.Command("go", "list",
  133. "-buildmode=c-shared",
  134. "-installsuffix", "testcshared",
  135. "-f", "{{.Target}}",
  136. "./libgo").CombinedOutput()
  137. if err != nil {
  138. log.Panicf("go list failed: %v\n%s", err, output)
  139. }
  140. target := string(bytes.TrimSpace(output))
  141. libgoname = filepath.Base(target)
  142. installdir = filepath.Dir(target)
  143. libSuffix = strings.TrimPrefix(filepath.Ext(target), ".")
  144. return m.Run()
  145. }
  146. func goEnv(key string) string {
  147. out, err := exec.Command("go", "env", key).Output()
  148. if err != nil {
  149. log.Printf("go env %s failed:\n%s", key, err)
  150. log.Panicf("%s", err.(*exec.ExitError).Stderr)
  151. }
  152. return strings.TrimSpace(string(out))
  153. }
  154. func cmdToRun(name string) string {
  155. return "./" + name + exeSuffix
  156. }
  157. func adbCmd() []string {
  158. cmd := []string{"adb"}
  159. if flags := os.Getenv("GOANDROID_ADB_FLAGS"); flags != "" {
  160. cmd = append(cmd, strings.Split(flags, " ")...)
  161. }
  162. return cmd
  163. }
  164. func adbPush(t *testing.T, filename string) {
  165. if runtime.GOOS == GOOS || GOOS != "android" {
  166. return
  167. }
  168. args := append(adbCmd(), "push", filename, fmt.Sprintf("%s/%s", androiddir, filename))
  169. cmd := exec.Command(args[0], args[1:]...)
  170. if out, err := cmd.CombinedOutput(); err != nil {
  171. t.Fatalf("adb command failed: %v\n%s\n", err, out)
  172. }
  173. }
  174. func adbRun(t *testing.T, env []string, adbargs ...string) string {
  175. if GOOS != "android" {
  176. t.Fatalf("trying to run adb command when operating system is not android.")
  177. }
  178. args := append(adbCmd(), "exec-out")
  179. // Propagate LD_LIBRARY_PATH to the adb shell invocation.
  180. for _, e := range env {
  181. if strings.Contains(e, "LD_LIBRARY_PATH=") {
  182. adbargs = append([]string{e}, adbargs...)
  183. break
  184. }
  185. }
  186. shellcmd := fmt.Sprintf("cd %s; %s", androiddir, strings.Join(adbargs, " "))
  187. args = append(args, shellcmd)
  188. cmd := exec.Command(args[0], args[1:]...)
  189. out, err := cmd.CombinedOutput()
  190. if err != nil {
  191. t.Fatalf("adb command failed: %v\n%s\n", err, out)
  192. }
  193. return strings.Replace(string(out), "\r", "", -1)
  194. }
  195. func run(t *testing.T, extraEnv []string, args ...string) string {
  196. t.Helper()
  197. cmd := exec.Command(args[0], args[1:]...)
  198. if len(extraEnv) > 0 {
  199. cmd.Env = append(os.Environ(), extraEnv...)
  200. }
  201. if GOOS != "windows" {
  202. // TestUnexportedSymbols relies on file descriptor 30
  203. // being closed when the program starts, so enforce
  204. // that in all cases. (The first three descriptors are
  205. // stdin/stdout/stderr, so we just need to make sure
  206. // that cmd.ExtraFiles[27] exists and is nil.)
  207. cmd.ExtraFiles = make([]*os.File, 28)
  208. }
  209. out, err := cmd.CombinedOutput()
  210. if err != nil {
  211. t.Fatalf("command failed: %v\n%v\n%s\n", args, err, out)
  212. } else {
  213. t.Logf("run: %v", args)
  214. }
  215. return string(out)
  216. }
  217. func runExe(t *testing.T, extraEnv []string, args ...string) string {
  218. t.Helper()
  219. if runtime.GOOS != GOOS && GOOS == "android" {
  220. return adbRun(t, append(os.Environ(), extraEnv...), args...)
  221. }
  222. return run(t, extraEnv, args...)
  223. }
  224. func runCC(t *testing.T, args ...string) string {
  225. t.Helper()
  226. // This function is run in parallel, so append to a copy of cc
  227. // rather than cc itself.
  228. return run(t, nil, append(append([]string(nil), cc...), args...)...)
  229. }
  230. func createHeaders() error {
  231. // The 'cgo' command generates a number of additional artifacts,
  232. // but we're only interested in the header.
  233. // Shunt the rest of the outputs to a temporary directory.
  234. objDir, err := os.MkdirTemp("", "testcshared_obj")
  235. if err != nil {
  236. return err
  237. }
  238. defer os.RemoveAll(objDir)
  239. // Generate a C header file for p, which is a non-main dependency
  240. // of main package libgo.
  241. //
  242. // TODO(golang.org/issue/35715): This should be simpler.
  243. args := []string{"go", "tool", "cgo",
  244. "-objdir", objDir,
  245. "-exportheader", "p.h",
  246. filepath.Join(".", "p", "p.go")}
  247. cmd := exec.Command(args[0], args[1:]...)
  248. out, err := cmd.CombinedOutput()
  249. if err != nil {
  250. return fmt.Errorf("command failed: %v\n%v\n%s\n", args, err, out)
  251. }
  252. // Generate a C header file for libgo itself.
  253. args = []string{"go", "install", "-buildmode=c-shared",
  254. "-installsuffix", "testcshared", "./libgo"}
  255. cmd = exec.Command(args[0], args[1:]...)
  256. out, err = cmd.CombinedOutput()
  257. if err != nil {
  258. return fmt.Errorf("command failed: %v\n%v\n%s\n", args, err, out)
  259. }
  260. args = []string{"go", "build", "-buildmode=c-shared",
  261. "-installsuffix", "testcshared",
  262. "-o", libgoname,
  263. filepath.Join(".", "libgo", "libgo.go")}
  264. if GOOS == "windows" && strings.HasSuffix(args[6], ".a") {
  265. args[6] = strings.TrimSuffix(args[6], ".a") + ".dll"
  266. }
  267. cmd = exec.Command(args[0], args[1:]...)
  268. out, err = cmd.CombinedOutput()
  269. if err != nil {
  270. return fmt.Errorf("command failed: %v\n%v\n%s\n", args, err, out)
  271. }
  272. if GOOS == "windows" {
  273. // We can't simply pass -Wl,--out-implib, because this relies on having imports from multiple packages,
  274. // which results in the linkers output implib getting overwritten at each step. So instead build the
  275. // import library the traditional way, using a def file.
  276. err = os.WriteFile("libgo.def",
  277. []byte("LIBRARY libgo.dll\nEXPORTS\n\tDidInitRun\n\tDidMainRun\n\tDivu\n\tFromPkg\n\t_cgo_dummy_export\n"),
  278. 0644)
  279. if err != nil {
  280. return fmt.Errorf("unable to write def file: %v", err)
  281. }
  282. out, err = exec.Command(cc[0], append(cc[1:], "-print-prog-name=dlltool")...).CombinedOutput()
  283. if err != nil {
  284. return fmt.Errorf("unable to find dlltool path: %v\n%s\n", err, out)
  285. }
  286. dlltoolpath := strings.TrimSpace(string(out))
  287. if filepath.Ext(dlltoolpath) == "" {
  288. // Some compilers report slash-separated paths without extensions
  289. // instead of ordinary Windows paths.
  290. // Try to find the canonical name for the path.
  291. if lp, err := exec.LookPath(dlltoolpath); err == nil {
  292. dlltoolpath = lp
  293. }
  294. }
  295. args := []string{dlltoolpath, "-D", args[6], "-l", libgoname, "-d", "libgo.def"}
  296. if filepath.Ext(dlltoolpath) == "" {
  297. // This is an unfortunate workaround for
  298. // https://github.com/mstorsjo/llvm-mingw/issues/205 in which
  299. // we basically reimplement the contents of the dlltool.sh
  300. // wrapper: https://git.io/JZFlU.
  301. // TODO(thanm): remove this workaround once we can upgrade
  302. // the compilers on the windows-arm64 builder.
  303. dlltoolContents, err := os.ReadFile(args[0])
  304. if err != nil {
  305. return fmt.Errorf("unable to read dlltool: %v\n", err)
  306. }
  307. if bytes.HasPrefix(dlltoolContents, []byte("#!/bin/sh")) && bytes.Contains(dlltoolContents, []byte("llvm-dlltool")) {
  308. base, name := filepath.Split(args[0])
  309. args[0] = filepath.Join(base, "llvm-dlltool")
  310. var machine string
  311. switch prefix, _, _ := strings.Cut(name, "-"); prefix {
  312. case "i686":
  313. machine = "i386"
  314. case "x86_64":
  315. machine = "i386:x86-64"
  316. case "armv7":
  317. machine = "arm"
  318. case "aarch64":
  319. machine = "arm64"
  320. }
  321. if len(machine) > 0 {
  322. args = append(args, "-m", machine)
  323. }
  324. }
  325. }
  326. out, err = exec.Command(args[0], args[1:]...).CombinedOutput()
  327. if err != nil {
  328. return fmt.Errorf("unable to run dlltool to create import library: %v\n%s\n", err, out)
  329. }
  330. }
  331. if runtime.GOOS != GOOS && GOOS == "android" {
  332. args = append(adbCmd(), "push", libgoname, fmt.Sprintf("%s/%s", androiddir, libgoname))
  333. cmd = exec.Command(args[0], args[1:]...)
  334. out, err = cmd.CombinedOutput()
  335. if err != nil {
  336. return fmt.Errorf("adb command failed: %v\n%s\n", err, out)
  337. }
  338. }
  339. return nil
  340. }
  341. var (
  342. headersOnce sync.Once
  343. headersErr error
  344. )
  345. func createHeadersOnce(t *testing.T) {
  346. headersOnce.Do(func() {
  347. headersErr = createHeaders()
  348. })
  349. if headersErr != nil {
  350. t.Fatal(headersErr)
  351. }
  352. }
  353. func cleanupAndroid() {
  354. if GOOS != "android" {
  355. return
  356. }
  357. args := append(adbCmd(), "exec-out", "rm", "-rf", androiddir)
  358. cmd := exec.Command(args[0], args[1:]...)
  359. out, err := cmd.CombinedOutput()
  360. if err != nil {
  361. log.Panicf("cleanupAndroid failed: %v\n%s\n", err, out)
  362. }
  363. }
  364. // test0: exported symbols in shared lib are accessible.
  365. func TestExportedSymbols(t *testing.T) {
  366. t.Parallel()
  367. cmd := "testp0"
  368. bin := cmdToRun(cmd)
  369. createHeadersOnce(t)
  370. runCC(t, "-I", installdir, "-o", cmd, "main0.c", libgoname)
  371. adbPush(t, cmd)
  372. defer os.Remove(bin)
  373. out := runExe(t, []string{"LD_LIBRARY_PATH=."}, bin)
  374. if strings.TrimSpace(out) != "PASS" {
  375. t.Error(out)
  376. }
  377. }
  378. func checkNumberOfExportedFunctionsWindows(t *testing.T, exportAllSymbols bool) {
  379. const prog = `
  380. package main
  381. import "C"
  382. //export GoFunc
  383. func GoFunc() {
  384. println(42)
  385. }
  386. //export GoFunc2
  387. func GoFunc2() {
  388. println(24)
  389. }
  390. func main() {
  391. }
  392. `
  393. tmpdir := t.TempDir()
  394. srcfile := filepath.Join(tmpdir, "test.go")
  395. objfile := filepath.Join(tmpdir, "test.dll")
  396. if err := os.WriteFile(srcfile, []byte(prog), 0666); err != nil {
  397. t.Fatal(err)
  398. }
  399. argv := []string{"build", "-buildmode=c-shared"}
  400. if exportAllSymbols {
  401. argv = append(argv, "-ldflags", "-extldflags=-Wl,--export-all-symbols")
  402. }
  403. argv = append(argv, "-o", objfile, srcfile)
  404. out, err := exec.Command("go", argv...).CombinedOutput()
  405. if err != nil {
  406. t.Fatalf("build failure: %s\n%s\n", err, string(out))
  407. }
  408. f, err := pe.Open(objfile)
  409. if err != nil {
  410. t.Fatalf("pe.Open failed: %v", err)
  411. }
  412. defer f.Close()
  413. section := f.Section(".edata")
  414. if section == nil {
  415. t.Skip(".edata section is not present")
  416. }
  417. // TODO: deduplicate this struct from cmd/link/internal/ld/pe.go
  418. type IMAGE_EXPORT_DIRECTORY struct {
  419. _ [2]uint32
  420. _ [2]uint16
  421. _ [2]uint32
  422. NumberOfFunctions uint32
  423. NumberOfNames uint32
  424. _ [3]uint32
  425. }
  426. var e IMAGE_EXPORT_DIRECTORY
  427. if err := binary.Read(section.Open(), binary.LittleEndian, &e); err != nil {
  428. t.Fatalf("binary.Read failed: %v", err)
  429. }
  430. // Only the two exported functions and _cgo_dummy_export should be exported
  431. expectedNumber := uint32(3)
  432. if exportAllSymbols {
  433. if e.NumberOfFunctions <= expectedNumber {
  434. t.Fatalf("missing exported functions: %v", e.NumberOfFunctions)
  435. }
  436. if e.NumberOfNames <= expectedNumber {
  437. t.Fatalf("missing exported names: %v", e.NumberOfNames)
  438. }
  439. } else {
  440. if e.NumberOfFunctions != expectedNumber {
  441. t.Fatalf("got %d exported functions; want %d", e.NumberOfFunctions, expectedNumber)
  442. }
  443. if e.NumberOfNames != expectedNumber {
  444. t.Fatalf("got %d exported names; want %d", e.NumberOfNames, expectedNumber)
  445. }
  446. }
  447. }
  448. func TestNumberOfExportedFunctions(t *testing.T) {
  449. if GOOS != "windows" {
  450. t.Skip("skipping windows only test")
  451. }
  452. t.Parallel()
  453. t.Run("OnlyExported", func(t *testing.T) {
  454. checkNumberOfExportedFunctionsWindows(t, false)
  455. })
  456. t.Run("All", func(t *testing.T) {
  457. checkNumberOfExportedFunctionsWindows(t, true)
  458. })
  459. }
  460. // test1: shared library can be dynamically loaded and exported symbols are accessible.
  461. func TestExportedSymbolsWithDynamicLoad(t *testing.T) {
  462. t.Parallel()
  463. if GOOS == "windows" {
  464. t.Logf("Skipping on %s", GOOS)
  465. return
  466. }
  467. cmd := "testp1"
  468. bin := cmdToRun(cmd)
  469. createHeadersOnce(t)
  470. if GOOS != "freebsd" {
  471. runCC(t, "-o", cmd, "main1.c", "-ldl")
  472. } else {
  473. runCC(t, "-o", cmd, "main1.c")
  474. }
  475. adbPush(t, cmd)
  476. defer os.Remove(bin)
  477. out := runExe(t, nil, bin, "./"+libgoname)
  478. if strings.TrimSpace(out) != "PASS" {
  479. t.Error(out)
  480. }
  481. }
  482. // test2: tests libgo2 which does not export any functions.
  483. func TestUnexportedSymbols(t *testing.T) {
  484. t.Parallel()
  485. if GOOS == "windows" {
  486. t.Logf("Skipping on %s", GOOS)
  487. return
  488. }
  489. cmd := "testp2"
  490. bin := cmdToRun(cmd)
  491. libname := "libgo2." + libSuffix
  492. run(t,
  493. nil,
  494. "go", "build",
  495. "-buildmode=c-shared",
  496. "-installsuffix", "testcshared",
  497. "-o", libname, "./libgo2",
  498. )
  499. adbPush(t, libname)
  500. linkFlags := "-Wl,--no-as-needed"
  501. if GOOS == "darwin" || GOOS == "ios" {
  502. linkFlags = ""
  503. }
  504. runCC(t, "-o", cmd, "main2.c", linkFlags, libname)
  505. adbPush(t, cmd)
  506. defer os.Remove(libname)
  507. defer os.Remove(bin)
  508. out := runExe(t, []string{"LD_LIBRARY_PATH=."}, bin)
  509. if strings.TrimSpace(out) != "PASS" {
  510. t.Error(out)
  511. }
  512. }
  513. // test3: tests main.main is exported on android.
  514. func TestMainExportedOnAndroid(t *testing.T) {
  515. t.Parallel()
  516. switch GOOS {
  517. case "android":
  518. break
  519. default:
  520. t.Logf("Skipping on %s", GOOS)
  521. return
  522. }
  523. cmd := "testp3"
  524. bin := cmdToRun(cmd)
  525. createHeadersOnce(t)
  526. runCC(t, "-o", cmd, "main3.c", "-ldl")
  527. adbPush(t, cmd)
  528. defer os.Remove(bin)
  529. out := runExe(t, nil, bin, "./"+libgoname)
  530. if strings.TrimSpace(out) != "PASS" {
  531. t.Error(out)
  532. }
  533. }
  534. func testSignalHandlers(t *testing.T, pkgname, cfile, cmd string) {
  535. libname := pkgname + "." + libSuffix
  536. run(t,
  537. nil,
  538. "go", "build",
  539. "-buildmode=c-shared",
  540. "-installsuffix", "testcshared",
  541. "-o", libname, pkgname,
  542. )
  543. adbPush(t, libname)
  544. if GOOS != "freebsd" {
  545. runCC(t, "-pthread", "-o", cmd, cfile, "-ldl")
  546. } else {
  547. runCC(t, "-pthread", "-o", cmd, cfile)
  548. }
  549. adbPush(t, cmd)
  550. bin := cmdToRun(cmd)
  551. defer os.Remove(libname)
  552. defer os.Remove(bin)
  553. defer os.Remove(pkgname + ".h")
  554. out := runExe(t, nil, bin, "./"+libname)
  555. if strings.TrimSpace(out) != "PASS" {
  556. t.Error(run(t, nil, bin, libname, "verbose"))
  557. }
  558. }
  559. // test4: test signal handlers
  560. func TestSignalHandlers(t *testing.T) {
  561. t.Parallel()
  562. if GOOS == "windows" {
  563. t.Logf("Skipping on %s", GOOS)
  564. return
  565. }
  566. testSignalHandlers(t, "./libgo4", "main4.c", "testp4")
  567. }
  568. // test5: test signal handlers with os/signal.Notify
  569. func TestSignalHandlersWithNotify(t *testing.T) {
  570. t.Parallel()
  571. if GOOS == "windows" {
  572. t.Logf("Skipping on %s", GOOS)
  573. return
  574. }
  575. testSignalHandlers(t, "./libgo5", "main5.c", "testp5")
  576. }
  577. func TestPIE(t *testing.T) {
  578. t.Parallel()
  579. switch GOOS {
  580. case "linux", "android":
  581. break
  582. default:
  583. t.Logf("Skipping on %s", GOOS)
  584. return
  585. }
  586. createHeadersOnce(t)
  587. f, err := elf.Open(libgoname)
  588. if err != nil {
  589. t.Fatalf("elf.Open failed: %v", err)
  590. }
  591. defer f.Close()
  592. ds := f.SectionByType(elf.SHT_DYNAMIC)
  593. if ds == nil {
  594. t.Fatalf("no SHT_DYNAMIC section")
  595. }
  596. d, err := ds.Data()
  597. if err != nil {
  598. t.Fatalf("can't read SHT_DYNAMIC contents: %v", err)
  599. }
  600. for len(d) > 0 {
  601. var tag elf.DynTag
  602. switch f.Class {
  603. case elf.ELFCLASS32:
  604. tag = elf.DynTag(f.ByteOrder.Uint32(d[:4]))
  605. d = d[8:]
  606. case elf.ELFCLASS64:
  607. tag = elf.DynTag(f.ByteOrder.Uint64(d[:8]))
  608. d = d[16:]
  609. }
  610. if tag == elf.DT_TEXTREL {
  611. t.Fatalf("%s has DT_TEXTREL flag", libgoname)
  612. }
  613. }
  614. }
  615. // Test that installing a second time recreates the header file.
  616. func TestCachedInstall(t *testing.T) {
  617. tmpdir, err := os.MkdirTemp("", "cshared")
  618. if err != nil {
  619. t.Fatal(err)
  620. }
  621. defer os.RemoveAll(tmpdir)
  622. copyFile(t, filepath.Join(tmpdir, "src", "testcshared", "go.mod"), "go.mod")
  623. copyFile(t, filepath.Join(tmpdir, "src", "testcshared", "libgo", "libgo.go"), filepath.Join("libgo", "libgo.go"))
  624. copyFile(t, filepath.Join(tmpdir, "src", "testcshared", "p", "p.go"), filepath.Join("p", "p.go"))
  625. env := append(os.Environ(), "GOPATH="+tmpdir, "GOBIN="+filepath.Join(tmpdir, "bin"))
  626. buildcmd := []string{"go", "install", "-x", "-buildmode=c-shared", "-installsuffix", "testcshared", "./libgo"}
  627. cmd := exec.Command(buildcmd[0], buildcmd[1:]...)
  628. cmd.Dir = filepath.Join(tmpdir, "src", "testcshared")
  629. cmd.Env = env
  630. t.Log(buildcmd)
  631. out, err := cmd.CombinedOutput()
  632. t.Logf("%s", out)
  633. if err != nil {
  634. t.Fatal(err)
  635. }
  636. var libgoh, ph string
  637. walker := func(path string, info os.FileInfo, err error) error {
  638. if err != nil {
  639. t.Fatal(err)
  640. }
  641. var ps *string
  642. switch filepath.Base(path) {
  643. case "libgo.h":
  644. ps = &libgoh
  645. case "p.h":
  646. ps = &ph
  647. }
  648. if ps != nil {
  649. if *ps != "" {
  650. t.Fatalf("%s found again", *ps)
  651. }
  652. *ps = path
  653. }
  654. return nil
  655. }
  656. if err := filepath.Walk(tmpdir, walker); err != nil {
  657. t.Fatal(err)
  658. }
  659. if libgoh == "" {
  660. t.Fatal("libgo.h not installed")
  661. }
  662. if err := os.Remove(libgoh); err != nil {
  663. t.Fatal(err)
  664. }
  665. cmd = exec.Command(buildcmd[0], buildcmd[1:]...)
  666. cmd.Dir = filepath.Join(tmpdir, "src", "testcshared")
  667. cmd.Env = env
  668. t.Log(buildcmd)
  669. out, err = cmd.CombinedOutput()
  670. t.Logf("%s", out)
  671. if err != nil {
  672. t.Fatal(err)
  673. }
  674. if _, err := os.Stat(libgoh); err != nil {
  675. t.Errorf("libgo.h not installed in second run: %v", err)
  676. }
  677. }
  678. // copyFile copies src to dst.
  679. func copyFile(t *testing.T, dst, src string) {
  680. t.Helper()
  681. data, err := os.ReadFile(src)
  682. if err != nil {
  683. t.Fatal(err)
  684. }
  685. if err := os.MkdirAll(filepath.Dir(dst), 0777); err != nil {
  686. t.Fatal(err)
  687. }
  688. if err := os.WriteFile(dst, data, 0666); err != nil {
  689. t.Fatal(err)
  690. }
  691. }
  692. func TestGo2C2Go(t *testing.T) {
  693. switch GOOS {
  694. case "darwin", "ios", "windows":
  695. // Non-ELF shared libraries don't support the multiple
  696. // copies of the runtime package implied by this test.
  697. t.Skipf("linking c-shared into Go programs not supported on %s; issue 29061, 49457", GOOS)
  698. case "android":
  699. t.Skip("test fails on android; issue 29087")
  700. }
  701. t.Parallel()
  702. tmpdir, err := os.MkdirTemp("", "cshared-TestGo2C2Go")
  703. if err != nil {
  704. t.Fatal(err)
  705. }
  706. defer os.RemoveAll(tmpdir)
  707. lib := filepath.Join(tmpdir, "libtestgo2c2go."+libSuffix)
  708. var env []string
  709. if GOOS == "windows" && strings.HasSuffix(lib, ".a") {
  710. env = append(env, "CGO_LDFLAGS=-Wl,--out-implib,"+lib, "CGO_LDFLAGS_ALLOW=.*")
  711. lib = strings.TrimSuffix(lib, ".a") + ".dll"
  712. }
  713. run(t, env, "go", "build", "-buildmode=c-shared", "-o", lib, "./go2c2go/go")
  714. cgoCflags := os.Getenv("CGO_CFLAGS")
  715. if cgoCflags != "" {
  716. cgoCflags += " "
  717. }
  718. cgoCflags += "-I" + tmpdir
  719. cgoLdflags := os.Getenv("CGO_LDFLAGS")
  720. if cgoLdflags != "" {
  721. cgoLdflags += " "
  722. }
  723. cgoLdflags += "-L" + tmpdir + " -ltestgo2c2go"
  724. goenv := []string{"CGO_CFLAGS=" + cgoCflags, "CGO_LDFLAGS=" + cgoLdflags}
  725. ldLibPath := os.Getenv("LD_LIBRARY_PATH")
  726. if ldLibPath != "" {
  727. ldLibPath += ":"
  728. }
  729. ldLibPath += tmpdir
  730. runenv := []string{"LD_LIBRARY_PATH=" + ldLibPath}
  731. bin := filepath.Join(tmpdir, "m1") + exeSuffix
  732. run(t, goenv, "go", "build", "-o", bin, "./go2c2go/m1")
  733. runExe(t, runenv, bin)
  734. bin = filepath.Join(tmpdir, "m2") + exeSuffix
  735. run(t, goenv, "go", "build", "-o", bin, "./go2c2go/m2")
  736. runExe(t, runenv, bin)
  737. }