Skip to content

Commit d923309

Browse files
committed
cmd/link: add optional sanity checking for duplicate symbols
Introduce a new linker command line option "-strictdups", which enables sanity checking of "ok to duplicate" symbols, especially DWARF info symbols. Acceptable values are 0 (no checking) 1 (issue warnings) and 2 (issue a fatal error checks fail). Currently if we read a DWARF symbol (such as "go.info.PKG.FUNCTION") from one object file, and then encounter the same symbol later on while reading another object file, we simply discard the second one and move on with the link, since the two should in theory be identical. If as a result of a compiler bug we wind up with symbols that are not identical, this tends to (silently) result in incorrect DWARF generation, which may or may not be discovered depending on who is consuming the DWARF and what's being done with it. When this option is turned on, at the point where a duplicate symbol is detected in the object file reader, we check to make sure that the length/contents of the symbol are the same as the previously read symbol, and print a descriptive warning (or error) if not. For the time being this can be used for one-off testing to find problems; at some point it would be nice if we can enable it by default. Updates #30908. Change-Id: I64c4e07c326b4572db674ff17c93307e2eec607c Reviewed-on: https://go-review.googlesource.com/c/go/+/168410 Run-TryBot: Than McIntosh <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Keith Randall <[email protected]>
1 parent 0fbf681 commit d923309

File tree

3 files changed

+56
-2
lines changed

3 files changed

+56
-2
lines changed

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

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1725,7 +1725,18 @@ func ldobj(ctxt *Link, f *bio.Reader, lib *sym.Library, length int64, pn string,
17251725
ldpkg(ctxt, f, lib, import1-import0-2, pn) // -2 for !\n
17261726
f.Seek(import1, 0)
17271727

1728-
objfile.Load(ctxt.Arch, ctxt.Syms, f, lib, eof-f.Offset(), pn)
1728+
flags := 0
1729+
switch *FlagStrictDups {
1730+
case 0:
1731+
break
1732+
case 1:
1733+
flags = objfile.StrictDupsWarnFlag
1734+
case 2:
1735+
flags = objfile.StrictDupsErrFlag
1736+
default:
1737+
log.Fatalf("invalid -strictdups flag value %d", *FlagStrictDups)
1738+
}
1739+
objfile.Load(ctxt.Arch, ctxt.Syms, f, lib, eof-f.Offset(), pn, flags)
17291740
addImports(ctxt, lib, pn)
17301741
return nil
17311742
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ var (
8585
Flag8 bool // use 64-bit addresses in symbol table
8686
flagInterpreter = flag.String("I", "", "use `linker` as ELF dynamic linker")
8787
FlagDebugTramp = flag.Int("debugtramp", 0, "debug trampolines")
88+
FlagStrictDups = flag.Int("strictdups", 0, "sanity check duplicate symbol contents during object file reading (1=warn 2=err).")
8889

8990
FlagRound = flag.Int("R", -1, "set address rounding `quantum`")
9091
FlagTextAddr = flag.Int64("T", -1, "set text segment `address`")

src/cmd/link/internal/objfile/objfile.go

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,10 @@ import (
1717
"cmd/internal/objabi"
1818
"cmd/internal/sys"
1919
"cmd/link/internal/sym"
20+
"fmt"
2021
"io"
2122
"log"
23+
"os"
2224
"strconv"
2325
"strings"
2426
)
@@ -39,6 +41,7 @@ type objReader struct {
3941
pn string
4042
dupSym *sym.Symbol
4143
localSymVersion int
44+
flags int
4245

4346
// rdBuf is used by readString and readSymName as scratch for reading strings.
4447
rdBuf []byte
@@ -54,9 +57,22 @@ type objReader struct {
5457
file []*sym.Symbol
5558
}
5659

60+
// Flags to enable optional behavior during object loading/reading.
61+
62+
const (
63+
NoFlag int = iota
64+
65+
// Sanity-check duplicate symbol contents, issuing warning
66+
// when duplicates have different lengths or contents.
67+
StrictDupsWarnFlag
68+
69+
// Similar to StrictDupsWarnFlag, but issue fatal error.
70+
StrictDupsErrFlag
71+
)
72+
5773
// Load loads an object file f into library lib.
5874
// The symbols loaded are added to syms.
59-
func Load(arch *sys.Arch, syms *sym.Symbols, f *bio.Reader, lib *sym.Library, length int64, pn string) {
75+
func Load(arch *sys.Arch, syms *sym.Symbols, f *bio.Reader, lib *sym.Library, length int64, pn string, flags int) {
6076
start := f.Offset()
6177
r := &objReader{
6278
rd: f.Reader,
@@ -66,6 +82,7 @@ func Load(arch *sys.Arch, syms *sym.Symbols, f *bio.Reader, lib *sym.Library, le
6682
pn: pn,
6783
dupSym: &sym.Symbol{Name: ".dup"},
6884
localSymVersion: syms.IncVersion(),
85+
flags: flags,
6986
}
7087
r.loadObjFile()
7188
if f.Offset() != start+length {
@@ -340,6 +357,31 @@ overwrite:
340357
if s.Type == sym.SDWARFINFO {
341358
r.patchDWARFName(s)
342359
}
360+
361+
if isdup && r.flags&(StrictDupsWarnFlag|StrictDupsErrFlag) != 0 {
362+
// Compare the just-read symbol with the previously read
363+
// symbol of the same name, verifying that they have the same
364+
// payload. If not, issue a warning and possibly an error.
365+
if !bytes.Equal(s.P, dup.P) {
366+
reason := "same length but different contents"
367+
if len(s.P) != len(dup.P) {
368+
reason = fmt.Sprintf("new length %d != old length %d",
369+
len(data), len(dup.P))
370+
}
371+
fmt.Fprintf(os.Stderr, "cmd/link: while reading object for '%v': duplicate symbol '%s', previous def at '%v', with mismatched payload: %s\n", r.lib, dup, dup.Lib, reason)
372+
373+
// For the moment, whitelist DWARF subprogram DIEs for
374+
// auto-generated wrapper functions. What seems to happen
375+
// here is that we get different line numbers on formal
376+
// params; I am guessing that the pos is being inherited
377+
// from the spot where the wrapper is needed.
378+
whitelist := strings.HasPrefix(dup.Name, "go.info.go.interface")
379+
380+
if r.flags&StrictDupsErrFlag != 0 && !whitelist {
381+
log.Fatalf("failed duplicate symbol check on '%s' reading %s", dup.Name, r.pn)
382+
}
383+
}
384+
}
343385
}
344386

345387
func (r *objReader) patchDWARFName(s *sym.Symbol) {

0 commit comments

Comments
 (0)