x86 asm printf在使用intel语法(gcc)时导致段错误

时间:2015-11-02 16:43:53

标签: gcc assembly x86 segmentation-fault printf

我刚开始学习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宏究竟是做什么的?

然而,这些只是一个侧面问题,因为我的真正的问题是我找不到使用更容易读/写/理解英特尔语法来运行此方法的方法。 / p>

以下是代码:

.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

运行与上面相同的内容,它只是打印"分段错误"。

我做错了什么?

1 个答案:

答案 0 :(得分:2)

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

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

push intout生成一条指令,读取从intout引用的地址开始的4个字节并将它们推送到堆栈(具体来说,它从ESP中减去4,然后将它们复制到4个字节现在由ESP指出。)行push $intout(或push OFFSET intout)在组件上推送组成intout的32位地址的4个字节。

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