【问题标题】:No symbol traces from stripped binaries in google breakpadgoogle breakpad 中剥离的二进制文件没有符号痕迹
【发布时间】:2021-04-30 15:32:13
【问题描述】:

来自documentation,google breakpad 是:

允许您分发应用程序的库和工具套件 给删除了编译器提供的调试信息的用户

为了证明上述引用,我们将使用这个最小的 c++17 示例进行尝试:

#include <thread>
#include <filesystem>

#include <client/linux/handler/exception_handler.h>

namespace breakpad = google_breakpad;

static bool DumpCallback(const breakpad::MinidumpDescriptor& md,
                         void* context,
                         bool success) {
    (void)md;
    (void)context;
    return success;
}

static void fault(unsigned after) {
    std::this_thread::sleep_for(std::chrono::seconds{after});
    delete reinterpret_cast<std::string*>(0xFEE1DEAD);
}

int32_t main(int argc, char** argv) {
    (void)argc;
    (void)argv;

    auto pwd = std::filesystem::current_path();
    const auto dumpDir = pwd.string() + "/dumps";
    std::filesystem::create_directory(dumpDir);
    breakpad::MinidumpDescriptor md(dumpDir);
    new google_breakpad::ExceptionHandler(
        md,
        /* FilterCallback */ nullptr,
        DumpCallback,
        /* callback_context */ nullptr,
        true,
        -1
    );

    fault(1U);

    return EXIT_SUCCESS;
}

在正常的Debug 构建中,这是预期的结果,因此,如果我们尝试运行它,并处理生成的 minidump 文件(借助 dump_symsminidump_stackwalk 等主要实用程序),结果是一个很好的符号跟踪:

Operating system: Linux
                  0.0.0 Linux 4.19.0-16-amd64 #1 SMP Debian 4.19.181-1 (2021-03-19) x86_64
CPU: amd64
     family 6 model 58 stepping 9
     1 CPU

GPU: UNKNOWN

Crash reason:  SIGSEGV /SEGV_MAPERR
Crash address: 0xfee1dead
Process uptime: not available

Thread 0 (crashed)
 0  core!std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_data() const [basic_string.h : 176 + 0x4]
    rax = 0x00000000fee1dead   rdx = 0x00007ffc12803cc0
    rcx = 0x00007fc328087bc1   rbx = 0x000055cdac272940
    rsi = 0x00007ffc12803cc0   rdi = 0x00000000fee1dead
    rbp = 0x00007ffc12803c80   rsp = 0x00007ffc12803c80
     r8 = 0x0000000000000000    r9 = 0x000055cdac276bd8
    r10 = 0x0000000000000000   r11 = 0x0000000000000246
    r12 = 0x000055cdabc1af40   r13 = 0x00007ffc12803f80
    r14 = 0x0000000000000000   r15 = 0x0000000000000000
    rip = 0x000055cdabc1bc20
    Found by: given as instruction pointer in context
 1  core!std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_is_local() const [basic_string.h : 211 + 0xc]
    rbx = 0x000055cdac272940   rbp = 0x00007ffc12803cb0
    rsp = 0x00007ffc12803c90   r12 = 0x000055cdabc1af40
    r13 = 0x00007ffc12803f80   r14 = 0x0000000000000000
    r15 = 0x0000000000000000   rip = 0x000055cdabc1bf0b
    Found by: call frame info
 2  core!std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_dispose() [basic_string.h : 220 + 0xc]
    rbx = 0x000055cdac272940   rbp = 0x00007ffc12803cd0
    rsp = 0x00007ffc12803cc0   r12 = 0x000055cdabc1af40
    r13 = 0x00007ffc12803f80   r14 = 0x0000000000000000
    r15 = 0x0000000000000000   rip = 0x000055cdabc1bc3e
    Found by: call frame info
 3  core!std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string() [basic_string.h : 657 + 0xc]
    rbx = 0x000055cdac272940   rbp = 0x00007ffc12803cf0
    rsp = 0x00007ffc12803ce0   r12 = 0x000055cdabc1af40
    r13 = 0x00007ffc12803f80   r14 = 0x0000000000000000
    r15 = 0x0000000000000000   rip = 0x000055cdabc1b5b6
    Found by: call frame info
 4  core!fault [main.cpp : 18 + 0xa]
    rbx = 0x000055cdac272940   rbp = 0x00007ffc12803d20
    rsp = 0x00007ffc12803d00   r12 = 0x000055cdabc1af40
    r13 = 0x00007ffc12803f80   r14 = 0x0000000000000000
    r15 = 0x0000000000000000   rip = 0x000055cdabc1b070
    Found by: call frame info
 5  core!main [main.cpp : 38 + 0xa]
    rbx = 0x000055cdac272940   rbp = 0x00007ffc12803ea0
    rsp = 0x00007ffc12803d30   r12 = 0x000055cdabc1af40
    r13 = 0x00007ffc12803f80   r14 = 0x0000000000000000
    r15 = 0x0000000000000000   rip = 0x000055cdabc1b182
    Found by: call frame info
 6  libc.so.6 + 0x2409b
    rbx = 0x0000000000000000   rbp = 0x000055cdabc43ec0
    rsp = 0x00007ffc12803eb0   r12 = 0x000055cdabc1af40
    r13 = 0x00007ffc12803f80   r14 = 0x0000000000000000
    r15 = 0x0000000000000000   rip = 0x00007fc327ed909b
    Found by: call frame info
 7  core!fault [main.cpp : 19 + 0x3]
    rsp = 0x00007ffc12803ed0   rip = 0x000055cdabc1b082
    Found by: stack scanning
 8  core!google_breakpad::FileID::ElfFileIdentifier(google_breakpad::wasteful_vector<unsigned char>&) [file_id.cc : 158 + 0x10]
    rsp = 0x00007ffc12803ee8   rip = 0x000055cdabc1af40
    Found by: stack scanning
 9  ld-linux-x86-64.so.2 + 0xf476
    rsp = 0x00007ffc12803f40   rip = 0x00007fc3283ef476
    Found by: stack scanning
