program1.c - Hello World!

Here is the source code for program1.c:

// 
// program1.c 
// One variable and a print statement
//

#include <stdio.h>

int main(void)
{        
	int var1 = 100;        
	printf("Hello world\\n");        

	return 0;
}

Here is the corresponding assembly code for main:

0x0000555555555139 <+0>:     push   %rbp
0x000055555555513a <+1>:     mov    %rsp,%rbp
0x000055555555513d <+4>:     sub    $0x10,%rsp
0x0000555555555141 <+8>:     movl   $0x64,-0x4(%rbp)
0x0000555555555148 <+15>:    lea    0xeb5(%rip),%rax        # 0x555555556004
0x000055555555514f <+22>:    mov    %rax,%rdi
0x0000555555555152 <+25>:    call   0x555555555030 <puts@plt>
0x0000555555555157 <+30>:    mov    $0x0,%eax
0x000055555555515c <+35>:    leave
0x000055555555515d <+36>:    ret

The first three lines we see are the function prologue that was mentioned above. Meaning in the first line, the base pointer is pushed onto the stack. Then the stack pointer is made equal to the base pointer. Then the stack pointer is decremented to make room for local variables within the main function.

The next instruction movl $0x64,-0x4(%rbp) stands for move long, and it is moving the value of 0x64 in hex (100 in decimal) to a local variable 4 bytes above the current base pointer. In the source code, we can see this being the line that declares an integer variable that gets initialized to 100 int var1 = 100;.

The next three instructions (lea, mov, and call) are all for printing the hello world statement. The lea 0xeb5(%rip),%rax instruction finds and calculates the address of the string to print and stores it into rax. Then, it copies the value (the address that has been calculated that contains the string) in rax and stores it into rdi. This is because the puts function (which is about to be called) expects the address of the string (to print) in the RDI register. Then the call instruction calls puts which will print the text found at the address that was calculated.

The mov instruction is moving the value of hex 0 (decimal 0) into the eax register. The eax register usually stores the return value.

The leave instruction is equivalent to the two instructions mov %rbp, %rsp and pop %rbp. It effectively undoes the actions of the function prologue, restoring RSP and RBP to their original values. Notice how the mov instruction has the arguments switched compared to the beginning when the function prologue starts. The pop command pops it (removes it) off the stack.

The ret instruction pops the return address off the stack and jumps to it, thus giving control back to the calling function (in this case, there is none so the whole program

Bonus: Navigating Through GDB

First step is to load in GDB (by running gdb with the program name as the argument), set a break point in main, run it, and view the disassembly of main:

The commands used were:

  • b main - set a breakpoint in main

  • r - run the program

  • disass main - show disassembly in main

Then, setting a breakpoint after the string’s address location is calculated, and reading into the register is saved into:

The commands used:

  • b *[address] - set a breakpoint at that specific address

  • c - continue execution

  • x/s $rax - prints the string stored at the memory address stored in the RAX register. The x command is used to examine memory and the /s flag specifies to display the contents of memory as a string.

Other useful commands

  • stepi - step to the next instruction

  • info registers - display information about the registers and their values

Last updated