为啥这个 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 函数。


printfscanf 也可能会崩溃,因为堆栈仅对齐 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 汇编代码会出现分段错误?的主要内容,如果未能解决你的问题,请参考以下文章

刚开始使用汇编(GAS),并且在这个短代码中有分段错误

在 x86_64 AT&T 中调用 scanf 时出现分段错误

为啥这个向量代码会出现分段错误?

AT&T汇编语法与x86语法基本区别

英特尔 AT&T 汇编程序的逐步执行?

用不到 4 行汇编编写这个练习 AT&T