Quote:
It may have tangential relation to SEH, but the primary purpose is different.
You are probably right! But I also know that GCC and MSVC creates a VERY strange code!!! Take a look at this example using variadics:
Code:
// test.c
// Compile with:
// x86_64-w64-mingw32-gcc -O2 -S -masm=intel test.c
//
#include <stdio.h>
#include <stdarg.h>
__attribute__ ( ( noinline ) ) int f ( int x ) { return 2 * x; }
__attribute__ ( ( noinline ) ) int g ( int x, ... )
{
int y;
va_list ap;
va_start ( ap, x );
y = va_arg ( ap, int );
va_end ( ap );
return x * y;
}
void dosomething ( int x, int y )
{
printf ( "%d\n", f ( x ) );
printf ( "%d\n", g ( x, y ) );
}
Which generate strange code like this:
Code:
; ECX = x
f:
; OK! very straightforward!
lea eax, [rcx+rcx]
ret
; ECX = x, EDX = y (NOT on stack, as you will see later!)
g:
; Notice: at this point the structure of stack should be:
; <caller stkframe>
; RSP-> retaddr
sub rsp, 24 ; reserve 3 QWORDS on stack (WHY?)
; The stack now is (in qwords):
; <caller stkframe>
; retaddr
; ?
; ?
; RSP-> ?
mov eax, edx ; EAX=y
mov [rsp+40], rdx ; saves y on stack,
; as if the arguments were stacked
; before the call (they aren't!).
; OBS: The hypothetical stacked x is [rsp+32] and
; never pushed!
lea rdx, [rsp+40] ; RDX now POINTS TO hypothetical stacked y.
imul eax, ecx ; EAX=y*x
; RSP+48 and RSP+56 points to hypothetical 3rd and 4th arguments
; (There are none!).
mov [rsp+48], r8 ; saves after the last argument?
mov [rsp+56], r9 ; again? even further?
mov [rsp+8], rdx ; saves ptr to y on stack on a local reserved space (WHY?).
; why 24 bytes were allocated if not used?
; Notice that there four qwords, after retaddr, overwrites the caller
; stack frame...
; R9
; R8
; y
; ?
; retaddr
; ?
; RDX
; RSP-> ?
add rsp, 24 ; reclaim reserved space.
ret
dosomething:
push rsi
push rbx
sub rsp, 40 ; WHY?
mov ebx, ecx
mov esi, edx
call f
lea rcx, .LC0[rip]
mov edx, eax
call printf
; See? A simple msabi calling convetion call, no stack used for the arguments.
mov edx, esi
mov ecx, ebx
call g
lea rcx, .LC0[rip]
mov edx, eax
add rsp, 40 ; WHY?
pop rbx
pop rsi
jmp printf
Notice that g() isn't using the stack (x and y are taken directly from ECX and EDX), writes y, R8 and R9 as if they were pushed on stack before the call and store the POINTER of y to a local var (reserved on stack) to discard right after. It NEVER writes x and never uses R8 and R9...
The dosomething() function is less strange, but still, reserves 40 bytes (5 QWORDS)... WHY? We're not using local vars or SEH here! This is ok if you consider the 4 qwords saved by g(). So, dosomething() is reserving this local space so g() don't overwrite dosomething()'s stack frame. Put why 40? Why not 32?
For me this is a very, very strange code.