10  core!google_breakpad::FileID::ElfFileIdentifier(google_breakpad::wasteful_vector<unsigned char>&) [file_id.cc : 158 + 0x10]
    rsp = 0x00007ffc12803f58   rip = 0x000055cdabc1af40
    Found by: stack scanning

Loaded modules:
0x55cdabc08000 - 0x55cdabc43fff  core  ???  (main)
0x7fc327eb5000 - 0x7fc32801efff  libc.so.6  ???  (WARNING: No symbols, libc.so.6, A8A9B91823C5CFE5E5B5D946D605D0920)
0x7fc328076000 - 0x7fc32808afff  libpthread.so.0  ???
0x7fc328097000 - 0x7fc3280aafff  libgcc_s.so.1  ???
0x7fc3280b1000 - 0x7fc32815cfff  libm.so.6  ???
0x7fc328234000 - 0x7fc328368fff  libstdc++.so.6  ???
0x7fc3283e0000 - 0x7fc3283fefff  ld-linux-x86-64.so.2  ???  (WARNING: No symbols, ld-linux-x86-64.so.2, 7BFD5DF2BE95A34B86FD71080ACCAE8C0)
0x7ffc12932000 - 0x7ffc12933fff  linux-gate.so  ???

但是在部署中,典型的情况是有两个release版本,一个是普通的Release,另一个是RelWithDebInfo(同一个release,但是有调试符号)。因此,如果您尝试与上述完全相同的例程,而是使用从正常发布二进制文件(部署到客户端)生成的转储文件和来自RelWithDebInfo 二进制文件的符号,您会收到以下跟踪警告您有关主二进制文件的符号:

Operating system: Linux
                  0.0.0 Linux 4.19.0-16-amd64 #1 SMP Debian 4.19.181-1 (2021-03-19) x86_64
CPU: amd64
     family 6 model 58 stepping 9
     1 CPU

GPU: UNKNOWN

Crash reason:  SIGSEGV /SEGV_MAPERR
Crash address: 0xfee1dead
Process uptime: not available

