ASM_(x86-64)::Control_Flow
So far, all our programs have been linear, meaning that they run from the first instruction to the last instruction without any change. This is great for simple programs, but it is very inefficient/impossible when we start to use programming concepts like loops or if/else statements.
Loops
Let's try to use our existing knowledge to create a function like this.
int foo(int a){
for(int i = 0; i < 5; i++){
a += 1
}
return a;
}
To provide context, our argument a
will come from the rdi
register, and we will "return" the final value into the rax
register.
If rdi
starts at 0, our assembly snippet should thus end with rax = 5
.
A naive attempt would lead us to the following code snippet.
Initializing...
And this in fact works very well.
However, what if our loop was dynamic?
int foo(int a, int b){
for(int i = 0; i < b; i++){
a += 1
}
return a;
}
b
will be in register
rsi
Now, we're in trouble.
We do not know how many times we need to create the add rdi, 1
instruction, as it is dependent on the value of rsi
.
Let there be jumps
This is where we need to learn more instructions!
The family of instructions we need here are jumps.
There are a variety of jumps, but the simplest to understand is jmp
.
jmp
takes one operand, which is the memory (relative address) / register that you shold jump to.
It'd be easiest to understand this through an example.
Here is an infinite loop (do not use Play
, only Step
through).
Initializing...
You can observe that rdi
's value will increment forever*!
With jmp
, we've managed to create non-linear code execution, allowing for constructs such as loops.
Obviously, we've still not been able to replicate the C code snippet, but we're getting closer!
Comparisons
The next class of instructions we require are comparison operators. There are a whole group of them, but in general they just aid to perform comparisons between two operands.
The cmp
instruction takes 2 operands, and compares their value.
It will then implicitly set a special register called RFLAGS
according to the result of the comparison.
Then, future instructions can check the RFLAGS
register to determine the result of the comparison that just occured.
We will not delve into the details to prevent too much confusion, but you can read more about this special register here (RFLAGS
).
Here, we show the instruction in action!
We will be using the je
instruction, which is the jump if equal type of jump.
As its name suggests, it only jumps if the previous comparison has equal operands, if not it will do a single step as usual.
Initializing...
rax
or
rbx
How will the code run differently?
Bring it all together
Now that we understand these two core concepts of jumps and comparisons, let's try to piece it together to create our function defined above.
See if you can implement it yourself!
This can be done using the instructions we've covered thus far (reminder: cmp
, je
, jmp
, mov
).
But if you need extra instructions, here's some more useful "jumps" we can provide you with (reference).
Instruction | Purpose |
---|---|
ja | jump if above |
jae | jump if above or equal |
jb | jump if below |
jbe | jump if below or equal |
je | jump if equal |
jg | jump if greater |
jge | jump if greater or equal |
jl | jump if lesser |
jle | jump if lesser or equal |
For reference, we are trying to recreate the following function (rdi=a
, rsi=b
, rax=return_value
):
int foo(int a, int b){
for(int i = 0; i < b; i++){
a += 1
}
return a;
}
Edit the following omulator box to write and debug your answer:
Initializing...