程序集Linux系统调用与程序集OS x系统调用

时间:2015-10-18 04:08:36

标签: linux macos assembly

我在mac上运行汇编代码时遇到问题。我目前正在阅读Jeff Duntemann的“逐步装配”一书。问题是它专注于为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。

1 个答案:

答案 0 :(得分:1)

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

Mac OS X有BSD系统调用。例子很难找到,因为大多数BSD程序都没有直接系统调用;它们链接到libc并在libc中调用函数来包装系统调用。但是在汇编语言中,直接系统调用可以比libc调用更简单。

对于32位英特尔代码,OS X和Linux都响应int 0x80。它们都使用eax中的系统调用号,并且它们都返回eax中的结果。主要区别在于:

  • Linux接受寄存器(ebxecxedx)中的参数,但OS X接受堆栈上的参数。你按相反的顺序推送参数,然后再推4个字节。
  • 当发生错误时,Linux会在eax中输入一个负数,但OS X会在eax中输入一个正数。 OS X还设置了一个条件代码,因此如果错误发生或未发生,jbjnb会跳转。
  • 系统调用具有不同的编号,有些具有不同的参数。

重要文件: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字节双字推送。英特尔处理器是小端的,并且堆栈会逐渐减少,因此我们在推动低位双击之前推高高位。 lseek(2)的返回类型也是off_t。它位于寄存器eaxedx中,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内核不同。