To better understand assembly, I compiled a simple C++ program using g++ and then used gdbgui to step through the execution. I made a graphic of the state of the stack and registers at several consecutive points in the program to help myself better understand exactly what was going on. It appears that the program writes/reads at memory beyond the stack pointer several times during execution. This surprised me. I was under the impression that a program should never write beyond the stack pointer. I understand how it works, the relative addressing is based off of the base pointer, but I would've expected the program to adjust the stack pointer in some way to encompass the memory it planned on using. Is this method of writing beyond the stack a common technique for compilers?
C++:
#include <iostream>
int square(int i) {
i = i * i;
return i;
}
int main() {
int i = square(2);
}
Compiled on Ubuntu 19.10 with:
g++ -o square_2 square_2.cpp
g++ version:
g++ -v
Using built-in specs.
COLLECT_GCC=g++
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/9/lto-wrapper
OFFLOAD_TARGET_NAMES=nvptx-none:hsa
OFFLOAD_TARGET_DEFAULT=1
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 9.2.1-9ubuntu2' --with-bugurl=file:///usr/share/doc/gcc-9/README.Bugs --enable-languages=c,ada,c++,go,brig,d,fortran,objc,obj-c++,gm2 --prefix=/usr --with-gcc-major-version-only --program-suffix=-9 --program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --enable-bootstrap --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-plugin --enable-default-pie --with-system-zlib --with-target-system-zlib=auto --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-offload-targets=nvptx-none,hsa --without-cuda-driver --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 9.2.1 20191008 (Ubuntu 9.2.1-9ubuntu2)
Relevant assembly from gdb:
0x55555555516d push rbp
0x55555555516e mov rbp,rsp
0x555555555171 mov DWORD PTR [rbp-0x4],edi
0x555555555174 mov eax,DWORD PTR [rbp-0x4]
0x555555555177 imul eax,eax
0x55555555517a mov DWORD PTR [rbp-0x4],eax
0x55555555517d mov eax,DWORD PTR [rbp-0x4]
0x555555555180 pop rbp
0x555555555181 ret
Execution Trace
I used yellow highlighting to indicate when a value was updated, and green highlighting to show which memory contents the stack pointer is currently pointing at.
gdbgui screenshot
On x86-64 under the SysV ABI, it is legal for a function to write up to 128 bytes below the stack pointer; see Section 3.2.2. This area is known as the red zone. Any sort of asynchronous code that might use the same stack is required to leave those 128 bytes alone.
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