【问题标题】:x86 asm printf causes segfault when using intel syntax (gcc)x86 asm printf 使用 intel 语法 (gcc) 时会导致段错误
【发布时间】:2016-02-02 15:10:31
【问题描述】:

我刚开始学习 x86 汇编,我有点困惑为什么这个小例子不起作用。我要做的就是将 eax 寄存器的内容打印为十进制值。这是我在 AT&T 语法中的代码:

.data
intout:
    .string "%d\n"
.text
.globl main

main:

movl $666, %eax
pushl %eax
pushl $intout
call printf

movl $1, %eax
int $0x80

我编译运行如下:

gcc -m32 -o hello helloworld.S
./hello

这是例外情况(将 666 打印到控制台)。顺便说一句,我想指出我不明白 "movl $1, %eax""int $0x80" 到底应该做什么在这里完成。我也不确定 "pushl $intout" 做了什么。为什么我的输出由两个单独的堆栈条目组成? .string 宏到底是做什么的?

不过,这些只是附带问题,因为我的真正问题是我找不到使用更容易读/写/理解 Intel 语法来运行此程序的方法。

代码如下:

.intel_syntax noprefix

.data
    intout:
        .string "%d\n"
.text
.globl main

main:

mov eax, 666
push eax
push intout
call printf

mov eax, 1
int 0x80

和上面一样运行,它只是打印“Segmentation fault”。

我做错了什么?

【问题讨论】:

  • 其实不推荐使用movl $1,%eax; int $0x80。如果您按原样使用 C 库,则应该只使用 call exit。或者你可以ret,假设你正确地清理了你没有做的堆栈。您有 2 个条目,因为您传递了 2 个参数(格式字符串和数字)。至于.string 做了什么,我想你可以猜到,或者,你知道的,阅读manual
  • int 0x80和call exit有什么区别?
  • 前者是直接的系统调用,没有机会让 C 库正常关闭。而且它的便携性也较差。
  • 所以 C 库基本上把它抽象掉了,基于它编译的系统?

标签: gcc assembly x86 segmentation-fault printf


【解决方案1】:

您需要使用push OFFSET intout 否则存储在intout 的32 位值将被压入堆栈,而不是其地址。

intout 只是一个标签,它基本上是分配给程序中地址的名称。紧随其后的.string "%d\n" 指令定义了程序中的字节序列,既分配内存又初始化该内存。具体来说,它在.data 部分中分配了4 个字节,并用字符'%''d''\n''\0' 初始化它们。由于标签 intout 是在 .string 行之前定义的,因此它具有字符串中第一个字节的地址。

push intout 行产生一条指令,该指令从 inut 引用的地址开始读取 4 个字节并将它们压入堆栈(特别是它从 ESP 中减去 4,然后将它们复制到现在指向的 4 个字节由 ESP 发送。)push $intout(或push OFFSET intout)行将构成intout 的32 位地址的4 个字节压入堆栈。

这意味着push intout 行将一个无意义的值压入堆栈。函数printf 最终将其解释为一个指针,一个应该存储格式字符串的地址,但由于它没有指向内存中的有效位置,所以程序崩溃了。

【讨论】:

  • 这只适用于 Intel 语法吗?
  • at&t 语法中的$ 也是如此。
  • 啊,明白了,谢谢。这到底是如何工作的? inut 存储在哪里,为什么它知道要格式化的数字在堆栈上。我很混乱。是否有任何指南可以更好地了解其工作原理。到目前为止,我的讲座只涉及寄存器和流控制。
猜你喜欢
  • 2015-09-03
  • 2011-10-16
  • 1970-01-01
  • 1970-01-01
  • 2020-08-22
  • 2016-01-07
  • 1970-01-01
  • 2020-08-11
  • 1970-01-01
相关资源
最近更新 更多