When does stack grow? How does OS know when to grow stack?

Viewed 250

Note: This question is about x86_64 architecture and Linux ABI.

When the program is launched, some space is allocated for stack. Later on, during program execution the stack area can get resized (when more space is required) up to some maximum specified by OS.

Let's take for instance simple program:

int main() {
    char bytes[7 * 1024 * 1024];
}

Let's run it under gdb and set breakpoints: before main and after declaring an array.

gdb> b *main
gdb> b main
gdb> r
gdb> info proc mapping // breakpoint before pushing stack
          Start Addr           End Addr       Size     Offset objfile
      0x7ffffffde000     0x7ffffffff000    0x21000        0x0 [stack]
gdb> c
gdb> info proc mapping // breakpoint after pushing stack
          Start Addr           End Addr       Size     Offset objfile
      0x7fffff8fe000     0x7ffffffff000   0x701000        0x0 [stack]

So we can see the stack actually got resized.

The question is how does the OS know when the stack has to be resized?. Some internet resources say OS handles page fault exception and if the accessed address is within possible stack address range, it gets resized.

But when I was debugging the program step by step, it turned out, the stack got resized just after following instruction:

0x40129d <main+4>     sub    rsp, 0x700010

So (as far as I know), there is no page fault yet since we are not accessing the addresses actually. We only change rsp register. So how is it possible OS handles it? Or maybe there is a page fault exception after changing rsp?

1 Answers

But when I was debugging the program step by step, it turned out, the stack got resized just after following instruction:

0x40129d <main+4>     sub    rsp, 0x700010

That's not correct. If you step the program you have shown (even if compiled without optimizations), there is no increase in the mapping at all since there are no writes to the stack pages.

But even if you step a less trivial program, you will also see the mapping does not change on the sub instruction, but rather on every ever-increasing access to the stack.

For instance, if you do:

bytes[0] = 42;

you will see it increases by 0x70000, since you are writing into the top. But if you instead do something like:

bytes[3 * 1024 * 1024] = 42;

You will see it increases up only by 0x40000.

Note that you may be getting confused if you are hitting an instruction that happens to push into the stack, like a call.

Related