abi::__cxa_demangle无法解析符号?

4

我不确定为什么这个无法解析符号:

#include <cxxabi.h>
void _debugBacktrace(code_part part)
{
#if defined(WZ_OS_LINUX) && defined(__GLIBC__)
    void *btv[20];
    unsigned num = backtrace(btv, sizeof(btv) / sizeof(*btv));
    char **btc = backtrace_symbols(btv, num);
    unsigned i;
    char buffer[255];
    for (i = 1; i + 2 < num; ++i) 
    {
        int status = -1;
        size_t demangledLen = 0;
        memset(buffer, 0, 255);
        char *readableName = abi::__cxa_demangle(btc[i], buffer, &demangledLen, &status);
        if (status == 0)
        {
            _debug(0, part, "BT", "%s", readableName);
        }
        else
        {
            _debug(0, part, "BT", "%s [status: %i]", btc[i], status);
        }
        
    }
    free(btc);
#else
    // debugBacktrace not implemented.
#endif
}

标准输出:

error   |02:25:21: [BT:0] ./src/warzone2100(_ZNK5EcKey4signEPKvm+0x98) [0x16405da] [status: -2]
error   |02:25:21: [BT:0] ./src/warzone2100(_Z8recvPing8NETQUEUE+0x1ab) [0x126bc2d] [status: -2]
error   |02:25:21: [BT:0] ./src/warzone2100(_ZN27WzMultiplayerOptionsTitleUI21frontendMultiMessagesEb+0x7d9) [0x11ce219] [status: -2]
error   |02:25:21: [BT:0] ./src/warzone2100(_ZN27WzMultiplayerOptionsTitleUI3runEv+0x45) [0x11cf32b] [status: -2]
error   |02:25:21: [BT:0] ./src/warzone2100(_Z9titleLoopv+0x1aa) [0x1480b35] [status: -2]
error   |02:25:21: [BT:0] ./src/warzone2100() [0x1169cbc] [status: -2]
...
...
### BUT this works?
> addr2line -f 0x16405da -C  -e ./src/warzone2100 
EcKey::sign(void const*, unsigned long) const
/home/docker/code/lib/framework/crc.cpp:284

使用docker编译(C++11),使用以下内容:

docker@b12fbaed9c6c:~/code> g++ --version
g++ (SUSE Linux) 11.2.1 20210816 [revision 056e324ce46a7924b5cf10f61010cf9dd2ca10e9]
docker@b12fbaed9c6c:~/code> ld --version
GNU ld (GNU Binutils; openSUSE Tumbleweed) 2.37.20210803-1

在主机上执行:

> g++ --version
g++ (SUSE Linux) 11.2.1 20220103 [revision d4a1d3c4b377f1d4acb34fe1b55b5088a3f293f6]

> ld -version
GNU ld (GNU Binutils; openSUSE Tumbleweed) 2.37.20211112-3

1
请注意,_debugBacktrace是全局命名空间中标准库的保留名称(因为它以下划线开头)。用户代码不允许在那里声明该标识符。 - user17732522
2个回答

2
一方面,您没有正确调用__cxa_demangle文档中说:

output_buffer
一块内存区域,使用malloc分配,长度为*length字节,用于存储解码后的名称。如果output_buffer长度不够,则使用realloc进行扩展。 output_buffer也可以是NULL;在这种情况下,解码后的名称将放置在使用malloc分配的内存区域中。

您正在传递一个堆栈buffer。 摆脱它(以及无意义的memset),改为执行以下操作:
    for (i = 1; i + 2 < num; ++i) 
    {
        int status = -1;
        char *readableName = abi::__cxa_demangle(btc[i], NULL, NULL, &status);
        if (status == 0)
        {
            _debug(0, part, "BT", "%s", readableName);
        }
        else
        {
            _debug(0, part, "BT", "%s [status: %i]", btc[i], status);
        }
        free(readableName);
    }

更新:

