Under the hood of a simple C++ program

Lets take a simple C++ program and see what happens under the hood at the assembly level.

int add_nums(const int a, const int b) 
    return a+b; 

int main()
    int x = add_nums(2,3); 
    return 0; 

Now, lets create the assembly code. -S is the flag to give to either clang or gcc to generate the assembly code. We don’t use any sort of optimization to keep things simple.

clang++ -S add_nums.cpp

This generates the output file add_nums.s which is the assembler file.

The complete listing is here at github: add_nums.s so we will look into the more interesting parts here. This is compiled on the x86_64 architecture.

In main, the first instruction is to save the previous frame pointer. This I believe would be the frame pointer of the caller to main since main itself is the callee.

pushq   %rbp

Next notice how the base pointer is overwritten by the stack pointer since the previous frame pointer was already saved.

movq %rsp, %rbp

Since the stack pointer moves from top to bottom, this next instruction allocates 16 bytes for the stack.

subq    $16, %rsp

The next instructions move 2 and 3 to registers and makes the call to  __Z8add_numsii

callq __Z8add_numsii

Notice the name mangling here by default in C++. If instead this was a C program, the call would have been callq __add_nums. The name mangling is to support function overridding in C++.

Once main is ready to exit, the stack pointer is moved back to original position. Next, ebp is restored as well by popping the value from the stack and the function returns.

  addq $16, %rsp 
  popq %rbp 

This is how things work on Intel. This stackoverflow thread is another great reading to understand things as well. stackoverflow thread.

On the next post we will explore how the assembly is different on ARM architecture for the exact same program.