【问题标题】:Assembly Linux system calls vs assembly OS x system calls汇编 Linux 系统调用与汇编 OS x 系统调用
【发布时间】:2016-01-16 14:34:24
【问题描述】:

在我的 Mac 上运行汇编代码时遇到问题。我目前正在阅读 Jeff Duntemann 的《Assembly Step by Step》一书。问题在于它专注于为 32 位 linux 系统编写程序集。我使用的是 64 位 mac os x 系统。我仍然可以使用 nasm -f macho32 在我的 64 位系统上运行 32 位汇编,但显然 Duntemann 书中的代码不起作用,因为 Linux 和 mac os x 中的系统调用不同。我将如何转换这个程序:

;  Executable name : EATSYSCALL
;  Version         : 1.0
;  Created date    : 1/7/2009
;  Last update     : 2/18/2009
;  Author          : Jeff Duntemann
;  Description     : A simple program in assembly for Linux, using NASM 2.05,
;    demonstrating the use of Linux INT 80H syscalls to display text.
;
;  Build using these commands:
;    nasm -f elf -g -F stabs eatsyscall.asm
;    ld -o eatsyscall eatsyscall.o
;

 SECTION .data          ; Section containing initialised data

     EatMsg: db "Eat at Joe's!",10
     EatLen: equ $-EatMsg   

 SECTION .bss           ; Section containing uninitialized data 

 SECTION .text          ; Section containing code

 global     _start          ; Linker needs this to find the entry point!

_start:
     nop            ; This no-op keeps gdb happy...
     mov eax,4      ; Specify sys_write call
     mov ebx,1      ; Specify File Descriptor 1: Standard Output
     mov ecx,EatMsg     ; Pass offset of the message
     mov edx,EatLen     ; Pass the length of the message
     int 80H            ; Make kernel call

     mov eax,1      ; Code for Exit Syscall
     mov ebx,0      ; Return a code of zero 
     int 80H            ; Make kernel call

以便它可以在我的 mac os x 系统上运行?我更喜欢 32 位汇编的解决方案,因为我正在尝试学习它而不是更复杂的 64 位汇编。

我在网上找到了一个解决方案,但是它使用堆栈并且有其他差异,例如从 esp 寄存器中减去,即使 Duntemann 的程序根本没有引用 esp 寄存器:

global start

 section .text
 start:
    push    dword msg.len
       push    dword msg
    push    dword 1
    mov     eax, 4
    sub     esp, 4
    int     0x80
    add     esp, 16

    push    dword 0
    mov     eax, 1
    sub     esp, 12
    int     0x80

 section .data

 msg:    db      "Hello, world!", 10
.len:   equ     $ - msg

所以我想我想知道的是如何将linux系统调用转换为mac os x系统调用的逐步过程?这样,当我阅读这本书时,我就可以做到这一点,而不必在虚拟机上下载 linux 或其他东西。

【问题讨论】:

  • 正如我在your other question 中指出的,Mac OS X 系统调用不是公开记录的接口。本教程使用 32 位 Linux 系统。 (例如,在虚拟机中。)
  • 如果在 x86_64 上构建 x86,您应该使用nasm -f elf -o asmfile.o asmfile.asm 构建并链接ld -m elf_i386 -o progname asmfile.o for linux(您可以在 mac 上尝试,但如上所述 - 不保证)。至于系统调用号码,linux 有unistd_32.h(或者可能只是unistd.h,如果它只是32 位)。通常在/usr/include/asm、/usr/include/asm-x86 中找到。如果没有公开记录的接口,则您的 mac 上可能没有类似的接口。该文件包含 linux(32 位)系统上的所有系统调用号。
  • 另外,如果您注意到,您的两个版本正在做完全相同的事情并使用完全相同的系统调用4-syswrite, 1-exit。唯一的区别似乎是 mac 版本正在填充 ebx, ecx & edx 与推入堆栈的值以进行 syswrite 调用,然后将 0 (退出状态)推入堆栈而不是之前直接加载 ebx打电话给exit

标签: linux macos assembly x86 32-bit


【解决方案1】:

那个解决方案是错误的。 sub esp, 12 应该是 sub esp, 4。它没有传递 0 作为退出状态;它传递了一个垃圾值。

Mac OS X 有 BSD 系统调用。很难找到示例,因为大多数 BSD 程序不直接进行系统调用;它们链接到 libc 并调用 libc 中包装系统调用的函数。但在汇编语言中,直接系统调用可能比 libc 调用更简单。

对于 32 位 Intel 代码,OS X 和 Linux 都响应 int 0x80。它们都在eax 中获取系统调用号,并在eax 中返回结果。主要区别如下:

  • Linux 采用寄存器中的参数(ebx、ecx、edx),但 OS X 采用堆栈中的参数。您以相反的顺序推送参数,然后再推送 4 个字节。
  • 当发生错误时,Linux 会在eax 中输入一个负数,但 OS X 会在eax 中输入一个正数。 OS X 还设置了一个条件代码,以便 jb 或 jnb 在发生或未发生错误时跳转。
  • 系统调用有不同的编号,有些有不同的参数。

重要文件:syscalls.master

BSD 系统使用名为 syscalls.master 的文件来定义系统调用。我已经链接到syscalls.master from Mac OS X 10.4.11x86。我们可以使用它来查找每个系统调用的名称、参数和返回类型。例如:

4   PRE     NONE    ALL { user_ssize_t write(int fd, user_addr_t cbuf, user_size_t nbyte); } 

write(2) 系统调用编号为 4,因此我们使用 4 加载 eax。它有 3 个参数,因此我们以相反的顺序推送它们:我们推送缓冲区中的字节数,然后推送一个指针到缓冲区,然后推送文件描述符。推送参数后,我们推送了 4 个额外的字节,可能是 sub esp, 4。然后我们做int 0x80。然后我们可能想add esp, 16 删除我们推送的内容。

大多数参数和返回值都是 4 字节整数,但 OS X 中的 off_t 始终是 8 字节整数。我们必须小心像 lseek(2) 这样的调用。

199 NONE    NONE    ALL { off_t lseek(int fd, off_t offset, int whence); } 

offset 参数是一个 8 字节整数,因此我们将其作为一对 4 字节双字推送。 Intel 处理器是 little-endian,堆栈会向下增长,所以我们先推高 dword,然后再推低 dword。 lseek(2) 的返回类型也是 off_t。它在寄存器eax 和edx 中,低位字在eax,高位字在edx。

有些系统调用很奇怪。为了捕捉信号,OS X 没有对 signal(3) 的系统调用,这与 Linux 不同。我们必须使用 sigaction(2),但这很奇怪:

46  NONE    KERN    ALL { int sigaction(int signum, struct __sigaction *nsa, struct sigaction *osa); } 

第二个参数不是常规的 sigaction 结构。这是一个更大的结构,包括一个额外的蹦床字段。如果我们不在 libc 中调用 sigaction(),那么我们必须provide our own trampoline!它不同于 Linux 和其他 BSD 内核。

【讨论】:

  • jc/jnc更直接的表达了测试CF判断错误与否的语义。 (jb 的同义词,相同的操作码,但对人类的意义不同。)
猜你喜欢
  • 2012-01-20
  • 1970-01-01
  • 2010-12-18
  • 1970-01-01
  • 2012-05-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-08-14
相关资源
最近更新 更多