lldb has a problem parsing executables built with the Go compiler. For example here's the error message when I set a breakpoint:
(lldb) breakpoint set --name main.main
warning: .debug_arange set has bad header at 0x00000000: length=0x03011101, version=0x1308, cu_offset=0x1201110b, addr_size=1, seg_size=16
Breakpoint 1: where = sleep`main.main, address = 0x0000000000002000
(lldb)
It turns out that lldb is using 0 for the offset in the file of the .debug_aranges section from the beginning of the DWARF section. So lldb is trying to interpret the dwarf header as the .debug_aranges header.
I've traced it down to the SymbolFileDWARF::GetCachedSectionData function in SymbolFileDWARF.cpp, line 749:
data.SetData(m_dwarf_data, section_sp->GetOffset (), section_sp->GetFileSize());
This call uses m_dwarf_data which is a memory mapped section of the Dwarf data. It expects GetOffset() to be the number of bytes in the file from the beginning of the DWARF segment to the beginning of the .debug_aranges section. But that's not what Section::GetOffset returns. It returns the number of bytes from the beginning of the DWARF section in memory to the .debug_aranges section.
The Go compiler sets all the Dwarf virtual memory addresses to zero, as shown by a piece of the output from otool -l below. So the offset of the virtual memory address of the .debug_aranges section from the beginning of the DWARF segment is 0. That's why lldb was trying to use the DWARF header for the .debug_aranges header.
otool -l output: Load command 4 cmd LC_SEGMENT_64 cmdsize 632 segname __DWARF vmaddr 0x0000000000000000 vmsize 0x0000000000000000 fileoff 1699840 filesize 535094 maxprot 0x00000000 initprot 0x00000000 nsects 7 flags 0x0 Section sectname __debug_abbrev segname __DWARF addr 0x0000000000000000 size 0x00000000000000d3 offset 1699840 align 2^0 (1) reloff 0 nreloc 0 flags 0x00000000 reserved1 0 reserved2 0 Section sectname __debug_line segname __DWARF addr 0x0000000000000000 size 0x000000000000d6f1 offset 1700051 align 2^0 (1) reloff 0 nreloc 0 flags 0x00000000 reserved1 0 reserved2 0 <snip __debug_frame, __debug_info, __debug_pubnames and __debug_pubtypes sections> Section sectname __debug_aranges segname __DWARF addr 0x0000000000000000 size 0x0000000000000030 offset 2234886 align 2^0 (1) reloff 0 nreloc 0 flags 0x00000000 reserved1 0 reserved2 0
My suggested patch explicitly calculates the difference between the byte offset of the parent and the child section in the file.
// See if we memory mapped the DWARF segment? if (m_dwarf_data.GetByteSize()) { // Get the offset of this section from the beginning of the file. lldb::offset_t offset = section_sp->GetFileOffset(); SectionSP parent_sp (section_sp->GetParent()); // If this section has a parent then we need the offset in the file from // the beginning of the parent section. if (parent_sp) { offset = offset - parent_sp->GetFileOffset(); } data.SetData(m_dwarf_data, offset, section_sp->GetFileSize()); } else {
A possible criticism is that the code doesn't check to make sure the child's GetFileOffset() is larger than the parent's GetFileOffset(). Since offset is an unsigned number, if the parent address is greater than the child address then offset would be a very large number. SetData will clip the size of the data if it is out of range of what's available.
Not checking at this level is consistent with other parts of lldb. For example line 1619 of ObjectFileMachO.cpp has this entry:
sect64.addr - segment_sp->GetFileAddress(),
This is subtracting the in-memory address of the parent from the in-memory address of the section. No checking is done to make sure the parent's address is < child's address.
I also suggest adding the following comment to declaration of Section::GetOffset to make it clear the method is the offset in memory, not in the file:
// The offset in memory of this section from its parent. If // there is no parent then this method returns the offset from // the beginning of the program in memory. lldb::addr_t GetOffset () const;
The next question is whether GetOffset is being used incorrectly anyplace else in the code. I changed the declaration and definition of GetOffset in Section.h and Section.cpp to include an int argument so the compiler would flag other uses.
lldb::addr_t GetOffset (int noop) const;
Besides the use in SymbolFileDWARF.cpp this change broke the build in the following 2 methods:
Section::GetLoadBaseAddress Section::ResolveContainedAddress
Based on their names, these methods want to use GetOffset to refer to the location of a section in memory, not in a file. That's the correct use of GetOffset.
I've run dotest.py on both the original and changed versions. Both report failures and unexpected successes, but they don't seem to be due to my change. For example
<with-change>/test/2014-09-28-08_56_00/Failure-x86_64-clang-TestCallWithTimeout.ExprCommandWithTimeoutsTestCase.test_with_dwarf.log File "/Users/sm10879/repos/lldb_3/test/expression_command/timeout/TestCallWithTimeout.py", line 76, in call_function self.assertTrue (return_value == lldb.eReturnStatusFailed) <without-change>/test/2014-09-28-09_22_46/Failure-x86_64-clang-TestCallWithTimeout.ExprCommandWithTimeoutsTestCase.test_with_dwarf.log File "/Users/sm10879/repos/lldb_3/test/expression_command/timeout/TestCallWithTimeout.py", line 69, in call_function self.assertTrue (value.GetError().Success() == False)
Unfortunately this fix alone isn't enough to allow lldb to be used with Go code. The line numbers seem to be off by one in the DWARF information produced by the Go compiler. I'll follow up with the Go team on that.
I'm using Mac OS X 10.9.5.
Many thanks to Gary Clayton for taking the time to help get me started with using lldb on Go programs at WWDC.
Section.h uses the terminology "absolute file virtual address of this section if m_parent == NULL. offset from parent file virtual address if m_parent != NULL". I'd use similar wording here, maybe
The absolute file virtual address of this section if it has no parent Section. If there is a parent Section, this is the offset from the parent Section's file virtual address."