为什么此 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 函数
printf
或 scanf
也可能会崩溃,因为当 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/
这是我的第一个汇编源代码,我想使用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
.
printf
或 scanf
也可能会崩溃,因为当 ABI 允许它假定为 16 时,堆栈仅按 8 个字节对齐。堆栈在进入 [= 时按 16 对齐18=],但是你在 printf.
push
+ 1x sub , %esp
但大多数 Linux 发行版不使用 -msse2
构建 32 位代码,因此编译器不会使用 16 字节对齐要求 loads/stores 来复制东西. (x86-64 glibc 的 scanf
如果调用未对齐的堆栈会崩溃。)
而且您确实使用对齐的堆栈调用了 scanf
;从条目到 _start
的总偏移量为 16,因为您不会在 printf
returns 之后浪费指令清理堆栈。这是一个有效的选择,只要您意识到自己正在这样做。 (它值得评论,并且/或者您可以使用 mov
而不是 push
以便以后的参数重用那些相同的堆栈槽。)
我建议查看(优化的)编译器输出以获得满足您要求的函数。例如在 https://godbolt.org/