我刚开始学习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
运行与上面相同的内容,它只是打印"分段错误"。
我做错了什么?
答案 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
最终将其解释为指针,一个应该存储格式字符串的地址,但由于它没有指向内存中的有效位置,程序崩溃。