diff options
Diffstat (limited to 'src/cmd')
-rw-r--r-- | src/cmd/cgo/out.go | 23 | ||||
-rw-r--r-- | src/cmd/compile/internal/gc/ssa.go | 8 | ||||
-rw-r--r-- | src/cmd/compile/internal/gc/subr.go | 6 | ||||
-rw-r--r-- | src/cmd/compile/internal/gc/typecheck.go | 2 | ||||
-rw-r--r-- | src/cmd/go/internal/modfetch/coderepo_test.go | 5 | ||||
-rw-r--r-- | src/cmd/go/internal/work/exec.go | 60 | ||||
-rw-r--r-- | src/cmd/go/internal/work/security.go | 8 | ||||
-rw-r--r-- | src/cmd/go/internal/work/security_test.go | 5 | ||||
-rw-r--r-- | src/cmd/internal/obj/dwarf.go | 46 | ||||
-rw-r--r-- | src/cmd/link/internal/ld/dwarf.go | 16 | ||||
-rw-r--r-- | src/cmd/link/internal/ld/dwarf_test.go | 119 | ||||
-rw-r--r-- | src/cmd/link/internal/ld/testdata/issue39757/issue39757main.go | 15 |
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") + } +} |