cmd/{compile,link}: fix problem with DWARF end_sequence ops

During DWARF line table emission in the linker, prior to issuing a
DW_LNE_end_sequence op to mark the end of the line table for a
compilation unit, advance the PC to produce an address beyond the last
text address in the unit (this is required by the DWARF standard).
Because of the way that GDB interprets end-sequence ops, we were
effectively losing the last row in the line table for each unit, which
degraded the debugging experience.

This problem has been around for a while, but has surfaced recently
due to changes in line table generation. Prior to Go 1.14, the DWARF
line table was emitted entirely in the linker, and a single monolithic
line table was created for each Go package (including functions from
assembly). In 1.14 we moved to having the compiler emit line table
fragments for each function, and having the linker stitch together the
fragments. As part of this change we moved to a model in which each
"go tool compile/asm" output has its own DWARF line table instance,
meaning that there are many more "end sequence" ops, which made the
problem more visible.

Fixes #38192.

Change-Id: Ic29e2f6e0ac952360c81fcba5268ad70b2b44184
Reviewed-on: https://go-review.googlesource.com/c/go/+/235739
Run-TryBot: Than McIntosh <thanm@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Austin Clements <austin@google.com>
Reviewed-by: Jeremy Faller <jeremy@golang.org>
diff --git a/src/cmd/link/internal/ld/dwarf.go b/src/cmd/link/internal/ld/dwarf.go
index 95e6b12..8df03d7 100644
--- a/src/cmd/link/internal/ld/dwarf.go
+++ b/src/cmd/link/internal/ld/dwarf.go
@@ -1264,6 +1264,18 @@
 		}
 	}
 
+	// 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)