반응형

호출 규약(Calling Convention)함수를 호출할 때 파라미터와 복귀 주소 등을 지정하는 규칙입니다.

보호 모드에서 사용하는 대표적인 호출 규약에는 stdcall, cdecl, fastcall이 있으며 약간씩 차이가 있습니다.

→ stdcall 방식

● 파라미터를 스택에 저장하며, 호출된 쪽에서 스택을 정리합니다.

cdecl 방식

파라미터를 스택에 저장하지만, 함수를 호출한 쪽에서 스택을 정리합니다.

fastcall 방식

파라미터를 레지스터에 저장하는 것을 제외하면 stdcall 방식과 같습니다.


참고) 지금까지는 리얼 모드, 즉 16bit 모드를 기준으로 했지만 보호 모드는 32bit 이므로 레지스터와 스택의 크기가 리얼 모드의 2배입니다.
따라서 스택의 기본 크기는 4byte(DWORD)입니다.
앞에서는 스택의 파라미터 접근 방법은 보호 모드나 리얼 모드나 같지만, 스택의 기본 크기가 2배이므로 Offset도 2배를 해줘야 합니다.
레지스터 역시 크기가 2배이며, 16bit 레지스터 이름에 E 접두사가 붙은 EAX, EBX, ECX, ESP, EBP 레지스터4byte 레지스터를 의미합니다.
크기가 2배로 늘어났어도 역할은 리얼 모드와 같습니다.

 


함수 호출 예시 C언어 코드


stdcall(Standard Call)파라미터를 스택에 넣을 때 오른쪽에서 왼쪽 순서로 집어넣습니다.

그리고 함수의 반환 값은 EAX 레지스터(32bit AX 레지스터)를 사용하며 스택에서 파라미터를 제거하는 작업을 호출된 함수가 처리합니다.

 

함수 호출 예시 어셈블리어-stdcall 방식


위의 코드를 보면 호출되는 함수인 Add() 함수의 마지막에서 ret 12를 호출하여 파라미터를 제거합니다.

ret 12스택 프레임 레지스터(sp)에 12를 더한 후, 복귀 주소로 되돌아가는 명령으로 add esp, 12 후 ret를 수행하는 것과 같습니다.

12를 더하는 이유보호 모드에서 스택 크기가 4byte이므로 파라미터 3개는 4byte * 3개 = 12byte를 스택에서 차지하기 때문입니다.


cdecl(C-Declare call)stdcall과 동일하게 파라미터의 오른쪽에서 왼쪽 순서스택에 집어넣습니다.

함수의 반환 값 역시 AX 레지스터를 사용합니다.

하지만 단 한 가지 차이점은 스택에서 파라미터를 제거하는 작업을 호출한 함수가 대신 처리합니다.

 

함수 호출의 예(어셈블리어-cdecl 방식)

 

위의 코드를 보면 stdcall 방식과 달리 호출한 main() 함수의 마지막에서 add esp, 12를 호출하여 스택의 파라미터를 제거합니다.

 

fastcall컴파일마다 구현하는 방식이 조금씩 다릅니다.

윈도우에서 많이 쓰는 마이크로소프트사의 컴파일러를 기준으로 설명하면, 처음 2개의 파라미터를 ECX와 EDX 레지스터에 삽입하는 점을 제외하고는 stdcall과 같습니다.

함수 호출의 예 어셈블리어-fastcall 방식


IA-32e 모드의 호출 규약은 fastcall을 확장한 방식이며, 보다 많은 레지스터를 파라미터 전달용으로 사용합니다.

IA-32e 모드로 전환되면 기존 레지스터 8개에 범용 레지스터 8개(R8 ~ R15)가 추가됩니다.

IA-32e 모드의 호출 규약은 16개의 레지스터를 이용하여 파라미터를 최대 6개까지 전달할 수 있도록 설계되었기 때문에 파라미터 개수만 제한하여 사용함으로써 스택 관련 작업을 줄일 수 있습니다.


 

→ 최종 부트 로더 소스 코드


이전의 내용을 모두 적용한 부트 로더의 전체 소스 코드는 아래와 같습니다.

아래의 코드 중 124번 라인은 원래 서적에서는 cmp al, 19이었는데, 추후 최신 QEMU에서 맨 위의 사진과 같은 결과가 나왔습니다.

그리하여 아래에서와 같이 124번 라인을 수정하니 정상적으로 출력되었습니다.

124번 라인cmp al, 37로 해줍니다.

자세한 내용은 github.com/sean-baek/64bit_multicore_os/blob/main/issue.md 에서 7번 글을 확인하시면됩니다.

124번 줄에 cmp al, 19라고 했을 때의 결과물(위와 같이 나온다면 코드에 문제가 있는것입니다.)

 

최종 부트 로더 1

 

최종 부트 로더 2

 

최종 부트 로더 3

 

최종 부트 로더 4

 

최종 부트 로더 5

 

최종 부트 로더 6



OS 이미지가 정상적으로 로딩되었다면 0x10000 위치로 이동해서 보호 모드 커널 코드를 실행할 것입니다.

하지만 아직 부트 로드는 완성이 되었어도 로딩할 커널 이미지가 없기 때문에 부트 로더를 빌드해서 실행하면 검은 화면만 표시됩니다.

이미지 로딩 기능을 구현할 때 부트 로더를 제외한 나머지 섹터의 크기를 1024로 설정했으므로 부트 로더를 포함하여 최소 1025개 이상의 섹터로 구성된 OS 이미지가 필요하지만 지금은 막 완성된 부트 로더를 테스트하는 단계이므로 가상 OS 이미지로 실제 OS 이미지를 대신합니다.

반응형

+ Recent posts