Adding Integers: i386#

../../../../../../_images/add_int_C_i386_splash1.jpg

Introduction#

In this section we will examine the disassembly of a simple C program that adds two integers. The program is compiled for the i386 architecture, which is a 32-bit architecture commonly used in older computers and embedded systems. The disassembly will help us understand how the C code translates into assembly instructions, and how these instructions are executed by the CPU.

The C Program#

#include <stdio.h>

int main(int argc, char * argv[])
{
	int a, b, c;
	
	a = 1;
	b = 9;

	c = a + b;
}

Compilation of the C Program to Produce Assembly Code and Object Code#

The C program is compiled using the GCC compiler with the -m32 flag to specify the i386 architecture. The -masm=intel flag creates assembly code in the Intel format. The -S flag generates assembly code, the -fverbose-asm flag includes C source code in the assembly code, and the -c flag compiles the assembly code into an object file.

gcc --sysroot=/ -m32 -masm=intel -S -fverbose-asm add_int.c -o add_int.s
gcc --sysroot=/ -m32 -c add_int.s -o add_int.o

Viewing the Assembly Code#

The assembly code is in Intel syntax, which is commonly used for x86 assembly language.

	.file	"add_int.c"
	.intel_syntax noprefix
# GNU C17 (conda-forge gcc 14.2.0-2) version 14.2.0 (x86_64-conda-linux-gnu)
#	compiled by GNU C version 14.2.0, GMP version 6.2.1, MPFR version 4.1.0, MPC version 1.2.1, isl version isl-0.24-GMP

# GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
# options passed: -m32 -masm=intel -mtune=generic -march=x86-64
	.text
	.globl	main
	.type	main, @function
main:
.LFB0:
	.cfi_startproc
	push	ebp	#
	.cfi_def_cfa_offset 8
	.cfi_offset 5, -8
	mov	ebp, esp	#,
	.cfi_def_cfa_register 5
	sub	esp, 16	#,
	call	__x86.get_pc_thunk.ax	#
	add	eax, OFFSET FLAT:_GLOBAL_OFFSET_TABLE_	# tmp98,
# add_int.c:7: 	a = 1;
	mov	DWORD PTR -4[ebp], 1	# a,
# add_int.c:8: 	b = 9;
	mov	DWORD PTR -8[ebp], 9	# b,
# add_int.c:10: 	c = a + b;
	mov	edx, DWORD PTR -4[ebp]	# tmp105, a
	mov	eax, DWORD PTR -8[ebp]	# tmp106, b
	add	eax, edx	# c_3, tmp105
	mov	DWORD PTR -12[ebp], eax	# c, c_3
	mov	eax, 0	# _4,
# add_int.c:11: }
	leave	
	.cfi_restore 5
	.cfi_def_cfa 4, 4
	ret	
	.cfi_endproc
.LFE0:
	.size	main, .-main
	.section	.text.__x86.get_pc_thunk.ax,"axG",@progbits,__x86.get_pc_thunk.ax,comdat
	.globl	__x86.get_pc_thunk.ax
	.hidden	__x86.get_pc_thunk.ax
	.type	__x86.get_pc_thunk.ax, @function
__x86.get_pc_thunk.ax:
.LFB1:
	.cfi_startproc
	mov	eax, DWORD PTR [esp]	#,
	ret
	.cfi_endproc
.LFE1:
	.ident	"GCC: (conda-forge gcc 14.2.0-2) 14.2.0"
	.section	.note.GNU-stack,"",@progbits

Explanation of the Assembly Code#

The attached file is an assembly code file (add_int.s) generated from a C program (add_int.c). It is written in Intel syntax and compiled for a 32-bit architecture (-m32) using GCC 14.2.0. Below is a detailed explanation of the code:


Header Information#

  1. File Metadata:

    • .file "add_int.c": Indicates that this assembly file was generated from the C source file add_int.c.

  2. Compiler Information:

    • The file was compiled using GCC 14.2.0 with the -m32 flag for 32-bit architecture.

    • Other flags include -masm=intel (Intel syntax), -mtune=generic (generic CPU tuning), and -march=x86-64 (targeting x86-64 architecture).

  3. GCC Heuristics:

    • Parameters like ggc-min-expand and ggc-min-heapsize are used for garbage collection optimization during compilation.


Main Function#

The main function (main) is defined as a global symbol (.globl main) and is marked as a function (.type main, @function).

Prologue#

