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
147 lines
4.9 KiB
C++
147 lines
4.9 KiB
C++
//===-- LZMA.cpp ------------------------------------------------*- C++ -*-===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "lldb/Host/Config.h"
|
|
#include "llvm/ADT/StringRef.h"
|
|
#include "llvm/Support/Error.h"
|
|
|
|
#if LLDB_ENABLE_LZMA
|
|
#include <lzma.h>
|
|
#endif // LLDB_ENABLE_LZMA
|
|
|
|
namespace lldb_private {
|
|
|
|
namespace lzma {
|
|
|
|
#if !LLDB_ENABLE_LZMA
|
|
bool isAvailable() { return false; }
|
|
llvm::Expected<uint64_t>
|
|
getUncompressedSize(llvm::ArrayRef<uint8_t> InputBuffer) {
|
|
llvm_unreachable("lzma::getUncompressedSize is unavailable");
|
|
}
|
|
|
|
llvm::Error uncompress(llvm::ArrayRef<uint8_t> InputBuffer,
|
|
llvm::SmallVectorImpl<uint8_t> &Uncompressed) {
|
|
llvm_unreachable("lzma::uncompress is unavailable");
|
|
}
|
|
|
|
#else // LLDB_ENABLE_LZMA
|
|
|
|
bool isAvailable() { return true; }
|
|
|
|
static const char *convertLZMACodeToString(lzma_ret Code) {
|
|
switch (Code) {
|
|
case LZMA_STREAM_END:
|
|
return "lzma error: LZMA_STREAM_END";
|
|
case LZMA_NO_CHECK:
|
|
return "lzma error: LZMA_NO_CHECK";
|
|
case LZMA_UNSUPPORTED_CHECK:
|
|
return "lzma error: LZMA_UNSUPPORTED_CHECK";
|
|
case LZMA_GET_CHECK:
|
|
return "lzma error: LZMA_GET_CHECK";
|
|
case LZMA_MEM_ERROR:
|
|
return "lzma error: LZMA_MEM_ERROR";
|
|
case LZMA_MEMLIMIT_ERROR:
|
|
return "lzma error: LZMA_MEMLIMIT_ERROR";
|
|
case LZMA_FORMAT_ERROR:
|
|
return "lzma error: LZMA_FORMAT_ERROR";
|
|
case LZMA_OPTIONS_ERROR:
|
|
return "lzma error: LZMA_OPTIONS_ERROR";
|
|
case LZMA_DATA_ERROR:
|
|
return "lzma error: LZMA_DATA_ERROR";
|
|
case LZMA_BUF_ERROR:
|
|
return "lzma error: LZMA_BUF_ERROR";
|
|
case LZMA_PROG_ERROR:
|
|
return "lzma error: LZMA_PROG_ERROR";
|
|
default:
|
|
llvm_unreachable("unknown or unexpected lzma status code");
|
|
}
|
|
}
|
|
|
|
llvm::Expected<uint64_t>
|
|
getUncompressedSize(llvm::ArrayRef<uint8_t> InputBuffer) {
|
|
lzma_stream_flags opts{};
|
|
if (InputBuffer.size() < LZMA_STREAM_HEADER_SIZE) {
|
|
return llvm::createStringError(
|
|
llvm::inconvertibleErrorCode(),
|
|
"size of xz-compressed blob (%lu bytes) is smaller than the "
|
|
"LZMA_STREAM_HEADER_SIZE (%lu bytes)",
|
|
InputBuffer.size(), LZMA_STREAM_HEADER_SIZE);
|
|
}
|
|
|
|
// Decode xz footer.
|
|
lzma_ret xzerr = lzma_stream_footer_decode(
|
|
&opts, InputBuffer.data() + InputBuffer.size() - LZMA_STREAM_HEADER_SIZE);
|
|
if (xzerr != LZMA_OK) {
|
|
return llvm::createStringError(llvm::inconvertibleErrorCode(),
|
|
"lzma_stream_footer_decode()=%s",
|
|
convertLZMACodeToString(xzerr));
|
|
}
|
|
if (InputBuffer.size() < (opts.backward_size + LZMA_STREAM_HEADER_SIZE)) {
|
|
return llvm::createStringError(
|
|
llvm::inconvertibleErrorCode(),
|
|
"xz-compressed buffer size (%lu bytes) too small (required at "
|
|
"least %lu bytes) ",
|
|
InputBuffer.size(), (opts.backward_size + LZMA_STREAM_HEADER_SIZE));
|
|
}
|
|
|
|
// Decode xz index.
|
|
lzma_index *xzindex;
|
|
uint64_t memlimit(UINT64_MAX);
|
|
size_t inpos = 0;
|
|
xzerr =
|
|
lzma_index_buffer_decode(&xzindex, &memlimit, nullptr,
|
|
InputBuffer.data() + InputBuffer.size() -
|
|
LZMA_STREAM_HEADER_SIZE - opts.backward_size,
|
|
&inpos, InputBuffer.size());
|
|
if (xzerr != LZMA_OK) {
|
|
return llvm::createStringError(llvm::inconvertibleErrorCode(),
|
|
"lzma_index_buffer_decode()=%s",
|
|
convertLZMACodeToString(xzerr));
|
|
}
|
|
|
|
// Get size of uncompressed file to construct an in-memory buffer of the
|
|
// same size on the calling end (if needed).
|
|
uint64_t uncompressedSize = lzma_index_uncompressed_size(xzindex);
|
|
|
|
// Deallocate xz index as it is no longer needed.
|
|
lzma_index_end(xzindex, nullptr);
|
|
|
|
return uncompressedSize;
|
|
}
|
|
|
|
llvm::Error uncompress(llvm::ArrayRef<uint8_t> InputBuffer,
|
|
llvm::SmallVectorImpl<uint8_t> &Uncompressed) {
|
|
llvm::Expected<uint64_t> uncompressedSize = getUncompressedSize(InputBuffer);
|
|
|
|
if (auto err = uncompressedSize.takeError())
|
|
return err;
|
|
|
|
Uncompressed.resize(*uncompressedSize);
|
|
|
|
// Decompress xz buffer to buffer.
|
|
uint64_t memlimit = UINT64_MAX;
|
|
size_t inpos = 0;
|
|
size_t outpos = 0;
|
|
lzma_ret ret = lzma_stream_buffer_decode(
|
|
&memlimit, 0, nullptr, InputBuffer.data(), &inpos, InputBuffer.size(),
|
|
Uncompressed.data(), &outpos, Uncompressed.size());
|
|
if (ret != LZMA_OK) {
|
|
return llvm::createStringError(llvm::inconvertibleErrorCode(),
|
|
"lzma_stream_buffer_decode()=%s",
|
|
convertLZMACodeToString(ret));
|
|
}
|
|
|
|
return llvm::Error::success();
|
|
}
|
|
|
|
#endif // LLDB_ENABLE_LZMA
|
|
|
|
} // end of namespace lzma
|
|
} // namespace lldb_private
|