I'm creating a simple stack for an a.out loader and I've come up with this:
stack = (char *)mmap(NULL,
65535,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_STACK |
MAP_GROWSDOWN | MAP_UNINITIALIZED |
MAP_ANONYMOUS,
-1,
0);
However, I'm not sure about this.
Do I have to add 65535
(the size of the mapping) to the resulting pointer before I move it to the stack pointer because I used the MAP_STACK
and MAP_GROWSDOWN
flags? Or can I just use it as it is?
The documentation for this hasn't been very clear, and I haven't been able to find anything when searching the Internet.
Specifically, this (from mmap(2)) confuses me:
The return address is one page lower than the memory area that is actually created in the process's virtual address space.
Yes, you have to add 65536 to the resulting pointer. Note, not 65535. Most architectures implement push(x) as *--sp = x; so having the sp above the stack is ok to start with. More importantly it has to be aligned, and 65535 is not.
The documentation appears to be wrong. I think it intends "is one page higher than the...". That better aligns with the source implementation, and the result of the little sample program below:
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/mman.h>
volatile int sp;
void segv(int signo) {
char buf[80];
int n = snprintf(buf, 80, "(%d): sp = %#x\n", signo, sp);
write(1, buf, n);
_exit(1);
}
int main(void) {
int N = 65535;
signal(SIGSEGV, segv);
signal(SIGBUS, segv);
char *stack = (char *)mmap(NULL,
N,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_STACK |
MAP_GROWSDOWN | /*MAP_UNINITIALIZED |*/
MAP_ANONYMOUS,
-1,
0);
printf("stack %p\n", stack);
for (sp = 0; sp < N; sp += 4096) {
if (stack[sp]) {
printf("stack[%d] = %x\n", sp, stack[sp]);
}
}
for (sp = 0; sp > -N; sp -= 4096) {
if (stack[sp]) {
printf("stack[%d] = %x\n", sp, stack[sp]);
}
}
return 0;
}
which prints out:
$ ./a.out
stack 0x7f805c5fb000
(11): sp = -4096
on my system:
$ uname -a
Linux u2 4.15.0-42-generic #45-Ubuntu SMP Thu Nov 15 19:32:57 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
Why'd you comment out
MAP_UNINITIALIZED
?My linux didn’t have that enabled [ see: stackoverflow.com/questions/42784442/… last comment ]
Really? I have it enabled on 3.18 (I use
asm/mman.h
though)./usr/local/include/asm-generic/mman-common.h: # define MAP_UNINITIALIZED 0x4000000 /* For anonymous mmap, memory could be uninitialized */
Its a security thing, which is why I guess ubuntu turns it down. Note that glibc can filter your options down, so being able to spec; if you run it as strace -e trace=mmap ./a.out, you might not see your option in the args.