2019独角兽企业重金招聘Python工程师标准>>>
今天在学习 C 语言内嵌汇编的实验过程中,发现内嵌汇编极容易造成段错误。经过分析,我发现错误源于对于栈指针 esp 的不当操作导致的,现将整个问题记录如下。
实验需求是,在内存中分配一段空间用作函数调用栈。代码如下:
/* test.c */#include <stdio.h>#define STACK_SIZE 1024 // 1Kint main(void)
{unsigned char stack[STACK_SIZE];int i;// A. Initialize stackasm volatile ("pushl %%ebp\n\t" // store ebp"movl %%esp, %%ebp\n\t" // temp esp"movl %0, %%esp\n\t" // new esp"pushl %%ebp\n\t" // store old esp"movl %%esp, %%ebp\n\t" // new ebp :: "m" (stack));// Any codes here// Check the status of the stackfor (i = 0; i < STACK_SIZE; i++){printf("%02X ", stack[i]);}printf("\n");// Restore system-allocated stackasm volatile ("movl %%ebp, %%esp\n\t" // clear stack"popl %%ebp\n\t" // obtain old esp"movl %%ebp, %%esp\n\t" // restore esp"popl %%ebp\n\t" // restore ebp:: );return 0;
}
这段代码可以通过编译,然而运行的时候总是出现 `Segmentation fault`。没办法,我只能首先用 `gcc -S test.c -o test.s -m32` 查看生成的汇编代码。这里给出中间部分输出栈内容部分的汇编代码:
#NO_APPmovl $0, 24(%esp)jmp .L2
.L3:leal 28(%esp), %edxmovl 24(%esp), %eaxaddl %edx, %eaxmovzbl (%eax), %eaxmovzbl %al, %eaxmovl %eax, 4(%esp)movl $.LC0, (%esp)call printfaddl $1, 24(%esp)
.L2:cmpl $1023, 24(%esp)jle .L3movl $10, (%esp)call putchar
在更简单的情景中也可能因为 esp 和 ebp 的移动引起程序工作的异常。例如下面这段简单的代码:
/* test2.c */
#include <stdio.h>int main(void)
{int x = 2;asm volatile ("pushl %%esp\n\t":);printf("%X", x);return 0;
}
这个错误的来源也是因为压栈导致了 esp 指针的移动,从而在通过间接寻址时查找 x 的时候发生了异常。找到的不是 x ,而是我们刚刚压入的 esp 寄存器的部分字节。
因此,一个好习惯是:在 C 语言中内嵌汇编时,如果需要移动栈指针,必须在结束汇编代码前将栈指针恢复到内嵌汇编前的状态。