GAS (GNU Assembler) Linker Script와 Long Mode -> Real Mode 로 전환할때의 문제....

gpminsuk의 이미지

안녕하세요, 취미로 OS 공부해보고 있는 직딩입니다.

GAS, gcc로 개발 진행 중인데요.. Long mode에서 Real mode 전환한 후에 문제가 생겼습니다.

일단 Bootloader -> Second bootloader -> Kernel 식으로 로드하고 Kernel에 들어갈땐 Long mode전환된 상태로 들어갑니다.
메모리 0x7C00 0x500 0x20000

Kernel 링크할 때 스크립트를 사용하는데 스크립트는 다음과 같습니다.

SECTIONS
{
    . = 0x20000;
 
    .text ALIGN(0x1000) :
	{ 
		*(.text)
	}
 
    .rodata ALIGN(0x1000) :
    {
        start_ctors = .;
        *(.ctor*)
        end_ctors = .;
 
        start_dtors = .;
        *(.dtor*)
        end_dtors = .;
 
        *(.rodata*)
    }
 
    .data ALIGN(0x1000) :
	{
		*(.data)
	}
 
    .bss  ALIGN(0x1000) :
	{
		*(.bss)
	}
}

그냥 웹에서 긁어와서 조금 수정했습니다.. 뭔가 많이 부족한 링크 스크립트 같습니다.

문제는 여기입니다.

Kernel에서 .code16으로 Real mode전환 후 16bit 코딩을 하는데

저 위에 locator가 처음에 0x20000으로 셋팅을 해놓으니 (0x20000에 불러오니 이렇게 해야할 것 같아서 했는데 다른 방법이 있으면 좀 알려주세요)

모든 Address를 0x20000을 기준으로 하는겁니다.

.code64
.intel_syntax noprefix
.text
.org 0x0

Label:
jmp Label

이러면 Label에 0x20000을 더한 address로 점프를 하더라구요 (맞습니다 flat 메모리 구조에서는 가능합니다만)

문제는 Real-mode 전환 시 16비트 코드에서 생깁니다...

.code16
.intel_syntax noprefix
.text
.org 0x0

Label:
jmp far 0x2000:Label

에러납니다... 왜냐하면 0x20000 어드레스가 16비트에서는 표현 불가능 하기 때문인 것 같습니다.

0x2000:0x0 으로 표현 가능해야 하는데

링커가 세그먼트는 모르는지 0x0에다가 그냥 0x20000을 더해서 0x2000:0x20000으로 바꿔버리는 것 같습니다.

그렇다고 jmp far 0x0:Label 하면

relocation truncated to fit: R_X86_64_16 against `.text'

이런 링커 에러가 납니다.

그렇다고 jmp far 0x2000:Label - 0x20000 꼼수를 써봐도

Error: value of fffffffffffe002b too large for field of 2 bytes at 000000000000002a

어셈블러 컴파일 에러가 납니다.

한가지 더 해 본 시도는

section을 16/64비트 코드를 나눠서

--section-start 옵션을 사용해 봤는데

그러면 .bin 출력 파일 자체가 굉장히 커집니다. ( 0x0: 16비트 코드 | 이 사이는 모두 0로 채워짐 | 0x20000: 64비트 코드)

링커 스크립트를 고쳐서 특정 파일이나 구간만 relocation(??이라고 부르는게 맞을까요)을 하지 않도록 하는 방법은 없을까요...

혹은 아예 다른 방법은 없을까요

저의 접근 방법이 잘못됬다면 어떤 방법이 좋을까요