我遇到了一个奇怪的错误,并试图理解。特别是,至少,我想确定我应该研究的软件:链接器,glibc,汇编器,编译器或其他。
我确实知道减少测试用例至关重要。但是,对无法这样做感到抱歉。它不会在x86中复制。它可以在arm64和特定的Linux发行版上重现,但我不确定是否对公众开放。我对测试用例本身还不够了解,无法简化测试用例。
可执行文件在__libc_start_main处出现分段错误。在那里的原因是控件试图跳转到有效保存在寄存器x0中的地址,该地址奇怪地为0。x0在_start函数中设置。
// in sysdep/aarch64/start.S of glibc
MOVL (0, main)
MOVL宏似乎在同一目录的sysdep.h中定义如下:
# define MOVL(R, NAME) \
movz PTR_REG (R), #:abs_g3:NAME; \
movk PTR_REG (R), #:abs_g2_nc:NAME; \
movk PTR_REG (R), #:abs_g1_nc:NAME; \
movk PTR_REG (R), #:abs_g0_nc:NAME;
因此,我猜#:abs_g0_nc:main和朋友应该是一些非零值,但定义为0。
所以,我的问题是应该归咎于哪个部分。可执行文件来自Go源代码。 Go工具无需外部链接程序的帮助即可构建共享库。然后,Go工具会编译Go源代码,并将其链接到Go共享库以及其他带有外部链接器的库,以及带有gcc链接器的gcc。
我确实在start.S中看到了_start与最终的可执行文件类似。只是#:abs_g *事情消失了。谁用数值(在这种情况下为0)替换宏(?)?
gcc或编译器是否已经预先编译_start并将已编译/汇编的二进制文件放在/ usr / lib {64}之类的地方?或者,当链接程序完全链接时,链接程序是否将宏替换为某个数值?如果在通过链接构建有问题的可执行文件时链接器这样做,则链接器依赖于什么来计算#:abs_g0_nc和朋友的值?
答案 0 :(得分:2)
哪个组件发生故障?主程序还是共享对象?
sysdep/aarch64/start.S
不应链接到动态共享库中,因为它是主要可执行文件启动代码的一部分。大多数程序没有main
的动态符号,因此即使使用了正确的重定位,绑定也会失败。 (#:abs_gX
重定位与共享对象所需的位置无关代码不兼容。)
如果主程序失败,则可能是许多问题。您的问题中确实没有足够的信息来提供坚定的建议。我的猜测是某种PIE /非PIE不匹配,导致选择了错误版本的启动代码。请注意,start.S
在glibc构建过程中以不同的预处理器标志进行了多次组装,并链接到crt1.o
(非PIE / ET_EXEC
可执行文件),Scrt1.o
(PIE)中。 / ET_DYN
可执行文件)和gcrt1.o
(分析)。