And*_*ndy 6 c memory x86 assembly abi
注意:这个问题是关于 x86_64 架构和 Linux ABI。
当程序启动时,会为堆栈分配一些空间。稍后,在程序执行期间,堆栈区域可以调整大小(当需要更多空间时)达到操作系统指定的某个最大值。
让我们以简单的程序为例:
int main() {
char bytes[7 * 1024 * 1024];
}
Run Code Online (Sandbox Code Playgroud)
让我们在 gdb 下运行它并设置断点:main 之前和声明数组之后。
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]
Run Code Online (Sandbox Code Playgroud)
所以我们可以看到堆栈实际上被调整了大小。
问题是操作系统如何知道何时必须调整堆栈大小?. 一些互联网资源说操作系统处理page fault exception,如果访问的地址在可能的堆栈地址范围内,它会被调整大小。
但是当我一步一步调试程序时,结果是,在执行以下指令后,堆栈的大小就被调整了:
0x40129d <main+4> sub rsp, 0x700010
Run Code Online (Sandbox Code Playgroud)
所以(据我所知)还没有page fault,因为我们实际上没有访问地址。我们只更改rsp注册。那么操作系统如何处理它呢?或者也许有一个page fault exception改变后rsp?
但是当我一步步调试程序时,结果发现,在执行以下指令后,堆栈的大小就被调整了:
Run Code Online (Sandbox Code Playgroud)0x40129d <main+4> sub rsp, 0x700010
这是不正确的。如果您单步执行所显示的程序(即使在没有优化的情况下进行编译),则映射根本不会增加,因为没有对堆栈页进行写入。
但即使您单步执行一个不那么简单的程序,您也会看到映射不会在sub指令上发生变化,而是在每次对堆栈的不断增加的访问上发生变化。
例如,如果您这样做:
bytes[0] = 42;
Run Code Online (Sandbox Code Playgroud)
你会看到它增加了 0x70000,因为你正在写入顶部。但如果你这样做:
bytes[3 * 1024 * 1024] = 42;
Run Code Online (Sandbox Code Playgroud)
您将看到它仅增加了 0x40000。
请注意,如果您遇到的指令恰好压入堆栈,例如call.