cmd/link: add new linker testpoint for "ld -r" host object
This adds a new test that builds a small Go program with linked
against a *.syso file that is the result of an "ld -r" link. The
sysobj in question has multiple static symbols in the same section
with the same name, which triggered a bug in the loader in -newobj
mode.
Updates #35779.
Change-Id: Ibe1a75662dc1d49c4347279e55646ee65a81508e
Reviewed-on: https://go-review.googlesource.com/c/go/+/208478
Reviewed-by: Cherry Zhang <cherryyz@google.com>
diff --git a/src/cmd/link/elf_test.go b/src/cmd/link/elf_test.go
index 3df9869..84f373a 100644
--- a/src/cmd/link/elf_test.go
+++ b/src/cmd/link/elf_test.go
@@ -7,6 +7,7 @@
package main
import (
+ "fmt"
"internal/testenv"
"io/ioutil"
"os"
@@ -16,6 +17,27 @@
"testing"
)
+func getCCAndCCFLAGS(t *testing.T, env []string) (string, []string) {
+ goTool := testenv.GoToolPath(t)
+ cmd := exec.Command(goTool, "env", "CC")
+ cmd.Env = env
+ ccb, err := cmd.Output()
+ if err != nil {
+ t.Fatal(err)
+ }
+ cc := strings.TrimSpace(string(ccb))
+
+ cmd = exec.Command(goTool, "env", "GOGCCFLAGS")
+ cmd.Env = env
+ cflagsb, err := cmd.Output()
+ if err != nil {
+ t.Fatal(err)
+ }
+ cflags := strings.Fields(string(cflagsb))
+
+ return cc, cflags
+}
+
var asmSource = `
.section .text1,"ax"
s1:
@@ -61,21 +83,7 @@
}
goTool := testenv.GoToolPath(t)
- cmd := exec.Command(goTool, "env", "CC")
- cmd.Env = env
- ccb, err := cmd.Output()
- if err != nil {
- t.Fatal(err)
- }
- cc := strings.TrimSpace(string(ccb))
-
- cmd = exec.Command(goTool, "env", "GOGCCFLAGS")
- cmd.Env = env
- cflagsb, err := cmd.Output()
- if err != nil {
- t.Fatal(err)
- }
- cflags := strings.Fields(string(cflagsb))
+ cc, cflags := getCCAndCCFLAGS(t, env)
asmObj := filepath.Join(dir, "x.o")
t.Logf("%s %v -c -o %s %s", cc, cflags, asmObj, asmFile)
@@ -102,7 +110,91 @@
t.Fatal(err)
}
- cmd = exec.Command(goTool, "build")
+ cmd := exec.Command(goTool, "build")
+ cmd.Dir = dir
+ cmd.Env = env
+ t.Logf("%s build", goTool)
+ if out, err := cmd.CombinedOutput(); err != nil {
+ t.Logf("%s", out)
+ t.Fatal(err)
+ }
+}
+
+var cSources35779 = []string{`
+static int blah() { return 42; }
+int Cfunc1() { return blah(); }
+`, `
+static int blah() { return 42; }
+int Cfunc2() { return blah(); }
+`,
+}
+
+// TestMinusRSymsWithSameName tests a corner case in the new
+// loader. Prior to the fix this failed with the error 'loadelf:
+// $WORK/b001/_pkg_.a(ldr.syso): duplicate symbol reference: blah in
+// both main(.text) and main(.text)'
+func TestMinusRSymsWithSameName(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+ testenv.MustHaveCGO(t)
+ t.Parallel()
+
+ dir, err := ioutil.TempDir("", "go-link-TestMinusRSymsWithSameName")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(dir)
+
+ gopath := filepath.Join(dir, "GOPATH")
+ env := append(os.Environ(), "GOPATH="+gopath)
+
+ if err := ioutil.WriteFile(filepath.Join(dir, "go.mod"), []byte("module elf_test\n"), 0666); err != nil {
+ t.Fatal(err)
+ }
+
+ goTool := testenv.GoToolPath(t)
+ cc, cflags := getCCAndCCFLAGS(t, env)
+
+ objs := []string{}
+ csrcs := []string{}
+ for i, content := range cSources35779 {
+ csrcFile := filepath.Join(dir, fmt.Sprintf("x%d.c", i))
+ csrcs = append(csrcs, csrcFile)
+ if err := ioutil.WriteFile(csrcFile, []byte(content), 0444); err != nil {
+ t.Fatal(err)
+ }
+
+ obj := filepath.Join(dir, fmt.Sprintf("x%d.o", i))
+ objs = append(objs, obj)
+ t.Logf("%s %v -c -o %s %s", cc, cflags, obj, csrcFile)
+ if out, err := exec.Command(cc, append(cflags, "-c", "-o", obj, csrcFile)...).CombinedOutput(); err != nil {
+ t.Logf("%s", out)
+ t.Fatal(err)
+ }
+ }
+
+ sysoObj := filepath.Join(dir, "ldr.syso")
+ t.Logf("%s %v -nostdlib -r -o %s %v", cc, cflags, sysoObj, objs)
+ if out, err := exec.Command(cc, append(cflags, "-nostdlib", "-r", "-o", sysoObj, objs[0], objs[1])...).CombinedOutput(); err != nil {
+ t.Logf("%s", out)
+ t.Fatal(err)
+ }
+
+ cruft := [][]string{objs, csrcs}
+ for _, sl := range cruft {
+ for _, s := range sl {
+ if err := os.Remove(s); err != nil {
+ t.Fatal(err)
+ }
+ }
+ }
+
+ goFile := filepath.Join(dir, "main.go")
+ if err := ioutil.WriteFile(goFile, []byte(goSource), 0444); err != nil {
+ t.Fatal(err)
+ }
+
+ t.Logf("%s build", goTool)
+ cmd := exec.Command(goTool, "build")
cmd.Dir = dir
cmd.Env = env
t.Logf("%s build", goTool)