Thread 0 (crashed)
 0  core + 0xf26a
    rax = 0x0000000000000000   rdx = 0x00005604fdc66209
    rcx = 0x00007f77d9bcfbc1   rbx = 0x00007fffbc001120
    rsi = 0x00007fffbc0010b0   rdi = 0x00007fffbc0010b0
    rbp = 0x00007fffbc0011e0   rsp = 0x00007fffbc0010a0
     r8 = 0x0000000000000000    r9 = 0x00005604ff997be8
    r10 = 0x0000000000000000   r11 = 0x0000000000000246
    r12 = 0x00007fffbc0010e0   r13 = 0x00007fffbc0010c0
    r14 = 0x00007fffbc0010b0   r15 = 0x00005604ff993940
    rip = 0x00005604fdc6626a
    Found by: given as instruction pointer in context
 1  core + 0x36995
    rbp = 0x00007fffbc0011e0   rsp = 0x00007fffbc0011b0
    rip = 0x00005604fdc8d995
    Found by: stack scanning
 2  ld-linux-x86-64.so.2 + 0xf530
    rbp = 0x00007fffbc0011e0   rsp = 0x00007fffbc0011b8
    rip = 0x00007f77d9f37530
    Found by: stack scanning
 3  core + 0xf520
    rbp = 0x00007fffbc0011e0   rsp = 0x00007fffbc0011c8
    rip = 0x00005604fdc66520
    Found by: stack scanning
 4  core + 0x36950
    rsp = 0x00007fffbc0011e8   rip = 0x00005604fdc8d950
    Found by: stack scanning
 5  libc.so.6 + 0x2409b
    rsp = 0x00007fffbc0011f0   rip = 0x00007f77d9a2109b
    Found by: stack scanning
 6  core + 0xef90
    rsp = 0x00007fffbc001210   rip = 0x00005604fdc65f90
    Found by: stack scanning
 7  core + 0xf520
    rsp = 0x00007fffbc001228   rip = 0x00005604fdc66520
    Found by: stack scanning
 8  ld-linux-x86-64.so.2 + 0xf476
    rsp = 0x00007fffbc001280   rip = 0x00007f77d9f37476
    Found by: stack scanning
 9  core + 0xf520
    rsp = 0x00007fffbc001298   rip = 0x00005604fdc66520
    Found by: stack scanning
10  core + 0xf54a
    rsp = 0x00007fffbc0012b0   rip = 0x00005604fdc6654a
    Found by: stack scanning

Loaded modules:
0x5604fdc57000 - 0x5604fdc8dfff  core  ???  (main)  (WARNING: No symbols, core, C33015040F685CBAD56AEFBFD7109D4C0)
0x7f77d99fd000 - 0x7f77d9b66fff  libc.so.6  ???  (WARNING: No symbols, libc.so.6, A8A9B91823C5CFE5E5B5D946D605D0920)
0x7f77d9bbe000 - 0x7f77d9bd2fff  libpthread.so.0  ???
0x7f77d9bdf000 - 0x7f77d9bf2fff  libgcc_s.so.1  ???
0x7f77d9bf9000 - 0x7f77d9ca4fff  libm.so.6  ???
0x7f77d9d7c000 - 0x7f77d9eb0fff  libstdc++.so.6  ???
0x7f77d9f28000 - 0x7f77d9f46fff  ld-linux-x86-64.so.2  ???  (WARNING: No symbols, ld-linux-x86-64.so.2, 7BFD5DF2BE95A34B86FD71080ACCAE8C0)
0x7fffbc1ad000 - 0x7fffbc1aefff  linux-gate.so  ???

还有什么我们需要考虑的吗?

2001 年 5 月 21 日更新

我们用于符号生成的实际脚本:

#!/bin/bash

#
# e.g ./dump.sh ./exec $PWD/dumps
#

set -e
set -u

DBG_INFO=$(realpath ${1})
DUMPS_DIR=$(realpath ${2:-$PWD/dumps})
DUMP_SYMS=${3:-~/WorkSpace/libraries/breakpad/src/tools/linux/dump_syms/dump_syms}
STAK_WALK=${4:-~/WorkSpace/libraries/breakpad/src/processor/minidump_stackwalk}

#
# Generate debug symbols
#
base=$(basename $DBG_INFO)
$DUMP_SYMS $DBG_INFO > $DUMPS_DIR/$base.sym

#
# Create dump dir structure
#
list=($(head -n1 $DUMPS_DIR/$base.sym))
hash=${list[3]}
mkdir -p $DUMPS_DIR/symbols/$base/$hash
mv $DUMPS_DIR/$base.sym $DUMPS_DIR/symbols/$base/$hash

