부트로더 - 6
이전에 올린 코드에 대해서 하나하나 분석하는 시간을 갖도록 하겠다.
1
[ORG 0x00]
ORG는 Origin의 약자로 해당 코드는 0x00번지부터 시작한다는 뜻이다.
1
[BITS 16]
BITS 16 이라고 적혀있는데 이는 16bit 코드를 말한다.
어셈블리어로 입력하면 명령어는 다 동일한데 왜 16bit 코드라고 명시하는지 의문이 들 수 있겠다. 이는 당연하지만 어셈블리어로는 같을 수 있으나 기계어로 변경했을때 코드가 다르기 때문이며 16bit는 16bit용으로 32bit는 32bit용으로 별개로 코드가 존재한다. 그렇기 때문에 16bit 아키텍쳐에서 구동시키고 싶다면 별도로 명시를 해줘야 해당 아키텍처에서 구동가능한 형태의 기계어로 컴파일된다.
1
SECTION .text
이 다음부터 기재하는 코드는 text 섹션에 속한다는 뜻이다.
여기서 말하는 섹션은 무얼까?
간단히 말해서 적재된 데이터의 형태를 구분짓는 경계라고 생각하면 편하다.
기본적으로 지원하는 섹션의 목록은 아래와 같다.
- .text : 컴파일된 바이너리 코드
- .rodata : 읽기 전용 데이터(ex - 상수)
- .data : 초기화 된 전역 변수나 정적 변수
- .bss : 초기화 되지 않은 전역 변수가 정적 변수
이외의 사용자가 별도로 정의하는 섹션이 있을 수 있다.
1
jmp 0x07C0:START
단순하게 생각한다면 START 레이블로 코드를 건너뛰는 코드라고 볼수 있다. 하지만 이전 포스팅에서는 CS 레지스터에 0x07c0을 넣는 코드라고 했다.
이게 무슨 말일까? 이는 jmp 명령어가 어떻게 구동되는지에 대해 알아야 이해할 수 있는 부분이다.
jmp가 점프하는 방식에는 세 가지 방식이 있다.
하나는 short 점프, 하나는 long 점프,다른 나머지 하나는 far 점프라고 불리는것이 그것이다.
여기서 앞에 두개, 즉 short와 long은 주소만 지정하여 점프하는 경우가 대부분이지만 far 점프의 경우 CS 레지스터, 즉 코드 세그먼트 레지스터를 이용하여 좀 더 큰 범위로 점프하게 된다.
이럴때 쓰는 방식이 far 점프이며 jmp [세그먼트]:[오프셋] 방식이 되고 여기서 세그먼트애 해당하는 값이 코드 세그먼트 레지스터로 들어가게 되는 것이다.
1
2
TOTALSECTORCOUNT: dw 0x02
KERNEL32SECTORCOUNT: dw 0x02
각각 TOTALSECTORCOUNT 레이블과 KERNEL32SECTORCOUNT 값을 0x02로 지정하되 dw로 만큼 지정한다는 뜻이다. 여기서 dw는 무엇일까? 이 dw를 알기 위해서는 데이터를 취급하는 단위에 대해서 알아야한다. 16bit, 32bit, 64bit 프로세서는 각각 한번에 처리하는 데이터의 양이 16bit, 32bit, 64bit라고 볼 수 있다. 이러한 기본 단위를 WORD라고 표현한다. 코드에서 언급된 dw는 DOUBLE WORD의 줄임말로 16bit 아키텍처에서 WORD를 두 배로 한 크기라는 뜻이다. 그렇기 때문에 각각 TOTALSECTORCOUNT 레이블과 KERNEL32SECTORCOUNT 값은 32bit 크기를 할당하여 0x02 값을 넣었다는 뜻이다.
1
2
3
4
5
START:
mov ax, 0x07C0 ; 부트 로더의 시작 어드레스(0x7C00)를 세그먼트 레지스터 값으로 변환
mov ds, ax ; DS 세그먼트 레지스터에 설정
mov ax, 0xB800 ; 비디오 메모리의 시작 어드레스(0x7C00)를 세그먼트 레지스터 값으로 변환
mov es, ax ; ES 세그먼트 레지스터에 설정
세그먼트 레지스터에는 직접 값을 넣을 수 없기 때문에 ax레지스터에 넣고나서 그걸 간접적으로 옮겨야한다.
1
2
3
4
5
; 스택을 0x0000:0000~0x0000:FFFF 영역에 64KB 크기로 생성
mov ax, 0x0000 ; 스택 세그먼트의 시작 어드레스(0x0000)를 세그먼트 레지스터 값으로 변환
mov ss, ax ; SS 세그먼트 레지스터에 설정
mov sp, 0xFFFE ; SP 레지스터의 어드레스를 0xFFFE로 설정
mov bp, 0xFFFE ; BP 레지스터의 어드레스를 0xFFFE로 설정
스택을 64kb 크기로 생성한다고 되어있는데 스택에 관련된 레지스터를 이용하여 스택 크기를 잡을 수 있다. ss는 스택의 시작 위치를 잡아주는 세그먼트 레지스터이다. 해당 레지스터에는 0x0000을 넣어서 스택의 제한을 기재한다. sp는 스택에서 가장 최근에 저장된 데이터의 위치를 가르키는데 64kb로 지정하고 아직 데이터를 아무것도 넣지 않았으니 64kb 위치를 지정하고 있다. bp는 32bit 이상의 경우 하나의 스택프레임의 기준점이 된다.
스택 프레임이라는 것은 프로그램이 사용하고 있는 하나의 스택 영역이라고 생각하면 된다. 크게 할당된 스택내에서 해당 스택의 범위 만큼만 프로세스가 사용하는 식인 것인데 BP는 그러한 스택 프레임의 기준점이 된다.
하지만 16BIT 리얼모드에서는 다른 프로세스가 돌아갈일이 없다. 거의 일괄처리 시스템이나 다를바 없기 때문이다. 그렇기 때문에 당장 여기선 sp와 동일하게 사용된다.
나머지 코드는 이어서 포스팅하도록 하겠다.
참고 문헌
- 64Bit 멀티코어 OS의 구조 - 한승훈 저
- 한빛출판사 - 64Bit 멀티코어 OS의 구조 첨부 부트로더 코드
업데이트
2024-02-07 : 스택 프레임에 대한 부가 설명