Assembly + C - Part #5
During my journey of learning Assembly, i was taking some notes .. and now, I'm sharing it openly with you <3 ...
Last updated
During my journey of learning Assembly, i was taking some notes .. and now, I'm sharing it openly with you <3 ...
Last updated
Let's look at the following simple C code:
The generated assembly code for the previous c code is a as the following:
CALL's job is to transfer control to a different function; in a way that control can later be resumed where it left off.
First it pushes the address of the next instruction onto the stack.
For use by RET for when the procedure is done
Then it changes RIP to the address given in the instruction.
Destination address for the target function can be specified in multiple ways:
Absolute address
Relative address (relative to the end of the end instruction, or some other register)
Two forms:
Pop the top of the stack into RIP (remember, pop implicitly increments stack point, RSP)
In this form, the instruction is just written as "ret".
Pop the top of the stack into RIP also add a constant number of bytes to RSP
In this form, the instruction is written as "ret 0x8" or "ret 0x20" etc.
Windows: Think algebra or C: y = 2x +1;
*nix/GNU: Think elementary school: 1 + 2 = 3
so registers get a % prefix and immediate get a $
During my notes i'll use Intel syntax It's important to know both, so you can read documents in either format.
Can move:
register to register
memory to register, register to memory
immediate to register, immediate to memory
But ! ... Never memory to memory
Memory addresses are given in "r/mX" form.
Adds or Substracts, just as expected
Destination operand can be r/mX or register
Source operand can be r/mX or register or immedaite
No source and destination as r/mXs, because that could allow for memory to memory transfer, which isn't allowed on x86.
You can't tell, but it "zero extended" the rax reg (meaning it filled in the upper 32 bits of raw with zeros)
From section 3.4.1.1 in the Nov 2020 Intel Manual:
NOTE: This only applies to writing to registers, not memory ! If you write a 32 bit value to what you're imagining as a "64-bit" memory location (such as for a 64 bit local variable), still only 32 bits will be changed !
Let's back to the execution
5.
6.
7.
8.
func()
is dead code - its return value is not used for anything, and main() always returns 0xF00D. If optimizations were turned on in the compiler, it would remove func().
We don't yet understand why main()
does sub rsp, 28h
and add rsp,28h
With .. another mystery lister :D (will come to it later)
Old: Why do the GCC/Clang HelloWorlds have balanced Push/Pop instruction but Visual Studio doesn't ?
Wha'ts up with the sub/add 0x28
in main()
Let's take the following C code:
The stack will be as the following:
NOP (6%) PUSH/POP (17%) CALL/RET (9%) MOV (23%) ADD/SUB (5%)
Now, we know probably 60% from all of assembly instructions !! <3