The prologue sets up the stack frame for the function:

  1. push ebp: Saves the base pointer of the previous stack frame.

  2. mov ebp, esp: Sets the base pointer (ebp) to the current stack pointer (esp).

  3. sub esp, 16: Allocates 16 bytes of space on the stack for local variables.

Global Offset Table Setup#

  • call __x86.get_pc_thunk.ax: Calls a helper function to get the program counter (PC).

  • add eax, OFFSET FLAT:_GLOBAL_OFFSET_TABLE_: Adjusts the PC to point to the Global Offset Table (GOT), used for position-independent code.

Variable Initialization#

The C code initializes three variables (a, b, and c):

  1. mov DWORD PTR -4[ebp], 1: Assigns 1 to a (stored at -4[ebp]).

  2. mov DWORD PTR -8[ebp], 9: Assigns 9 to b (stored at -8[ebp]).

Addition Operation#

The C code computes c = a + b:

  1. mov edx, DWORD PTR -4[ebp]: Loads the value of a into edx.

  2. mov eax, DWORD PTR -8[ebp]: Loads the value of b into eax.

  3. add eax, edx: Adds a and b, storing the result in eax.

  4. mov DWORD PTR -12[ebp], eax: Stores the result (c) at -12[ebp].

Return Value#

  • mov eax, 0: Sets the return value of the function to 0 (standard for int main() in C).

Epilogue#

The epilogue restores the stack frame:

  1. leave: Restores the previous base pointer and stack pointer.

  2. ret: Returns control to the caller.


Helper Function: __x86.get_pc_thunk.ax#

This function is used to retrieve the program counter (PC) for position-independent code:

  1. mov eax, DWORD PTR [esp]: Loads the return address (PC) from the stack into eax.

  2. ret: Returns to the caller.


Other Sections#

  1. .ident Section:

    • Contains metadata about the compiler version: "GCC: (conda-forge gcc 14.2.0-2) 14.2.0".

  2. .note.GNU-stack Section:

    • Indicates that the stack is not executable (a security feature).


Summary#

This assembly code represents a simple C program that initializes two integers (a = 1 and b = 9), adds them (c = a + b), and returns 0. The code includes standard prologue and epilogue for stack management and uses a helper function for position-independent code.

Compilation to Produce Executable Code#

The object file is linked to create an executable file using the -o flag.

/usr/bin/gcc -m32 add_int.o -o add_int_C_i386

Disassembly of the Executable Code#

The executable file is disassembled using the objdump command with the -d flag to display the disassembly.

objdump -x -D -s -t -Mintel add_int.o > objdump_of_dot_o.txt
objdump -x -D -s -t -Mintel add_int_C_i386 > objdump_of_dot_exe.txt

Explanation of the Switches used in the objdump command:#

  • -x: Displays all headers.

  • -D: Disassembles all sections.

  • -s: Displays the full contents of all sections.

  • -t: Displays the symbol table.

  • -Mintel: Specifies Intel syntax for disassembly.

Viewing the Object File Disassembly#


add_int.o:     file format elf32-i386
add_int.o
architecture: i386, flags 0x00000011:
HAS_RELOC, HAS_SYMS
start address 0x00000000

Sections:
Idx Name          Size      VMA       LMA       File off  Algn
  0 .group        00000008  00000000  00000000  00000034  2**2
                  CONTENTS, READONLY, GROUP, LINK_ONCE_DISCARD
  1 .text         00000030  00000000  00000000  0000003c  2**0
                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
  2 .data         00000000  00000000  00000000  0000006c  2**0
                  CONTENTS, ALLOC, LOAD, DATA
  3 .bss          00000000  00000000  00000000  0000006c  2**0
                  ALLOC
  4 .text.__x86.get_pc_thunk.ax 00000004  00000000  00000000  0000006c  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  5 .comment      00000028  00000000  00000000  00000070  2**0
                  CONTENTS, READONLY
  6 .note.GNU-stack 00000000  00000000  00000000  00000098  2**0
                  CONTENTS, READONLY
  7 .note.gnu.property 00000028  00000000  00000000  00000098  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  8 .eh_frame     0000004c  00000000  00000000  000000c0  2**2
                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, DATA
SYMBOL TABLE:
00000000 l    df *ABS*	00000000 add_int.c
00000000 l    d  .text	00000000 .text
00000000 l    d  .text.__x86.get_pc_thunk.ax	00000000 .text.__x86.get_pc_thunk.ax
00000000 g     F .text	00000030 main
00000000 g     F .text.__x86.get_pc_thunk.ax	00000000 .hidden __x86.get_pc_thunk.ax
00000000         *UND*	00000000 _GLOBAL_OFFSET_TABLE_


