I've been trying to get familiar with assembly on mac, and from what I can tell, the documentation is really sparse, and most books on the subject are for windows or linux. I thought I would be able to translate from linux to mac pretty easily, however this (linux)
.file "simple.c"
.text
.globl simple
.type simple, #function
simple:
pushl %ebp
movl %esp, %ebp
movl 8(%ebp), %edx
movl 12(%ebp), %eax
addl (%edx), %eax
movl %eax, (%edx)
popl %ebp
ret
.size simple, .-simple
.ident "GCC: (Ubuntu 4.3.2-1ubuntu11) 4.3.2"
.section .note.GNU-stack,"",#progbits
seems pretty different from this (mac)
.section __TEXT,__text,regular,pure_instructions
.globl _simple
.align 4, 0x90
_simple: ## #simple
.cfi_startproc
## BB#0:
pushq %rbp
Ltmp2:
.cfi_def_cfa_offset 16
Ltmp3:
.cfi_offset %rbp, -16
movq %rsp, %rbp
Ltmp4:
.cfi_def_cfa_register %rbp
addl (%rdi), %esi
movl %esi, (%rdi)
movl %esi, %eax
popq %rbp
ret
.cfi_endproc
.subsections_via_symbols
The "normal" (for lack of a better word) instructions and registers such as pushq %rbp don't worry me. But the "weird" ones like .cfi_startproc and Ltmp2: which are smack dab in the middle of the machine instructions don't make any sense.
I have no idea where to go to find out what these are and what they mean. I'm about to pull my hair out as I've been trying to find a good resource for beginners for months. Any suggestions?
To begin with, you're comparing 32-bit x86 assembly with 64-bit x86-64. While the OS X Mach-O ABI supports 32-bit IA32, I suspect you want the x86-64 SysV ABI. (Thankfully, the x86-64.org site seems to be up again). The Mach-O x86-64 model is essentially a variant of the ELF / SysV ABI, so the differences are relatively minor for user-space code, even with different assemblers.
The .cfi directives are DWARF debugging directives that you don't strictly need for assembly - they are used for call frame information, etc. Here are some minimal examples:
ELF x64-64 assembler:
.text
.p2align 4
.globl my_function
.type my_function,#function
my_function:
...
.L__some_address:
.size my_function,[.-my_function]
Mach-O x86-64 assembler:
.text
.p2align 4
.globl _my_function
_my_function:
...
L__some_address:
Short of writing an asm tutorial, the main differences between the assemblers are: leading underscores for Mach-O functions names, .L vs L for labels (destinations). The assembler with OS X understands the '.p2align' directive. .align 4, 0x90 essentially does the same thing.
Not all the directives in compiler-generated code are essential for the assembler to generate valid object code. They are required to generate stack frame (debugging) and exception handling data. Refer to the links for more information.
Obviously the Linux code is 32-bit Linux code. Note that 64-bit Linux can run both 32- and 64-bit code!
The Mac code is definitely 64-bit code.
This is the main difference.
The ".cfi_xxx" lines are only information used for the Mac specific file format.
Related
I have looked at similar questions, but cannot seem to find what is wrong with my code.
I am attempting to make the "write" syscall on MacOS to print a string to standard output.
I am able to do it with printf perfectly, and am familiar with calling other functions in x64 assembly.
This is, however, my first attempt at a syscall.
I am using GCC's GAS assembler.
This is my code:
.section __TEXT,__text
.globl _main
_main:
pushq %rbp
movq %rsp, %rbp
subq $32, %rsp
movq $0x20000004, %rax
movq $1, %rdi
leaq syscall_str(%rip), %rsi
movq $25, %rdx
syscall
jc error
xorq %rax, %rax
leave
ret
error:
movq $1, %rax
leave
ret
.section __DATA,__data
syscall_str:
.asciz "Printed with a syscall.\n"
There does not seem to be any error; there is simply nothing written to stdout.
I know that start is usually used as the starting point for an executable on MacOS, but it does not compile with GCC.
You are using the incorrect SYSCALL number for MacOS. The base for the user system calls is 0x2000000. You are incorrectly using that base. As a result you have encoded the write SYSCALL as $0x20000004 when it should have been $0x2000004 (one less zero)
As a rule of thumb, make sure you are using the correct value for the SYSCALL number in the %rax register; ensure you are using the correct arguments for the write SYSCALL. The write SYSCALL expects the following arguments:
%rdi: file descriptor to write to (e.g. 1 for standard output)
%rsi: pointer to the buffer containing the data to be written
%rdx: number of bytes to be written
On macOS, you need to use the syscall instruction to invoke a system call.
I am fairly new to assembly and architectures so I was playing around with the GCC to assemble my assembler file.
I am running Windows 10, with AMD 3750H (IDK if this helps)
It is a fairly simple program, that does the following:
Creates a stack frame
Pushes two numbers to the stack
Pops them one at a time, calling printf once for each. (So the last pop is after the first call)
exits
Here is the code I wrote:
.data
form:
.ascii "%d\n\0";
.text
.globl main
main:
pushq %rbp
movq %rsp, %rbp
subq $32, %rsp;
pushq $420;
pushq $69;
lea form(%rip) , %rcx;
xor %eax, %eax
popq %rdx
call printf
lea form(%rip) , %rcx;
xor %eax, %eax
popq %rdx
call printf
mov %rbp, %rsp
popq %rbp
ret
But the output I get is (rather strangely):
69
4199744
I read about shadow space in the Windows x64 calling convention but I couldn't find the proper way to work with it when using push/pop:
What is the 'shadow space' in x64 assembly?
gcc output on cygwin using stack space outside stack frame
This is what I tried (thanks to Jester) and it worked
# subtract 32 when I push
pushq $420;
subq $32, %rsp
# Add 32 when I pop
addq $32, %rsp
popq %rdx
But for some reason I feel there maybe a more elegant way to go about this
Do I have to leave 32 bytes after every push? That seems like a lot of space is being wasted.
I am trying to learn x86_64 assembly, and am using GCC as my assembler. The exact command I'm using is:
gcc -nostdlib tapydn.S -D__ASSEMBLY__
I'm mainly using gcc for its preprocessor. Here is tapydn.S:
.global _start
#include <asm-generic/unistd.h>
syscall=0x80
.text
_start:
movl $__NR_exit, %eax
movl $0x00, %ebx
int $syscall
This results in a segmentation fault. I believe the problem is with the following line:
movl $__NR_exit, %eax
I used __NR_exit because it was more descriptive than some magic number. However, it appears that my usage of it is incorrect. I believe this to be the case because when I change the line in question to the following, it runs fine:
movl $0x01, %eax
Further backing up this trail of thought is the contents of usr/include/asm-generic/unistd.h:
#define __NR_exit 93
__SYSCALL(__NR_exit, sys_exit)
I expected the value of __NR_exit to be 1, not 93! Clearly I am misunderstanding its purpose and consequently its usage. For all I know, I'm getting lucky with the $0x01 case working (much like undefined behaviour in C++), so I kept digging...
Next, I looked for the definition of sys_exit. I couldn't find it. I tried using it anyway as follows (with and without the preceeding $):
movl $sys_exit, %eax
This wouldn't link:
/tmp/cc7tEUtC.o: In function `_start':
(.text+0x1): undefined reference to `sys_exit'
collect2: error: ld returned 1 exit status
My guess is that it's a symbol in one of the system libraries and I'm not linking it due to my passing -nostdlib to GCC. I'd like to avoid linking such a large library for just one symbol if possible.
In response to Jester's comment about mixing 32 and 64 bit constants, I tried using the value 0x3C as suggested:
movq $0x3C, %eax
movq $0x00, %ebx
This also resulting a segmentation fault. I also tried swapping out eax and ebx for rax and rbx:
movq $0x3C, %rax
movq $0x00, %rbx
The segmentation fault remained.
Jester then commented stating that I should be using syscall rather than int $0x80:
.global _start
#include <asm-generic/unistd.h>
.text
_start:
movq $0x3C, %rax
movq $0x00, %rbx
syscall
This works, but I was later informed that I should be using rdi instead of rbx as per the System V AMD64 ABI:
movq $0x00, %rdi
This also works fine, but still ends up using the magic number 0x3C for the system call number.
Wrapping up, my questions are as follows:
What is the correct usage of __NR_exit?
What should I be using instead of a magic number for the exit system call?
The correct header file to get the system call numbers is sys/syscall.h. The constants are called SYS_### where ### is the name of the system call you are interested in. The __NR_### macros are implementation details and should not be used. As a rule of thumb, if an identifier begins with an underscore it should not be used, if it begins with two it should definitely not be used. The arguments go into rdi, rsi, rdx, r10, r8, and r9. Here is a sample program for Linux:
#include <sys/syscall.h>
.globl _start
_start:
mov $SYS_exit,%eax
xor %edi,%edi
syscall
These conventions are mostly portable to other UNIX-like operating systems.
I'm learning the basics of x86 via this free book.
Keep in mind this is specific to macOS x86 compared to Linux x86.
Its made for GNU Linux, so I have to change some of the code which is probably where I went wrong. I took this code snippet:
.section .data
.section .text
.globl _start
_start:
movl $1, %eax
movl $0, %ebx
int $0x80
After a bit of googling about x86 on macOS I turned that bit of code into this:
.data
.text
.globl _main
_main:
movl $1, %eax
movl $0, %ebx
int $0x80
I compiled this using gcc test.s which compiles it into a.out. When trying to run it using ./a.out I get the error [1] 17301 illegal hardware instruction ./a.out.
Any help is appreciated, thanks!
#Jester helped me out. You can view the comment on my question, but basically call convention is different for macOS. I found this resource which helped me out.
I'm trying to follow the book Professional Asssembly Language on Mac OS X Montain Lion.
On google I found a port for Mac OS X at the following url: Assembly on MacOS X
Created the file with Vim and compiled it with GAS:
as -g -arch i386 -o cpuid.o cpuid.s
Linked the code using gcc:
gcc -m32 -arch i386 -o cpuid cpuid.o
The resulting executable cpuid, runs without errors but if I try to debug it with gdb at the end it says Program exited with code 044 instead of Program exited normally.
Trying to find a way to make it exit correctly I've created an hello world example in C and generated assembly code it with:
gcc -Wall -03 -m32 -fno-PIC hello_pf.c -S -o hello_pf.s
The resulting assembly code is bellow:
.section __TEXT,__text,regular,pure_instructions
.globl _main
.align 4, 0x90
_main:
pushl %ebp
movl %esp, %ebp
subl $24, %esp
leal L_.str, %eax
movl %eax, (%esp)
call _puts
movl $0, -8(%ebp)
movl -8(%ebp), %eax
movl %eax, -4(%ebp)
movl -4(%ebp), %eax
addl $24, %esp
popl %ebp
ret
.section __TEXT,__cstring,cstring_literals
L_.str:
.asciz "Hello world!\n"
.subsections_via_symbols
Can someone provide any help regarding this issue?
How can I make a working version of cpuid from the link provided above using IA-32 mac ox s assembly?
Where can I look for a detailed description of stack align problem in Mac OS X? I've read what's on Apple site but for a beginner is not very helpful.
What are for the instructions after call _puts from the above sample code?
How does calling libc functions from assembly really works? Any good detailed articles on this topic?
Thank you!
First you need to understand the register usage in the calling conventions, a good place for that is
http://www.agner.org/optimize/calling_conventions.pdf
You will find that on Mac OS X 64-bit the return value for a function returning an "int" - such as main() - is in %rax. You seem to want to use a 32-bit executable, in which case the return value is in %eax. One convenient way to zero out a register is to XOR it with itself, so you should add this to the end of your routine:
xorl %eax,%eax
That'll set %eax to zero, and that will be your exit code.