为啥这个 AT&T 汇编代码会出现分段错误?
Posted
技术标签:
【中文标题】为啥这个 AT&T 汇编代码会出现分段错误?【英文标题】:Why does this AT&T assembly code give a segmentation fault?为什么这个 AT&T 汇编代码会出现分段错误? 【发布时间】:2018-08-08 08:46:28 【问题描述】:这是我的第一个汇编源代码,我想使用 scanf
函数但是这个 ELF 给出了分段错误。
所以,我尝试使用核心转储来解决,但我不能。
使用scanf函数
.section .data
string:
.ascii "input yournumber : \0"
value:
.ascii "your value is %d \n\0"
scanf:
.ascii "%d"
.section .text
.globl _start
_start:
movl %esp, %ebp
subl $4, %esp
pushl $string
call printf
leal -4(%ebp), %ebx
pushl %ebx
pushl $scanf
call scanf
pushl -4(%ebp)
pushl $value
call printf
pushl $0
call exit
【问题讨论】:
你是如何构建和运行它的?如果你静态链接它,或者你在 MinGW 上尝试过这个,那么 libc 不会被初始化,因为你是从_start
运行它的。
【参考方案1】:
不要只查看核心转储,而是通过在 gdb
中启动可执行文件来查看您是如何到达那里的。 使用 si
的单步指令。更多 GDB 技巧见底部https://***.com/tags/x86/info。
您有大量可能导致崩溃的错误,其中一些肯定会。
您定义了一个scanf
符号,所以call scanf
跳转到那里。
它位于.data
部分,因此该页面可能无法执行。
或者,考虑到这些寄存器值,"%d"
的字节可能会解码为崩溃的 x86 机器代码。
对 scanf 格式字符串使用不同的符号名称,例如 scanf_fmt
。另外不要忘记使用.asciz
以零终止隐式长度的 C 字符串。
如果您静态链接它,或者您在 MinGW 或 Cygwin 上尝试过此操作,则 libc 不会初始化,因为您定义的是 _start
而不是 main
。在动态链接的 Linux 可执行文件中,glibc 使用动态链接器挂钩来调用其 init 函数。即便如此,不建议使用来自_start
的 libc 函数。
printf
或 scanf
也可能会崩溃,因为堆栈仅对齐 8 个字节,而 ABI 允许它假定为 16。堆栈在进入 _start
时按 16 对齐,但你只做1 push
+ 1x sub $4, %esp
在 printf 之前。
但大多数 Linux 发行版不使用 -msse2
构建 32 位代码,因此编译器不会使用 16 字节对齐要求的加载/存储来复制内容。 (x86-64 glibc 的 scanf
如果使用对齐不足的堆栈调用确实会崩溃。)
实际上,您确实使用对齐的堆栈调用了scanf
;从入口到_start
的总偏移量为 16,因为您不会浪费在printf
返回后清理堆栈的指令。这是一个有效的选择,只要您知道自己正在这样做。 (它值得评论,和/或您可以使用 mov
而不是 push
以便以后的 args 重用这些相同的堆栈槽。)
我建议您查看(优化的)编译器输出,以获得您想要的功能。例如在https://godbolt.org/
【讨论】:
以上是关于为啥这个 AT&T 汇编代码会出现分段错误?的主要内容,如果未能解决你的问题,请参考以下文章