[lldb][ELF] Read symbols from .gnu_debugdata sect.

Summary:
If the .symtab section is stripped from the binary it might be that
there's a .gnu_debugdata section which contains a smaller .symtab in
order to provide enough information to create a backtrace with function
names or to set and hit a breakpoint on a function name.

This change looks for a .gnu_debugdata section in the ELF object file.
The .gnu_debugdata section contains a xz-compressed ELF file with a
.symtab section inside. Symbols from that compressed .symtab section
are merged with the main object file's .dynsym symbols (if any).
In addition we always load the .dynsym even if there's a .symtab
section.

For example, the Fedora and RHEL operating systems strip their binaries
but keep a .gnu_debugdata section. While gdb already can read this
section, LLDB until this patch couldn't. To test this patch on a
Fedora or RHEL operating system, try to set a breakpoint on the "help"
symbol in the "zip" binary. Before this patch, only GDB can set this
breakpoint; now LLDB also can do so without installing extra debug
symbols:

    lldb /usr/bin/zip -b -o "b help" -o "r" -o "bt" -- -h

The above line runs LLDB in batch mode and on the "/usr/bin/zip -h"
target:

    (lldb) target create "/usr/bin/zip"
    Current executable set to '/usr/bin/zip' (x86_64).
    (lldb) settings set -- target.run-args  "-h"

Before the program starts, we set a breakpoint on the "help" symbol:

    (lldb) b help
    Breakpoint 1: where = zip`help, address = 0x00000000004093b0

Once the program is run and has hit the breakpoint we ask for a
backtrace:

    (lldb) r
    Process 10073 stopped
    * thread #1, name = 'zip', stop reason = breakpoint 1.1
        frame #0: 0x00000000004093b0 zip`help
    zip`help:
    ->  0x4093b0 <+0>:  pushq  %r12
        0x4093b2 <+2>:  movq   0x2af5f(%rip), %rsi       ;  + 4056
        0x4093b9 <+9>:  movl   $0x1, %edi
        0x4093be <+14>: xorl   %eax, %eax

    Process 10073 launched: '/usr/bin/zip' (x86_64)
    (lldb) bt
    * thread #1, name = 'zip', stop reason = breakpoint 1.1
      * frame #0: 0x00000000004093b0 zip`help
        frame #1: 0x0000000000403970 zip`main + 3248
        frame #2: 0x00007ffff7d8bf33 libc.so.6`__libc_start_main + 243
        frame #3: 0x0000000000408cee zip`_start + 46

In order to support the .gnu_debugdata section, one has to have LZMA
development headers installed. The CMake section, that controls this
part looks for the LZMA headers and enables .gnu_debugdata support by
default if they are found; otherwise or if explicitly requested, the
minidebuginfo support is disabled.

GDB supports the "mini debuginfo" section .gnu_debugdata since v7.6
(2013).

Reviewers: espindola, labath, jankratochvil, alexshap

Reviewed By: labath

Subscribers: rnkovacs, wuzish, shafik, emaste, mgorny, arichardson, hiraditya, MaskRay, lldb-commits

Tags: #lldb, #llvm

Differential Revision: https://reviews.llvm.org/D66791

llvm-svn: 373891
This commit is contained in:
Konrad Kleine
2019-10-07 10:32:16 +00:00
parent 5ce8c39149
commit 2c082b4827
15 changed files with 481 additions and 8 deletions

View File

