问题
I have a problem with running assembly code on my mac. I am currently going through Jeff Duntemann's book Assembly Step by Step. The problem is that it focuses on writing assembly for 32 bit linux systems. I am using a 64 bit mac os x system. I can still run 32 bit assembly on my 64 bit system using nasm -f macho32, but apparently the code from Duntemann's book does not work because the system calls in Linux and mac os x are different. How would I convert this program:
; 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
so that it would run on my mac os x system? I would prefer a solution that in 32 bit assembly because I am trying to learn that instead of 64 bit assembly which is much more complicated.
I have found a solution online, but it uses the stack and has other differences such as subtracting from the esp register even though Duntemann's program doesn't reference the esp register at all:
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
So I guess what I want to know is a step by step process of how to convert a linux system call to a mac os x system call? That way as I'm going through this book I can just do that instead of having to download linux on a virtual machine or something.
回答1:
That solution is wrong. The line sub esp, 12
should be sub esp, 4
. It's not passing 0 as the exit status; it's passing a garbage value.
Mac OS X has BSD system calls. Examples are hard to find, because most programs for BSD don't make direct system calls; they link to libc and call functions in libc that wrap the system calls. But in assembly language, direct system calls can be simpler than libc calls.
For 32-bit Intel code, OS X and Linux both respond to int 0x80
. They both take the system call number in eax
, and they both return the result in eax
. The major differences are these:
- Linux takes the arguments in registers (
ebx
,ecx
,edx
), but OS X takes the arguments on the stack. You push the arguments in reverse order, then push an extra 4 bytes. - When an error happens, Linux puts a negative number in
eax
, but OS X puts a positive number ineax
. OS X also sets a condition code sojb
orjnb
would jump if an error did or did not happen. - The system calls have different numbers, and some have different arguments.
Important file: syscalls.master
BSD systems use a file named syscalls.master to define system calls. I have linked to syscalls.master from Mac OS X 10.4.11x86. We can use it to find the name, arguments, and return type for each system call. For example:
4 PRE NONE ALL { user_ssize_t write(int fd, user_addr_t cbuf, user_size_t nbyte); }
The write(2) system call is number 4, so we load eax
with 4. It has 3 arguments, so we push them in reverse order: we push the number of bytes in the buffer, then push a pointer to the buffer, then push the file descriptor. After pushing the arguments, we push 4 extra bytes, perhaps with sub esp, 4
. Then we do int 0x80
. Then we probably want to add esp, 16
to remove what we pushed.
Most arguments and return values are 4-byte integers, but off_t
in OS X is always an 8-byte integer. We must be careful with calls like lseek(2).
199 NONE NONE ALL { off_t lseek(int fd, off_t offset, int whence); }
The offset argument is an 8-byte integer, so we push it as a pair of 4-byte double-words. Intel processors are little-endian, and the stack grows down, so we push the high dword before pushing the low dword. The return type of lseek(2) is also an off_t. It comes in registers eax
and edx
, with the low word in eax
and the high word in edx
.
Some system calls are strange. To catch a signal, OS X has no system call for signal(3), unlike Linux. We must use sigaction(2), but it's strange:
46 NONE KERN ALL { int sigaction(int signum, struct __sigaction *nsa, struct sigaction *osa); }
The second argument isn't a regular sigaction struct. It's a bigger struct that includes an extra field for a trampoline. If we don't call sigaction() in libc, then we must provide our own trampoline! It's different from Linux and from other BSD kernels.
来源:https://stackoverflow.com/questions/33194207/assembly-linux-system-calls-vs-assembly-os-x-system-calls