어셈블리에서 C함수를 호출하기
C와 어셈블리를 같이 씀에 있어 가장 큰 장점은 어셈블리 코드를 통해서,
C 라이브러리와 유저가 작성한 함수들에 접근이 가능하다는 것이다.
segment .data
format db "%d"",0
segment .text
//...
lea eax,[ebp-16]
push eax
push dword format
call _scanf
add esp,8
//...
위와 같이 C의 scanf 함수를 호출할 수 있다.
이 때 C 호출 규약을 지킨다는 사실을 잊지 말아야 한다.
EBX, ESI, EDI 레지스터의 값을 저장하지만,
EAX, ECX, EDX 레지스터의 값은 변경 될 수 있다는 점이다.
재진입 및 재귀 서브프로그램
reetrant(재진입 서브프로그램)
재진입 서브프로그램은 다음 조건들을 만족하여야 한다.
-어떠한 코드 명령도 수정하면 안된다.
mov word [cs:$+7],5
add ax,2
위와 같이 코드를 수정하는 일은 실제모드에서 작동한다. 하지만 보호모드에서는 코드 세크먼트가 오지 읽기 전용이다.(Read-Only)
따라서 보호모드에서는 프로그램이 중단됨
-전역 데이터를 수정하면 안된다.(data나 bss 세그먼트의 데이터)모든 변수들은 스택에 보관된다.
재진입 코드의 장점
- 재진입 프로그램은 재귀적으로 호출 가능하다.
- 재진입 프로그램은 다수의 프로세스들에 의해 공유 가능하다.
( 하나의 프로그램에서 작동되는 여러 개의 인스턴스(instance)가 있을 때, 오직 코드의 한 부분만 메모리에 상주한다.
공유되는 라이브러리와 DLL(Dynamic Link Libraries) 또한 이 아이디어를 사용한다. )
- 재진입 프로그램은 multi-threaded 에서 더 잘 작동된다.
Recursive(재귀 서브프로그램)
이러한 서브프로그램들은 자기 자신을 호출한다.
재귀 호출은 직접(direct) 혹은 간점(indirect) 호출이 될 수 있다.
직접 재귀 호출은 foo라는 서브프로그램이 foo 내부에서 스스로 호출했을 때를 일컫는다.
간접 재귀 호출은 자기 자신으로부터 직접 호출되지 않았지만, 다른 서브프로그램이 호출하였기에 호출된 것을 의미한다.
재귀 서브프로그램들은 반드시 종료 조건(termination condition)이 있어야 한다.
segment .text
global _fact
_fact:
enter 0,0
mov eax,[ebp+8]
cmp eax,1
jbe term_cond
dec eax
push eax
call _fact
pop ecx
mul dword [ebp+8]
jmp short end_fact
term_cond:
mov eax,1
end_fact:
leave
ret
n(3) |
리턴주소 |
저장된 EBP |
n(2) |
리턴주소 |
저장된 EBP |
n(1) |
리턴주소 |
저장된 EBP |
3!를 구해주는 코드를 실행하면 위와 같은 스택 프레임이 형성된다.
%define i ebp-4
%define x ebp+8
segment .data
format db "%d",10,0
segment .text
global _f
extern _printf
_f:
enter 4,0
mov dword [i],0
lp:
mov eax,[i]
cmp eax,[x]
jnl quit
push eax
push format
call _printf
add esp,8
push dword[i]
call _f
pop eax
inc dword [i]
call _f
pop eax
inc dword [i]
jmp short lp
quit:
leave
ret
void f(int x){
int i;
for(i=0;i<x;i++){
printf("%d\n",i);
f(i);
}
}
C변수 저장 형식
global(전역)
모든 함수의 외부에서 정의되며, 고정된 메모리 위치에 저자오디어 있다.(data or bss)
프로그램의 시작부터 끝까지 계속 존재한다.
기본적으로 프로그램의 어떠한 함수에서든지 접근이 가능하다.
static(정적)
함수의 지역 변수로 static으로 선언된다.(C에서는 두가지 용도로 사용한다.)
고정된 메모리에 상주한다.(data or bss)
오직 정의되어 있는 함수에서만 직접적으로 접근이 가능하다.
automatic(자동)
이는 C변수가 함수내에서 정의될 때 기본적으로 지정되는 형식이다.
함수가 호출될 때 스택에 할당되고, 함수가 리턴될 때 스택에서 사라진다.
고정된 메모리를 가지고 있지 않다.
register(레지스터)
변수의 데이터를 위해 레지스터를 사용하도록 한다.
(이는 단순히 요청으로 컴파일러에서 반드시 지켜야 할 필요는 없다.
이 변수의 주소가 프로그램에서 사용된다면 요청은 무시된다. 레지스터는 주소가 없기 때문)
오직 단순한 정수 형식들 만이 레지스터의 값이 될 수 있다.(구조형은 될 수 없다.)
C 컴파일러는 프로그래머에 알리지 않고 레지스터 변수로 바꾸어 주기도 한다.
volatile(휘발성)
이 키워드는 컴파일러에게 변수의 값이 아무 때나 바뀔 수 있음을 알려준다.
컴파일러가 이 변수가 언제 수정될지에 대한 정보가 알 수 없다는 뜻이다.
컴파일러는 종종 변수를 레지스터에 잠시 보관해 놓고 변수가 사용되는 코드에서 이를 대신 사용한다.
그러나, volatile 형식의 변수들에겐 이러한 형태의 최적화를 할 수 없다.
(항상 메모리에 접근)
이는 쓰레드에 의해 값이 변경될 수 있는 변수에 사용한다.