| // Copyright 2019 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| // +build dragonfly freebsd linux netbsd openbsd |
| |
| package main |
| |
| import ( |
| "fmt" |
| "internal/testenv" |
| "io/ioutil" |
| "os" |
| "os/exec" |
| "path/filepath" |
| "runtime" |
| "strings" |
| "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: |
| .byte 0 |
| .section .text2,"ax" |
| s2: |
| .byte 0 |
| ` |
| |
| var goSource = ` |
| package main |
| func main() {} |
| ` |
| |
| // The linker used to crash if an ELF input file had multiple text sections |
| // with the same name. |
| func TestSectionsWithSameName(t *testing.T) { |
| testenv.MustHaveGoBuild(t) |
| testenv.MustHaveCGO(t) |
| t.Parallel() |
| |
| objcopy, err := exec.LookPath("objcopy") |
| if err != nil { |
| t.Skipf("can't find objcopy: %v", err) |
| } |
| |
| dir, err := ioutil.TempDir("", "go-link-TestSectionsWithSameName") |
| 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) |
| } |
| |
| asmFile := filepath.Join(dir, "x.s") |
| if err := ioutil.WriteFile(asmFile, []byte(asmSource), 0444); err != nil { |
| t.Fatal(err) |
| } |
| |
| goTool := testenv.GoToolPath(t) |
| cc, cflags := getCCAndCCFLAGS(t, env) |
| |
| asmObj := filepath.Join(dir, "x.o") |
| t.Logf("%s %v -c -o %s %s", cc, cflags, asmObj, asmFile) |
| if out, err := exec.Command(cc, append(cflags, "-c", "-o", asmObj, asmFile)...).CombinedOutput(); err != nil { |
| t.Logf("%s", out) |
| t.Fatal(err) |
| } |
| |
| asm2Obj := filepath.Join(dir, "x2.syso") |
| t.Logf("%s --rename-section .text2=.text1 %s %s", objcopy, asmObj, asm2Obj) |
| if out, err := exec.Command(objcopy, "--rename-section", ".text2=.text1", asmObj, asm2Obj).CombinedOutput(); err != nil { |
| t.Logf("%s", out) |
| t.Fatal(err) |
| } |
| |
| for _, s := range []string{asmFile, asmObj} { |
| 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) |
| } |
| |
| 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)'. See issue #35779. |
| func TestMinusRSymsWithSameName(t *testing.T) { |
| testenv.MustHaveGoBuild(t) |
| testenv.MustHaveCGO(t) |
| t.Parallel() |
| |
| // Skip this test on MIPS for the time being since it seems to trigger |
| // problems with unknown relocations. |
| if strings.Contains(runtime.GOARCH, "mips") { |
| testenv.SkipFlaky(t, 35779) |
| } |
| |
| 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 |
| if out, err := cmd.CombinedOutput(); err != nil { |
| t.Logf("%s", out) |
| t.Fatal(err) |
| } |
| } |