New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
cmd/link, debug/dwarf: missing debug information using gdb set breakpoint on Entry point #38192
Comments
I looked at this a little. There were a number of changes in DWARF generation between 1.13 and 1.14, but I think the one that seems to be most relevant is that chunks of the line table are emitted directly by the compiler as opposed to being synthesized in the linker. As part of moving more of line table generation into the compiler, things were changed so that each Go object file is given its own DWARF compilation unit, as opposed a single compilation unit per package. So if you have a package ABC with a couple of *.go files and one *.s file, at the DWARF level you'll see two compilation units, one for the Go code and one for the assembly. In 1.14 the routine in question is just a blob within the giant runtime compilation unit. Here's what the relevant fragment from line table looks like (this is objdump --dwarf=rawline):
The entrypoint in question (_rt0_amd64_linux) is at address 0x44d720 above. Now here's what things look like in 1.14:
In the dump above (_rt0_amd64_linux) is at address 0x4558f0. What's interesting here is that the first section is ok (it sets the file correctly to rt0_linux_amd64.s) and the line to 8, but then it seems to come along and apply a second line number of 1 to the same location. Looking at things in the decodedline dump I see:
Note the "view" -- I am not really sure what objdump is trying to say here (since "view" is not a real register in the DWARF line table AFAIK) but it doesn't look quite right to have the location be both line 8 and line 1. I am speculating that this is what's confusing GDB. CC'ing @jeremyfaller , since he did the work there. |
Oops, got the wrong github user when I pinged before. Trying again: @jeremyfaller |
thanks, @thanm. I'll take a look. |
@thanm @jeremyfaller shall we move this change perhaps to Go1.16 instead? |
I think @jeremyfaller is tied up with other things things this week. I'll see if I can figure out a fix. Stay tuned. |
I spent some time working on this bug. It is an interesting puzzle (seems like this happens a lot with DWARF bugs). When I first looked at this problem, I assumed that the confusion on the part of GDB was due to this code: which is one of the main places where the DWARF line table contents are different between 1.13 and 1.14, in addition to the finer granularity of compile units. After spending some time debugging and experimenting, I'm not sure if my original theory holds water. I think the problem looks more due to a quirk in how GDB is reading the line table and how it handles the end_sequence op. The translation unit in question contains (after dead code elimination) a single function (this is from an assembly source):
The line table fragment from this looks like:
So to summarize, there are two rows (one from the special opcode at 0x912 and then next from the copy at 0x918). I ran the program under GDB using a hidden maintainence command that traces the GDB line table reader:
Note the two "Record line" trace lines: these correspond to the point where the GDB line table reader takes the current contents of the line table registers (according to its decoder) and copies them into its own internal representation of the line table. The first one looks good (line 8, all fine here), but then there is the second: Recording line 0, file rt0_linux_amd64.s, address 0x464f00 This "record line" operation is the one being triggered by the end_sequence op, and it effectively overwrites the original line of 8 (for PC 0x464f00) with a line of zero. The 0-valued line seems to be the thing doing the damage here. I did some more experiments. First, I changed the assembly source to:
This gets rid of the problem completely: when the end_sequence operator is encountered. I also spent some time looking at how various C compilers handle this same situation. Here's a small C routine:
This code gets compiled down to a single instruction ("ret"). However if you play the same game with GDB for this function, you get a different story. Example:
Note what the line table reader is telling us. Even though 'empty' is only a single instruction long, the line table for it advances the PC past that instruction to the next instruction before issuing the end_sequence. So when the end_sequence triggers recording of a row, the "line 0" is applied to the next instruction, not the inst in "empty". |
Change https://golang.org/cl/235739 mentions this issue: |
Nice work and investigation, thank you @thanm! |
Change https://golang.org/cl/235917 mentions this issue: |
Add a test case for an issue with how Go emits DWARF line tables, specifically relating to the line table "end sequence" operator. Updates #38192. Change-Id: I878b262e6ca6c550c0e460c3d5a1969ac4a2c31b Reviewed-on: https://go-review.googlesource.com/c/go/+/235917 Run-TryBot: Than McIntosh <thanm@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Austin Clements <austin@google.com>
Change https://golang.org/cl/258422 mentions this issue: |
During Go 1.15 development, a fix was added to the toolchain for issue information. The 1.15 line tables were slightly malformed in the way that they used the DWARF "end sequence" operator, resulting in incorrect line table info for the final instruction in the final function of a compilation unit. This problem was fixed in https://golang.org/cl/235739, which made it into Go 1.15. It now appears that while the fix works OK for linux, in certain cases it causes issues with the Darwin linker (the "address not in any section" ld64 error reported in issue #40974). During Go 1.16 development, the fix in https://golang.org/cl/235739 was revised so as to fix another related problem (described in issue #39757); the newer fix does not trigger the problem in the Darwin linker however. This CL back-ports the changes in https://golang.org/cl/239286 to the 1.15 release branch, so as to fix the Darwin linker error. Updates #38192. Updates #39757. Fixes #40974. Change-Id: I9350fec4503cd3a76b97aaea0d8aed1511662e29 Reviewed-on: https://go-review.googlesource.com/c/go/+/258422 Run-TryBot: Than McIntosh <thanm@google.com> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Austin Clements <austin@google.com> Reviewed-by: Jeremy Faller <jeremy@golang.org> Reviewed-by: Cherry Zhang <cherryyz@google.com> Trust: Than McIntosh <thanm@google.com>
What version of Go are you using (
go version
)?Does this issue reproduce with the latest release?
Yes
What operating system and processor architecture are you using (
go env
)?go env
OutputWhat did you do?
package main
func main() {
println("hello world")
}
go build -gcflags "all=-N -l" demo.go
gdb ./demo
(gdb) info files
Symbols from "/dev/shm/ex/demo".
Local exec file:
`/dev/shm/ex/demo', file type elf64-x86-64.
Entry point: 0x4542a0
...
What did you expect to see?
(gdb) b *0x4542a0
Breakpoint 1 at 0x4542a0: file /dev/shm/ex/go/src/runtime/rt0_linux_amd64.s, line 8.
What did you see instead?
(gdb) b *0x45cdd0
Breakpoint 1 at 0x45cdd0
The file and line information is missing, I tested go1.13.9,it's fine, only go1.14/go1.14.1 has this problem
The text was updated successfully, but these errors were encountered: