32 位 OS/X 代码通过 int 0x80 进行系统调用
代码:
segment .text
global _start
_start:
mov eax,1 ; 1 is the syscall number for exit
mov ebx,5 ; 5 is the value to return
int 0x80 ; execute a system call
建议您使用 32 位 Linux 教程。我做出这个结论是因为32 位 Linux ABI https://www.uclibc.org/docs/psABI-i386.pdf使用寄存器将参数传递给内核int 0x80
。 OS/X 则不同。您在堆栈上传递参数(从右到左传递它们)。在 32 位 OS/X 中,它看起来像:
global start
section .text
start:
; sys_write syscall
; See: https://opensource.apple.com/source/xnu/xnu-1504.3.12/bsd/kern/syscalls.master
; 4 AUE_NULL ALL { user_ssize_t write(int fd, user_addr_t cbuf, user_size_t nbyte); }
push dword msg.len ; Last argument is length
push dword msg ; 2nd last is pointer to string
push dword 1 ; 1st argument is File descriptor (1=STDOUT)
mov eax, 4 ; eax = 4 is write system call
sub esp, 4 ; On OS/X 32-bit code always need to allocate 4 bytes on stack
int 0x80
; sys_exit
; 1 AUE_EXIT ALL { void exit(int rval); }
push dword 42 ; Return value
mov eax, 1 ; eax=1 is exit system call
sub esp, 4 ; allocate 4 bytes on stack
int 0x80
section .rodata
msg: db "Hello, world!", 10
.len: equ $ - msg
组装并链接:
nasm -f macho testexit.asm
ld -macosx_version_min 10.7.0 -o testexit testexit.o
./testexit
echo $?
YASM参数应该与NASM。它应该输出:
Hello, world!
42
32 位 OS/X 代码中系统调用的经验法则:
Apple 在其文档中记录了 OS/X 系统调用website https://opensource.apple.com/source/xnu/xnu-1504.3.12/bsd/kern/syscalls.master.
64 位 OS/X 代码通过 SYSCALL 指令进行系统调用
64 位 OS/X 几乎使用与 64 位 Linux 相同的内核调用约定。这64 位 Linux 系统 V ABI https://www.uclibc.org/docs/psABI-x86_64.pdf适用于系统调用。特别是该部分A.2 AMD64 Linux 内核约定。该部分有以下规则:
- 用户级应用程序用作整数寄存器来传递序列
%rdi、%rsi、%rdx、%rcx、%r8 和 %r9。内核接口使用%rdi,
%rsi、%rdx、%r10、%r8 和 %r9。
- 系统调用是通过 syscall 指令完成的。内核破坏
寄存器 %rcx 和 %r11。
- 系统调用的编号必须在寄存器 %rax 中传递。
- 系统调用仅限于六个参数,没有参数直接传递
堆栈。
- 从系统调用返回,寄存器 %rax 包含以下结果
系统调用。 -4095 和 -1 之间的值表示错误,
它是-errno。
- 仅 INTEGER 类或 MEMORY 类的值会传递到内核。
64 位 OS/X 使用相同的系统调用号码 https://opensource.apple.com/source/xnu/xnu-1504.3.12/bsd/kern/syscalls.master与 32 位 OS/X 一样,但是所有数字都必须添加 0x02000000。上面的代码可以修改为 64 位 OS/X 程序:
global start
section .text
start:
mov eax, 0x2000004 ; write system call
mov edi, 1 ; stdout = 1
mov rsi, msg ; address of the message to print
;lea rsi, [rel msg]; Alternative way using RIP relative addressing
mov edx, msg.len ; length of message
syscall ; Use syscall, NOT int 0x80
mov eax, 0x2000001 ; exit system call
mov edi, 42 ; return 42 when exiting
syscall
section .rodata
msg: db "Hello, world!", 10
.len: equ $ - msg
请注意,写入 32 位寄存器时,CPU 会自动将零扩展到 64 位寄存器。上面的代码通过写入寄存器来使用此功能,例如EAX, EDI代替RAX and RDI。您可以使用 64 位寄存器,但使用 32 位寄存器可以在代码中节省一个字节。
组装并链接:
nasm -f macho64 testexit64.asm
ld -macosx_version_min 10.7.0 -lSystem -o testexit64 testexit64.o
./testexit64
echo $?
它应该输出:
Hello, world!
42
Note:其中一些信息本质上与此类似OS/X 教程 https://filippo.io/making-system-calls-from-assembly-in-mac-os-x/修复了一些更正和编码错误。