views:

1295

answers:

5

I would like to collect here what happens when you run an executable on Windows, Linux and OSX. In particular, I would like to understand exactly the order of the operations: my guess is that the executable file format (PE, ELF or Mach-O) is loaded by the kernel (but I ignore the various sections of the ELF and their meaning), and then you have the dynamic linker that resolves the references, then the __init part of the executable is run, then the main, then the __fini, and then the program is completed, but I am sure it's very rough, and probably wrong.

Edit: the question is now CW. I am filling up for linux. If anyone wants to do the same for Win and OSX it would be great.

A: 

Well, depending on your exact definition you have to account for JIT compilers for languages like .Net and Java. When you run a .Net "exe" which isn't technically "executable", the JIT compiler steps in and compiles it.

Max Schmeling
The .Net runtime is an executable... The fact that it runs an entire virtual environment and optimizes bytecode is irrelevant.
Tal Pressman
+1  A: 

As soon as the image is loaded into memory, magic takes over.

routeNpingme
That's if you have it set to "Magic." "More Magic" breaks the universe.
Hooked
+12  A: 

This is just at a very high and abstract level of course!

Executable - No Shared Libary: 

Client request to run application
  ->Shell informs kernel to run binary
  ->Kernel allocates memory from the pool to fit the binary image into
  ->Kernel loads binary into memory
  ->Kernel jumps to specific memory address
  ->Kernel starts processing the machine code located at this location
  ->If machine code has stop
  ->Kernel releases memory back to pool

Executable - Shared Library

Client request to run application
  ->Shell informs kernel to run binary
  ->Kernel allocates memory from the pool to fit the binary image into
  ->Kernel loads binary into memory
  ->Kernel jumps to specific memory address
  ->Kernel starts processing the machine code located at this location
  ->Kernel pops current location into an execution stack
  ->Kernel jumps out of current memory to a shared memory location
  ->Kernel executes code from this shared memory location
  ->Kernel pops back the last memory location and jumps to that address
  ->If machine code has stop
  ->Kernel releases memory back to pool

Java/.NET/Perl/Python/PHP/Ruby (Interpretted Languages)

Client request to run application
  ->Shell informs kernel to run binary
  ->Kernel has a hook that recognises binary images needs a JIT
  ->Kernel calls JIT
  ->JIT loads the code and jumps to a specific address
  ->JIT reads the code and compiles the instruction into the 
    machine code that the interpretter is running on
  ->Interpretture passes machine code to the kernel
  ->kernel executes the required instruction
  ->JIT then increments the program counter
  ->If code has a stop
  ->Jit releases application from its memory pool

As routeNpingme says, registers are set inside the CPU and the magic happens!

Update: Yeah, I cant speell properly today!

Wayne
+2  A: 

Ok, Answering my own question. This will be done progressively, and only for Linux (and maybe Mach-O). Feel free to add more stuff to your personal answers, so that they get upvoted (and you can get badges, since it's now CW).

I'll start halfway, and build the rest as I find out. This document has been made with a x86_64, gcc (GCC) 4.1.2.

Opening the file, initialization

In this section, we describe what happens when the program is invoked, from the kernel point of view, until the program is ready to be executed.

  1. The ELF is opened.
  2. the kernel looks for the .text section and loads it into memory. Marks it as readonly
  3. the kernel loads the .data section
  4. the kernel loads the .bss section, and initializes all the content to zero.
  5. the kernel transfers the control to the dynamic linker (whose name is inside the ELF file, in the .interp section). The dynamic linker resolves all the shared library calls.
  6. the control is transferred to the application

Execution of the program

  1. the function _start gets invoked, as the ELF header specifies it as the entry point for the executable
  2. _start calls __libc_start_main in glibc (through the PLT) passing the following information to it

    1. the address of the actual main function
    2. the argc address
    3. the argv address
    4. the address of the _init routine
    5. the address of the _fini routine
    6. a function pointer for the atexit() registration
    7. the highest stack address available
  3. _init gets called

    1. calls call_gmon_start to initialize gmon profiling. not really related to execution.
    2. calls frame_dummy, which wraps __register_frame_info(eh_frame section address, bss section address) (FIXME: what does this function do? initializes global vars from the BSS section apparently)
    3. calls __do_global_ctors_aux, whose role is to call all the global constructors listed in the .ctors section.
  4. main gets called
  5. main ends
  6. _fini gets called, which in turns calls __do_global_dtors_aux to run all destructors as specified in the .dtors section.
  7. the program exits.
Stefano Borini
I don't know how much detail you want to go into, but I have trouble following this because I don't know what an ELF is. (well, either that or linux is _very_ different under the hood from what I imagined)
John Fouhy
I will continue this part as soon as I have time to continue reading the docs I found. ELF is a binary format for executables under Linux. It's like PE in win and Mach-O in OSX
Stefano Borini
+2  A: 

On Windows, first the image is loaded into memory. The kernel analizes which libraries (read "DLL") it is going to require and loads them up too.

It then edits the program image to insert the memory addresses of each of the library functions it requires. These addresses have a space in the .EXE binary already, but they are just filled with zeros.

Each DLL's DllMain() procedure then gets executed, one by one, from the most required DLL to the last, like following an order of dependences.

Once all libraries were loaded and got ready, finally the image is started, and whatever happens now will depend on language used, compiler used, and the program routine itself.

Havenard