来自 pyinstaller 二进制文件的核心转储
core dump from pyinstaller binary
有没有办法使用 gdb 来分析由 pyinstaller 二进制文件创建的核心转储?我正在将 python/C++ 文件打包成一个二进制文件,而 gdb 无法从 python 或二进制文件中读取符号。
我试过了,但只收到来自 gdb 的问号。
gdb $(which python) -c core
gdb my_binary -c core
I have tried and receive only question marks from gdb.
这可能有两个原因:
- 您可能不匹配生成它的核心转储和二进制文件。
尝试使用这些命令找出二进制文件的确切路径:
file core
或 gdb -c core
。顺便说一下,它应该是 python 二进制文件的路径。
- gdb 无法找到符号并且无法在堆栈跟踪中显示有意义的函数名称。尝试使用
apt-get
或 yum/dnf
. 在 OS 中安装 python 的调试符号
将 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
命令一起使用的核心文件来自对 build_id.py
使用 pyinstaller
的尝试,因此在路径名中出现了名称 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。此外,为我的 build_id
二进制文件报告的构建 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
的二进制文件,我不再得到核心转储,所以我猜测空指针是 [=43= 的 return 值].另外,我在 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
和从源代码构建的)。如果不需要使用剥离的引导加载程序,则不剥离 run
文件可能更容易,但这可能比单独加载调试符号更有效。
pyinstaller
为每个包含的引导加载程序包含调试符号可能是有意义的(据我所知,它会这样做,但我还没有找到它们,但我对此表示怀疑)。如果您希望 gdb 使用 libc
的调试符号,其中一部分可能涉及安装单独的包。在我的 Debian 系统上,该软件包是 libc6-dbg
。我简要地研究了使用调试符号构建 pyinstaller 引导加载程序,但我还没有完成解密 waf
。
同时修复:
https://github.com/pyinstaller/pyinstaller/pull/2178/files
最近,引导加载程序也被重新编译并提交到 repo,所以它现在应该可以工作了。
有没有办法使用 gdb 来分析由 pyinstaller 二进制文件创建的核心转储?我正在将 python/C++ 文件打包成一个二进制文件,而 gdb 无法从 python 或二进制文件中读取符号。
我试过了,但只收到来自 gdb 的问号。
gdb $(which python) -c core
gdb my_binary -c core
I have tried and receive only question marks from gdb.
这可能有两个原因:
- 您可能不匹配生成它的核心转储和二进制文件。
尝试使用这些命令找出二进制文件的确切路径:
file core
或gdb -c core
。顺便说一下,它应该是 python 二进制文件的路径。 - gdb 无法找到符号并且无法在堆栈跟踪中显示有意义的函数名称。尝试使用
apt-get
或yum/dnf
. 在 OS 中安装 python 的调试符号
将 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
命令一起使用的核心文件来自对 build_id.py
使用 pyinstaller
的尝试,因此在路径名中出现了名称 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。此外,为我的 build_id
二进制文件报告的构建 ID 与 pyinstaller
:
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
的二进制文件,我不再得到核心转储,所以我猜测空指针是 [=43= 的 return 值].另外,我在 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
和从源代码构建的)。如果不需要使用剥离的引导加载程序,则不剥离 run
文件可能更容易,但这可能比单独加载调试符号更有效。
pyinstaller
为每个包含的引导加载程序包含调试符号可能是有意义的(据我所知,它会这样做,但我还没有找到它们,但我对此表示怀疑)。如果您希望 gdb 使用 libc
的调试符号,其中一部分可能涉及安装单独的包。在我的 Debian 系统上,该软件包是 libc6-dbg
。我简要地研究了使用调试符号构建 pyinstaller 引导加载程序,但我还没有完成解密 waf
。
同时修复: https://github.com/pyinstaller/pyinstaller/pull/2178/files
最近,引导加载程序也被重新编译并提交到 repo,所以它现在应该可以工作了。