第二个问题是,您似乎期望__cxa_demangle()会忽略任何不属于名称的字符,并对其余部分进行反编译。(您没有展示btc[i]的实际内容,但从输出中可以看出它包含类似于./src/warzone2100(_ZNK5EcKey4signEPKvm+0x98) [0x16405da]的字符串。)

(重新阅读您发布的代码,btc[i]来自backtrace_symbols,这是文档中记录的返回函数名称、十六进制偏移量和实际返回地址,因此肯定包含一些__cxa_demangle()不希望出现的“额外”垃圾。)

如下测试所示,__cxa_demangle()不会忽略任何内容;它需要的是名称,而不是“包含名称的东西”。

#include <cxxabi.h>
#include <string.h>

#include <array>
#include <iostream>

int main()
{
  const std::array<const char *, 5> bt = {
    "./src/warzone2100(_ZNK5EcKey4signEPKvm+0x98) [0x16405da]",
    "./src/warzone2100(_Z8recvPing8NETQUEUE+0x1ab) [0x126bc2d]",
    "./src/warzone2100(_ZN27WzMultiplayerOptionsTitleUI21frontendMultiMessagesEb+0x7d9) [0x11ce219]",
    "./src/warzone2100(_ZN27WzMultiplayerOptionsTitleUI3runEv+0x45) [0x11cf32b]",
    "./src/warzone2100(_Z9titleLoopv+0x1aa) [0x1480b35]",
  };
  std::cout << "Wrong way:" << std::endl << std::endl;
  for (const char* p : bt) {
    int status;
    char *demangled =  abi::__cxa_demangle(p, NULL, NULL, &status);
    std::cout << p << " " << status << std::endl;
    free(demangled);
  }

  auto trim = [](const char *in, char *out) {
    const char *begin = strchr(in, '_');
    const char *end = strchr(begin, '+');
    memcpy(out, begin, end - begin);
    out[end - begin] = '\0';
  };

  std::cout << std::endl << "Right way:" << std::endl << std::endl;
  for (const char* p : bt) {
    int status;
    char buf[1024];

    trim(p, buf);
    char *demangled =  abi::__cxa_demangle(buf, NULL, NULL, &status);
    std::cout << buf << " -> " << demangled << " " << status << std::endl;
    free(demangled);
  }
}

使用 (Debian 11.2.0-14),我得到了以下结果:

g++ -g foo.cc && ./a.out
Wrong way:

./src/warzone2100(_ZNK5EcKey4signEPKvm+0x98) [0x16405da] -2
./src/warzone2100(_Z8recvPing8NETQUEUE+0x1ab) [0x126bc2d] -2
./src/warzone2100(_ZN27WzMultiplayerOptionsTitleUI21frontendMultiMessagesEb+0x7d9) [0x11ce219] -2
./src/warzone2100(_ZN27WzMultiplayerOptionsTitleUI3runEv+0x45) [0x11cf32b] -2
./src/warzone2100(_Z9titleLoopv+0x1aa) [0x1480b35] -2

Right way:

_ZNK5EcKey4signEPKvm -> EcKey::sign(void const*, unsigned long) const 0
_Z8recvPing8NETQUEUE -> recvPing(NETQUEUE) 0
_ZN27WzMultiplayerOptionsTitleUI21frontendMultiMessagesEb -> WzMultiplayerOptionsTitleUI::frontendMultiMessages(bool) 0
_ZN27WzMultiplayerOptionsTitleUI3runEv -> WzMultiplayerOptionsTitleUI::run() 0
_Z9titleLoopv -> titleLoop() 0

不幸的是,这没有帮助 :( 我得到了完全相同的结果。 - dgan
尽管__cxa_demangle的文档可能会这样说,但您永远不应该在信号处理程序中分配内存(而且像这样的代码很可能会从信号处理程序中调用),因此使用足够大的堆栈缓冲区通常是最明智的选择。 - James

0
根据文档,状态为-2表示“不是有效的名称混淆”。可能是因为名称混淆嵌入在字符串中。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接