First we need to assemble the output.
as -g --32 <SRC_PATH> -o <HALF_OUTPUT_PATH>
Then we need to use linker
ld -m elf_i386 <HALF_OUTPUT_PATH> -o <FINAL_OUTPUT_PATH>
Now you should be able to execute the program. (./<FINAL_OUTPUT_PATH>)
As we are not printing out anything in most cases we need some way to see what is hapenning in the program.
That's where debugger comes to rescue. On Linux we can use gdb.
gdb --tui ./<FINAL_OUTPUT_PATH>
Basic commands:
Basic code structure we will use 99% of the time:
.section .text
.globl _start
_start:
# Some code
# Program exit
mov $0, %EBX # return code
mov $1, %EAX
int $0x80 # system call exit
Adding two values:
add A, B is equivalent to B += A
Subtracting two values:
sub A, B is equivalent to B -= A
.globl _start
Register | Value |
---|---|
EAX | ? |
EBX | ? |
ECX | ? |
EDX | ? |
mov $2, %EBX
Move literal 2 to EBX register. (EBX = 2)
Register | Value |
---|---|
EAX | ? |
EBX | 2 |
ECX | ? |
EDX | ? |
mov $1, %EDX
Move literal 1 to EBX register. (EDX = 1)
Register | Value |
---|---|
EAX | ? |
EBX | 2 |
ECX | ? |
EDX | 1 |
mov $10, %ECX
Move literal 10 to ECX register. (EDX = 10)
Register | Value |
---|---|
EAX | ? |
EBX | 2 |
ECX | 10 |
EDX | 1 |
add %EDX, %EBX
Add EDX to EBX. (EBX += EDX | EBX = 2 + 1)
Register | Value |
---|---|
EAX | 7 |
EBX | 3 |
ECX | 10 |
EDX | 1 |
sub %EBX, %ECX
Subtract ECX from EDX. (ECX -= EDX | ECX = 10 - 3)
Register | Value |
---|---|
EAX | ? |
EBX | 3 |
ECX | 7 |
EDX | 1 |
mov %ECX, %EAX
Move ECX value to EAX register. (EAX = ECX | EAX = 7)
Register | Value |
---|---|
EAX | 7 |
EBX | 3 |
ECX | 7 |
EDX | 1 |
nop
nop - no operation - end of program
Register | Value |
---|---|
EAX | 7 |
EBX | 3 |
ECX | 7 |
EDX | 1 |
Multiplying two values:
mul A is NOT so simple as it will always give us 64bit value (in 32bit architecture)
Example 4x3 will look like this:
00000000 00000000 00000000 00000100 | = 4 | |
x | 00000000 00000000 00000000 00000011 | = 3 |
----------------------------------- | ||
00000000 00000000 00000000 00000000 | 00000000 00000000 00000000 00001100 | = 12 |
| | | | |
\/ | \/ | |
EDX | EAX |
mov %EDX, %EAX
EAX = EDX (EAX = 4)
mul %EBX
EBX * EAX = 4 * 3 - result is pushed into EDX and EAX
mov %EAX, %ECX
ECX = EAX (ECX = 12)
Dividing two values:
div A is NOT so simple as it will always give us two 32bit values - EAX (quotient) and EDX (remainder) (in 32bit architecture)
Example 12/4 will look like this:
00000000 00000000 00000000 00001100 | <- EAX = 12 | |
/ | 00000000 00000000 00000000 00000100 | <- given variable = 4 |
----------------------------------- | ||
00000000 00000000 00000000 00000011 | 00000000 00000000 00000000 00000000 | |
| | | | |
quotient | remainder | |
\/ | \/ | |
EAX = 3 | EDX = 0 |
mov %EBX, %EAX
EAX = EBX (EAX = 12)
mov %EDX, %EBX
EBX = EDX (EBX = 3)
mov $0, %EDX
IMPORTANT! EDX must be zero
div %EBX
EAX / EBX (12/3) - result is pushed into EAX (quotient) and EDX (remainder)
mov %EAX, %ECX
ECX = EAX (ECX = 4)
1 | 1 | 0 | 0 |
1 | 0 | 1 | 0 |
- | - | - | - |
1 | 0 | 0 | 0 |
1 | 1 | 0 | 0 |
1 | 0 | 1 | 0 |
- | - | - | - |
1 | 1 | 1 | 0 |
1 | 1 | 0 | 0 |
1 | 0 | 1 | 0 |
- | - | - | - |
0 | 1 | 1 | 0 |
1 | 1 | 0 | 0 |
1 | 0 | 1 | 0 |
- | - | - | - |
0 | 1 | 1 | 1 |
1 | 1 | 0 | 0 |
1 | 0 | 1 | 0 |
- | - | - | - |
0 | 0 | 0 | 1 |
1 | 1 | 0 | 0 |
1 | 0 | 1 | 0 |
- | - | - | - |
1 | 0 | 0 | 1 |
6 basic gates on 1bit values are simple, BUT we use those operators on 32bit values.
That means when using any of those instructions we change every bit.
Example AND %EAX, %EBX where EAX = 15 EBX = 9
00000000 00000000 00000000 00001001 | = 9 | |
AND | 00000000 00000000 00000000 00001111 | = 15 |
----------------------------------- | ||
00000000 00000000 00000000 00001001 | = 9 | |
| | ||
\/ | ||
EBX = 9 |
We can use 16bit values
Example AND %AX, %BX where AX = 15 BX = 9
00000000 00001001 | = 9 | |
OR | 00000000 00001111 | = 15 |
----------------- | ||
00000000 00001111 | = 15 | |
| | ||
\/ | ||
BX = 9 |
or even 8bit values
Example AND %AL, %AH where AL = 15 AH = 9
00001001 | = 9 | |
OR | 00001111 | = 15 |
-------- | ||
00001111 | = 15 | |
| | ||
\/ | ||
AH = 9 |
Exchange values
xchg %EAX, %EBX
EAX = 3, EBX = 12
INC can be understood as var++
DEC can be understood as var--
Using compare is tricky with AT&T syntax.
Example CMP %EAX, %EBX
It should be read as EBX ? EAX where ? can be exchanged by:
Basic usage: JMP DESTINATION
Will change where the processor should look for next operation. Thinking abstractly about it - change cursor position.
Tightly connected with CMP command jump command have multiple comparison options:
For understanding arrays it is recommended to know where we can allocate data
For our usage we are interested in .data and .bss sections
Example -4(%ebx, %ecx, 4) (every register can be used to do it not only %ebx and %ecx)
-4 - displacement (usually the size of the array or one value)
%ebx - base register (usually start or end of the array)
%ecx - index
4 - scale factor (usually the size of the value)
And the whole thing can be represented as mathematic formula -4 + EBX + (ECX * 4)
Side note: Brackets '()' are changing how the processor is looking at numbers (as pointers and not as values)
See that literal numbers are not preceded by $
Example table tab[0] = 5; tab[1] = 8; tab[2] = 7; tab[11] = 11;
EBX is pointing at the end of the table and ECX is equal 0