Contents of section .group:
 0000 01000000 06000000                    ........        
Contents of section .text:
 0000 5589e583 ec10e8fc ffffff05 01000000  U...............
 0010 c745fc01 000000c7 45f80900 00008b55  .E......E......U
 0020 fc8b45f8 01d08945 f4b80000 0000c9c3  ..E....E........
Contents of section .text.__x86.get_pc_thunk.ax:
 0000 8b0424c3                             ..$.            
Contents of section .comment:
 0000 00474343 3a202863 6f6e6461 2d666f72  .GCC: (conda-for
 0010 67652067 63632031 342e322e 302d3229  ge gcc 14.2.0-2)
 0020 2031342e 322e3000                     14.2.0.        
Contents of section .note.gnu.property:
 0000 04000000 18000000 05000000 474e5500  ............GNU.
 0010 020001c0 04000000 00000000 010001c0  ................
 0020 04000000 01000000                    ........        
Contents of section .eh_frame:
 0000 14000000 00000000 017a5200 017c0801  .........zR..|..
 0010 1b0c0404 88010000 1c000000 1c000000  ................
 0020 00000000 30000000 00410e08 8502420d  ....0....A....B.
 0030 056cc50c 04040000 10000000 3c000000  .l..........<...
 0040 00000000 04000000 00000000           ............    

Disassembly of section .group:

00000000 <.group>:
   0:	01 00                	add    DWORD PTR [eax],eax
   2:	00 00                	add    BYTE PTR [eax],al
   4:	06                   	push   es
   5:	00 00                	add    BYTE PTR [eax],al
	...

Disassembly of section .text:

00000000 <main>:
   0:	55                   	push   ebp
   1:	89 e5                	mov    ebp,esp
   3:	83 ec 10             	sub    esp,0x10
   6:	e8 fc ff ff ff       	call   7 <main+0x7>
			7: R_386_PC32	__x86.get_pc_thunk.ax
   b:	05 01 00 00 00       	add    eax,0x1
			c: R_386_GOTPC	_GLOBAL_OFFSET_TABLE_
  10:	c7 45 fc 01 00 00 00 	mov    DWORD PTR [ebp-0x4],0x1
  17:	c7 45 f8 09 00 00 00 	mov    DWORD PTR [ebp-0x8],0x9
  1e:	8b 55 fc             	mov    edx,DWORD PTR [ebp-0x4]
  21:	8b 45 f8             	mov    eax,DWORD PTR [ebp-0x8]
  24:	01 d0                	add    eax,edx
  26:	89 45 f4             	mov    DWORD PTR [ebp-0xc],eax
  29:	b8 00 00 00 00       	mov    eax,0x0
  2e:	c9                   	leave
  2f:	c3                   	ret

Disassembly of section .text.__x86.get_pc_thunk.ax:

00000000 <__x86.get_pc_thunk.ax>:
   0:	8b 04 24             	mov    eax,DWORD PTR [esp]
   3:	c3                   	ret

Disassembly of section .comment:

00000000 <.comment>:
   0:	00 47 43             	add    BYTE PTR [edi+0x43],al
   3:	43                   	inc    ebx
   4:	3a 20                	cmp    ah,BYTE PTR [eax]
   6:	28 63 6f             	sub    BYTE PTR [ebx+0x6f],ah
   9:	6e                   	outs   dx,BYTE PTR ds:[esi]
   a:	64 61                	fs popa
   c:	2d 66 6f 72 67       	sub    eax,0x67726f66
  11:	65 20 67 63          	and    BYTE PTR gs:[edi+0x63],ah
  15:	63 20                	arpl   WORD PTR [eax],sp
  17:	31 34 2e             	xor    DWORD PTR [esi+ebp*1],esi
  1a:	32 2e                	xor    ch,BYTE PTR [esi]
  1c:	30 2d 32 29 20 31    	xor    BYTE PTR ds:0x31202932,ch
  22:	34 2e                	xor    al,0x2e
  24:	32 2e                	xor    ch,BYTE PTR [esi]
  26:	30 00                	xor    BYTE PTR [eax],al

Disassembly of section .note.gnu.property:

