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>
diff --git a/src/cmd/link/elf_test.go b/src/cmd/link/elf_test.go
new file mode 100644
index 0000000..9eb8d1a
--- /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 a22a77b..a38c90b 100644
--- a/src/cmd/link/internal/loadelf/ldelf.go
+++ b/src/cmd/link/internal/loadelf/ldelf.go
@@ -678,6 +678,8 @@
 	// 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 @@
 		}
 
 		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) {