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)
+ }
+}