mov rbp-XX vs push/pop semantics - is it "proper" stack use?

99 views Asked by At

I wrote a program and compiled it with GCC 12.2.1 on Fedora x64 Linux using the flags -Wall and -g3, and I disassembled it in gdb-gef. Source code:

#include <stdio.h>

int addNumbers(int a,int b,int c,int d, int e, int f, int g, int h);
int main(void)
{
    printf("%d\n", addNumbers(5,17,900,2483,24,67,98,4923));
    return 0;
}

int addNumbers(int a, int b, int c, int d, int e, int f, int g, int h)
{
    int local1 = 16;
    int local2 = 24;
    return a + b + c + d + e + f + g + h + local1 + local2;
}

Below is the disassembly:

gef➤  disass addNumbers 
Dump of assembler code for function addNumbers:
   0x0000000000401172 <+0>: push   rbp
   0x0000000000401173 <+1>: mov    rbp,rsp
   0x0000000000401176 <+4>: mov    DWORD PTR [rbp-0x14],edi
   0x0000000000401179 <+7>: mov    DWORD PTR [rbp-0x18],esi
   0x000000000040117c <+10>:    mov    DWORD PTR [rbp-0x1c],edx
   0x000000000040117f <+13>:    mov    DWORD PTR [rbp-0x20],ecx
   0x0000000000401182 <+16>:    mov    DWORD PTR [rbp-0x24],r8d
   0x0000000000401186 <+20>:    mov    DWORD PTR [rbp-0x28],r9d
=> 0x000000000040118a <+24>:    mov    DWORD PTR [rbp-0x4],0x10
   0x0000000000401191 <+31>:    mov    DWORD PTR [rbp-0x8],0x18
   0x0000000000401198 <+38>:    mov    edx,DWORD PTR [rbp-0x14]
   0x000000000040119b <+41>:    mov    eax,DWORD PTR [rbp-0x18]
   0x000000000040119e <+44>:    add    edx,eax
   0x00000000004011a0 <+46>:    mov    eax,DWORD PTR [rbp-0x1c]
   0x00000000004011a3 <+49>:    add    edx,eax
   0x00000000004011a5 <+51>:    mov    eax,DWORD PTR [rbp-0x20]
   0x00000000004011a8 <+54>:    add    edx,eax
   0x00000000004011aa <+56>:    mov    eax,DWORD PTR [rbp-0x24]
   0x00000000004011ad <+59>:    add    edx,eax
   0x00000000004011af <+61>:    mov    eax,DWORD PTR [rbp-0x28]
   0x00000000004011b2 <+64>:    add    edx,eax
   0x00000000004011b4 <+66>:    mov    eax,DWORD PTR [rbp+0x10]
   0x00000000004011b7 <+69>:    add    edx,eax
   0x00000000004011b9 <+71>:    mov    eax,DWORD PTR [rbp+0x18]
   0x00000000004011bc <+74>:    add    edx,eax
   0x00000000004011be <+76>:    mov    eax,DWORD PTR [rbp-0x4]
   0x00000000004011c1 <+79>:    add    edx,eax
   0x00000000004011c3 <+81>:    mov    eax,DWORD PTR [rbp-0x8]
   0x00000000004011c6 <+84>:    add    eax,edx
   0x00000000004011c8 <+86>:    pop    rbp
   0x00000000004011c9 <+87>:    ret 

So one major difference (it's been a while since I stared at disassembly between the x86-32 programs I've recently disassembled and this x86-64 one is that we see during the first few lines of that addNumbers function, that the compiler has accepted the function arguments into registers rdi, rsi, rdx, rcx, r8d, r9d, and the rest on the stack as the System V calling convention states, then it moves these into stack locations via mov DWORD PTR [rbp-0x14], edi and so on... My question pertains to this part... For all intents and purposes, is this behavior considered proper stack usage? The reason I ask is because as far as I learned, the stack is an abstract data structure that has a conceptual top of the stack which is typically denoted by rsp or just SP and a bottom for this frame, typically denoted as rbp or BP. However, in this case, the compiler is using mov instructions like the one shown to put things in areas of stack memory without using the stack in a traditional way which is to push or pop. It is likely doing this because mov instructions on this sort of data are faster than push/pop, but would we state that the compiler is actually using the stack as a proper stack here or is it breaking any sort of stack conventions or rules by addressing areas of the stack without first extending the stack pointer to properly include those areas? One final example is that clearly in the below stack memory dump, both rbp and rsp point to the same address and the movs are occurring during this state, which means the movs do occur outside the scope of the stack frame between rbp and rsp:

gef➤  telescope $rbp-0x20
0x00007fffffffda10│+0x0000: 0x00000384000009b3
0x00007fffffffda18│+0x0008: 0x0000000500000011
0x00007fffffffda20│+0x0010: 0x0000000000000000
0x00007fffffffda28│+0x0018: 0x0000000000000000
0x00007fffffffda30│+0x0020: 0x00007fffffffda50  →  0x0000000000000001    ← $rsp, $rbp
0x00007fffffffda38│+0x0028: 0x0000000000401156  →  <main+48> add rsp, 0x10
0x00007fffffffda40│+0x0030: 0x0000000000000062 ("b"?)
0x00007fffffffda48│+0x0038: 0x000000000000133b
0x00007fffffffda50│+0x0040: 0x0000000000000001
0x00007fffffffda58│+0x0048: 0x00007ffff7c29550  →  <__libc_start_call_main+128> mov edi, eax
0

There are 0 answers