[Lldb-commits] [PATCH] lldb incorrectly parses DWARF information from Go binary

Stephen McDonald si1428 at hotmail.com
Wed Oct 1 18:05:45 PDT 2014


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.
Index: source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
===================================================================
--- source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp	(revision 218650)
+++ source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp	(working copy)
@@ -746,7 +746,16 @@
                 // See if we memory mapped the DWARF segment?
                 if (m_dwarf_data.GetByteSize())
                 {
-                    data.SetData(m_dwarf_data, section_sp->GetOffset (), section_sp->GetFileSize());
+                    // 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:
Index: include/lldb/Core/Section.h
===================================================================
--- include/lldb/Core/Section.h	(revision 218650)
+++ include/lldb/Core/Section.h	(working copy)
@@ -200,6 +200,9 @@
     bool
     SetFileAddress (lldb::addr_t file_addr);
 
+    // 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.

http://reviews.llvm.org/D5568

Files:
  include/lldb/Core/Section.h
  source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
-------------- next part --------------
A non-text attachment was scrubbed...
Name: D5568.14302.patch
Type: text/x-patch
Size: 1645 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/lldb-commits/attachments/20141002/ff63189b/attachment.bin>


More information about the lldb-commits mailing list