I am playing around and trying to understand the low-level operation of computers and programs. To that end, I am experimenting with linking Assembly and C.
I have 2 program files:
Some C code here in "callee.c":
#include <unistd.h>void my_c_func() { write(1, "Hello, World!\n", 14); return;}
I also have some GAS x86_64 Assembly here in "caller.asm":
.text.globl my_entry_ptmy_entry_pt: # call my c function call my_c_func # this function has no parameters and no return data # make the 'exit' system call mov $60, %rax # set the syscall to the index of 'exit' (60) mov $0, %rdi # set the single parameter, the exit code to 0 for normal exit syscall
I can build and execute the program like this:
$ as ./caller.asm -o ./caller.obj$ gcc -c ./callee.c -o ./callee.obj$ ld -e my_entry_pt -lc ./callee.obj ./caller.obj -o ./prog.out -dynamic-linker /lib64/ld-linux-x86-64.so.2$ ldd ./prog.out linux-vdso.so.1 (0x00007fffdb8fe000) libc.so.6 => /lib64/libc.so.6 (0x00007f46c7756000) /lib64/ld-linux-x86-64.so.2 (0x00007f46c7942000)$ ./prog.outHello, World!
Along the way, I had some problems. If I don't set the -dynamic-linker option, it defaults to this:
$ ld -e my_entry_pt -lc ./callee.obj ./caller.obj -o ./prog.out$ ldd ./prog.out linux-vdso.so.1 (0x00007ffc771c5000) libc.so.6 => /lib64/libc.so.6 (0x00007f8f2abe2000) /lib/ld64.so.1 => /lib64/ld-linux-x86-64.so.2 (0x00007f8f2adce000)$ ./prog.outbash: ./prog.out: No such file or directory
Why is this? Is there a problem with the linker defaults on my system? How can/should I fix it?
Also, static linking doesn't work.
$ ld -static -e my_entry_pt -lc ./callee.obj ./caller.obj -o ./prog.outld: ./callee.obj: in function `my_c_func':callee.c:(.text+0x16): undefined reference to `write'
Why is this? Shouldn't write() just be a c library wrapper for the syscall 'write'? How can I fix it?
Where can I find the documentation on the C function calling convention so I can read up on how parameters are passed back and forth, etc...?
Lastly, while this seems to work for this simple example, am I doing something wrong in my initialization of the C stack? I mean, right now, I'm doing nothing. Should I be allocing memory from the kernel for the stack, setting bounds, and setting %rsp and %rbp before I start trying to call functions. Or is the kernel loader taking care of all this for me? If so, will all architectures under a Linux kernel take care of it for me?