#
# Produce stack trace
#
RED='\033[0;36m'
NC='\033[0m' # No Color
tree $DUMPS_DIR
for dmp in $DUMPS_DIR/*.dmp ; do
    filename=$(basename -- "${dmp}")
    filename="${filename%.*}"
    echo -e "generating stack trace for -> ${RED}${dmp}${NC}"
    $STAK_WALK ${dmp} $DUMPS_DIR/symbols > $DUMPS_DIR/${filename}.txt 2>/dev/null
done

【问题讨论】:

  • 我不确定我是否正确理解了这个问题:我认为发布版本会删除调试符号,因为这通常被认为是正确的行为?如果 google breakpad 背后有一些魔法来创建这些小型转储(不知道该工具,但我非常感兴趣:))那么它应该需要调试二进制文件,因为发行版没有包含任何调试信息,对吗?
  • 是的。没错,您应该像 Google Chrome 或 Mozilla Firefox 一样从 已部署的版本 二进制文件中获取故障转储,然后使用完全相同的 debug 二进制文件 转储它
  • 是的,我就是这么想的。尽管如此,由 breakpad 生成并随后用于分析传入故障转储的符号文件需要有一个调试构建的可执行文件才能从中获取其信息,不是吗?当尝试从已剥离的发布二进制文件中生成符号文件时,其中没有任何信息可用于填充符号文件。不知道从调试二进制文件生成的符号文件是否适用于有关故障转储的发布二进制文件。
  • 是的,这是我们应该注意的事情,实际上,我们有两个版本的二进制文件,一个是 Release,一个是 RelWithDebInfo(如CMake)但仍然没有结果,在我看来优化级别会影响此过程,因此如果您尝试使用-O2 为调试版本生成跟踪,则符号消失了! (步骤同上)
  • 你可以试试void __attribute__ ((noinline)) fault(unsigned after),没有staticeither。

标签: c++ debug-symbols crash-dumps google-breakpad


【解决方案1】:

在尝试将 minidump_stackwalk 用于应用程序的发布版本时,我遇到了同样的问题。我正在使用 qmake 并使用 qmake 的 CONFIG 标志 force_debug_infoseparate_debug_info 解决了这个问题。构建后会在构建文件夹中出现appName.debug 可执行文件)。要转储符号,只需将appName.debug 文件复制到某处(例如,在构建文件夹中创建子文件夹debug)并将其重命名为appName。最后,调用dump_syms build_folder/debug/appName &gt; appName.sym,您将能够通过 minidump_stackwalk 遍历应用程序的发布版本。

PS:认为对于基于 CMake 的项目和其他项目有类似的解决方案。

【讨论】:

  • 没有意义,.debug 只是在主二进制文件上运行的一个 objcopy --only-keep-debug 命令,以保持调试符号分开。
  • 也许我误解了你,但正如我在你的主要帖子中看到的那样,你正在尝试使用 RelWithDebInfo 的符号来调试发布版本。它行不通。您需要从完全相同的二进制文件生成的符号。唯一的 id 应该是相同的等等。
  • 我们需要完全相同的构建,这是真的,但关键是如果您在之前的构建中添加最低优化级别(假设两者都有调试符号),输出上没有符号化跟踪!
  • 顺便说一下,我在原帖中明确提到了相同的版本,但带有调试符号
【解决方案2】:

您发送的二进制文件与您的调试符号不匹配。

我认为符号生成本身很好。但是,您在为您的 RelWithDebinfo 构建生成符号的同时发布了一个 Release 构建。

如果您从您的 RelWithDebInfo 构建中获取二进制文件并将其剥离(即在其上使用 strip),您最终应该会得到一个剥离的二进制文件,您可以发送该二进制文件并且与您提取的调试符号匹配。

【讨论】:

  • 这两个构建配置本质上是一样的,RelWithDebinfo 是一个集成了调试符号的Release 构建(-g)。但是,这里有一个基本点,如果您在调试版本中添加优化级别(比如-O1),您将无法获得像原始帖子那样的符号!所以我认为优化级别存在问题
  • 您可以只 readelf -n 生成的二进制文件并查看构建 ID 是否匹配。如果它们匹配符号应该在构建中工作,否则它们不会。但是,不抓住这个机会并通过不构建两次来节省时间可能更容易且不易出错。
【解决方案3】:

minidump 没有符号。

breakpad 文档告诉开发人员将使用包含的 dump_syms 或 symupload 工具或其他合适的工具创建供 Breakpad 使用的符号文件,并将符号文件放置在处理器的符号供应商能够找到它们的位置。

所以使用dump_syms生成符号文件,例如'./dump_sys ./my-binary > my.sym'。

然后在使用minidump_stackwalk之前,把符号文件放到文件第一行指定的目录下。请参阅 Mozilla 存储库中的 symbolstore.py。 https://github.com/MozillaReality/symbolgenerator

最后,生成一个trace stack,使用minidump_stackwalk,这个命令生成trace stack并打印出来。

【讨论】:

    猜你喜欢
    • 2012-10-22
    • 1970-01-01
    • 2013-04-05
    • 2014-05-06
    • 1970-01-01
    • 1970-01-01
    • 2014-03-06
    • 2016-03-21
    • 1970-01-01
    相关资源
    最近更新 更多