예전에도 몇번 올린 내용이지만, 처음 만들게 된 계기는 바로 coroutine을 구현 하면서 였다..
함수에 대한 주소만 가지고 함수를 호출해야 하는 상황이 되는데, 문제는 기존의 방식으로는 제한된
함수의 인자만을 가질수 있다는 것이다. 즉, 다양한 인자를 갖는 함수를 다룰수가 없다는 문제가 있다.
기본의 방식은..
DWORD (*CALLBACK)( LPVOID args) = &CALLBACK_FUNC;
와 같이 접근해 처리해 주어야 한다는 것이었다.
얼마 지나지 않아, coroutine에 적재되는 함수의 형태에 따라 다양한 인자를 유동적으로 주고 싶어 졌다.
그래서 만든것이 invoke 함수이다.
이 함수는 기존방식 처럼 함수의 주소를 기반으로 접근을 하게 되지만, N개의 인자를 함수별로 별도로
줄수 있으며 다양한 형태의 반환값도 받을수 있다는 것이다.
참고로, 아래 소스의 문제라면 stack으로 넘겨주어야 하는 인자가 buffer에 저장되어 있어야 한다는 것이다.
#define COARGS_OFFSET(n) ((sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1))
#define COARGS_PTR(ptr,t) *(t *)((ptr += COARGS_OFFSET(t)) - COARGS_OFFSET(t))
typedef struct co__Args {
char *buffer;
int size;
} COARGS, *LPCOARGS;
// COARGS << value;
template <typename T> COARGS &operator << (COARGS &args, const T v) {
int l = COARGS_OFFSET(T); {
*(T *)(args.buffer + args.size) = v;
args.size += l;
} return args;
}
typedef __int64 CORETURN, *LPCORETURN;
enum CORETURN_TYPE {
TYPE_INT32 = 0,
TYPE_INT64,
TYPE_FP32, /* float */
TYPE_FP64, /* double */
TYPE_VOID = TYPE_INT32, /* __int32 */
TYPE_PTR = TYPE_INT64, /* __int64 */
};
void invoke( LPVOID pStack, LPVOID func, LPCOARGS args, LPVOID retP, enum CORETURN_TYPE rtype)
{
char *begin;
__asm {
mov begin, esp // 현재 ESP를 저장
mov eax, pStack // user stack이 존재하는지 검사
cmp eax, 0
jne copy_start
mov eax, esp // user stack 설정
copy_start:
cmp dword ptr [args],0
jne copy_args // if (args == NULL)
mov esp, eax // stack 설정
jmp run
copy_args:
mov ebx,dword ptr [args]
mov ecx,dword ptr [ebx+4] // ecx = args->size
sub eax, ecx // ESP에 args를 저장하기 위한 공간 확보
mov esp, eax // esp에 변경된 위치 저장
// ------------- eax(=esp) 의 위치부터 args를 복사
mov edi, eax // eax = stack(esp)
mov esi, dword ptr [ebx] // esi = args->buffer
push ecx // { ecx = args->size
sar ecx,2 // args->size >> 2
cld // clear 방향 flag
rep movsd // count 수 만큼 dword 복사 (esi -> edi) ecx 만큼
pop ecx // }
and ecx,80000003h // ecx = args->size % 4
rep movsb // left 수 만큼 byte 복사 (esi -> edi) ecx 만큼
// ------------- 변경된 esp 설정
run:
call func
mov esp, begin // stack 복구
cmp dword ptr [retP],0
je done // if (retP == NULL) return;
mov esi, retP
cmp rtype, TYPE_INT64
jne not_int64 // if (rtype == TYPE_INT64) {
mov [esi], eax
mov [esi+4], edx
jmp done
not_int64:
cmp rtype, TYPE_FP64
jne not_f64 // } else if (rtype == TYPE_FP64) {
// f64
fstp QWORD PTR [esi]
jmp done
not_f64:
cmp rtype, TYPE_INT32
jne not_int32 // } else if (rtype == TYPE_INT32) {
mov [esi], eax
jmp done
not_int32: // } else
fstp DWORD PTR [esi]
done:
}
}
PS.
문제점이나.. 수정 사항이 있다면 알려주는 센스~