【问题标题】:making system call from 32-bit assembly in mac os x failed在 mac os x 中从 32 位程序集进行系统调用失败
【发布时间】:2014-08-31 11:25:20
【问题描述】:

我尝试编写一个简短的 32 位汇编程序来测试 mac os x 中的系统调用。

我编写了如下所示的第一个版本,调用 write() 函数在屏幕上打印一个字符串。它确实有效。

.data
str:
    .ascii  "Hello World\n"

.text
.globl  _main
_main:
    pushl   $12
    pushl   $str                
    pushl   $1                  
    movl    $4, %eax            
    subl    $4, %esp            
    int     $0x80               
    addl    $16, %esp          
    pushl   $0                  
    movl    $1, %eax            
    subl    $12, %esp           
    int     $0x80 

在此之后,我决定编写另一个版本,使用 mkdir() 在“/tmp”中创建一个新目录,但我失败了。 mkdir() 的系统调用号是 0x88,我的代码如下所示。谁能告诉我我错在哪里。非常感谢。

.data
path:
    .ascii "/tmp/new_dir"

.text
.globl _main
_main:
    nop
    pushl   $path
    pushl   $0x1ff
    movl    $0x88, %eax
    subl    $8, %esp
    int     $0x80
    addl    $16, %esp

    pushl   $0                 
    movl    $1, %eax            
    subl    $12, %esp          
    int     $0x80 

【问题讨论】:

  • OS X 需要堆栈以 16 字节对齐。我已经推送了 8 个字节。所以,我需要subl $8 来对齐堆栈。
  • 堆栈对齐必须发生在你推送参数之前。否则,被调用者怎么知道在哪里可以找到参数?请参阅我的回答 here 了解更多关于发生了什么。
  • 是的,你是对的。对齐应该放在 push 参数之前。谢谢。

标签: macos assembly system-calls 32-bit


【解决方案1】:

Here 我找到了一份关于 MacOS 系统调用的简单指南。根据我的阅读,我假设%eax 寄存器的值是唯一通过寄存器传递给系统调用的值。其余寄存器必须以EDX 开始压入堆栈。

+----------+------------------------+--------------------------------+
| Register |          Use           |            Location            |
+----------+------------------------+--------------------------------+
| EAX      | Unused, just a padding | [ESP]                          |
| EBX      | Name of directory      | [ESP+4]                        |
| ECX      | Access                 | [ESP+8]                        |
| EDX      | Unused                 | Unused, but let's say [ESP+12] |
+----------+------------------------+--------------------------------+

根据此表,您的mkdir 调用应如下所示:

pushl $0        /* for EDX, unused */
pushl $0x1FF    /* for ECX */
pushl $path /* for EBX */
movl $0x88, %eax
pushl %eax     /* for EAX but unused */
int $0x80
addl $16, %esp  /* clean up */

另外,您的exit 调用似乎也不好(我不确定,我对这种系统调用方式不是很熟悉)

subl %esp, $8 /* unused EDX and ECX */
pushl $0      /* for EBX */
movl $1, %eax
pushl %eax    /* unused EAX */
int 0x80

编辑: 在挖掘了一些关于 BSD 系统调用主题的材料后,我还找到了一个建议让堆栈对齐 16 字节的来源。这意味着当您调用少于 4 个参数的系统调用时,您应该推送额外的双字,当您调用具有 5 个或全部 6 个参数的系统调用时,您还应该推送一些双字(如果免费)ESI、@987654330 @ 和ESP

【讨论】:

  • 感谢您的回答。但是我想知道如何处理超过六个参数?有书说,如果需要超过六个参数,%ebx 寄存器用于包含指向输入参数的内存位置的指针,按顺序存储。你能告诉我一个处理超过六个参数的例子吗?
  • 据我所知,特定的系统调用通常有自己的附加参数格式。您对哪个系统调用感兴趣?
  • 我从未读过 32 位 abi,​​但我假设您使用了一些通用寄存器,然后将其余的放在堆栈上。至少这是 64 位 abi 指定的
  • 不,x86 和 x86-64 系统调用调用约定不同。 xyzBSD 默认使用非unix 方式将系统调用参数传递给系统调用过程。不过,有一种方法可以更改它 (brandelf)。
  • 你的表很混乱。 32 位 BSD / MacOS 系统调用不关心 EAX = 调用号以外的寄存器。我认为该表用于移植 Linux 系统调用,但问题中没有任何内容试图使用寄存器,所以你只是突然引入它,让它看起来像寄存器一样重要。不,你不必推动 EAX。在普通的 BSD 系统调用包装函数(在 libc 中)中,该堆栈槽包含一个返回地址,因此包装函数可以将 int $0x80 与函数调用约定放置的 args 一起使用。
猜你喜欢
  • 2016-01-12
  • 1970-01-01
  • 2015-09-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-18
  • 2011-03-19
  • 1970-01-01
相关资源
最近更新 更多