I'm studying the ELF specification (http://www.skyfree.org/linux/references/ELF_Format.pdf), and one point that is not clear to me about the program loading process is how the stack is initialized, and what the initial page size is. Here's the test (on Ubuntu x86-64):
$ cat test.s
.text
.global _start
_start:
mov $0x3c,%eax
mov $0,%edi
syscall
$ as test.s -o test.o && ld test.o
$ gdb a.out -q
Reading symbols from a.out...(no debugging symbols found)...done.
(gdb) b _start
Breakpoint 1 at 0x400078
(gdb) run
Starting program: ~/a.out
Breakpoint 1, 0x0000000000400078 in _start ()
(gdb) print $sp
$1 = (void *) 0x7fffffffdf00
(gdb) info proc map
process 20062
Mapped address spaces:
Start Addr End Addr Size Offset objfile
0x400000 0x401000 0x1000 0x0 ~/a.out
0x7ffff7ffa000 0x7ffff7ffd000 0x3000 0x0 [vvar]
0x7ffff7ffd000 0x7ffff7fff000 0x2000 0x0 [vdso]
0x7ffffffde000 0x7ffffffff000 0x21000 0x0 [stack]
0xffffffffff600000 0xffffffffff601000 0x1000 0x0 [vsyscall]
The ELF specification has very little to say about how or why this stack page exists in the first place, but I can find references that say that the stack should be initialized with SP pointing to argc, with argv, envp and the auxiliary vector just above that, and I have confirmed this. But how much space is available below SP? On my system there are 0x1FF00
bytes mapped below SP, but presumably this is counting down from the top of the stack at 0x7ffffffff000
, and there are 0x21000
bytes in the full mapping. What influences this number?
I am aware that the page just below the stack is a "guard page" that automatically becomes writable and "grows down the stack" if I write to it (presumably so that naive stack handling "just works"), but if I allocate a huge stack frame then I could overshoot the guard page and segfault, so I want to determine how much space is already properly allocated to me right at process start.
EDIT: Some more data makes me even more unsure what's going on. The test is the following:
.text
.global _start
_start:
subq $0x7fe000,%rsp
movq $1,(%rsp)
mov $0x3c,%eax
mov $0,%edi
syscall
I played with different values of the constant 0x7fe000
here to see what happens, and for this value it is nondeterministic whether I get a segfault or not. According to GDB, the subq
instruction on its own will expand the size of the mmap, which is mysterious to me (how does linux know what's in my register?), but this program will usually crash GDB on exit for some reason. It can't be ASLR causing the nondeterminism because I'm not using a GOT or any PLT section; the executable is always loaded at the same locations in virtual memory every time. So is this some randomness of the PID or physical memory bleeding through? All in all I'm very confused as to how much stack is actually legally available for random access, and how much is requested on changing RSP or on writing to areas "just out of range" of legal memory.
readelf
andobjdump
have been very useful to this end. This is just one last bit of underspecified behavior that I hope to settle. (Put another way, my ultimate goal isn't "I wonder what's going on under the hood" so much as "I want an exact description of which bytes go where on the following input")