aboutsummaryrefslogtreecommitdiff
path: root/src/cmd
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd')
-rw-r--r--src/cmd/cgo/out.go23
-rw-r--r--src/cmd/compile/internal/gc/ssa.go8
-rw-r--r--src/cmd/compile/internal/gc/subr.go6
-rw-r--r--src/cmd/compile/internal/gc/typecheck.go2
-rw-r--r--src/cmd/go/internal/modfetch/coderepo_test.go5
-rw-r--r--src/cmd/go/internal/work/exec.go60
-rw-r--r--src/cmd/go/internal/work/security.go8
-rw-r--r--src/cmd/go/internal/work/security_test.go5
-rw-r--r--src/cmd/internal/obj/dwarf.go46
-rw-r--r--src/cmd/link/internal/ld/dwarf.go16
-rw-r--r--src/cmd/link/internal/ld/dwarf_test.go119
-rw-r--r--src/cmd/link/internal/ld/testdata/issue39757/issue39757main.go15
12 files changed, 261 insertions, 52 deletions
diff --git a/src/cmd/cgo/out.go b/src/cmd/cgo/out.go
index cbdb4e0f5b..dcd69edb44 100644
--- a/src/cmd/cgo/out.go
+++ b/src/cmd/cgo/out.go
@@ -336,6 +336,8 @@ func dynimport(obj string) {
if s.Version != "" {
targ += "#" + s.Version
}
+ checkImportSymName(s.Name)
+ checkImportSymName(targ)
fmt.Fprintf(stdout, "//go:cgo_import_dynamic %s %s %q\n", s.Name, targ, s.Library)
}
lib, _ := f.ImportedLibraries()
@@ -351,6 +353,7 @@ func dynimport(obj string) {
if len(s) > 0 && s[0] == '_' {
s = s[1:]
}
+ checkImportSymName(s)
fmt.Fprintf(stdout, "//go:cgo_import_dynamic %s %s %q\n", s, s, "")
}
lib, _ := f.ImportedLibraries()
@@ -365,6 +368,8 @@ func dynimport(obj string) {
for _, s := range sym {
ss := strings.Split(s, ":")
name := strings.Split(ss[0], "@")[0]
+ checkImportSymName(name)
+ checkImportSymName(ss[0])
fmt.Fprintf(stdout, "//go:cgo_import_dynamic %s %s %q\n", name, ss[0], strings.ToLower(ss[1]))
}
return
@@ -382,6 +387,7 @@ func dynimport(obj string) {
// Go symbols.
continue
}
+ checkImportSymName(s.Name)
fmt.Fprintf(stdout, "//go:cgo_import_dynamic %s %s %q\n", s.Name, s.Name, s.Library)
}
lib, err := f.ImportedLibraries()
@@ -397,6 +403,23 @@ func dynimport(obj string) {
fatalf("cannot parse %s as ELF, Mach-O, PE or XCOFF", obj)
}
+// checkImportSymName checks a symbol name we are going to emit as part
+// of a //go:cgo_import_dynamic pragma. These names come from object
+// files, so they may be corrupt. We are going to emit them unquoted,
+// so while they don't need to be valid symbol names (and in some cases,
+// involving symbol versions, they won't be) they must contain only
+// graphic characters and must not contain Go comments.
+func checkImportSymName(s string) {
+ for _, c := range s {
+ if !unicode.IsGraphic(c) || unicode.IsSpace(c) {
+ fatalf("dynamic symbol %q contains unsupported character", s)
+ }
+ }
+ if strings.Index(s, "//") >= 0 || strings.Index(s, "/*") >= 0 {
+ fatalf("dynamic symbol %q contains Go comment")
+ }
+}
+
// Construct a gcc struct matching the gc argument frame.
// Assumes that in gcc, char is 1 byte, short 2 bytes, int 4 bytes, long long 8 bytes.
// These assumptions are checked by the gccProlog.
diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go
index 3e21450deb..7da04547c2 100644
--- a/src/cmd/compile/internal/gc/ssa.go
+++ b/src/cmd/compile/internal/gc/ssa.go
@@ -4976,7 +4976,10 @@ func (s *state) storeTypeScalars(t *types.Type, left, right *ssa.Value, skip ski
case t.IsBoolean() || t.IsInteger() || t.IsFloat() || t.IsComplex():
s.store(t, left, right)
case t.IsPtrShaped():
- // no scalar fields.
+ if t.IsPtr() && t.Elem().NotInHeap() {
+ s.store(t, left, right) // see issue 42032
+ }
+ // otherwise, no scalar fields.
case t.IsString():
if skip&skipLen != 0 {
return
@@ -5020,6 +5023,9 @@ func (s *state) storeTypeScalars(t *types.Type, left, right *ssa.Value, skip ski
func (s *state) storeTypePtrs(t *types.Type, left, right *ssa.Value) {
switch {
case t.IsPtrShaped():
+ if t.IsPtr() && t.Elem().NotInHeap() {
+ break // see issue 42032
+ }
s.store(t, left, right)
case t.IsString():
ptr := s.newValue1(ssa.OpStringPtr, s.f.Config.Types.BytePtr, right)
diff --git a/src/cmd/compile/internal/gc/subr.go b/src/cmd/compile/internal/gc/subr.go
index c82eefb63d..9118405fb1 100644
--- a/src/cmd/compile/internal/gc/subr.go
+++ b/src/cmd/compile/internal/gc/subr.go
@@ -1876,8 +1876,10 @@ func isdirectiface(t *types.Type) bool {
}
switch t.Etype {
- case TPTR,
- TCHAN,
+ case TPTR:
+ // Pointers to notinheap types must be stored indirectly. See issue 42076.
+ return !t.Elem().NotInHeap()
+ case TCHAN,
TMAP,
TFUNC,
TUNSAFEPTR:
diff --git a/src/cmd/compile/internal/gc/typecheck.go b/src/cmd/compile/internal/gc/typecheck.go
index 98b52a506a..b187612b9b 100644
--- a/src/cmd/compile/internal/gc/typecheck.go
+++ b/src/cmd/compile/internal/gc/typecheck.go
@@ -2502,7 +2502,7 @@ func lookdot(n *Node, t *types.Type, dostrcmp int) *types.Field {
n.Left = nod(OADDR, n.Left, nil)
n.Left.SetImplicit(true)
n.Left = typecheck(n.Left, ctxType|ctxExpr)
- } else if tt.IsPtr() && !rcvr.IsPtr() && types.Identical(tt.Elem(), rcvr) {
+ } else if tt.IsPtr() && (!rcvr.IsPtr() || rcvr.IsPtr() && rcvr.Elem().NotInHeap()) && types.Identical(tt.Elem(), rcvr) {
n.Left = nod(ODEREF, n.Left, nil)
n.Left.SetImplicit(true)
n.Left = typecheck(n.Left, ctxType|ctxExpr)
diff --git a/src/cmd/go/internal/modfetch/coderepo_test.go b/src/cmd/go/internal/modfetch/coderepo_test.go
index f69c193b86..9a0cd7dba6 100644
--- a/src/cmd/go/internal/modfetch/coderepo_test.go
+++ b/src/cmd/go/internal/modfetch/coderepo_test.go
@@ -657,11 +657,6 @@ var codeRepoVersionsTests = []struct {
},
{
vcs: "git",
- path: "gopkg.in/russross/blackfriday.v2",
- versions: []string{"v2.0.0", "v2.0.1"},
- },
- {
- vcs: "git",
path: "gopkg.in/natefinch/lumberjack.v2",
versions: []string{"v2.0.0"},
},
diff --git a/src/cmd/go/internal/work/exec.go b/src/cmd/go/internal/work/exec.go
index 071c9d2db9..13d4c8cbb4 100644
--- a/src/cmd/go/internal/work/exec.go
+++ b/src/cmd/go/internal/work/exec.go
@@ -2711,6 +2711,66 @@ func (b *Builder) cgo(a *Action, cgoExe, objdir string, pcCFLAGS, pcLDFLAGS, cgo
noCompiler()
}
+ // Double check the //go:cgo_ldflag comments in the generated files.
+ // The compiler only permits such comments in files whose base name
+ // starts with "_cgo_". Make sure that the comments in those files
+ // are safe. This is a backstop against people somehow smuggling
+ // such a comment into a file generated by cgo.
+ if cfg.BuildToolchainName == "gc" && !cfg.BuildN {
+ var flags []string
+ for _, f := range outGo {
+ if !strings.HasPrefix(filepath.Base(f), "_cgo_") {
+ continue
+ }
+
+ src, err := ioutil.ReadFile(f)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ const cgoLdflag = "//go:cgo_ldflag"
+ idx := bytes.Index(src, []byte(cgoLdflag))
+ for idx >= 0 {
+ // We are looking at //go:cgo_ldflag.
+ // Find start of line.
+ start := bytes.LastIndex(src[:idx], []byte("\n"))
+ if start == -1 {
+ start = 0
+ }
+
+ // Find end of line.
+ end := bytes.Index(src[idx:], []byte("\n"))
+ if end == -1 {
+ end = len(src)
+ } else {
+ end += idx
+ }
+
+ // Check for first line comment in line.
+ // We don't worry about /* */ comments,
+ // which normally won't appear in files
+ // generated by cgo.
+ commentStart := bytes.Index(src[start:], []byte("//"))
+ commentStart += start
+ // If that line comment is //go:cgo_ldflag,
+ // it's a match.
+ if bytes.HasPrefix(src[commentStart:], []byte(cgoLdflag)) {
+ // Pull out the flag, and unquote it.
+ // This is what the compiler does.
+ flag := string(src[idx+len(cgoLdflag) : end])
+ flag = strings.TrimSpace(flag)
+ flag = strings.Trim(flag, `"`)
+ flags = append(flags, flag)
+ }
+ src = src[end:]
+ idx = bytes.Index(src, []byte(cgoLdflag))
+ }
+ }
+ if err := checkLinkerFlags("LDFLAGS", "go:cgo_ldflag", flags); err != nil {
+ return nil, nil, err
+ }
+ }
+
return outGo, outObj, nil
}
diff --git a/src/cmd/go/internal/work/security.go b/src/cmd/go/internal/work/security.go
index 3ee68ac1b4..0d9628241f 100644
--- a/src/cmd/go/internal/work/security.go
+++ b/src/cmd/go/internal/work/security.go
@@ -42,8 +42,8 @@ import (
var re = lazyregexp.New
var validCompilerFlags = []*lazyregexp.Regexp{
- re(`-D([A-Za-z_].*)`),
- re(`-U([A-Za-z_]*)`),
+ re(`-D([A-Za-z_][A-Za-z0-9_]*)(=[^@\-]*)?`),
+ re(`-U([A-Za-z_][A-Za-z0-9_]*)`),
re(`-F([^@\-].*)`),
re(`-I([^@\-].*)`),
re(`-O`),
@@ -51,8 +51,8 @@ var validCompilerFlags = []*lazyregexp.Regexp{
re(`-W`),
re(`-W([^@,]+)`), // -Wall but not -Wa,-foo.
re(`-Wa,-mbig-obj`),
- re(`-Wp,-D([A-Za-z_].*)`),
- re(`-Wp,-U([A-Za-z_]*)`),
+ re(`-Wp,-D([A-Za-z_][A-Za-z0-9_]*)(=[^@,\-]*)?`),
+ re(`-Wp,-U([A-Za-z_][A-Za-z0-9_]*)`),
re(`-ansi`),
re(`-f(no-)?asynchronous-unwind-tables`),
re(`-f(no-)?blocks`),
diff --git a/src/cmd/go/internal/work/security_test.go b/src/cmd/go/internal/work/security_test.go
index 11e74f29c6..aec9789185 100644
--- a/src/cmd/go/internal/work/security_test.go
+++ b/src/cmd/go/internal/work/security_test.go
@@ -13,6 +13,7 @@ var goodCompilerFlags = [][]string{
{"-DFOO"},
{"-Dfoo=bar"},
{"-Ufoo"},
+ {"-Ufoo1"},
{"-F/Qt"},
{"-I/"},
{"-I/etc/passwd"},
@@ -24,6 +25,8 @@ var goodCompilerFlags = [][]string{
{"-Wall"},
{"-Wp,-Dfoo=bar"},
{"-Wp,-Ufoo"},
+ {"-Wp,-Dfoo1"},
+ {"-Wp,-Ufoo1"},
{"-fobjc-arc"},
{"-fno-objc-arc"},
{"-fomit-frame-pointer"},
@@ -78,6 +81,8 @@ var badCompilerFlags = [][]string{
{"-O@1"},
{"-Wa,-foo"},
{"-W@foo"},
+ {"-Wp,-DX,-D@X"},
+ {"-Wp,-UX,-U@X"},
{"-g@gdb"},
{"-g-gdb"},
{"-march=@dawn"},
diff --git a/src/cmd/internal/obj/dwarf.go b/src/cmd/internal/obj/dwarf.go
index 4118c6442c..cd1d5b8687 100644
--- a/src/cmd/internal/obj/dwarf.go
+++ b/src/cmd/internal/obj/dwarf.go
@@ -44,14 +44,12 @@ func (ctxt *Link) generateDebugLinesSymbol(s, lines *LSym) {
}
}
- // Set up the debug_lines state machine.
- // NB: This state machine is reset to this state when we've finished
- // generating the line table. See below.
- // TODO: Once delve can support multiple DW_LNS_end_statements, we don't have
- // to do this.
+ // Set up the debug_lines state machine to the default values
+ // we expect at the start of a new sequence.
stmt := true
line := int64(1)
pc := s.Func.Text.Pc
+ var lastpc int64 // last PC written to line table, not last PC in func
name := ""
prologue, wrotePrologue := false, false
// Walk the progs, generating the DWARF table.
@@ -86,30 +84,32 @@ func (ctxt *Link) generateDebugLinesSymbol(s, lines *LSym) {
if line != int64(newLine) || wrote {
pcdelta := p.Pc - pc
+ lastpc = p.Pc
putpclcdelta(ctxt, dctxt, lines, uint64(pcdelta), int64(newLine)-line)
line, pc = int64(newLine), p.Pc
}
}
- // Because these symbols will be concatenated together by the linker, we need
- // to reset the state machine that controls the debug symbols. The fields in
- // the state machine that need to be reset are:
- // file = 1
- // line = 1
- // column = 0
- // stmt = set in header, we assume true
- // basic_block = false
- // Careful readers of the DWARF specification will note that we don't reset
- // the address of the state machine -- but this will happen at the beginning
- // of the NEXT block of opcodes.
- dctxt.AddUint8(lines, dwarf.DW_LNS_set_file)
+ // Because these symbols will be concatenated together by the
+ // linker, we need to reset the state machine that controls the
+ // debug symbols. Do this using an end-of-sequence operator.
+ //
+ // Note: at one point in time, Delve did not support multiple end
+ // sequence ops within a compilation unit (bug for this:
+ // https://github.com/go-delve/delve/issues/1694), however the bug
+ // has since been fixed (Oct 2019).
+ //
+ // Issue 38192: the DWARF standard specifies that when you issue
+ // an end-sequence op, the PC value should be one past the last
+ // text address in the translation unit, so apply a delta to the
+ // text address before the end sequence op. If this isn't done,
+ // GDB will assign a line number of zero the last row in the line
+ // table, which we don't want.
+ lastlen := uint64(s.Size - (lastpc - s.Func.Text.Pc))
+ putpclcdelta(ctxt, dctxt, lines, lastlen, 0)
+ dctxt.AddUint8(lines, 0) // start extended opcode
dwarf.Uleb128put(dctxt, lines, 1)
- dctxt.AddUint8(lines, dwarf.DW_LNS_advance_line)
- dwarf.Sleb128put(dctxt, lines, int64(1-line))
- if !stmt {
- dctxt.AddUint8(lines, dwarf.DW_LNS_negate_stmt)
- }
- dctxt.AddUint8(lines, dwarf.DW_LNS_copy)
+ dctxt.AddUint8(lines, dwarf.DW_LNE_end_sequence)
}
func putpclcdelta(linkctxt *Link, dctxt dwCtxt, s *LSym, deltaPC uint64, deltaLC int64) {
diff --git a/src/cmd/link/internal/ld/dwarf.go b/src/cmd/link/internal/ld/dwarf.go
index 8df03d74f1..6d330061ab 100644
--- a/src/cmd/link/internal/ld/dwarf.go
+++ b/src/cmd/link/internal/ld/dwarf.go
@@ -1264,22 +1264,6 @@ func (d *dwctxt2) writelines(unit *sym.CompilationUnit, ls loader.Sym) {
}
}
- // Issue 38192: the DWARF standard specifies that when you issue
- // an end-sequence op, the PC value should be one past the last
- // text address in the translation unit, so apply a delta to the
- // text address before the end sequence op. If this isn't done,
- // GDB will assign a line number of zero the last row in the line
- // table, which we don't want. The 1 + ptrsize amount is somewhat
- // arbitrary, this is chosen to be consistent with the way LLVM
- // emits its end sequence ops.
- lsu.AddUint8(dwarf.DW_LNS_advance_pc)
- dwarf.Uleb128put(d, lsDwsym, int64(1+d.arch.PtrSize))
-
- // Emit an end-sequence at the end of the unit.
- lsu.AddUint8(0) // start extended opcode
- dwarf.Uleb128put(d, lsDwsym, 1)
- lsu.AddUint8(dwarf.DW_LNE_end_sequence)
-
if d.linkctxt.HeadType == objabi.Haix {
saveDwsectCUSize(".debug_line", unit.Lib.Pkg, uint64(lsu.Size()-unitLengthOffset))
}
diff --git a/src/cmd/link/internal/ld/dwarf_test.go b/src/cmd/link/internal/ld/dwarf_test.go
index fb9c45b07d..f3dd53792a 100644
--- a/src/cmd/link/internal/ld/dwarf_test.go
+++ b/src/cmd/link/internal/ld/dwarf_test.go
@@ -1479,3 +1479,122 @@ func TestIssue38192(t *testing.T) {
t.Logf("row %d: A=%x F=%s L=%d\n", i, r.Address, r.File.Name, r.Line)
}
}
+
+func TestIssue39757(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ if runtime.GOOS == "plan9" {
+ t.Skip("skipping on plan9; no DWARF symbol table in executables")
+ }
+
+ // In this bug the DWARF line table contents for the last couple of
+ // instructions in a function were incorrect (bad file/line). This
+ // test verifies that all of the line table rows for a function
+ // of interest have the same file (no "autogenerated").
+ //
+ // Note: the function in this test was written with an eye towards
+ // ensuring that there are no inlined routines from other packages
+ // (which could introduce other source files into the DWARF); it's
+ // possible that at some point things could evolve in the
+ // compiler/runtime in ways that aren't happening now, so this
+ // might be something to check for if it does start failing.
+
+ tmpdir, err := ioutil.TempDir("", "TestIssue38192")
+ if err != nil {
+ t.Fatalf("could not create directory: %v", err)
+ }
+ defer os.RemoveAll(tmpdir)
+ wd, err := os.Getwd()
+ if err != nil {
+ t.Fatalf("where am I? %v", err)
+ }
+ pdir := filepath.Join(wd, "testdata", "issue39757")
+ f := gobuildTestdata(t, tmpdir, pdir, DefaultOpt)
+
+ syms, err := f.Symbols()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ var addr uint64
+ for _, sym := range syms {
+ if sym.Name == "main.main" {
+ addr = sym.Addr
+ break
+ }
+ }
+ if addr == 0 {
+ t.Fatal("cannot find main.main in symbols")
+ }
+
+ // Open the resulting binary and examine the DWARF it contains.
+ // Look for the function of interest ("main.main")
+ // and verify that all line table entries show the same source
+ // file.
+ dw, err := f.DWARF()
+ if err != nil {
+ t.Fatalf("error parsing DWARF: %v", err)
+ }
+ rdr := dw.Reader()
+ ex := examiner{}
+ if err := ex.populate(rdr); err != nil {
+ t.Fatalf("error reading DWARF: %v", err)
+ }
+
+ // Locate the main.main DIE
+ mains := ex.Named("main.main")
+ if len(mains) == 0 {
+ t.Fatalf("unable to locate DIE for main.main")
+ }
+ if len(mains) != 1 {
+ t.Fatalf("more than one main.main DIE")
+ }
+ maindie := mains[0]
+
+ // Collect the start/end PC for main.main
+ lowpc := maindie.Val(dwarf.AttrLowpc).(uint64)
+ highpc := maindie.Val(dwarf.AttrHighpc).(uint64)
+
+ // Now read the line table for the 'main' compilation unit.
+ mainIdx := ex.idxFromOffset(maindie.Offset)
+ cuentry := ex.Parent(mainIdx)
+ if cuentry == nil {
+ t.Fatalf("main.main DIE appears orphaned")
+ }
+ lnrdr, lerr := dw.LineReader(cuentry)
+ if lerr != nil {
+ t.Fatalf("error creating DWARF line reader: %v", err)
+ }
+ if lnrdr == nil {
+ t.Fatalf("no line table for main.main compilation unit")
+ }
+ rows := []dwarf.LineEntry{}
+ mainrows := 0
+ var lne dwarf.LineEntry
+ for {
+ err := lnrdr.Next(&lne)
+ if err == io.EOF {
+ break
+ }
+ rows = append(rows, lne)
+ if err != nil {
+ t.Fatalf("error reading next DWARF line: %v", err)
+ }
+ if lne.Address < lowpc || lne.Address > highpc {
+ continue
+ }
+ if !strings.HasSuffix(lne.File.Name, "issue39757main.go") {
+ t.Errorf("found row with file=%s (not issue39757main.go)", lne.File.Name)
+ }
+ mainrows++
+ }
+ f.Close()
+
+ // Make sure we saw a few rows.
+ if mainrows < 3 {
+ t.Errorf("not enough line table rows for main.main (got %d, wanted > 3", mainrows)
+ for i, r := range rows {
+ t.Logf("row %d: A=%x F=%s L=%d\n", i, r.Address, r.File.Name, r.Line)
+ }
+ }
+}
diff --git a/src/cmd/link/internal/ld/testdata/issue39757/issue39757main.go b/src/cmd/link/internal/ld/testdata/issue39757/issue39757main.go
new file mode 100644
index 0000000000..76e0ea1b08
--- /dev/null
+++ b/src/cmd/link/internal/ld/testdata/issue39757/issue39757main.go
@@ -0,0 +1,15 @@
+// Copyright 2020 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.
+
+package main
+
+var G int
+
+func main() {
+ if G != 101 {
+ println("not 101")
+ } else {
+ println("well now that's interesting")
+ }
+}