diff --git a/src/cmd/link/internal/ld/dwarf_test.go b/src/cmd/link/internal/ld/dwarf_test.go index a1c8496eeae..fb9c45b07d0 100644 --- a/src/cmd/link/internal/ld/dwarf_test.go +++ b/src/cmd/link/internal/ld/dwarf_test.go @@ -1368,3 +1368,114 @@ func main() { rdr.SkipChildren() } } + +func TestIssue38192(t *testing.T) { + testenv.MustHaveGoBuild(t) + + if runtime.GOOS == "plan9" { + t.Skip("skipping on plan9; no DWARF symbol table in executables") + } + + // Build a test program that contains a translation unit whose + // text (from am assembly source) contains only a single instruction. + 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", "issue38192") + f := gobuildTestdata(t, tmpdir, pdir, DefaultOpt) + + // Open the resulting binary and examine the DWARF it contains. + // Look for the function of interest ("main.singleInstruction") + // and verify that the line table has an entry not just for the + // single instruction but also a dummy instruction following it, + // so as to test that whoever is emitting the DWARF doesn't + // emit an end-sequence op immediately after the last instruction + // in the translation unit. + // + // NB: another way to write this test would have been to run the + // resulting executable under GDB, set a breakpoint in + // "main.singleInstruction", then verify that GDB displays the + // correct line/file information. Given the headache and flakiness + // associated with GDB-based tests these days, a direct read of + // the line table seems more desirable. + rows := []dwarf.LineEntry{} + dw, err := f.DWARF() + if err != nil { + t.Fatalf("error parsing DWARF: %v", err) + } + rdr := dw.Reader() + for { + e, err := rdr.Next() + if err != nil { + t.Fatalf("error reading DWARF: %v", err) + } + if e == nil { + break + } + if e.Tag != dwarf.TagCompileUnit { + continue + } + // NB: there can be multiple compile units named "main". + name := e.Val(dwarf.AttrName).(string) + if name != "main" { + continue + } + lnrdr, err := dw.LineReader(e) + if err != nil { + t.Fatalf("error creating DWARF line reader: %v", err) + } + if lnrdr != nil { + var lne dwarf.LineEntry + for { + err := lnrdr.Next(&lne) + if err == io.EOF { + break + } + if err != nil { + t.Fatalf("error reading next DWARF line: %v", err) + } + if !strings.HasSuffix(lne.File.Name, "ld/testdata/issue38192/oneline.s") { + continue + } + rows = append(rows, lne) + } + } + rdr.SkipChildren() + } + f.Close() + + // Make sure that: + // - main.singleInstruction appears in the line table + // - more than one PC value appears the line table for + // that compilation unit. + // - at least one row has the correct line number (8) + pcs := make(map[uint64]bool) + line8seen := false + for _, r := range rows { + pcs[r.Address] = true + if r.Line == 8 { + line8seen = true + } + } + failed := false + if len(pcs) < 2 { + failed = true + t.Errorf("not enough line table rows for main.singleInstruction (got %d, wanted > 1", len(pcs)) + } + if !line8seen { + failed = true + t.Errorf("line table does not contain correct line for main.singleInstruction") + } + if !failed { + return + } + 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/issue38192/main.go b/src/cmd/link/internal/ld/testdata/issue38192/main.go new file mode 100644 index 00000000000..3b7df60f156 --- /dev/null +++ b/src/cmd/link/internal/ld/testdata/issue38192/main.go @@ -0,0 +1,11 @@ +// 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 + +func singleInstruction() + +func main() { + singleInstruction() +} diff --git a/src/cmd/link/internal/ld/testdata/issue38192/oneline.s b/src/cmd/link/internal/ld/testdata/issue38192/oneline.s new file mode 100644 index 00000000000..f5290d7f2ed --- /dev/null +++ b/src/cmd/link/internal/ld/testdata/issue38192/oneline.s @@ -0,0 +1,8 @@ +// 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. + +#include "textflag.h" + +TEXT ·singleInstruction(SB),NOSPLIT,$0 + RET