程序集 Linux 系统调用 vs 程序集 OS x 系统调用
Assembly Linux system calls vs assembly OS x system calls
我的 mac 上的 运行ning 汇编代码有问题。我目前正在阅读 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 系统调用的逐步过程?这样,当我阅读本书时,我就可以这样做,而不必在虚拟 machine 或其他东西上下载 linux。
该解决方案是错误的。 sub esp, 12
行应该是 sub esp, 4
。它没有传递 0 作为退出状态;它正在传递垃圾值。
Mac OS X 有 BSD 系统调用。很难找到例子,因为大多数 BSD 程序不直接进行系统调用;他们 link 到 libc 并调用 libc 中包装系统调用的函数。但是在汇编语言中,直接系统调用可以比libc调用更简单。
对于 32 位 Intel 代码,OS X 和 Linux 都响应 int 0x80
。他们都在 eax
中取系统调用号,他们都 return 结果在 eax
中。主要区别如下:
- Linux 在寄存器中获取参数(
ebx
、ecx
、edx
),但是 OS X 在堆栈中获取参数。您以相反的顺序推送参数,然后推送额外的 4 个字节。
- 发生错误时,Linux在
eax
中放入一个负数,但OS X在eax
中放入一个正数。 OS X 还设置了条件代码,因此 jb
或 jnb
会在错误发生或未发生时跳转。
- 系统调用有不同的编号,有些有不同的参数。
重要文件:syscalls.master
BSD 系统使用名为 syscalls.master 的文件来定义系统调用。我已经 link 编辑到 syscalls.master from Mac OS X 10.4.11x86。我们可以使用它来查找每个系统调用的名称、参数和 return 类型。例如:
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
删除我们推送的内容。
大多数参数和 return 值都是 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 处理器是小端,堆栈向下增长,所以我们在压入低位双字之前压入高位双字。 return 类型的 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 内核。
我的 mac 上的 运行ning 汇编代码有问题。我目前正在阅读 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 系统调用的逐步过程?这样,当我阅读本书时,我就可以这样做,而不必在虚拟 machine 或其他东西上下载 linux。
该解决方案是错误的。 sub esp, 12
行应该是 sub esp, 4
。它没有传递 0 作为退出状态;它正在传递垃圾值。
Mac OS X 有 BSD 系统调用。很难找到例子,因为大多数 BSD 程序不直接进行系统调用;他们 link 到 libc 并调用 libc 中包装系统调用的函数。但是在汇编语言中,直接系统调用可以比libc调用更简单。
对于 32 位 Intel 代码,OS X 和 Linux 都响应 int 0x80
。他们都在 eax
中取系统调用号,他们都 return 结果在 eax
中。主要区别如下:
- Linux 在寄存器中获取参数(
ebx
、ecx
、edx
),但是 OS X 在堆栈中获取参数。您以相反的顺序推送参数,然后推送额外的 4 个字节。 - 发生错误时,Linux在
eax
中放入一个负数,但OS X在eax
中放入一个正数。 OS X 还设置了条件代码,因此jb
或jnb
会在错误发生或未发生时跳转。 - 系统调用有不同的编号,有些有不同的参数。
重要文件:syscalls.master
BSD 系统使用名为 syscalls.master 的文件来定义系统调用。我已经 link 编辑到 syscalls.master from Mac OS X 10.4.11x86。我们可以使用它来查找每个系统调用的名称、参数和 return 类型。例如:
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
删除我们推送的内容。
大多数参数和 return 值都是 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 处理器是小端,堆栈向下增长,所以我们在压入低位双字之前压入高位双字。 return 类型的 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 内核。