Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why would a simple C program need syscalls?

Related to this other question. I am trying to run this simple C program in gem5:

int main() {
    int a=1, b=2;
    int c=a+b;
    return c;
}

And it fails because gem5 doesn't have some syscalls implemented.

My question is, why would a simple program like this require syscalls? This should run bare-metal without trouble. Is there a way to compile this to avoid syscalls? I am using arm-linux-gnueabi-gcc -static -DUNIX to compile it.

like image 641
tgonzalez89 Avatar asked Dec 01 '22 14:12

tgonzalez89


1 Answers

Without syscalls the program cannot exit. The way it works is typically something like this:

// Not how it's actually implemented... just a sketch.
void _start() {
    char **argv = ...;
    int argc = ...;
    // ... other initialization code ...
    int retcode = main(argc, argv);
    exit(retcode);
}

The exact details depend on the operating system, but exit(), which terminates the process, typically has to be a system call or is implemented with system calls.

Note that this is true for "hosted" C implementations, not for "freestanding" C implementations, and is highly operating-system specific. There are freestanding C implementations can run on bare metal, but hosted C implementations usually need an operating system.

You can compile without standard libraries and without the runtime but your entry point cannot return... there is nothing to return to, without a runtime.

Creating a baremetal program

It is generally possible to compile programs capable of running baremetal.

  • Use -ffreestanding. This makes GCC generate code that does not assume that the standard library is available (and has other effects).

  • Use -nostdlib. This will prevent GCC from linking with the standard library. Note that memcmp, memset, memcpy, and memmove calls may be generated anyway, so you may have to provide these yourself.

At this point you can write your program, but you typically have to use _start instead of main:

void _start(void) {
    while (1) { }
}

Note that you can't return from _start! Think about it... there is nowhere to return to. When you compile a program like this you can see that it doesn't use any system calls and doesn't have a loader.

$ gcc -ffreestanding -nostdlib test.c

We can verify that it loads no libraries:

$ ldd a.out                              
    statically linked
$ readelf -d a.out 

Dynamic section at offset 0xf30 contains 8 entries:
  Tag        Type                         Name/Value
 0x000000006ffffef5 (GNU_HASH)           0x278
 0x0000000000000005 (STRTAB)             0x2b0
 0x0000000000000006 (SYMTAB)             0x298
 0x000000000000000a (STRSZ)              1 (bytes)
 0x000000000000000b (SYMENT)             24 (bytes)
 0x0000000000000015 (DEBUG)              0x0
 0x000000006ffffffb (FLAGS_1)            Flags: PIE
 0x0000000000000000 (NULL)               0x0

We can also see that it doesn't contain any code that makes system calls:

$ objdump -d a.out

a.out:     file format elf64-x86-64


Disassembly of section .text:

00000000000002c0 <_start>:
 2c0:   eb fe                   jmp    2c0 <_start>
like image 127
Dietrich Epp Avatar answered Dec 04 '22 12:12

Dietrich Epp