From 01c8062308e2c8cf0fe7b1577318f2227771ebf5 Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Wed, 17 Apr 2019 22:41:51 -0700 Subject: [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 TryBot-Result: Gobot Gobot Reviewed-by: Cherry Zhang (cherry picked from commit 3235f7c0720338a160debe6e9c632b8af968b4dd) Reviewed-on: https://go-review.googlesource.com/c/go/+/172702 --- src/cmd/link/elf_test.go | 112 +++++++++++++++++++++++++++++++++ src/cmd/link/internal/loadelf/ldelf.go | 8 +++ 2 files changed, 120 insertions(+) create mode 100644 src/cmd/link/elf_test.go 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) { -- cgit v1.2.3-54-g00ecf