Multiplying Integers: i386#

Introduction#
In this section we will examine the disassembly of a simple C program that multiplies 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 = 2;
b = 3;
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 mult_int.c -o mult_int.s
gcc --sysroot=/ -m32 -c mult_int.s -o mult_int.o
Viewing the Assembly Code#
The assembly code is in Intel syntax, which is commonly used for x86 assembly language.
.file "mult_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,
# mult_int.c:7: a = 2;
mov DWORD PTR -4[ebp], 2 # a,
# mult_int.c:8: b = 3;
mov DWORD PTR -8[ebp], 3 # b,
# mult_int.c:10: c = a * b;
mov eax, DWORD PTR -4[ebp] # tmp102, a
imul eax, DWORD PTR -8[ebp] # c_3, b
mov DWORD PTR -12[ebp], eax # c, c_3
mov eax, 0 # _4,
# mult_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 code is an assembly file (mult_int.s
) generated from a simple C program (mult_int.c
) that performs integer multiplication. 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#
.file "mult_int.c"
.intel_syntax noprefix
.file "mult_int.c"
: Indicates that this assembly code was generated from the C source filemult_int.c
..intel_syntax noprefix
: Specifies that the assembly code uses Intel syntax (as opposed to AT&T syntax).
Global and Function Declarations#
.text
.globl main
.type main, @function
.text
: Marks the beginning of the code (text) section where executable instructions are stored..globl main
: Declares themain
function as global, making it accessible to the linker..type main, @function
: Specifies thatmain
is a function.
Main Function#
main:
.LFB0:
.cfi_startproc
main:
: The label for themain
function..LFB0:
: A local label used internally by the assembler for debugging purposes..cfi_startproc
: Marks the start of the Call Frame Information (CFI) for the function, used for stack unwinding during debugging.
Prologue#
push ebp #
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
mov ebp, esp #,
.cfi_def_cfa_register 5
sub esp, 16 #,
push ebp
: Saves the base pointer (ebp
) of the previous stack frame onto the stack.mov ebp, esp
: Sets the current base pointer (ebp
) to the value of the stack pointer (esp
), establishing a new stack frame.sub esp, 16
: Allocates 16 bytes of space on the stack for local variables.
Global Offset Table Setup#
call __x86.get_pc_thunk.ax #
add eax, OFFSET FLAT:_GLOBAL_OFFSET_TABLE_ # tmp98,
call __x86.get_pc_thunk.ax
: Calls a helper function to get the program counter (PC) value.add eax, OFFSET FLAT:_GLOBAL_OFFSET_TABLE_
: Adjusts the PC value to point to the Global Offset Table (GOT), which is used for position-independent code.
Variable Initialization#
# mult_int.c:7: a = 2;
mov DWORD PTR -4[ebp], 2 # a,
# mult_int.c:8: b = 3;
mov DWORD PTR -8[ebp], 3 # b,
mov DWORD PTR -4[ebp], 2
: Stores the value2
in the memory location-4[ebp]
, which corresponds to the variablea
.mov DWORD PTR -8[ebp], 3
: Stores the value3
in the memory location-8[ebp]
, which corresponds to the variableb
.
Multiplication#
# mult_int.c:10: c = a * b;
mov eax, DWORD PTR -4[ebp] # tmp102, a
imul eax, DWORD PTR -8[ebp] # c_3, b
mov DWORD PTR -12[ebp], eax # c, c_3
mov eax, DWORD PTR -4[ebp]
: Loads the value ofa
into theeax
register.imul eax, DWORD PTR -8[ebp]
: Multiplies the value ineax
(i.e.,a
) by the value ofb
(stored at-8[ebp]
), storing the result ineax
.mov DWORD PTR -12[ebp], eax
: Stores the result of the multiplication (c
) in the memory location-12[ebp]
.
Return Value#
mov eax, 0 # _4,
mov eax, 0
: Sets the return value of themain
function to0
(standard convention for successful execution).
Epilogue#
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
leave
: Restores the previous stack frame by settingesp
toebp
and popping the oldebp
value from the stack.ret
: Returns control to the caller.
Helper Function: __x86.get_pc_thunk.ax
#
.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
This function retrieves the program counter (PC) value and stores it in the
eax
register. It is used for position-independent code to calculate addresses relative to the GOT.
Summary#
This assembly code implements a simple C program that multiplies two integers (a = 2
and b = 3
) and stores the result in a variable (c
). The main
function sets up the stack frame, performs the multiplication using the imul
instruction, and returns 0
as the exit status. The helper function __x86.get_pc_thunk.ax
is used 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 mult_int.o -o mult_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 mult_int.o > objdump_of_dot_o.txt
objdump -x -D -s -t -Mintel mult_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#
mult_int.o: file format elf32-i386
mult_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 0000002f 00000000 00000000 0000003c 2**0
CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
2 .data 00000000 00000000 00000000 0000006b 2**0
CONTENTS, ALLOC, LOAD, DATA
3 .bss 00000000 00000000 00000000 0000006b 2**0
ALLOC
4 .text.__x86.get_pc_thunk.ax 00000004 00000000 00000000 0000006b 2**0
CONTENTS, ALLOC, LOAD, READONLY, CODE
5 .comment 00000028 00000000 00000000 0000006f 2**0
CONTENTS, READONLY
6 .note.GNU-stack 00000000 00000000 00000000 00000097 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 mult_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 0000002f 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 c745fc02 000000c7 45f80300 00008b45 .E......E......E
0020 fc0faf45 f88945f4 b8000000 00c9c3 ...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 2f000000 00410e08 8502420d ..../....A....B.
0030 056bc50c 04040000 10000000 3c000000 .k..........<...
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 02 00 00 00 mov DWORD PTR [ebp-0x4],0x2
17: c7 45 f8 03 00 00 00 mov DWORD PTR [ebp-0x8],0x3
1e: 8b 45 fc mov eax,DWORD PTR [ebp-0x4]
21: 0f af 45 f8 imul eax,DWORD PTR [ebp-0x8]
25: 89 45 f4 mov DWORD PTR [ebp-0xc],eax
28: b8 00 00 00 00 mov eax,0x0
2d: c9 leave
2e: 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: 2f das
25: 00 00 add BYTE PTR [eax],al
27: 00 00 add BYTE PTR [eax],al
29: 41 inc ecx
2a: 0e push cs
2b: 08 85 02 42 0d 05 or BYTE PTR [ebp+0x50d4202],al
31: 6b c5 0c imul eax,ebp,0xc
34: 04 04 add al,0x4
36: 00 00 add BYTE PTR [eax],al
38: 10 00 adc BYTE PTR [eax],al
3a: 00 00 add BYTE PTR [eax],al
3c: 3c 00 cmp al,0x0
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#
The attached file is the output of the objdump
command applied to an object file (mult_int.o
). It provides a detailed view of the binary structure, including sections, symbols, and disassembled machine code. Below is a detailed explanation of the key parts of the file:
File Metadata#
mult_int.o: file format elf32-i386
architecture: i386, flags 0x00000011:
HAS_RELOC, HAS_SYMS
start address 0x00000000
File format: The object file is in the ELF (Executable and Linkable Format) for a 32-bit Intel architecture (
elf32-i386
).Architecture: The code is compiled for the i386 architecture (32-bit x86).
Flags:
HAS_RELOC
: The file contains relocation entries, which are used to adjust addresses during linking.HAS_SYMS
: The file contains a symbol table.
Start address: The entry point of the object file is at address
0x00000000
.
Sections#
The object file is divided into sections, each serving a specific purpose. Below are the key sections:
1. .group
#
Idx Name Size VMA LMA File off Algn
0 .group 00000008 00000000 00000000 00000034 2**2
CONTENTS, READONLY, GROUP, LINK_ONCE_DISCARD
Purpose: Groups related sections for optimization and linking.
Attributes:
CONTENTS
: Contains data.READONLY
: Read-only section.GROUP
: Groups related sections.LINK_ONCE_DISCARD
: Ensures only one copy of the section is kept during linking.
2. .text
#
1 .text 0000002f 00000000 00000000 0000003c 2**0
CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
Purpose: Contains the executable code for the program.
Attributes:
CODE
: Contains machine instructions.READONLY
: Read-only section.RELOC
: Contains relocation entries for linking.
3. .data
#
2 .data 00000000 00000000 00000000 0000006b 2**0
CONTENTS, ALLOC, LOAD, DATA
Purpose: Contains initialized global and static variables.
Attributes:
DATA
: Contains data.ALLOC
: Allocated in memory during execution.
4. .bss
#
3 .bss 00000000 00000000 00000000 0000006b 2**0
ALLOC
Purpose: Contains uninitialized global and static variables.
Attributes:
ALLOC
: Allocated in memory during execution but not stored in the object file.
5. .text.__x86.get_pc_thunk.ax
#
4 .text.__x86.get_pc_thunk.ax 00000004 00000000 00000000 0000006b 2**0
CONTENTS, ALLOC, LOAD, READONLY, CODE
Purpose: Contains the helper function
__x86.get_pc_thunk.ax
, used for position-independent code.Attributes:
CODE
: Contains machine instructions.READONLY
: Read-only section.
6. .comment
#
5 .comment 00000028 00000000 00000000 0000006f 2**0
CONTENTS, READONLY
Purpose: Contains metadata about the compiler and build environment.
Attributes:
READONLY
: Read-only section.
7. .note.gnu.property
#
7 .note.gnu.property 00000028 00000000 00000000 00000098 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
Purpose: Contains properties for GNU tools, such as security features.
Attributes:
DATA
: Contains data.READONLY
: Read-only section.
8. .eh_frame
#
8 .eh_frame 0000004c 00000000 00000000 000000c0 2**2
CONTENTS, ALLOC, LOAD, RELOC, READONLY, DATA
Purpose: Contains exception handling information for stack unwinding.
Attributes:
DATA
: Contains data.RELOC
: Contains relocation entries for linking.
Symbol Table#
SYMBOL TABLE:
00000000 l df *ABS* 00000000 mult_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 0000002f main
00000000 g F .text.__x86.get_pc_thunk.ax 00000000 .hidden __x86.get_pc_thunk.ax
00000000 *UND* 00000000 _GLOBAL_OFFSET_TABLE_
Symbols:
main
: The main function, located in the.text
section.__x86.get_pc_thunk.ax
: A helper function for position-independent code._GLOBAL_OFFSET_TABLE_
: An undefined symbol used for dynamic linking.
Disassembly#
1. .text
Section#
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 02 00 00 00 mov DWORD PTR [ebp-0x4],0x2
17: c7 45 f8 03 00 00 00 mov DWORD PTR [ebp-0x8],0x3
1e: 8b 45 fc mov eax,DWORD PTR [ebp-0x4]
21: 0f af 45 f8 imul eax,DWORD PTR [ebp-0x8]
25: 89 45 f4 mov DWORD PTR [ebp-0xc],eax
28: b8 00 00 00 00 mov eax,0x0
2d: c9 leave
2e: c3 ret
This is the disassembled code for the
main
function. It matches the assembly code in the previous attachment:Sets up the stack frame.
Initializes variables
a
andb
.Multiplies
a
andb
using theimul
instruction.Stores the result in
c
.Returns
0
.
2. .text.__x86.get_pc_thunk.ax
Section#
00000000 <__x86.get_pc_thunk.ax>:
0: 8b 04 24 mov eax,DWORD PTR [esp]
3: c3 ret
This is the helper function
__x86.get_pc_thunk.ax
, which retrieves the program counter (PC) value for position-independent code.
Summary#
This objdump
output provides a detailed view of the object file mult_int.o
. It includes:
Metadata about the file format and architecture.
Section details, including their purpose and attributes.
A symbol table listing functions and symbols.
Disassembled machine code for the
main
function and the helper function__x86.get_pc_thunk.ax
.
The main
function performs integer multiplication (a * b
) and returns 0
. The helper function supports position-independent code by retrieving the program counter.
Project Code on GitHub#
Project code and resources can be found on my GitHub repository:
GitHub