在读《Assembly Language Step by Step - Programming with Linux》[1]这本书时,里面有这样一段代码[2]:
; 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
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
上面的代码通过80h中断来调用Linux的syscall,来向屏幕输出Eat at Joe's!这串字符。这个代码在MacOS下不能直接用的,因为MacOS的内核是FreeBSD的port,所以syscall的调用要遵从FreeBSD的习惯。
上面的例子中我们可以看到,在Linux中给syscall传参数主要靠寄存器(eax, ebx, ecx...),而在MacOS中调用则靠堆栈。这是由于Linux和FreeBSD在内核的实现上面有很大不同。因此,可以将上面的代码改写,使其可以在MacOS下面编译运行。
以下是改造后的代码:
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!
_syscall:
int 0x80 ;system call
ret
_start:
nop ; This no-op keeps gdb happy...
push dword EatLen ; Pass the length of the message
push dword EatMsg ; Pass offset of the message
push dword 1 ; Specify File Descriptor 1: Standard Output
mov eax,0x4 ; System call number (sys_write)
call _syscall ; Make kernel call
add esp, 12 ; Clean stack (3 arguments * 4)
push dword 0 ; Return a code of zero
mov eax,0x1 ; Code for Exit Syscall
call _syscall ; Make kernel call
注意到参数都改用push入栈了,这是FreeBSD的习惯。
其次,我们将80h调用封装进了_syscall函数,这是FreeBSD的习惯(如果不将_syscall封装进函数直接调用,则还需将eax入栈,详细说明请参考[5])。
最后,用来编译代码的命令也不同:
nasm -f macho -g eatsyscall.asm
ld -e _start -o eatsyscall eatsyscall.o
MacOS使用macho格式来保存可执行文件,此外在使用ld连接时,需要指定入口地址为_start。
更多关于MacOS下汇编的知识可以参考[3]和[4]。
[1] http://www.amazon.com/Assembly-Language-Step-Step-Programming/dp/0470497025
[2] http://www.duntemann.com/assembly.html
[3] http://orangejuiceliberationfront.com/intel-assembler-on-mac-os-x/
[4] http://salahuddin66.blogspot.com/2009/08/nasm-in-mac-os-x.html
[5] http://www.freebsd.org/doc/en/books/developers-handbook/x86-system-calls.html