Assembly - Control Flow
Alur Program
Kita akan belajar bagaimana alur program bekerja seperti percabangan, perulangan, serta fungsi bekerja.
Percabangan
Ada sebuah register khusus RIP
yang berfungsi sebagai Instruction Pointer.
Register tersebut memuat nilai alamat instruksi yang akan dijalankan nantinya.
CPU berpatokan pada RIP
untuk mengetahui alamat yang akan dieksekusinya.
main:
xor edi, edi ; rip
mov esi, 15 ; rip + 1
call kill ; rip + 2
Ada dua jenis percabangan, unconditional jump dan conditional jump.
Unconditional jump
Pertama, kita akan mengenal instruksi jmp
.
Kalian tahu goto
?. Yap, Instruksi jmp
bekerja seperti keyword goto
dalam bahasa C/C++.
Contohnya
_start:
goto end
end:
exit(0);
Di dalam bahasa assembly
_start:
jmp end
end:
xor edi, edi
call exit
Karena jmp
tidak membutuhkan informasi apapun tentang suatu kondisi maka jmp
juga digunakan untuk seluruh percabangan yang tidak melakukan perbandingan atau selalu benar,
jmp label
akan memuat alamat label
ke dalam rip
sehingga CPU akan mengeksekusi instruksi di alamat label yang ditunjuk oleh rip
.
Contohnya if (1)
. Dalam hal tersebut, Compiler langsung menghasilkan jmp
(atau tidak sama sekali) dikarenakan tidak ada perbandingan yang perlu dilakukan.
Conditional jump
Conditional jump berjalan apabila suatu kondisi terpenuhi. Dalam kasus ini, conditional jump akan melakukan melihat informasi kondisi di Register Flag. Register Flag adalah register yang menyimpan informasi hasil dari komparasi.
Ada 3 Register Flags yang biasanya dipakai, Sign Flag, Zero Flag, dan Carry Flag.
- Sign Flag (SF) adalah flag yang berisi status perlakuan kedua operan sebagai
signed
atauunsigned
. - Zero Flag (ZF) adalah flag yang berisi status hasil komparasi.
- Carry Flag (CF) adalah flag yang berfungsi untuk memberi tahu jika hasil komparasi terjadi overflow pada operasi unsigned.
Informasi-informasi di dalam rflags
dibuat setelah melakukan komparasi melalui instruksi cmp
atau test
.
Contoh kasusnya adalah operator logika ==
.
Jika nilai kedua operan sama, maka Zero Flag akan bernilai 1.
Control-Flow dengan nilai ZF sama dengan 1 akan dieksekusi dengan instruksi JE
(Jump if Equal).
Jika tidak maka Zero Flag akan bernilai 0.
Control-Flow dengan nilai ZF sama dengan 0 akan dieksekusi dengan instruksi JNE
(Jump if Not Equal).
section .rdata
szBerhasil db `RBX == 0\n\0`
nLenBer equ $-szBerhasil
szGagal db `RBX != 0\n\0`
nLenGal equ $-szGagal
section .text
global _start
_start:
mov eax, 1
mov edi, 1
push rbx ; save rbx ke stack, nanti di chapter stack penjelasan lanjutnya
xor ebx, ebx
cmp rbx, 0 ; Kalian juga bisa menggunakan test rbx, rbx untuk mengecek nilai rbx apakah 0
je true
jne false
true:
mov esi, szBerhasil
mov edx, nLenBer
syscall
jmp exit
false:
mov esi, szGagal
mov edx, nLenGal
syscall
jmp exit
exit:
pop rbx ; restore rbx from stack
mov eax, 60
xor edi, edi
syscall
Tugas
Nah, sekarang tugas kalian bereksperimen dengan flag dan instruksi lainnya di sini.
Perulangan
Dengan memodifikasi percabangan, kita bisa membuat perulangan :).
Contoh kasus
static char szText[] = "I like you!\n";
for (int i = 0; i < 143; i++)
write(STDOUT_FILENO, szText, sizeof(szText) - 1);
Itu akan berubah menjadi
static char szText[] = "I like you!\n";
int i = 0;
while (i < 9) {
write(STDOUT_FILENO, szText, sizeof(szText) - 1);
i++;
}
Kemudian berubah lagi menjadi
static char szText[] = "I like you!\n";
int i = 0;
loop:
if (i < 9) {
write(STDOUT_FILENO, szText, sizeof(szText) - 1);
i++;
goto loop;
}
Note : Disarankan untuk memakai non-volatile register agar nilai di dalam register terjamin tidak berubah setelah pemanggilan fungsi
_start:
push rbx
xor ebx, ebx
loop:
mov eax, 1
mov edi, 1
mov esi, szText
mov edx, 35
syscall
add ebx, 1
cmp ebx, 9
jl loop
pop rbx
Kurang lebih begitulah hasil konversi dari C ke Assembly, https://godbolt.org/z/odYKvd3h1