有没有办法使用 gdb 来分析 pyinstaller 二进制文件创建的核心转储?我将 python/C++ 文件打包到一个二进制文件中,但 gdb 无法从 python 或二进制文件中读取符号。
我已经尝试过,但只收到来自 gdb 的问号。
gdb $(which python) -c core
gdb my_binary -c core
最佳答案
将 python 二进制文件与 pyinstaller
生成的二进制文件中的核心文件一起使用是不正确的。 file
的输出命令将确认这一点:
core: ELF 64-bit LSB core file x86-64, version 1 (SYSV), SVR4-style, from './build_id/build/build_id/build_id',
(为了简洁起见,我省略了该输出行的末尾)。
与之一起使用的核心文件 file
命令来自尝试使用 pyinstaller
上build_id.py
,因此出现了名称 build_id
在路径名内。
假设my_binary
代表您尝试使用 pyinstaller
的结果,它是与该尝试中的核心文件一起使用的正确二进制文件。我已将核心文件中的构建 ID 与所有映射文件进行了比较,它们都匹配。这是对我的 build_id.py
的详细调用的输出在我的核心文件上:
0000000000400000 63116679c3030438046175bc610d21cbd50fbac0 /home/eirik/git/pyinstaller/build_id/build/build_id/build_id
00007f042f80d000 377b0152081112c82460680fe99ec01aa090cd81 /lib/x86_64-linux-gnu/libc-2.24.so
00007f042fbab000 adcc4a5e27d5de8f0bc3c6021b50ba2c35ec9a8e /lib/x86_64-linux-gnu/libz.so.1.2.8
00007f042fdc6000 4e43c23036c6bfd2d4dab183e458e29d87234adc /lib/x86_64-linux-gnu/libdl-2.24.so
00007f042ffca000 a731640ef1cd73c1d727c2a9521b10cafec33c15 /lib/x86_64-linux-gnu/ld-2.24.so
file
的输出这些路径名之一上的命令会报告在该文件的输出行中看到的相同构建 ID。此外,报告的构建 ID 为我的 build_id
二进制文件与 pyinstaller
提供的以下文件的构建 ID 匹配:
PyInstaller/bootloader/Linux-64bit/run
看来pyinstaller
使用objcopy
使用 run
构建二进制文件文件作为前缀。除了报告该 run
的构建 ID 之外文件,file
命令还告诉我它已被删除,这与我从 gdb 获得的回溯一致:
Core was generated by `./build_id/build/build_id/build_id'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0 strlen () at ../sysdeps/x86_64/strlen.S:106
106 ../sysdeps/x86_64/strlen.S: No such file or directory.
(gdb) backtrace
#0 strlen () at ../sysdeps/x86_64/strlen.S:106
#1 0x00007f042f8424d9 in __add_to_environ (name=0x405da8 "LD_LIBRARY_PATH_ORIG", value=0x0, combined=0x0, replace=1) at setenv.c:131
#2 0x0000000000404688 in ?? ()
#3 0x0000000000402db3 in ?? ()
#4 0x00007f042f82d2b1 in __libc_start_main (main=0x401950, argc=1, argv=0x7fffc57143c8, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>,
stack_end=0x7fffc57143b8) at ../csu/libc-start.c:291
#5 0x000000000040197e in ?? ()
(gdb) x/i $pc
=> 0x7f042f88d496 <strlen+38>: movdqu (%rax),%xmm4
(gdb) i r rax
rax 0x0 0
(gdb)
引导加载程序(run
文件)中的某些内容正在调用 __add_to_environ
(可能通过 setenv
,它有一个 jmpq
到 __add_to_environ
),然后将空指针传递给 strlen
(推测空指针源自 run
)。所有出现的 ??
该回溯来自 run
文件(它们的地址均以 0x00000000004
开头)。
如果我使用 LD_LIBRARY_PATH=/tmp
运行二进制文件,我不再得到核心转储,所以我猜测空指针是 getenv("LD_LIBRARY_PATH")
的返回值。另外,我在 bootloader/src/pyi_utils.c
中看到了这一点(在 set_dynamic_library_path
中):
orig_path = pyi_getenv(env_var);
pyi_setenv(env_var_orig, orig_path);
除了依赖 libc
中的调试符号之外,理解此类崩溃的最大希望是,可能是从源代码构建引导加载程序(run
文件),并保留其调试符号并将其加载到 gdb 中,在从该引导加载程序构建的可执行文件及其核心文件上调用。在这种情况下,重要的是使用从源代码构建的引导加载程序,而不是使用 pyinstaller
分发的引导加载程序。 (都使用 gdb,并构建二进制文件),除非构建 ID 恰好匹配(在分布式 run
和从源代码构建的 ID 之间)。如果不需要使用剥离的引导加载程序,则不剥离 run
可能会更容易。文件,但这可能比单独加载调试符号需要更多工作。
这对于 pyinstaller
可能有意义包含每个包含的引导加载程序的调试符号(据我所知,它确实做到了这一点,我只是还没有找到它们,但我对此表示怀疑)。如果您希望 gdb 使用 libc
的调试符号,其中一部分可能涉及安装单独的软件包。在我的 Debian 系统上,该软件包是 libc6-dbg
。我简要研究了使用调试符号构建 pyinstaller 引导加载程序,但我还没有完成破译 waf
还没有。
关于python - pyinstaller 二进制文件的核心转储,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38909028/