[汇编]小议MacOS停的汇编环境

[汇编]小议MacOS下的汇编环境
在读《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