views:

372

answers:

3

why is the entry point in each ELF binary something starting with 0x80xxxxx? Why doesn't the program start at (virtual) address 0x0?

When executed, program will start running from virtual address 0x80482c0 (see entry point address). The "0x" prefix here means it is a hexadecimal number. This address doesn't point to our main() procedure, but to a procedure named _start. Never felt you had created such thing? Of course you don't. _start procedure is created by the linker whose purpose is to initialize your program.

(Text from http://www.linuxforums.org/articles/understanding-elf-using-readelf-and-objdump_125.html)

I'm quite curious about that, so any idea? Thanks!

+2  A: 

Why not start at address 0x0? There's at least two reasons for this:

  • Because address zero is famously known as a NULL pointer, and used by programming languages to sane check pointers. You can't use an address value for that, if you're going to execute code there.
  • The actual contents at address 0 is often (but not always) the exception vector table, and is hence not accessible in non-privileged modes. Consult the documentation of your specific architecture.

As for the entrypoint start_ vs main: If you link against the C runtime (the C standard libraries), the library wraps the function named main, so it can initialize the environment before main is called. On Linux, these are the argc and argv parameters to the application, the env variables, and probably some synchronization primitives and locks. It also makes sure that returning from main passes on the status code, and calls the exit_ function, which terminates the process.

Mads Elvheim
+1  A: 

my google research so far just led me to some (vague) historical speculations like this: There is folklore that 0x08048000 once was STACK_TOP (that is, the stack grew downwards from near 0x08048000 towards 0) on a port of *NIX to i386 that was promulgated by a group from Santa Cruz, California. This was when 128MB of RAM was expensive, and 4GB of RAM was unthinkable.

can anyone confirm/deny this? i'm really curious about understanding the rationale (if any?) behind 0x8048000.

Michael L.
A: 

As Mads pointed out, in order to catch most accesses through null pointers, Unix-like systems tend to make the page at address zero "unmapped". Thus, accesses immediately trigger a CPU exception, in other words a segfault. This is quite better than letting the application go rogue. The exception vector table, however, can be at any address, at least on x86 processors (there is a special register for that, loaded with the lidt opcode).

The starting point address is part of a set of conventions which describe how memory is laid out. The linker, when it produces an executable binary, must know these conventions, so they are not likely to change. Basically, for Linux, the memory layout conventions are inherited from the very first versions of Linux, in the early 90's. A process must have access to several areas:

  • The code must be in a range which includes the starting point.
  • There must be a stack.
  • There must be a heap, with a limit which is increased with the brk() and sbrk() system calls.
  • There must be some room for mmap() system calls, including shared library loading.

Nowadays, the heap, where malloc() goes, is backed by mmap() calls which obtain chunks of memory at whatever address the kernel sees fit. But in older times, Linux was like previous Unix-like systems, and its heap required a big area in one uninterrupted chunk, which could grow towards increasing addresses. So whatever was the convention, it had to stuff code and stack towards low addresses, and give every chunk of the address space after a given point to the heap.

But there is also the stack, which is usually quite small but could grow quite dramatically in some occasions. The stack grows down, and when the stack is full, we really want the process to predictably crash rather than overwriting some data. So there had to be a wide area for the stack, with, at the low end of that area, an unmapped page. And lo! There is an unmapped page at address zero, to catch null pointer dereferences. Hence it was defined that the stack would get the first 128 MB of address space, except for the first page. This means that the code had to go after those 128 MB, at an address similar to 0x080xxxxx.

As Michael points out, "loosing" 128 MB of address space was no big deal because the address space was very large with regards to what could be actually used. At that time, the Linux kernel was limiting the address space for a single process to 1 GB, over a maximum of 4 GB allowed by the hardware, and that was not considered to be a big issue.

Thomas Pornin