In this document on p. 27 it says that text segment starts at 0x400000. Why was this particular address chosen? Is there any reason for that? The same address is chosen in GNU ld
on Linux
:
$ ld -verbose | grep -i text-segment PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x400000)); . = SEGMENT_START("text-segment", 0x400000) + SIZEOF_HEADERS;
It's surprising because this address is bigger in 32-bit x86 executables:
$ ld -verbose | grep -i text-segment PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x08048000)); . = SEGMENT_START("text-segment", 0x08048000) + SIZEOF_HEADERS;
I read this question which discusses why 0x080xxxxx address was chosen for i386 but it doesn't explain a change in x86_64. It's hard to find any explanation on that matter. Does anybody have a clue?
Bottom line: some technical limitations that amd64
has in using large addresses suggest dedicating the lower 2GiB
of address space to code and data for efficiency. Thus the stack has been relocated out of this range.
In i386
ABI1
0x8048000
downwards. Which provides "a little over 128 MB for the stack and about 2 GB for text and data" (p. 3-22).0x80000000
(2GiB),1GiB
, starting at at least 0xC0000000
(p. 3-21) (which is what it typically does).128MiB
(which is 288KiB
) will be reserved for that purpose.amd64
(whose ABI is formulated as an amendment to the i386
one (p. 9)) has a vastly bigger (48-bit) address space but most instructions only accept 32-bit immediate operands (which include direct addresses and offsets in jump instructions), requiring more work and less efficient code (especially when taking instruction interdependency into consideration) to handle larger values. Measures to work around these limitations are summarized by the authors by introducing a few "code models" they recommend to use to "allow the compiler to generate better code". (p. 33)
0x00000000
to 0x7effffff
" which allows some very efficient relative references and array iteration. This is 1.98GiB
which is more than enough for many programs.movabs
instruction, as in the medium code model, even for dealing with addresses inside the text section. Additionally, indirect branches are needed when branching to addresses whose offset from the current instruction pointer is unknown." They go on to suggest splitting the code base into multiple shared libraries since these measures do not apply for relative references with offsets that are known to be within bounds (as outlined in "Small position independent code model").Thus the stack was moved to under the shared library space (0x80000000000
, 128GiB
) because its addresses are never immediate operands, always referenced either indirectly or with lea
/mov
from another reference, thus only relative offset limitations apply.
The above explains why the loading address was moved to a lower address. Now, why was it moved to exactly 0x400000
(4MiB
)? Here, I came empty so, summarizing what I've read in the ABI specs, I can only guess that it felt "just right":
amd64
operates on, yet small enough to not waste much of the valuable starting 2GiB
of address space.1Note that actual x32 Linuxes have been deviating from this layout more and more as time goes. But we're talking about the ABI spec here since the amd64
one is formally based on it rather than any derived layout (see its paragraph for citation).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With