[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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user