diff options
author | Ian Lance Taylor <iant@golang.org> | 2019-04-17 22:41:51 -0700 |
---|---|---|
committer | Ian Lance Taylor <iant@golang.org> | 2019-04-18 15:46:21 +0000 |
commit | 01c8062308e2c8cf0fe7b1577318f2227771ebf5 (patch) | |
tree | ba77fcb7499d6f9884b8d5dca7ceb1e7f34a36ac | |
parent | 428e5f29a957b591d82e640b619b684aa25fba4e (diff) | |
download | go-01c8062308e2c8cf0fe7b1577318f2227771ebf5.tar.gz go-01c8062308e2c8cf0fe7b1577318f2227771ebf5.zip |
[release-branch.go1.11] cmd/link: don't fail if multiple ELF sections have the same name
New versions of clang can generate multiple sections named ".text"
when using vague C++ linkage. This is valid ELF, but would cause the
Go linker to report an error when using internal linking:
symbol PACKAGEPATH(.text) listed multiple times
Avoid the problem by renaming section symbol names if there is a name
collision.
Change-Id: I41127e95003d5b4554aaf849177b3fe000382c02
Reviewed-on: https://go-review.googlesource.com/c/go/+/172697
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
(cherry picked from commit 3235f7c0720338a160debe6e9c632b8af968b4dd)
Reviewed-on: https://go-review.googlesource.com/c/go/+/172702
-rw-r--r-- | src/cmd/link/elf_test.go | 112 | ||||
-rw-r--r-- | src/cmd/link/internal/loadelf/ldelf.go | 8 |
2 files changed, 120 insertions, 0 deletions
diff --git a/src/cmd/link/elf_test.go b/src/cmd/link/elf_test.go new file mode 100644 index 0000000000..9eb8d1a14b --- /dev/null +++ b/src/cmd/link/elf_test.go @@ -0,0 +1,112 @@ +// 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 ( + "internal/testenv" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "strings" + "testing" +) + +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) + 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) + 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)) + + asmObj := filepath.Join(dir, "x.o") + t.Logf("%s %v -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) + } +} diff --git a/src/cmd/link/internal/loadelf/ldelf.go b/src/cmd/link/internal/loadelf/ldelf.go index 8e32e7dee6..57d000a8bc 100644 --- a/src/cmd/link/internal/loadelf/ldelf.go +++ b/src/cmd/link/internal/loadelf/ldelf.go @@ -678,6 +678,8 @@ func Load(arch *sys.Arch, syms *sym.Symbols, f *bio.Reader, pkg string, length i // as well use one large chunk. // create symbols for elfmapped sections + sectsymNames := make(map[string]bool) + counter := 0 for i := 0; uint(i) < elfobj.nsect; i++ { sect = &elfobj.sect[i] if sect.type_ == SHT_ARM_ATTRIBUTES && sect.name == ".ARM.attributes" { @@ -709,6 +711,12 @@ func Load(arch *sys.Arch, syms *sym.Symbols, f *bio.Reader, pkg string, length i } name := fmt.Sprintf("%s(%s)", pkg, sect.name) + for sectsymNames[name] { + counter++ + name = fmt.Sprintf("%s(%s%d)", pkg, sect.name, counter) + } + sectsymNames[name] = true + s := syms.Lookup(name, localSymVersion) switch int(sect.flags) & (ElfSectFlagAlloc | ElfSectFlagWrite | ElfSectFlagExec) { |