@@ -18,6 +18,7 @@
#include "lldb/Core/PluginManager.h"
#include "lldb/Core/Section.h"
#include "lldb/Host/FileSystem.h"
#include "lldb/Host/LZMA.h"
#include "lldb/Symbol/DWARFCallFrameInfo.h"
#include "lldb/Symbol/SymbolContext.h"
#include "lldb/Target/SectionLoadList.h"
@@ -1842,6 +1843,70 @@ void ObjectFileELF::CreateSections(SectionList &unified_section_list) {
// unified section list.
if (GetType() != eTypeDebugInfo)
unified_section_list = *m_sections_up;
// If there's a .gnu_debugdata section, we'll try to read the .symtab that's
// embedded in there and replace the one in the original object file (if any).
// If there's none in the orignal object file, we add it to it.
if (auto gdd_obj_file = GetGnuDebugDataObjectFile()) {
if (auto gdd_objfile_section_list = gdd_obj_file->GetSectionList()) {
if (SectionSP symtab_section_sp =
gdd_objfile_section_list->FindSectionByType(
eSectionTypeELFSymbolTable, true)) {
SectionSP module_section_sp = unified_section_list.FindSectionByType(
eSectionTypeELFSymbolTable, true);
if (module_section_sp)
unified_section_list.ReplaceSection(module_section_sp->GetID(),
symtab_section_sp);
else
unified_section_list.AddSection(symtab_section_sp);
}
}
}
}
std::shared_ptr<ObjectFileELF> ObjectFileELF::GetGnuDebugDataObjectFile() {
if (m_gnu_debug_data_object_file != nullptr)
return m_gnu_debug_data_object_file;
SectionSP section =
GetSectionList()->FindSectionByName(ConstString(".gnu_debugdata"));
if (!section)
return nullptr;
if (!lldb_private::lzma::isAvailable()) {
GetModule()->ReportWarning(
"No LZMA support found for reading .gnu_debugdata section");
return nullptr;
}
// Uncompress the data
DataExtractor data;
section->GetSectionData(data);
llvm::SmallVector<uint8_t, 0> uncompressedData;
auto err = lldb_private::lzma::uncompress(data.GetData(), uncompressedData);
if (err) {
GetModule()->ReportWarning(
"An error occurred while decompression the section %s: %s",
section->GetName().AsCString(), llvm::toString(std::move(err)).c_str());
return nullptr;
}
// Construct ObjectFileELF object from decompressed buffer
DataBufferSP gdd_data_buf(
new DataBufferHeap(uncompressedData.data(), uncompressedData.size()));
auto fspec = GetFileSpec().CopyByAppendingPathComponent(
llvm::StringRef("gnu_debugdata"));
m_gnu_debug_data_object_file.reset(new ObjectFileELF(
GetModule(), gdd_data_buf, 0, &fspec, 0, gdd_data_buf->GetByteSize()));
// This line is essential; otherwise a breakpoint can be set but not hit.
m_gnu_debug_data_object_file->SetType(ObjectFile::eTypeDebugInfo);
ArchSpec spec = m_gnu_debug_data_object_file->GetArchitecture();
if (spec && m_gnu_debug_data_object_file->SetModulesArchitecture(spec))
return m_gnu_debug_data_object_file;
return nullptr;
}
// Find the arm/aarch64 mapping symbol character in the given symbol name.
@@ -2649,19 +2714,29 @@ Symtab *ObjectFileELF::GetSymtab() {
// while the reverse is not necessarily true.
Section *symtab =
section_list->FindSectionByType(eSectionTypeELFSymbolTable, true).get();
if (!symtab) {
// The symtab section is non-allocable and can be stripped, so if it
// doesn't exist then use the dynsym section which should always be
// there.
symtab =
section_list->FindSectionByType(eSectionTypeELFDynamicSymbols, true)
.get();
}
if (symtab) {
m_symtab_up.reset(new Symtab(symtab->GetObjectFile()));
symbol_id += ParseSymbolTable(m_symtab_up.get(), symbol_id, symtab);
}
// The symtab section is non-allocable and can be stripped, while the
// .dynsym section which should always be always be there. To support the
// minidebuginfo case we parse .dynsym when there's a .gnu_debuginfo
// section, nomatter if .symtab was already parsed or not. This is because
// minidebuginfo normally removes the .symtab symbols which have their
// matching .dynsym counterparts.
if (!symtab ||
GetSectionList()->FindSectionByName(ConstString(".gnu_debugdata"))) {
Section *dynsym =
section_list->FindSectionByType(eSectionTypeELFDynamicSymbols, true)
.get();
if (dynsym) {
if (!m_symtab_up)
m_symtab_up.reset(new Symtab(dynsym->GetObjectFile()));
symbol_id += ParseSymbolTable(m_symtab_up.get(), symbol_id, dynsym);
}
}
// DT_JMPREL
// If present, this entry's d_ptr member holds the address of
// relocation