There are a few related questions to this which I've come across, such as Printf with gas assembly and Calling C printf from assembly but I'm hoping this is a bit different.
I have the following program:
.section .data
format:
.ascii "%d\n"
.section .text
.globl _start
_start:
// print "55"
mov $format, %rdi
mov $55, %rsi
mov $0, %eax
call printf # how to link?
// exit
mov $60, %eax
mov $0, %rdi
syscall
Two questions related to this:
Is it possible to use only as (gas) and ld to link this to the printf function, using _start as the entry point? If so, how could that be done?
If not, other than changing _start to main, what would be the gcc invocation to run things properly?
It is possible to use ld, but not recommended: if you use libc functions, you need to initialise the C runtime. That is done automatically if you let the C compiler provide _start and start your program as main. If you use the libc but not the C runtime initialisation code, it may seem to work, but it can also lead to strange spurious failure.
If you start your program from main (your second case) instead, it's as simple as doing gcc -o program program.s where program.s is your source file. On some Linux distributions you may also need to supply -no-pie as your program is not written in PIC style (don't worry about this for now).
Note also that I recommend not mixing libc calls with raw system calls. Instead of doing a raw exit system call, call the C library function exit. This lets the C runtime deinitialise itself correctly, including flushing any IO streams.
Now if you assemble and link your program as I said in the first paragraph, you'll notice that it might crash. This is because the stack needs to be aligned to a multiple of 16 bytes on calls to functions. You can ensure this alignment by pushing a qword of data on the stack at the beginning of each of your functions (remember to pop it back off at the end).
Related
Problem
Let's consider:
int main(){
write(1, "hello", 5);
return 0;
}
I am reading a book that suggests the assembly output for the above code should be:
main:
mov $4, %eax
mov $1 %ebx
mov %string, %ecx
mov $len, %edx
int $0x80
(The above code was compiled with 32 bit architecture. Passing arguments by registers isn't caused by '64 bit convention passing arguments by registers' but it is caused by the fact, we make a syscall. )
And the output on my 64 bit Ubuntu machine with: gcc -S main.c -m32
is:
pushl $4
pushl $string
pushl $1
call write
My doubts
So it confused me. Why did gcc compile it as "normal" call, not as syscall.
In this situation, what is the way to make the processor use a kernel function (like write)?
I am reading a book that suggests the assembly output for the above code should be ...
You shouldn't believe everything you read :-)
There is no requirement that C code be turned into specific assembly code, the only requirement that the C standard mandates is that the resulting code behave in a certain manner.
Whether that's done by directly calling the OS system call with int $80 (or sysenter), or whether it's done by calling a library routine write() which eventually calls the OS in a similar fashion, is largely irrelevant.
If you were to locate and disassemble the write() code, you may well find it simply reads those values off the stack into registers and then calls the OS in much the same way as the code you've shown containing int $80.
As an aside, what if you wanted to port gcc to a totally different architecture that uses call 5 to do OS-level system calls. If gcc is injecting specific int $80 calls into the assembly stream, that's not going to work too well.
But, if it's injecting a call to a write() function, all you have to do is make sure you link it with the correct library containing a modified write() function (one that does call 5 rather than int $80).
I learned from my colleague that one can write and execute a C program without writing a main() function. It can be done like this:
my_main.c
/* Compile this with gcc -nostartfiles */
#include <stdlib.h>
void _start() {
int ret = my_main();
exit(ret);
}
int my_main() {
puts("This is a program without a main() function!");
return 0;
}
Compile it with this command:
gcc -o my_main my_main.c –nostartfiles
Run it with this command:
./my_main
When would one need to do this kind of thing? Is there any real world scenario where this would be useful?
The symbol _start is the entry point of your program. That is, the address of that symbol is the address jumped to on program start. Normally, the function with the name _start is supplied by a file called crt0.o which contains the startup code for the C runtime environment. It sets up some stuff, populates the argument array argv, counts how many arguments are there, and then calls main. After main returns, exit is called.
If a program does not want to use the C runtime environment, it needs to supply its own code for _start. For instance, the reference implementation of the Go programming language does so because they need a non-standard threading model which requires some magic with the stack. It's also useful to supply your own _start when you want to write really tiny programs or programs that do unconventional things.
While main is the entry point for your program from a programmers perspective, _start is the usual entry point from the OS perspective (the first instruction that is executed after your program was started from the OS)
In a typical C and especially C++ program, a lot of work has been done before the execution enters main. Especially stuff like initialization of global variables. Here you can find a good explanation of everything that's going on between _start() and main() and also after main has exited again (see comment below).
The necessary code for that is usually provided by the compiler writers in a startup file, but with the flag –nostartfiles you essentially tell the compiler: "Don't bother giving me the standard startup file, give me full control over what is happening right from the start".
This is sometimes necessary and often used on embedded systems. E.g. if you don't have an OS and you have to manually enable certain parts of your memory system (e.g. caches) before the initialization of your global objects.
Here is a good overview of what happens during program startup before main. In particular, it shows that __start is the actual entry point to your program from OS viewpoint.
It is the very first address from which the instruction pointer will start counting in your program.
The code there invokes some C runtime library routines just to do some housekeeping, then call your main, and then bring things down and call exit with whatever exit code main returned.
A picture is worth a thousand words:
P.S: this answer is transplanted from another question which SO has helpfully closed as duplicate of this one.
When would one need to do this kind of thing?
When you want your own startup code for your program.
main is not the first entry for a C program, _start is the first entry behind the curtain.
Example in Linux:
_start: # _start is the entry point known to the linker
xor %ebp, %ebp # effectively RBP := 0, mark the end of stack frames
mov (%rsp), %edi # get argc from the stack (implicitly zero-extended to 64-bit)
lea 8(%rsp), %rsi # take the address of argv from the stack
lea 16(%rsp,%rdi,8), %rdx # take the address of envp from the stack
xor %eax, %eax # per ABI and compatibility with icc
call main # %edi, %rsi, %rdx are the three args (of which first two are C standard) to main
mov %eax, %edi # transfer the return of main to the first argument of _exit
xor %eax, %eax # per ABI and compatibility with icc
call _exit # terminate the program
Is there any real world scenario where this would be useful?
If you mean, implement our own _start:
Yes, in most of the commercial embedded software I have worked with, we need to implement our own _start regarding to our specific memory and performance requirements.
If you mean, drop the main function and change it to something else:
No, I don't see any benefit doing that.
As always I thank you for your time and effort in assisting me on my journey :)
So being a nerd I have started pulling back the layers of how the OS works. Where I have a question is around the kernel and the standard libraries, such as glibc for Linux, which acts as a function wrapper.
Why does the OS need a standard library which is written in C? OR asked another way Could you write a standard library for the Linux kernel in another language other than C?
I assume that and that the language of the STD library may be dependent on the language chosen for the Kernel. So in our example of Linux, which is written in C, the wrapper STD library also needed to be C.
I understand why the kernel needs the STD library in general so that isn't really what I am trying to get at JIC I wasn't clear.
Thanks Again!
Let's dive in more details about OS - userspace communication. Do you know how it goes? Basically each platform uses its own approach to make a so called syscall -> control transfer from userspace to kernelspace.
For example x86 uses int instruction, x86-64 uses syscall instruction, arm uses swi and so on. Also each platform has its own understanding of how parameters and syscall number should be established before syscall instruction is called. Let's focus on x86-64:
For example for calling execve (syscall number 0x3b) this code is enough. You can try it.
section .text
global _start
_start:
mov rax, 0x3b
mov rdi, cmd
mov rsi, 0
mov rdx, 0
syscall
section .data
cmd: db '/bin/sh'
.end:
Now let's understand what is execve libc function. Basically if you will dive into libc code, you will see that it is wrapper which leads to syscall function (see syscall.S for your arch in libc). This syscall.S looks pretty same with our example above:
.text
ENTRY (syscall)
movq %rdi, %rax /* Syscall number -> rax. */
movq %rsi, %rdi /* shift arg1 - arg5. */
movq %rdx, %rsi
movq %rcx, %rdx
movq %r8, %r10
movq %r9, %r8
movq 8(%rsp),%r9 /* arg6 is on the stack. */
syscall /* Do the system call. */
So basically, as user4098326 and rcgldr have mentioned - the interconnection between uspace and kernel is assembly code, and all things above it - just wrappers. So as i understand all this wrappers could be written not only in C.
General kernel driver works through file operations, like open, close, read, & write.
Again, as kernel is also written completely in C.
So due to these 2 reasons
, I think, we can not write lower level library calls in other language.
I'm trying to use a function in assembly, invoked from a C project. This function is supposed to call a libc function let's say printf(), but I keep getting a segmentation fault.
In the .c file I have the declaration of the function let's say
int do_shit_in_asm()
In the .asm file I have
.extern printf
.section .data
printtext:
.ascii "test"
.section .text
.global do_shit_in_asm
.type do_shit_in_asm, #function
do_shit_in_asm:
pushl %ebp
movl %esp, %ebp
push printtext
call printf
movl %ebp, %esp
pop %ebp
ret
Any pointers comments would be appreciated.
as func.asm -o func.o
gcc prog.c func.o -o prog
Change push printtext to push $printtext.
As it is, you're loading a value from the address printtext and pushing that, rather than pushing the address. Thus, you're passing 'test' as a 32-bit number, rather than a pointer, and printf is trying to interpret that as an address and crashing.
One of the best ways to get started with assembly language functions is to write a similar function in C, and then build it with the compiler switch that generates an assembly listing (-S on gcc). Then you can study the output of what the compiler did, and modify as needed.
This is particularly useful if you're calling functions such as printf which use a different calling convention (because of the variable number of arguments). Calling those functions may be quite different from calling non-varargs functions.
the issue was that i was using
pushl printtext
rather that
pushl $printtext
Thanks everybody for your help and sorry for wasting your time :P
After this:
push printtext
call printf
You want:
addl $4, %esp
Further explanation:
Because you're using x86 Linux I assume the calling convention requires the callee to cleanup the parameters. Because you pushed a pointer before calling printf, your stack is off by 4 after that function's ret instruction happened.
Update:
Yeah, OK, I was used to Intel syntax so I was getting the order of the arguments backward in my head. Actually the lack of the addl back to esp doesn't matter, because you're restoring esp correctly near your ret. My next guess is that the string you're passing to printf is lacking a null terminator... Let me see what gas does...
Update 2:
OK, gas null terminates strings for you, so I guess my second hunch was wrong. It looks like you found the issue so the point is moot.
For the following snippet of code,
int n;
char buf[100];
int fd = open ("/etc/passwd", O_RDONLY);
n = read ( fd, buf, 100);
How the compiler comes to know that read is a system call not any library function?
How it retrieves the system call number (__NR_read)?
I very much doubt that the compiler knows it's a system call. It's far more likely that open is in a library somewhere and the code within the library calls the relevant kernel interface.
The assembly output from the simple program:
#include <stdio.h>
int main (void) {
int fd = open("xyz");
return 0;
}
is (irrelevant bits removed):
main:
pushl %ebp ; stack frame setup.
movl %esp, %ebp
andl $-16, %esp
subl $32, %esp
movl $.LC0, (%esp) ; Store file name address.
call open ; call the library function.
movl %eax, 28(%esp) ; save returned file descriptor.
movl $0, %eax ; return 0 error code.
leave ; stack frame teardown.
ret
.LC0:
.string "xyz" ; file name to open.
The first thing you'll notice is that there's a call to open. In other words, it's a function. There's not an int 80 or sysenter in sight, which is the mechanism used for proper system calls (on my platform anyway - YMMV).
The wrapper functions in libc are where the actual work of accessing the system call interface is done.
An excerpt from Wikipedia on system calls:
Generally, systems provide a library that sits between normal programs and the operating system, usually an implementation of the C library (libc), such as glibc. This library exists between the OS and the application, and increases portability.
On exokernel based systems, the library is especially important as an intermediary. On exokernels, libraries shield user applications from the very low level kernel API, and provide abstractions and resource management.
The terms "system call" and "syscall" are often incorrectly used to refer to C standard library functions, particularly those that act as a wrapper to corresponding system calls with the same name. The call to the library function itself does not cause a switch to kernel mode (if the execution was not already in kernel mode) and is usually a normal subroutine call (i.e., using a "CALL" assembly instruction in some ISAs). The actual system call does transfer control to the kernel (and is more implementation-dependent than the library call abstracting it). For example, fork and execve are GLIBC functions that in turn call the fork and execve system-calls.
And, after a bit of searching, the __open function is found in glibc 2.9 in the io/open.c file, and weakref'ed to open. If you execute:
nm /usr/lib/libc.a | egrep 'W __open$|W open$'
you can see them in there:
00000000 W __open
00000000 W open
read is a library call as far as the compiler is concerned. It just so happens that the libc implementation defines read to generate a software interrupt with the correct number.
The compiler can see the declaration of this function in <unistd.h>, and it generates object code that makes a call to that function.
Try compiling with gcc -S and you'll see something like:
movl $100, %edx
movq %rcx, %rsi
movl %eax, %edi
call read
The system call is made from the C library's implementation of read(2).
EDIT: specifically, GNU libc (which is likely what you have on Linux), establishes the relationships between syscall numbers and function names in glibc-2.12.1/sysdeps/syscalls.list. Each line of that file is converted to an assembly language source code (based on sysdeps/unix/syscall-template.S), compiled, and added to the library when libc is built.
open() is a library function, it located in libc.a / libc.so
The following is the Android implementation of read in bionic (the Android equivalent for libc)
/* autogenerated by gensyscalls.py */
#include <sys/linux-syscalls.h>
.text
.type read, #function
.globl read
.align 4
.fnstart
read:
.save {r4, r7}
stmfd sp!, {r4, r7}
ldr r7, =__NR_read
swi #0
ldmfd sp!, {r4, r7}
movs r0, r0
bxpl lr
b __set_syscall_errno
.fnend
You can see that it loads __NR_read into r7 and then calls SWI, SWI is the software interrupt that switches the prcessor into kernel mode. So the compiler needs to know nothing about how to make system calls, libc takes care of it.