00000000 <.note.gnu.property>:
   0:	04 00                	add    al,0x0
   2:	00 00                	add    BYTE PTR [eax],al
   4:	18 00                	sbb    BYTE PTR [eax],al
   6:	00 00                	add    BYTE PTR [eax],al
   8:	05 00 00 00 47       	add    eax,0x47000000
   d:	4e                   	dec    esi
   e:	55                   	push   ebp
   f:	00 02                	add    BYTE PTR [edx],al
  11:	00 01                	add    BYTE PTR [ecx],al
  13:	c0 04 00 00          	rol    BYTE PTR [eax+eax*1],0x0
  17:	00 00                	add    BYTE PTR [eax],al
  19:	00 00                	add    BYTE PTR [eax],al
  1b:	00 01                	add    BYTE PTR [ecx],al
  1d:	00 01                	add    BYTE PTR [ecx],al
  1f:	c0 04 00 00          	rol    BYTE PTR [eax+eax*1],0x0
  23:	00 01                	add    BYTE PTR [ecx],al
  25:	00 00                	add    BYTE PTR [eax],al
	...

Disassembly of section .eh_frame:

00000000 <.eh_frame>:
   0:	14 00                	adc    al,0x0
   2:	00 00                	add    BYTE PTR [eax],al
   4:	00 00                	add    BYTE PTR [eax],al
   6:	00 00                	add    BYTE PTR [eax],al
   8:	01 7a 52             	add    DWORD PTR [edx+0x52],edi
   b:	00 01                	add    BYTE PTR [ecx],al
   d:	7c 08                	jl     17 <.eh_frame+0x17>
   f:	01 1b                	add    DWORD PTR [ebx],ebx
  11:	0c 04                	or     al,0x4
  13:	04 88                	add    al,0x88
  15:	01 00                	add    DWORD PTR [eax],eax
  17:	00 1c 00             	add    BYTE PTR [eax+eax*1],bl
  1a:	00 00                	add    BYTE PTR [eax],al
  1c:	1c 00                	sbb    al,0x0
  1e:	00 00                	add    BYTE PTR [eax],al
  20:	00 00                	add    BYTE PTR [eax],al
			20: R_386_PC32	.text
  22:	00 00                	add    BYTE PTR [eax],al
  24:	30 00                	xor    BYTE PTR [eax],al
  26:	00 00                	add    BYTE PTR [eax],al
  28:	00 41 0e             	add    BYTE PTR [ecx+0xe],al
  2b:	08 85 02 42 0d 05    	or     BYTE PTR [ebp+0x50d4202],al
  31:	6c                   	ins    BYTE PTR es:[edi],dx
  32:	c5 0c 04             	lds    ecx,FWORD PTR [esp+eax*1]
  35:	04 00                	add    al,0x0
  37:	00 10                	add    BYTE PTR [eax],dl
  39:	00 00                	add    BYTE PTR [eax],al
  3b:	00 3c 00             	add    BYTE PTR [eax+eax*1],bh
  3e:	00 00                	add    BYTE PTR [eax],al
  40:	00 00                	add    BYTE PTR [eax],al
			40: R_386_PC32	.text.__x86.get_pc_thunk.ax
  42:	00 00                	add    BYTE PTR [eax],al
  44:	04 00                	add    al,0x0
  46:	00 00                	add    BYTE PTR [eax],al
  48:	00 00                	add    BYTE PTR [eax],al
	...

Explanation the Object File Disassembly#

This object code disassembly represents the compiled output of a C program for the i386 architecture. Below is an explanation of the key sections and their significance:


File Metadata#

add_int.o:     file format elf32-i386
architecture: i386, flags 0x00000011:
HAS_RELOC, HAS_SYMS
start address 0x00000000
  • File format: elf32-i386 indicates the ELF (Executable and Linkable Format) for the 32-bit i386 architecture.

  • Flags:

    • HAS_RELOC: The file contains relocation entries.

    • HAS_SYMS: The file contains a symbol table.

  • Start address: The starting address of the program is 0x00000000.


Sections#

The disassembly lists various sections in the object file, each serving a specific purpose:

  1. .group

    • Contains metadata for grouping related sections.

    • Flags: CONTENTS, READONLY, GROUP, LINK_ONCE_DISCARD.

  2. .text

    • Contains the executable code (instructions).

    • Flags: CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE.

  3. .data

    • Contains initialized global and static variables.

    • Flags: CONTENTS, ALLOC, LOAD, DATA.

  4. .bss

    • Contains uninitialized global and static variables.

    • Flags: ALLOC.

  5. .text.__x86.get_pc_thunk.ax

    • Contains helper code for position-independent code (PIC).

    • Flags: CONTENTS, ALLOC, LOAD, READONLY, CODE.

  6. .comment

    • Contains compiler version information.

    • Flags: CONTENTS, READONLY.

  7. .note.GNU-stack

    • Marks the stack as non-executable for security purposes.

    • Flags: CONTENTS, READONLY.

  8. .eh_frame

    • Contains exception handling information for stack unwinding.

    • Flags: CONTENTS, ALLOC, LOAD, RELOC, READONLY, DATA.


