【发布时间】:2019-01-27 21:35:40
【问题描述】:
当我在 x86-64 linux 上使用 libc 的 system() 函数时,我注意到一个非常奇怪的行为,有时对 system() 的调用会因分段错误而失败,这是我在使用 @987654323 调试它后得到的@。
我注意到这一行中出现了分段错误:
=> 0x7ffff7a332f6 <do_system+1094>: movaps XMMWORD PTR [rsp+0x40],xmm0
根据manual,这是SIGSEGV的原因:
当源或目标操作数是内存操作数时,操作数必须在 16 字节边界上对齐,否则会生成通用保护异常 (#GP)。
再往下看,我注意到我的 rsp 值确实不是 16 字节填充的(也就是说,它的十六进制表示不以 0 结尾)。在调用 system 之前手动修改 rsp 实际上可以使一切正常。
所以我写了以下程序:
#include <stdio.h>
#include <stdlib.h>
int main(void) {
register long long int sp asm ("rsp");
printf("%llx\n", sp);
if (sp & 0x8) /* == 0x8*/
{
printf("running system...\n");
system("touch hi");
}
return 0;
}
使用 gcc 7.3.0 编译 果然,在观察输出时:
sha@sha-desktop:~/Desktop/tda$ ltrace -f ./o_sample2
[pid 26770] printf("%llx\n", 0x7ffe3eabe6c87ffe3eabe6c8
) = 13
[pid 26770] puts("running system..."running system...
) = 18
[pid 26770] system("touch hi" <no return ...>
[pid 26771] --- SIGSEGV (Segmentation fault) ---
[pid 26771] +++ killed by SIGSEGV +++
[pid 26770] --- SIGCHLD (Child exited) ---
[pid 26770] <... system resumed> ) = 139
[pid 26770] +++ exited (status 0) +++
所以使用这个程序,我无法执行 system() 什么。
也是小事,我不知道它是否与问题有关,我几乎所有的运行都以错误的 rsp 值和一个被 SEGSEGV 杀死的孩子告终。
这让我想知道一些事情:
- 为什么
system会乱用xmms 寄存器? - 这是正常行为吗?或者我在如何正确使用
system()函数方面遗漏了一些基本知识?
提前致谢
【问题讨论】:
-
16(或 32 字节)对齐是 X86-64 System V ABI 的要求。它需要正确对齐 C 库出于性能原因经常使用 XMM 寄存器和相关指令。这是正常行为。使用
register long long int sp asm ("rsp");并在没有扩展内联汇编的情况下将其作为变量访问是未定义的行为。幸运的是它有效。我想看看显然失败的原始代码。您能否向我们展示您调用系统的原始代码并且它失败了?这闻起来像 XY 问题 -
@MichaelPetch 实际上,“原始代码”是当我尝试一些面向返回的编程时,因为我试图将某些函数的返回地址覆盖为
system's -
那么问题出在那个代码上。编译器自己保留对齐。您的 ROB 代码有问题。它需要在根据 x86-64 ABI 调用
system之前保持该对齐。正如您建议的那样,将 RSP 向下舍入到最接近的 16 字节边界有效。我假设你用and rsp, -16之类的东西来做? ABI 指出,在函数调用点,堆栈指针需要对齐 16 字节(某些情况下为 32 字节)。而且像system这样的函数使用对齐向量指令来提高性能也没有错(这是正常的)。 -
@MichaelPetch 所以我的代码
rsp未对齐的原因是因为asm?我认为这是gcc的已知指令,除了它编译我的程序之外,它不应该也不会“搞砸”我的rsp吗? -
程序可以编译,但这并不意味着它们会正常运行。是的,您的装配是问题的原因。 GCC 维护自己代码的对齐,以确保满足对齐要求。它不知道任何正在运行的 ROB 代码。在调用
system之前,由 ROB 代码确保保持对齐。如果您在 ROB 代码中执行的任何操作都未对齐堆栈,则对齐它是您的工作。
标签: x86 segmentation-fault libc sse