我在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。
答案 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
中的结果。主要区别在于:
ebx
,ecx
,edx
)中的参数,但OS X接受堆栈上的参数。你按相反的顺序推送参数,然后再推4个字节。eax
中输入一个负数,但OS X会在eax
中输入一个正数。 OS X还设置了一个条件代码,因此如果错误发生或未发生,jb
或jnb
会跳转。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。它位于寄存器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内核不同。