为什么此 AT&T 汇编代码会出现分段错误?

Why does this AT&T assembly code give a segmentation fault?

这是我的第一个汇编源代码,我想使用scanf函数但是这个ELF给出了一个段错误。

所以,我尝试使用核心转储来解决,但我不能。

使用 scanf 函数

.section .data
 
string: 
.ascii "input yournumber : [=10=]"
 
value: 
.ascii "your value is %d \n[=10=]"

scanf:
.ascii "%d"

.section .text

.globl _start

_start:

movl %esp, %ebp
subl , %esp

pushl $string
call printf

leal -4(%ebp), %ebx
pushl %ebx
pushl $scanf
call scanf

pushl -4(%ebp)
pushl $value
call printf

pushl [=10=]
call exit

不要只查看核心转储,而是通过在 gdb 中启动可执行文件来查看您是如何到达那里的。 [=11] 的单步指令=].有关更多 GDB 提示,请参阅 https://whosebug.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 也可能会崩溃,因为当 ABI 允许它假定为 16 时,堆栈仅按 8 个字节对齐。堆栈在进入 [= 时按 16 对齐18=],但是你在 printf.

之前只做了 1 push + 1x sub , %esp

但大多数 Linux 发行版不使用 -msse2 构建 32 位代码,因此编译器不会使用 16 字节对齐要求 loads/stores 来复制东西. (x86-64 glibc 的 scanf 如果调用未对齐的堆栈会崩溃。)

而且您确实使用对齐的堆栈调用了 scanf;从条目到 _start 的总偏移量为 16,因为您不会在 printf returns 之后浪费指令清理堆栈。这是一个有效的选择,只要您意识到自己正在这样做。 (它值得评论,并且/或者您可以使用 mov 而不是 push 以便以后的参数重用那些相同的堆栈槽。)

我建议查看(优化的)编译器输出以获得满足您要求的函数。例如在 https://godbolt.org/