Join GitHub today
GitHub is home to over 50 million developers working together to host and review code, manage projects, and build software together.Sign up
cmd/link, debug/dwarf: missing debug information using gdb set breakpoint on Entry point #38192
What version of Go are you using (
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.
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".