Skip to content

Commit 3235f7c

Browse files
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 <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Cherry Zhang <[email protected]>
1 parent c226f64 commit 3235f7c

File tree

2 files changed

+120
-0
lines changed

2 files changed

+120
-0
lines changed

src/cmd/link/elf_test.go

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
// Copyright 2019 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
// +build dragonfly freebsd linux netbsd openbsd
6+
7+
package main
8+
9+
import (
10+
"internal/testenv"
11+
"io/ioutil"
12+
"os"
13+
"os/exec"
14+
"path/filepath"
15+
"strings"
16+
"testing"
17+
)
18+
19+
var asmSource = `
20+
.section .text1,"ax"
21+
s1:
22+
.byte 0
23+
.section .text2,"ax"
24+
s2:
25+
.byte 0
26+
`
27+
28+
var goSource = `
29+
package main
30+
func main() {}
31+
`
32+
33+
// The linker used to crash if an ELF input file had multiple text sections
34+
// with the same name.
35+
func TestSectionsWithSameName(t *testing.T) {
36+
testenv.MustHaveGoBuild(t)
37+
t.Parallel()
38+
39+
objcopy, err := exec.LookPath("objcopy")
40+
if err != nil {
41+
t.Skipf("can't find objcopy: %v", err)
42+
}
43+
44+
dir, err := ioutil.TempDir("", "go-link-TestSectionsWithSameName")
45+
if err != nil {
46+
t.Fatal(err)
47+
}
48+
defer os.RemoveAll(dir)
49+
50+
gopath := filepath.Join(dir, "GOPATH")
51+
env := append(os.Environ(), "GOPATH="+gopath)
52+
53+
if err := ioutil.WriteFile(filepath.Join(dir, "go.mod"), []byte("module elf_test\n"), 0666); err != nil {
54+
t.Fatal(err)
55+
}
56+
57+
asmFile := filepath.Join(dir, "x.s")
58+
if err := ioutil.WriteFile(asmFile, []byte(asmSource), 0444); err != nil {
59+
t.Fatal(err)
60+
}
61+
62+
goTool := testenv.GoToolPath(t)
63+
cmd := exec.Command(goTool, "env", "CC")
64+
cmd.Env = env
65+
ccb, err := cmd.Output()
66+
if err != nil {
67+
t.Fatal(err)
68+
}
69+
cc := strings.TrimSpace(string(ccb))
70+
71+
cmd = exec.Command(goTool, "env", "GOGCCFLAGS")
72+
cmd.Env = env
73+
cflagsb, err := cmd.Output()
74+
if err != nil {
75+
t.Fatal(err)
76+
}
77+
cflags := strings.Fields(string(cflagsb))
78+
79+
asmObj := filepath.Join(dir, "x.o")
80+
t.Logf("%s %v -o %s %s", cc, cflags, asmObj, asmFile)
81+
if out, err := exec.Command(cc, append(cflags, "-c", "-o", asmObj, asmFile)...).CombinedOutput(); err != nil {
82+
t.Logf("%s", out)
83+
t.Fatal(err)
84+
}
85+
86+
asm2Obj := filepath.Join(dir, "x2.syso")
87+
t.Logf("%s --rename-section .text2=.text1 %s %s", objcopy, asmObj, asm2Obj)
88+
if out, err := exec.Command(objcopy, "--rename-section", ".text2=.text1", asmObj, asm2Obj).CombinedOutput(); err != nil {
89+
t.Logf("%s", out)
90+
t.Fatal(err)
91+
}
92+
93+
for _, s := range []string{asmFile, asmObj} {
94+
if err := os.Remove(s); err != nil {
95+
t.Fatal(err)
96+
}
97+
}
98+
99+
goFile := filepath.Join(dir, "main.go")
100+
if err := ioutil.WriteFile(goFile, []byte(goSource), 0444); err != nil {
101+
t.Fatal(err)
102+
}
103+
104+
cmd = exec.Command(goTool, "build")
105+
cmd.Dir = dir
106+
cmd.Env = env
107+
t.Logf("%s build", goTool)
108+
if out, err := cmd.CombinedOutput(); err != nil {
109+
t.Logf("%s", out)
110+
t.Fatal(err)
111+
}
112+
}

src/cmd/link/internal/loadelf/ldelf.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -678,6 +678,8 @@ func Load(arch *sys.Arch, syms *sym.Symbols, f *bio.Reader, pkg string, length i
678678
// as well use one large chunk.
679679

680680
// create symbols for elfmapped sections
681+
sectsymNames := make(map[string]bool)
682+
counter := 0
681683
for i := 0; uint(i) < elfobj.nsect; i++ {
682684
sect = &elfobj.sect[i]
683685
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
709711
}
710712

711713
name := fmt.Sprintf("%s(%s)", pkg, sect.name)
714+
for sectsymNames[name] {
715+
counter++
716+
name = fmt.Sprintf("%s(%s%d)", pkg, sect.name, counter)
717+
}
718+
sectsymNames[name] = true
719+
712720
s := syms.Lookup(name, localSymVersion)
713721

714722
switch int(sect.flags) & (ElfSectFlagAlloc | ElfSectFlagWrite | ElfSectFlagExec) {

0 commit comments

Comments
 (0)