在 x86 程序集中打印整数

Printing integer in x86 assembly

我正在尝试使用以下代码在 macOS High Sierra 上以 32 位 x86 程序集打印整数:

.cstring
STR_D:
.asciz "%d"
.globl _main

_main:
    movl 110, %edi     #moves constant int into register
    sub , %esp          #make space on stack
    movl %edi, 4(%esp)   #move int to stack
    movl $STR_D, 0(%esp) #move "%d" to stack
    call _printf
    add , %esp          #restore stack pointer
    ret

我正在使用命令编译

 gcc -m32 -Wl,-no_pie -o foo foo.s

然后执行可执行文件foo。然后我收到一条错误消息 "Illegal instruction: 4"。我很确定错误在于将字符串常量移动到堆栈的指令。如果我删除该行和以下函数调用,一切正常。 我做错了什么?

进一步information/question:我这样做是作为编写编译器项目的一部分。当我在 Linux 上执行这些指令时(当然会更改特定于平台的内容,例如 main 代替 _main,.data 代替 .cstring 等),它会起作用。为什么 macOS 不起作用?这与堆栈对齐有关吗?

我的编译器版本(通过 gcc -version 获得)是 Apple LLVM 9.0.0 (clang-900.0.39.2)。

好的,我找到问题所在了。它是双重的:

首先,我发现我缺少一个 .text 段。这显然是需要的,即使它是空的。这就是 "Illegal instruction: 4" 错误的原因。但是,如果您更正此错误,则会出现 SEGFAULT。

正如我在问题中所怀疑的那样,它与堆栈对齐有关。 macOS 的要求与 Linux 不同:栈在 macOS 上需要 16 位对齐。这意味着在执行函数调用(将 return 地址压入堆栈)之前,堆栈需要 12 对齐(意味着堆栈指针需要如下所示:0xnnnnnnnC)。为确保这一点,需要确保

1) 推送n次,其中n是3的倍数(推送3次、6次、9次等)

2)如果你自己修改栈指针(像我一样),确保符合12位要求

总而言之,适合我的代码如下所示:

.cstring
STR_D:
.asciz "%d"
.text                   #need this, even if it's empty
.globl _main

_main:
  movl 110, %edi     #moves constant int into register
  sub , %esp         #make space on stack, respecting alignment
  movl %edi, 4(%esp)    #move int to stack
  movl $STR_D, 0(%esp)  #move "%d" to stack
  call _printf
  add , %esp         #restore stack pointer
  ret

编辑:如果有人对堆栈对齐感兴趣,我发现 this website 上的解释非常有用!