Say I'm writing a routine in x86 assembly, like, "add" which adds two numbers passed as arguments.
For the most part this is a very simple method:
push ebp
mov ebp, esp
mov eax, [ebp+8]
add eax, [ebp+12]
mov esp, ebp
pop ebp
ret
But, is there any way I could rewrite this method to avoid the use of the "ret" instruction and still have it produce the exact same result?
Sure.
push ebp
mov ebp, esp
mov eax, [ebp+8]
add eax, [ebp+12]
mov esp, ebp
pop ebp
pop ecx ; these two instructions simulate "ret"
jmp ecx
This assumes you have a free register (e.g, ecx). Writing an equivalent that uses "no registers" is possible (after all the x86 is a Turing machine) but is likely to include a lot of convoluted register and stack shuffling.
Most current OSes offer thread-specific storage accessible by one of the segment registers. You could then simulate "ret" this way, safely:
pop gs:preallocated_tls_slot ; pick one
jmp gs:preallocated_tls_slot
This does not need any free registers to simulate ret
, but it needs 4 bytes of memory (a dword). Uses indirect jmp
. Edit: As noted by Ira Baxter, this code is not reentrant. Works fine in single-threaded code. Will crash if used in multithreaded code.
push ebp mov ebp, esp mov eax, [ebp+8] add eax, [ebp+12] mov ebp, [ebp+4] mov [return_address], ebp pop ebp add esp,4 jmp [return_address] .data return_address dd 0
To replace only the ret
instruction, without changing the rest of the code. Not reentrant. Do not use in multithreaded code. Edit: fixed bug in below code.
push ebp mov ebp, esp mov ebp, [ebp+4] mov [return_address], ebp pop ebp add esp,4 jmp [return_address] .data return_address dd 0
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With