Understanding C runtime environment (ARM) - where to start

前端 未结 5 1889
青春惊慌失措
青春惊慌失措 2021-02-06 01:54

I\'am embedded developer working with ARM Cortex-M devices mainly. Recently I\'ve switched to Linux and decided to learn more about the build/assemble/link process, how to write

5条回答
  •  梦毁少年i
    2021-02-06 02:27

    I've found this two-part blogpost quite a good and interesting read, explaining exactly the details you are asking for:

    https://blogs.oracle.com/ksplice/hello-from-a-libc-free-world-part-1
    https://blogs.oracle.com/ksplice/hello-from-a-libc-free-world-part-2

    Some of the central points of this:

    • main() is not the entry point to your program.

      The kernel/loader does not "call" any function at all, rather it sets up the virtual address space, places some data on the stack, and then starts executing the process at an address indicated by the executable file.

    • Your program cannot return as a function does.

      This is a direct consequence of the point above: There is simply no return address on the stack that the program could return to. Instead, the process has to make a syscall to ask the kernel to destroy the process. This syscall is the exit_group() syscall, to be precise.

      This is done by creating a software interrupt, which causes a kernel mode interrupt handler to run. This interrupt handler will then manipulate the kernels data structures to destroy and dispose off the process and release the resources it was holding. While the effect is quite similar to that of a function call (which never returns), the CPU mechanisms used here are quite different.

      Note that you do not need to link against any library to make a syscall, the syscall is simply some instructions to load the syscall arguments into CPU registers, followed by an interrupt instruction. The _exit() function that you saw missing in your linking attempts is not the syscall, it's just a wrapper around it. C does not know what a syscall is, the libc wrappers have to use language extensions to be able to make a syscall. That's why you generally link against the libc, and call its syscall wrappers instead of making them directly: It isolates you against the implementation defined details of making a syscall.

    • The libc code that runs before and after the invocation of main() generally takes care of loading dynamic libraries, initializing static data (if necessary), calling functions marked with __attribute__((constructor)) or __attribute__((destructor)), calling functions that were registered with atexit(), etc.

提交回复
热议问题