Symbol Table#

The symbol table lists symbols (functions, variables, and sections) in the object file:

00000000 g     F .text	00000030 main
00000000 g     F .text.__x86.get_pc_thunk.ax	00000000 .hidden __x86.get_pc_thunk.ax
00000000         *UND*	00000000 _GLOBAL_OFFSET_TABLE_
  • main: The main function is defined in the .text section and has a size of 0x30 bytes.

  • __x86.get_pc_thunk.ax: A helper function for position-independent code.

  • _GLOBAL_OFFSET_TABLE_: An undefined symbol used for dynamic linking.


Disassembly of .text Section#

This section contains the main function’s instructions:

00000000 <main>:
   0:	55                   	push   ebp
   1:	89 e5                	mov    ebp,esp
   3:	83 ec 10             	sub    esp,0x10
   6:	e8 fc ff ff ff       	call   7 <main+0x7>
			7: R_386_PC32	__x86.get_pc_thunk.ax
   b:	05 01 00 00 00       	add    eax,0x1
			c: R_386_GOTPC	_GLOBAL_OFFSET_TABLE_
  10:	c7 45 f4 01 00 00 00 	mov    DWORD PTR [ebp-0xc],0x1
  17:	c7 45 f8 09 00 00 00 	mov    DWORD PTR [ebp-0x8],0x9
  1e:	8b 55 f4             	mov    edx,DWORD PTR [ebp-0xc]
  21:	8b 45 f8             	mov    eax,DWORD PTR [ebp-0x8]
  24:	01 d0                	add    eax,edx
  26:	89 45 fc             	mov    DWORD PTR [ebp-0x4],eax
  29:	b8 00 00 00 00       	mov    eax,0x0
  2e:	c9                   	leave  
  2f:	c3                   	ret    
  • Prologue:

    • push ebp: Saves the base pointer of the previous stack frame.

    • mov ebp, esp: Sets up the current stack frame.

    • sub esp, 0x10: Allocates 16 bytes for local variables.

  • Position-Independent Code Setup:

    • call __x86.get_pc_thunk.ax: Calls a helper function to get the program counter.

    • add eax, _GLOBAL_OFFSET_TABLE_: Adjusts the program counter for the Global Offset Table (GOT).

  • Variable Initialization:

    • mov DWORD PTR [ebp-0xc], 0x1: Stores 1 in the local variable at -0xc.

    • mov DWORD PTR [ebp-0x8], 0x9: Stores 9 in the local variable at -0x8.

  • Addition:

    • mov edx, DWORD PTR [ebp-0xc]: Loads the first variable (1) into edx.

    • mov eax, DWORD PTR [ebp-0x8]: Loads the second variable (9) into eax.

    • add eax, edx: Adds edx to eax (result: 10).

    • mov DWORD PTR [ebp-0x4], eax: Stores the result (10) in the local variable at -0x4.

  • Return Value:

    • mov eax, 0x0: Sets the return value to 0.

    • leave: Restores the previous stack frame.

    • ret: Returns control to the caller.


Disassembly of .text.__x86.get_pc_thunk.ax#

This helper function retrieves the program counter for position-independent code:

00000000 <__x86.get_pc_thunk.ax>:
   0:	8b 04 24             	mov    eax,DWORD PTR [esp]
   3:	c3                   	ret    
  • mov eax, DWORD PTR [esp]: Loads the return address (program counter) into eax.

  • ret: Returns to the caller.


Disassembly of .comment#

Contains metadata about the compiler:

00000000 <.comment>:
   0:	00 47 43             	add    BYTE PTR [edi+0x43],al
   ...
  • This section is not executable and contains the string GCC: (Ubuntu 7.3.0-27ubuntu1~18.04) 7.3.0.


Disassembly of .eh_frame#

Contains exception handling information for stack unwinding:

00000000 <.eh_frame>:
   0:	14 00                	adc    al,0x0
   ...
  • This section is used by the runtime for exception handling and debugging.


Summary#

This object file disassembly shows:

  1. Code Structure: The .text section contains the main function and helper code for position-independent execution.

  2. Relocation: The GOT setup ensures compatibility with dynamically linked libraries.

  3. Metadata: Sections like .comment and .eh_frame provide additional information for debugging and exception handling.

Project Code on GitHub#

Project code and resources can be found on my GitHub repository:
GitHub