了解OSX 16字节对齐

时间:2014-01-26 18:30:36

标签: macos assembly

所以似乎everyone知道OSX系统调用总是16字节堆栈对齐。很好,当你有这样的代码时,这是有道理的:

section .data
  message db 'something', 10, 0

section .text
  global start

start:
push    10         ; size of the message (4 bytes)
push    msg        ; the address of the message (4 bytes)
push    1          ; we want to write to STD_OUT (4 bytes)
mov     eax, 4     ; write(...) syscall
sub     esp, 4     ; move stack pointer down to 4 bytes for a total of 16.
int     0x80       ; invoke
add     esp, 16    ; clean

完美,堆栈对齐到16个字节,非常有意义。虽然我们调用syscall(1)(exit)怎么样。逻辑上看起来像这样:

push    69         ; return value
mov     eax, 1    ; exit(...) syscall
sub     esp, 12   ; push down stack for total of 16 bytes.
int     0x80      ; invoke

虽然这不起作用,但确实如此:

push    69         ; return value
mov     eax, 1    ; exit(...) syscall
sub     esp, 4    ; push down stack for total of 8 bytes.
int     0x80      ; invoke

工作正常,但那只有8个字节???? Osx很酷,但这个ABI让我疯狂。有人可以对我不理解的内容有所了解吗?

1 个答案:

答案 0 :(得分:3)

简短版本:你可能不需要对齐到16个字节,你只需要在你的参数列表之前留下4个字节的间隙。

长版:

这就是我认为正在发生的事情:我不确定堆栈应该是16字节对齐的。但是,逻辑规定如果是,并且如果填充或调整堆栈是实现该对齐所必需的,则必须在之前发生系统调用的参数,而不是之后。在int 0x80指令时,堆栈指针与参数实际位置之间不能有任意数量的字节。内核不知道在哪里可以找到实际的参数。在推送参数以实现“对齐”之后从堆栈指针中减去不对齐参数,它通过在堆栈指针和参数之间插入任意数量的字节来对齐堆栈指针。无论其他什么都可能是真的,这是不对的。

那为什么第一个和第三个片段一直在工作?他们也不会在那里插入任意字节吗?他们偶然工作。这是因为它们都碰巧插入了4个字节。这种调整不是“成功”,因为它实现了堆栈对齐,它是系统调用ABI的一部分。显然,系统调用ABI期望并要求在参数列表之前有一个4字节的插槽。

可以找到syscall()函数的来源here。它看起来像这样:

LEAF(___syscall, 0)
    popl    %ecx        // ret addr
    popl    %eax        // syscall number
    pushl   %ecx
    UNIX_SYSCALL_TRAP
    movl    (%esp),%edx // add one element to stack so
    pushl   %ecx        // caller "pop" will work
    jnb 2f
    BRANCH_EXTERN(cerror)
2:
END(___syscall)

要调用此库函数,调用者将设置堆栈指针以指向syscall()函数的参数,该参数以系统调用号开头,然后具有实际系统调用的实参数。但是,调用者将使用call指令来调用它,将返回地址压入堆栈。

因此,上面的代码弹出返回地址,将系统调用号弹出到%eax,将返回地址推回到堆栈(系统调用号最初所在的位置),然后执行int 0x80。因此,堆栈指针指向返回地址,然后指向参数。还有4个字节:返回地址。我怀疑内核忽略了返回地址。我猜它在syscall ABI中的存在可能只是使系统调用的ABI类似于函数调用的ABI。

这对系统调用的对齐要求意味着什么?好吧,这个函数可以保证改变堆栈的对齐方式,使其与调用者设置的方式不同。调用者可能设置了16字节对齐的堆栈,此函数在中断之前将其移动4个字节。对于系统调用来说,堆栈需要16字节对齐可能只是一个神话。另一方面,对于调用系统库函数,16字节对齐要求绝对是真实的。我开发的葡萄酒项目被它烧毁了。它主要是128位SSE参数数据类型所必需的,但是如果alignemtn错误,即使对于不使用这些参数的函数,如果早期发现问题,Apple也会故意放弃他们的懒惰符号解析器。 Syscalls不会受到早期失败机制的影响。可能是内核不需要16字节对齐。我不确定是否有任何系统调用采用128位参数。