Skip to content

Commit db75205

Browse files
committed
[dev.link] cmd/link: restore -strictdups flag in newobj mode
Change-Id: I93ad769595fa343400afa342af12e1445abff084 Reviewed-on: https://go-review.googlesource.com/c/go/+/204918 Run-TryBot: Cherry Zhang <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Than McIntosh <[email protected]>
1 parent ab4a71f commit db75205

File tree

3 files changed

+145
-12
lines changed

3 files changed

+145
-12
lines changed

src/cmd/link/internal/ld/lib.go

+14-1
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,16 @@ func (ctxt *Link) findLibPath(libname string) string {
378378

379379
func (ctxt *Link) loadlib() {
380380
if *flagNewobj {
381-
ctxt.loader = loader.NewLoader()
381+
var flags uint32
382+
switch *FlagStrictDups {
383+
case 0:
384+
// nothing to do
385+
case 1, 2:
386+
flags = loader.FlagStrictDups
387+
default:
388+
log.Fatalf("invalid -strictdups flag value %d", *FlagStrictDups)
389+
}
390+
ctxt.loader = loader.NewLoader(flags)
382391
}
383392

384393
ctxt.cgo_export_static = make(map[string]bool)
@@ -550,6 +559,10 @@ func (ctxt *Link) loadlib() {
550559
ctxt.Loaded = true
551560

552561
importcycles()
562+
563+
if *flagNewobj {
564+
strictDupMsgCount = ctxt.loader.NStrictDupMsgs()
565+
}
553566
}
554567

555568
// Set up dynexp list.

src/cmd/link/internal/loader/loader.go

+66-11
Original file line numberDiff line numberDiff line change
@@ -119,9 +119,18 @@ type Loader struct {
119119
Reachparent []Sym
120120

121121
relocBatch []sym.Reloc // for bulk allocation of relocations
122+
123+
flags uint32
124+
125+
strictDupMsgs int // number of strict-dup warning/errors, when FlagStrictDups is enabled
122126
}
123127

124-
func NewLoader() *Loader {
128+
const (
129+
// Loader.flags
130+
FlagStrictDups = 1 << iota
131+
)
132+
133+
func NewLoader(flags uint32) *Loader {
125134
nbuiltin := goobj2.NBuiltin()
126135
return &Loader{
127136
start: make(map[*oReader]Sym),
@@ -132,6 +141,7 @@ func NewLoader() *Loader {
132141
itablink: make(map[Sym]struct{}),
133142
extStaticSyms: make(map[nameVer]Sym),
134143
builtinSyms: make([]Sym, nbuiltin),
144+
flags: flags,
135145
}
136146
}
137147

@@ -170,6 +180,9 @@ func (l *Loader) AddSym(name string, ver int, i Sym, r *oReader, dupok bool, typ
170180
}
171181
if oldi, ok := l.symsByName[ver][name]; ok {
172182
if dupok {
183+
if l.flags&FlagStrictDups != 0 {
184+
l.checkdup(name, i, r, oldi)
185+
}
173186
return false
174187
}
175188
oldr, li := l.toLocal(oldi)
@@ -366,6 +379,42 @@ func (l *Loader) IsDup(i Sym) bool {
366379
return l.symsByName[ver][name] != i
367380
}
368381

382+
// Check that duplicate symbols have same contents.
383+
func (l *Loader) checkdup(name string, i Sym, r *oReader, dup Sym) {
384+
li := int(i - l.startIndex(r))
385+
p := r.Data(li)
386+
if strings.HasPrefix(name, "go.info.") {
387+
p, _ = patchDWARFName1(p, r)
388+
}
389+
rdup, ldup := l.toLocal(dup)
390+
pdup := rdup.Data(ldup)
391+
if strings.HasPrefix(name, "go.info.") {
392+
pdup, _ = patchDWARFName1(pdup, rdup)
393+
}
394+
if bytes.Equal(p, pdup) {
395+
return
396+
}
397+
reason := "same length but different contents"
398+
if len(p) != len(pdup) {
399+
reason = fmt.Sprintf("new length %d != old length %d", len(p), len(pdup))
400+
}
401+
fmt.Fprintf(os.Stderr, "cmd/link: while reading object for '%v': duplicate symbol '%s', previous def at '%v', with mismatched payload: %s\n", r.unit.Lib, name, rdup.unit.Lib, reason)
402+
403+
// For the moment, whitelist DWARF subprogram DIEs for
404+
// auto-generated wrapper functions. What seems to happen
405+
// here is that we get different line numbers on formal
406+
// params; I am guessing that the pos is being inherited
407+
// from the spot where the wrapper is needed.
408+
whitelist := strings.HasPrefix(name, "go.info.go.interface") ||
409+
strings.HasPrefix(name, "go.info.go.builtin") ||
410+
strings.HasPrefix(name, "go.debuglines")
411+
if !whitelist {
412+
l.strictDupMsgs++
413+
}
414+
}
415+
416+
func (l *Loader) NStrictDupMsgs() int { return l.strictDupMsgs }
417+
369418
// Number of total symbols.
370419
func (l *Loader) NSym() int {
371420
return int(l.max + 1)
@@ -1194,24 +1243,30 @@ func loadObjFull(l *Loader, r *oReader) {
11941243

11951244
var emptyPkg = []byte(`"".`)
11961245

1197-
func patchDWARFName(s *sym.Symbol, r *oReader) {
1246+
func patchDWARFName1(p []byte, r *oReader) ([]byte, int) {
11981247
// This is kind of ugly. Really the package name should not
11991248
// even be included here.
1200-
if s.Size < 1 || s.P[0] != dwarf.DW_ABRV_FUNCTION {
1201-
return
1249+
if len(p) < 1 || p[0] != dwarf.DW_ABRV_FUNCTION {
1250+
return p, -1
12021251
}
1203-
e := bytes.IndexByte(s.P, 0)
1252+
e := bytes.IndexByte(p, 0)
12041253
if e == -1 {
1205-
return
1254+
return p, -1
12061255
}
1207-
p := bytes.Index(s.P[:e], emptyPkg)
1208-
if p == -1 {
1209-
return
1256+
if !bytes.Contains(p[:e], emptyPkg) {
1257+
return p, -1
12101258
}
12111259
pkgprefix := []byte(r.pkgprefix)
1212-
patched := bytes.Replace(s.P[:e], emptyPkg, pkgprefix, -1)
1260+
patched := bytes.Replace(p[:e], emptyPkg, pkgprefix, -1)
1261+
return append(patched, p[e:]...), e
1262+
}
12131263

1214-
s.P = append(patched, s.P[e:]...)
1264+
func patchDWARFName(s *sym.Symbol, r *oReader) {
1265+
patched, e := patchDWARFName1(s.P, r)
1266+
if e == -1 {
1267+
return
1268+
}
1269+
s.P = patched
12151270
s.Attr.Set(sym.AttrReadOnly, false)
12161271
delta := int64(len(s.P)) - s.Size
12171272
s.Size = int64(len(s.P))

src/cmd/link/link_test.go

+65
Original file line numberDiff line numberDiff line change
@@ -376,3 +376,68 @@ func TestIssue34788Android386TLSSequence(t *testing.T) {
376376
}
377377
}
378378
}
379+
380+
const testStrictDupGoSrc = `
381+
package main
382+
func f()
383+
func main() { f() }
384+
`
385+
386+
const testStrictDupAsmSrc1 = `
387+
#include "textflag.h"
388+
TEXT ·f(SB), NOSPLIT|DUPOK, $0-0
389+
RET
390+
`
391+
392+
const testStrictDupAsmSrc2 = `
393+
#include "textflag.h"
394+
TEXT ·f(SB), NOSPLIT|DUPOK, $0-0
395+
JMP 0(PC)
396+
`
397+
398+
func TestStrictDup(t *testing.T) {
399+
// Check that -strictdups flag works.
400+
testenv.MustHaveGoBuild(t)
401+
402+
tmpdir, err := ioutil.TempDir("", "TestStrictDup")
403+
if err != nil {
404+
t.Fatal(err)
405+
}
406+
defer os.RemoveAll(tmpdir)
407+
408+
src := filepath.Join(tmpdir, "x.go")
409+
err = ioutil.WriteFile(src, []byte(testStrictDupGoSrc), 0666)
410+
if err != nil {
411+
t.Fatal(err)
412+
}
413+
src = filepath.Join(tmpdir, "a.s")
414+
err = ioutil.WriteFile(src, []byte(testStrictDupAsmSrc1), 0666)
415+
if err != nil {
416+
t.Fatal(err)
417+
}
418+
src = filepath.Join(tmpdir, "b.s")
419+
err = ioutil.WriteFile(src, []byte(testStrictDupAsmSrc2), 0666)
420+
if err != nil {
421+
t.Fatal(err)
422+
}
423+
424+
cmd := exec.Command(testenv.GoToolPath(t), "build", "-ldflags=-strictdups=1")
425+
cmd.Dir = tmpdir
426+
out, err := cmd.CombinedOutput()
427+
if err != nil {
428+
t.Errorf("linking with -strictdups=1 failed: %v", err)
429+
}
430+
if !bytes.Contains(out, []byte("mismatched payload")) {
431+
t.Errorf("unexpected output:\n%s", out)
432+
}
433+
434+
cmd = exec.Command(testenv.GoToolPath(t), "build", "-ldflags=-strictdups=2")
435+
cmd.Dir = tmpdir
436+
out, err = cmd.CombinedOutput()
437+
if err == nil {
438+
t.Errorf("linking with -strictdups=2 did not fail")
439+
}
440+
if !bytes.Contains(out, []byte("mismatched payload")) {
441+
t.Errorf("unexpected output:\n%s", out)
442+
}
443+
}

0 commit comments

Comments
 (0)