ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [운영체제 만들기] 에러 사항
    프로젝트/운영체제 만들기 2023. 5. 24. 11:58

    글의 전제

    - 미해결글은 빨간색 볼드체다. 그 외에 검은색 볼드체 및 파란색 볼드체는 해결된 에러 사항이다.


    글의 내용

    - QEMU 64비트 연산(미해결)

    : 64비트 모드를 설정했는데, 64비트 데이터 타입이 64비트를 지원하지 않는다.

    - QEMU 자체에서 뭔가 설정할 필요도 없어보이는데, 문제를 잘 모르겠다. 램 사이즈를 4GB로 설정하긴 했지만, 사실 이 부분도 문제가 될 부분은 아니라고 생각된다. s64는 long long 이고, u64는 unsigned long long 이다. 그런데 2개다 모두 문제다. 아직 32비트 타입밖에 지원하지 않는 것 같다. 아직 확인이 더 필요한 사항이다. 램 사이즈를 256GB 허용하도록 했는데, unsigned long long 에 7GB 주소를 넣으면 위와 같이 GDB에서 음수로 출력이 된다. GDB에서 현재 디버깅하는 소스를 64비트가 아닌, 32비트로 인식하는 건지 확인이 필요하다. 

     

     

    - 16비트 부트 로더 링크 스크립트

    : YohdaOS는 PBL과 SBL로 16비트 부트 로더가 나뉜다. 그런데, GDB로 디버깅을 위해 2개의 부트 로더를 링크 스크립트로 통합할 필요가 있었다. CAT 명령어를 통한 파일 합치기는 순수 바이너리 2개를 단순히 합치는 거기 때문에 실질적인 디버깅은 어렵다. 그래서 링크 스크립트를 아래와 같이 작성했다.

    SECTIONS
    {
        .text 0x7C00 :
        {   
            pbl.o (.text)
            . = 0x7E00;                                                  
            sbl.o (.text)
            *(.rodata)
        }
    
        .data :
        {   
            *(.data)
        }
    
        .bss :
        {   
            *(.bss)
            *(COMMON)
        }   
    }

    : 그런데 위와 같이 링크 스크립트 작성해서 빌드하면 부트 로더의 사이즈가 32KB가 나온다. 내가 예상한 대로라면 1KB 가 나와야 한다.

     

    : 그런데 PBL과 SBL은 굳이 저렇게 주소를 딱 지정해 주지 않아도, 붙어있기 때문에 `. = 0x7E00`이 의미가 없다고 생각했다. 그래서 아래와 같이 수정했다.

    SECTIONS
    {
        .text 0x7C00 :
        {
            pbl.o (.text)
            sbl.o (.text)                                                
            *(.rodata)
        }
    
        .data :
        {
            *(.data)
        }
    
        .bss :
        {
            *(.bss)
            *(COMMON)
        }
    }

    : 위와 같이 바꿨는데, 정상적인 바이너리 사이즈가 나왔다.

     

     

    : xxd 명령어를 통해 확인한 바이너리 내용도 내가 예상한 대로 PBL 다음에 SBL이 이어진다.

    00000000: ea05 7c00 00fa 31c0 8ed8 a3c1 7cb8 00b8  ..|...1.....|...
    00000010: 8ec0 b800 008e d0bd c007 89ec 31f6 26c7  ............1.&.
    00000020: 0400 0f83 c602 81fe a00f 75f2 6809 7de8  ..........u.h.}.
    00000030: 5900 83c4 0206 57b8 c007 8ec0 bfbe 0126  Y.....W........&
    00000040: 8a05 3c08 740d 83c7 1081 fffe 0175 f05f  ..<.t........u._
    00000050: 07eb 0d68 c37c e832 0083 c402 0657 5f07  ...h.|.2.....W_.
    00000060: 06b8 e007 8ec0 31db b402 b020 b500 b102  ......1.... ....
    00000070: b600 cd13 073c 2074 0068 e27c e80c 0083  .....< t.h.|....
    00000080: c402 ff36 c17c ea00 7e00 0055 89e5 5053  ...6.|..~..U..PS
    00000090: 5657 06b8 00b8 8ec0 b8a0 00f7 26c1 7c89  VW..........&.|.
    000000a0: c7ff 06c1 7c8b 7604 8a1c 80fb 0074 0b26  ....|.v......t.&
    000000b0: 881d 83c6 0183 c702 ebee 075f 5e5b 585d  ..........._^[X]
    000000c0: c300 0054 6865 7265 2065 7869 7374 2061  ...There exist a
    000000d0: 2061 6374 6976 6520 7061 7274 6974 696f   active partitio
    000000e0: 6e00 5265 6164 7920 666f 7220 6a75 6d70  n.Ready for jump
    000000f0: 696e 6720 7365 636f 6e64 6172 7920 626f  ing secondary bo
    00000100: 6f74 6c6f 6164 6572 0059 6f68 6461 4f53  otloader.YohdaOS
    00000110: 2050 7269 6d61 7279 2042 6f6f 7420 4c6f   Primary Boot Lo
    00000120: 6164 6572 2053 7461 7274 0090 9090 9090  ader Start......
    00000130: 9090 9090 9090 9090 9090 9090 9090 9090  ................
    ...
    ...
    000001e0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    000001f0: 0000 0000 0000 0000 0000 0000 0000 55aa  ..............U.
    00000200: ea05 7e00 0058 a3cc 7e68 ce7e e887 0083  ..~..X..~h.~....
    00000210: c402 06b8 0000 8ec0 268b 1efe 7df7 d08e  ........&...}...
    00000220: c026 8b0e 0e7e 0739 cb75 0968 f27e e865  .&...~.9.u.h.~.e
    00000230: 0083 c402 682d 7fe8 5c00 83c4 0200 0000  ....h-..\.......
    00000240: 0000 0000 00ff ff00 0000 9acf 00ff ff00  ................
    00000250: 0000 92cf 0000 0000 0000 0066 31c0 8cd8  ...........f1...
    00000260: 66c1 e004 6605 3d7e 0000 66a3 577e 66b8  f...f.=~..f.W~f.
    00000270: 557e 0000 662d 3d7e 0000 a355 7e0f 0116  U~..f-=~...U~...
    00000280: 557e ebfe 0f20 c00c 010f 22c0 ea91 7e08  U~... ...."...~.
    00000290: 0066 31c0 ebfe 5589 e550 5356 5706 b800  .f1...U..PSVW...
    000002a0: b88e c0b8 a000 f726 cc7e 89c7 ff06 cc7e  .......&.~.....~
    000002b0: 8b76 048a 1c80 fb00 740b 2688 1d83 c601  .v......t.&.....
    000002c0: 83c7 02eb ee07 5f5e 5b58 5dc3 0000 596f  ......_^[X]...Yo
    000002d0: 6864 614f 5320 5365 636f 6e64 6172 7920  hdaOS Secondary 
    000002e0: 426f 6f74 204c 6f61 6465 7220 5374 6172  Boot Loader Star
    000002f0: 7400 466f 7220 656e 7465 7269 6e67 2074  t.For entering t
    00000300: 6f20 7072 6f74 6563 7465 6420 6d6f 6465  o protected mode
    00000310: 2c20 7072 6570 6172 696e 6720 666f 7220  , preparing for 
    00000320: 7468 6520 4761 7465 2d41 3230 0053 7461  the Gate-A20.Sta
    00000330: 7274 2070 7265 7061 7269 6e67 2066 6f72  rt preparing for
    00000340: 2047 4454 206f 6620 7072 6f74 6563 7465   GDT of protecte
    00000350: 6420 6d6f 6465 0090 9090 9090 9090 9090  d mode..........
    00000360: 9090 9090 9090 9090 9090 9090 9090 9090  ................
    ...
    ...
    000003e0: 9090 9090 9090 9090 9090 9090 9090 9090  ................
    000003f0: 9090 9090 9090 9090 9090 9090 9090 9090  ................

    : 문제는 왜 중간에 `. = 0x7E00`을 삽입하면, 파일 사이즈가 32KB가 나올까? 그 이유는 링커의 VMA아 LMA에 대해 어느정도 알고 있어야 한다. 링커 스크립트에 암묵적으로 작성하는 모든 주소는 VMA이다. 즉, 가상 주소다. 그러나 명시적으로 AT()를 사용하면, LMA로 실제 램에 로딩될 물리 주소를 지정할 수 있다. 저렇게 섹션안에 `. = 0x7E00` 을 해버리면 실제 물리주소를 매핑해버린다. 이 내용은 `readelf -e ${OBJ_FILE}`을 통해서 해당 오브젝트 파일의 물리 주소가 어떻게 되는지 확인이 가능하다. 그리고 일단 중간에 `. = 0x7E00`을 넣으면 생성 파일이 아래와 같아진다. 그리고 `nm -v ${OBJ_FILE}`를 통해 해당 오브젝트 파일의 심벌 테이블을 볼 수 있다. 이 명령어를 통해서 해당 파일에 제일 앞에 있는 심볼의 주소를 확인해보자. 

     

    00000000: ea05 7c00 00fa 31c0 8ed8 a3c1 7cb8 00b8  ..|...1.....|...
    00000010: 8ec0 b800 008e d0bd c007 89ec 31f6 26c7  ............1.&.
    00000020: 0400 0f83 c602 81fe a00f 75f2 6809 7de8  ..........u.h.}.
    00000030: 5900 83c4 0206 57b8 c007 8ec0 bfbe 0126  Y.....W........&
    00000040: 8a05 3c08 740d 83c7 1081 fffe 0175 f05f  ..<.t........u._
    00000050: 07eb 0d68 c37c e832 0083 c402 0657 5f07  ...h.|.2.....W_.
    00000060: 06b8 e007 8ec0 31db b402 b020 b500 b102  ......1.... ....
    00000070: b600 cd13 073c 2074 0068 e27c e80c 0083  .....< t.h.|....
    00000080: c402 ff36 c17c ea00 7e00 0055 89e5 5053  ...6.|..~..U..PS
    00000090: 5657 06b8 00b8 8ec0 b8a0 00f7 26c1 7c89  VW..........&.|.
    000000a0: c7ff 06c1 7c8b 7604 8a1c 80fb 0074 0b26  ....|.v......t.&
    000000b0: 881d 83c6 0183 c702 ebee 075f 5e5b 585d  ..........._^[X]
    000000c0: c300 0054 6865 7265 2065 7869 7374 2061  ...There exist a
    000000d0: 2061 6374 6976 6520 7061 7274 6974 696f   active partitio
    000000e0: 6e00 5265 6164 7920 666f 7220 6a75 6d70  n.Ready for jump
    000000f0: 696e 6720 7365 636f 6e64 6172 7920 626f  ing secondary bo
    00000100: 6f74 6c6f 6164 6572 0059 6f68 6461 4f53  otloader.YohdaOS
    00000110: 2050 7269 6d61 7279 2042 6f6f 7420 4c6f   Primary Boot Lo
    00000120: 6164 6572 2053 7461 7274 0090 9090 9090  ader Start......
    00000130: 9090 9090 9090 9090 9090 9090 9090 9090  ................
    ...
    ...
    00007e00: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    00007e10: 0000 0000 0000 0000 0000 0000 0000 55aa  ..............U.
    00007e20: ea05 7e00 0058 a3cc 7e68 ce7e e887 0083  ..~..X..~h.~....
    00007e30: c402 06b8 0000 8ec0 268b 1efe 7df7 d08e  ........&...}...
    00007e40: c026 8b0e 0e7e 0739 cb75 0968 f27e e865  .&...~.9.u.h.~.e
    00007e50: 0083 c402 682d 7fe8 5c00 83c4 0200 0000  ....h-..\.......
    00007e60: 0000 0000 00ff ff00 0000 9acf 00ff ff00  ................
    00007e70: 0000 92cf 0000 0000 0000 0066 31c0 8cd8  ...........f1...
    00007e80: 66c1 e004 6605 3d7e 0000 66a3 577e 66b8  f...f.=~..f.W~f.
    00007e90: 557e 0000 662d 3d7e 0000 a355 7e0f 0116  U~..f-=~...U~...
    00007ea0: 557e ebfe 0f20 c00c 010f 22c0 ea91 7e08  U~... ...."...~.
    00007eb0: 0066 31c0 ebfe 5589 e550 5356 5706 b800  .f1...U..PSVW...
    00007ec0: b88e c0b8 a000 f726 cc7e 89c7 ff06 cc7e  .......&.~.....~
    ...
    ...
    00007ee0: 9090 9090 9090 9090 9090 9090 9090 9090  ................
    00007ef0: 9090 9090 9090 9090 9090 9090 9090 9090  ................

    : sbl.o의 시작 주소가 0x7E00이 되었다. `0x200 ~ 0x7E00` 사이는 0으로 패딩이 채워진다. 이건 공간을 낭비하는 짓이다. 이 글을 참고하자.

      

     

    - 16비트 부트 로더 코드 점프

    : GDB를 통한 16비트 부트 로더의 디버깅은 MBR 부트 로더 하나만 디버깅하면 문제가 없다. 세그먼트 레지스터를 사용하지 못하는 문제가 있지만, YohdaOS는 FLAT MODEL을 기반으로 하기 때문에 큰 문제가 없다. 근데 VBR 부트 로더를 통합한 부트 로더를 GDB로 디버깅하는 것은 쉽지 않다. 일단, GDB 때문에 각각의 파일을 ELF로 만들어야 한다. 그리고 2개의 파일을 합쳐야 하니, 링킹도 해야 한다. 그리고 최종적으로 실행할 때는, 순수 바이너리로 만들기 위해 OBJCOPY를 활용해야 한다. 

     

    : 0x7C00에서 시작하는 것은 크게 문제가 없다. 문제는 0x7E00 부터다. 여기는 원 바이 원으로 코드가 진행이 안된다. 

     

     

    - i386:x86-64 architecture of input file `main.o' is incompatible with i386 output

    : 이 에러는 i386기반의 목적 파일들과 함께 x86_64 컴파일러를 통해 만든 목적 파일을 링킹할 때, 발생한 에러다. 

    nasm -f elf -g3 -F dwarf pbl.asm -o pbl.o
    ...
    x86_64-linux-gnu-gcc -c -m32 -ffreestanding main.c -o main.o
    ...
    ld -Tlink.ld -m elf_i386 pbl.o main.o -o bl.elf
    ...

    : NASM과 GCC에서는 에러가 없다. 이 에러는 LD 과정에서 발생한다. 이 문제는 목적 파일의 포맷이 서로 맞지 않기 때문에 발생한다. 크로스 컴파일러를 바꿔야 한다. 이러한 문제들 때문에, 개발 환경에서 툴체인을 먼저 만들고 가는게 중요하다. 툴체인안에 ASM, GCC, LD가 모두 통합되어 있기, 하나의 툴체인을 설치하면 해당 툴체인에 존재하는 모든 유틸리티들이 서로 호환되기 때문이다. 위의 NASM과 X86_64-LINUX-GNU는 서로 호환되지 않는 것이다.  

     

     

     

    - *** No rule to make target `../.././gcc/libgcc.mvars'.  Stop.

    : GCC 크로스 컴파일 빌드를 하다가 발생한 에러다. configure와 make는 소스가 있는 탑 레벨에서 빌드를 하면 안된다고 한다. 별도의 폴더를 만들고 그 폴더에서 configure와 make를 해야 한다. 

    cd $HOME/src
     
    mkdir build-binutils
    cd build-binutils
    ../binutils-x.y.z/configure --target=$TARGET --prefix="$PREFIX" --with-sysroot --disable-nls --disable-werror
    make
    make install

     : 위에 예시는 OSDEV에서 BINUTILS를 컴파일하는 예시인데, 탑레벨에서 `build-binutils` 라는 별도의 폴더를 만들어서 해당 폴더 안에서 CONFIGURE와 MAKE를 진행하는 것을 볼 수 있다. 그리고 참고로, `./configure` 명령을 수행하면, 해당 폴더에 `Makefile`이 만들어진다. 그래서 해당 폴더에서만 `make` 명령을 수행할 수 있다.

     

     

     

    - ld: cannot use executable file 'main.o' as input to a link.

    : 실행 파일을 링킹하려고 할 때, 발생하는 문제다. 위의 `main.o` 파일이 이미 실행 파일이라는 뜻이다. 이 문제는 대게 아래와 같은 코드를 짜면 발생한다.

    %.o: %.c
        $(CC) $(CFLAGS) -o $@ $^

    : 결론만 말하자면 `-c` 옵션을 사용하지 않아서 발생한 문제다. `-c` 옵션의 기능은 컴파일 및 어셈블만 진행하고 링킹은 진행하지 않는 것이다. 즉, 오브젝트 파일만 생성하고 실행 파일을 만들지는 않는다. 결국 결론은 아래와 같이 수정하면 *.o 파일은 실행 파일이 아닌, 목적 파일로 만들어진다.

     

    : 참고로, `-o` 옵션만 쓰게 되면 컴파일과 링킹을 모두 진행한다.

    %.o: %.c
        $(CC) $(CFLAGS) -c -o $@ $^

     

     

    - cannot find crt0.o: No such file or directory

    : C에서 main() 함수를 호출되는 과정을 crt0에 의해서 호출된다. OS를 개발시에 이 오류가 발생하면, 링커 및 컴파일시에 `-nostartfiles` 옵션을 빼먹지 않았는지 확인해야 한다. 그리고 `nostartfiles`와 항상 붙어다니는 `-nostdlib` 옵션 추가해야 한다.

     

     

     

     

    - 보호 모드 진입에서, 스택 세그먼트 설정 시 무한 재부팅

    : 보호 모드 진입 시,제일 먼저하는 작업은 당연 데이터 세그먼트를 로딩하는 작업이다. 근데, SS를 세팅하는 과정에서 QEMU가 계속 무한 재부팅을 했다.

    [BITS 32]
    
    _pmain:
    	xor ax, 0x10
    
    	mov ds, ax
    	mov es, ax
    	mov fs, ax
    	mov gs, ax
    	mov ss, ax
    
    	call main

    : 명백히 잘못된 코드다. 보호 모드 부터는 모든 세그먼트 레지스터를 가려진다. 즉, 직접 컨트롤 할 수가 없다. 위의 DS, ES, FS, GS, SS는 모두 `세그먼터 셀렉터`다. 그래서 데이터 세그먼트 엔트리가 있는 0x10을 대입해줘야 한다. 그런데, ax와 0x10을 xor한 값을 각 세그먼트 셀렉터에 넣으니 문제가 발생한 것이다. 근데, 다른 셀렉터는 문제가 없었는데 `mov ss, ax`는 곧 바로 무한 재부팅으로 빠져버렸다. 저 AX 레지스터의 값을 보니 0x01이 었다. 이건 널 셀렉터가 있는 위치다.

     

    : 그리고 만약에 다른 셀렉터가 되더라도 문제는 발생한다. 예를 들어, MOV 명령어를 통해 세그먼트 셀렉터에 값을 집어넣게 되면, 제일 먼저 유효성 검사를 한다. AX가 가리키는 셀렉터의 세그먼트가 읽기만 가능한 세그먼트라고 치자. 이 세그먼트가 DS, ES, FS, GS에 들어가는 것은 유효성 검사에서 문제가 안된다. 왜냐면, 저 셀렉터들은 데이터 읽기만이 가능할 수도 있기 때문이다. 그러나 SS는 다르다. 스택의 PUSH, POP, RET, CALL 같은 명령어들은 모두 읽기/쓰기를 전제로 한다. 즉, 스택은 읽기/쓰기가 가능한 영역이여야 한다. 그래서 SS에 값을 넣을 때, 에러가 발생한 것이다. 아마 그 에러는 `General Protection(0x10)` 였을 것이다. 이 에러는 트리플 폴트에러로 캐스팅 된다.

     

     

     

     

    - Q35에서 보호 모드 진입 후, ESP 설정 시 에러

    : 16비트 부트 로더 2개 모두 GDB로 디버깅이 되지만, FAR JUMP시에 `n`키를 누르면 해당 점프 위치로 가지 않고 그냥 끝까지 실행이 되버린다. 그래서 점프 위치에 브레이크 포인트를 잡고 `c`를 누르면 해당 위치에서 멈추게 해서 디버깅을 진행하고 있다.

    " 이 문제는 GDB가 16비트 디버깅시에, 세그먼트 주소 지정 방식을 이해못해서 생기는 문제로 보인다. FAR JUMP를 할 때, 세그먼트 레지스터에 값을 0으로 설정하면 문제가 없는데, 0이 아닌 다른 값으로 설정하면 `n`키를 누를 때 마다 이상한 위치로 이동하게 된다. 그러므로, GDB로 디버깅을 하고 싶다면 16비트 모드에서는 FAR JUMP시에 세그먼트 레지스터의 값은 0으로 하고 오프셋만을 이용해서 코딩해야 한다.

     

     

    - GDB에서 C 파일 디버깅시, `No Source Available`

    : GDB는 `-g` 옵션을 넣어 빌드하는 C 파일에 디버깅 정보를 같이 넣어 생성한다.

     

     

    -  GDB에서 `n` 키를 눌렀는데 라인 바이 라인으로 처리하지 못하고 코드가 이상한 라인으로 건너뛰는 경우

    : 여러 가지 원인이 있을 수 있다.

    1" GDB가 로딩한 ELF 파일과 QEMU에서 로딩하고 있는 소스가 싱크가 맞춰져 있는지 확인한다. 즉, QEMU에서 실행되고 파일과 GDB에서 디버깅하는 파일이 동일한 파일이어야 한다.
    2" 16비트 환경에서 CS를 0으로 설정하지 않은 경우.
    3" 디스크에서 커널 이미지의 일부만 가져온 경우. -> 아래의 `특정 라인부터 `add %al,(%eax)`로 나옴` 내용을 참고 

     

    - 커스텀 섹션 설정 시, GDB에서 `No Source Available` 문제(미해결)

    : pbl.asm과 sbl.asm에 .text 섹션이 아닌, .pbl.text 섹션과 .sbl.text 섹션으로 설정해서 링커 스크립트 .text 안에 넣었다. 그런데, 이렇게 하면 그냥 실행은 되는데 GDB 상에서 소스가 출력되지 않는다. 이 문제는 NASM에서 커스텀 섹션을 정의할 경우, 각 섹션에 플래그가 할당되지 않아서 발생하는 문제다. 아래 코드를 보자.

    ; lbl.asm
    section .text.mode64
    global _start64:

    _start64:
        ; Re-enable Paging
        mov rax, cr0 
        ;and rax, 1<<31
        mov cr0, rax    

        jmp $
    ; link.ld
    ENTRY(_start64)
    SECTIONS
    {
        . = 0x100000;
        .text.mode64 :
        {   
            lbl.o(.text.mode64)
        }
    }

    : 위와 같이 `.text.mode64`라는 섹션을 만들고 링커 스크립트에 작성했다. 근데, 저 섹션이 실행이 안된다. 해당, 파일을 분석해볼 필요가 있다. `readelf -a lbl.o` 을 통해서 나온 결과는 다음과 같다.

     

    ELF Header:
      ...
      Type:                              EXEC (Executable file)
      Machine:                           Advanced Micro Devices X86-64
      Version:                           0x1
      Entry point address:               0x100000
      Start of program headers:          64 (bytes into file)
       ...
      Number of section headers:         12
      Section header string table index: 11
    ...
    Section Headers:
      [Nr] Name              Type             Address           Offset
           Size              EntSize          Flags  Link  Info  Align
      [ 0]                   NULL             0000000000000000  00000000
           0000000000000000  0000000000000000           0     0     0
      [ 1] .text             PROGBITS         0000000000100000  00100000
           000000000000002b  0000000000000000  AX       0     0     16
      [ 2] .text.mode64      PROGBITS         000000000010002b  0010002b
           0000000000000008  0000000000000000   A       0     0     1
      [ 3] .data.mode64      PROGBITS         0000000000101000  00101000
           0000000000002000  0000000000000000   A       0     0     4096
     ...
     ...

    Key to Flags:
      W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
      L (link order), O (extra OS processing required), G (group), T (TLS),
      C (compressed), x (unknown), o (OS specific), E (exclude),
      l (large), p (processor specific)

    ...
    ...

    Symbol table '.symtab' contains 29 entries:
       Num:    Value          Size Type    Bind   Vis      Ndx Name
         0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
         1: 0000000000100000     0 SECTION LOCAL  DEFAULT    1 
        ...
        ...
        25: 0000000000103000     0 NOTYPE  LOCAL  DEFAULT    3 page_pml5
        26: ffff800000000000     0 NOTYPE  GLOBAL DEFAULT  ABS HH_BASE
        27: 000000000010002b     0 NOTYPE  GLOBAL DEFAULT    2 _start64
        28: 0000000000100000     0 NOTYPE  GLOBAL DEFAULT    1 page_lmode_enable

    No version information found in this file.

    : `.text` 섹션에 플래그값으로 A,X가 설정되어 있다. X가 중요하다. X는 실행이 가능한 섹션이라는 뜻이다. 이게 없으면, 실행을 하지 않는다. `.text.mode64`에는 A만 설정이 되어있는게 가장 큰 문제다.

     

    : 그리고 `_start64` 심볼의 위치가 0x10002b 로 나온다. 링커스크립트를 기준으로 하면 `_start64`이 0x100000에 나와야 하는데,  그 위치에 `page_lmode_enable`라는 심볼이 와있다. 엔트리 포인트 주소도 보면, 0x100000인데 이 주소는 `page_lmode_enable` 심볼이다. 원인은 링커가 실행 가능한 섹션을 실행 파일의 제일 앞쪽으로 배치하기 때문이다. 위 같은 경우, 실행 가능한 섹션이 `.text` 섹션만 존재하기 때문에 그런 것이다. 이제 아래와 같이 코드를 바꿔보자.

    ; lbl.asm
    section .text.mode64 exec
    global _start64:

    _start64:
        ; Re-enable Paging
        mov rax, cr0 
        ;and rax, 1<<31
        mov cr0, rax    

        jmp $
    ; link.ld
    SECTIONS
    {
        . = 0x100000;
        .text.mode64 :
        {   
            lbl.o(.text.mode64)
        }
    }

     

     

    ELF Header:
      ...
      Type:                              EXEC (Executable file)
      Machine:                           Advanced Micro Devices X86-64
      Version:                           0x1
      Entry point address:               0x100010
      Start of program headers:          64 (bytes into file)
       ...
      Number of section headers:         12
      Section header string table index: 11

    Section Headers:
      [Nr] Name              Type             Address           Offset
           Size              EntSize          Flags  Link  Info  Align
      [ 0]                   NULL             0000000000000000  00000000
           0000000000000000  0000000000000000           0     0     0
      [ 1] .text.mode64      PROGBITS         0000000000100000  00100000
           0000000000000008  0000000000000000  AX       0     0     1
      [ 2] .text             PROGBITS         0000000000100010  00100010
           000000000000002b  0000000000000000  AX       0     0     16
      [ 3] .data.mode64      PROGBITS         0000000000101000  00101000
           0000000000002000  0000000000000000   A       0     0     4096
      ...
      ...
      [10] .strtab           STRTAB           0000000000000000  00105528
           00000000000000bd  0000000000000000           0     0     1
      [11] .shstrtab         STRTAB           0000000000000000  001055e5
           0000000000000076  0000000000000000           0     0     1
    Key to Flags:
      W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
      L (link order), O (extra OS processing required), G (group), T (TLS),
      C (compressed), x (unknown), o (OS specific), E (exclude),
      l (large), p (processor specific)

    ...
    ...

    Symbol table '.symtab' contains 29 entries:
       Num:    Value          Size Type    Bind   Vis      Ndx Name
         0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
         1: 0000000000100000     0 SECTION LOCAL  DEFAULT    1 
        ...
        ...
        26: ffff800000000000     0 NOTYPE  GLOBAL DEFAULT  ABS HH_BASE
        27: 0000000000100000     0 NOTYPE  GLOBAL DEFAULT    1 _start64
        28: 0000000000100010     0 NOTYPE  GLOBAL DEFAULT    2 page_lmode_enable

    : `_start64` 심볼의 위치를 확인하자. 0x100000인 것을 확인할 수 있다. 그러나, 엔트리 포인트 주소가 아직 바뀌지 않았다. 왜 그럴까? GNU 링커는 엔트리 포인트를 지정해주지 않을 경우, 기본적으로 `_start` 심볼을 찾고 이 심볼의 주소로 엔트리 포인트를 설정하려고 한다. 그런데, `_start` 심볼이 없으면 `.text` 섹션의 제일 앞을 엔트리 포인트로 잡아버린다. `.text` 섹션 제일앞에 `page_lmode_enable` 심볼이 존재해서 위와 같이 0x100010이 엔트리 포인트가 된 것이다. 아래와 같이 수정하면 엔트리 포인트 값도 0x100000으로 바꿀 수 있다. 

     

    ; link.ld
    ENTRY(_start64)
    SECTIONS
    {
        . = 0x100000;
        .text.mode64 :
        {   
            lbl.o(.text.mode64)
        }
    }
    ELF Header:
      ...
      Type:                              EXEC (Executable file)
      Machine:                           Advanced Micro Devices X86-64
      Version:                           0x1
      Entry point address:               0x100000
      Start of program headers:          64 (bytes into file)
       ...
      Number of section headers:         12
      Section header string table index: 11
      ...

     

    - QEMU에서 부팅 디스크로 `-fda` 옵션은 되고, `-hda` 옵션은 안됨(미해결)

    : 디버깅을 안하고 그냥 실행시키 것에는 문제가 없지만, GDB를 통해 과정을 볼려고 QEMU 모니터상에 `No bootable device` 문구가 나온다... 즉, 플로피 드라이브는 부트 시그니처를 인식하면서, 하드 디스크 드라이브는 MBR 부트 시그니처를 찾지 못했다는 것 같다.

     

    : QEMU 하드 디스크 드라이브에서 `.img` 포맷만 인삭한다. 즉, 내가 계속 사용해 왔던 `.bin`은 인식하지 못한다. `-fda`에서는 `.bin`을 인식하는데, 그나마 다행이었다. CD-ROM 및 DVD는 `.iso` 만 인식한다. 그런데, `.iso`는 ISO-9660이라는 파일 시스템 포맷이 따로 존재한다. 그래서 그 포맷에 맞게 이미지를 만들어야 CD-ROM 및 DVD에서 제대로 인식한다. BIN 및 IMG는 그냥 raw 포맷이라 상관없다.

     

    - QEMU에서 YohdaOS 32비트 main에서 AHCI를 통해 READ 한 번에 16개 밖에 읽지 못함(미해결)

     : 2가지 오류가 있었다. 첫 번째로 `디스크 이미지는 반드시 섹터 단위로 만들어져야 한다`는 것이다. 64비트 커널 이미지가 32비트 커널 뒤쪽에 붙게 되는데, 섹터 단위로 정렬을 하지 않았다. 디스크 드라이브는 섹터 단위로 데이터를 읽기 때문에, 디스크를 읽다가 아직 요청한 사이즈인데 디스크가 끝나면 해당 요청 자체를 폐기한다.

     

    : 예를 들어, 디스크 이미지가 1623B다. 4개의 섹터 읽기를 요청했다고 하자. 여기서 중요한 차이가 있다. 섹터를 몇 개씩 읽냐가 중요하다.

    1" 1 개씩 섹터를 요청 - 3번째 섹터까지 읽고 마지막 섹터만 폐기
    2" 4 개 섹터를 한꺼번에 요청 - 요청을 전부 폐기

     

    : 두 번째는 

     

     

     

     

    - FBL에서 SBL 로딩할 때, 각 디스크에 따른 CHS 계산(미해결)

    : SBL에 32개의 섹터를 할당할 계획인데, SBL을 메모리에 로딩하는 것은 PBL이다. 근데, PBL에서 트랙 당 섹터의 단위가 32개보다 크거나 작으면 문제가 조금 복잡해진다. 즉, CHS 방식이라 계산식이 상당히 복잡해진다. 물론, 코드를 짤 수는 있는데 PBL의 사이즈(512B)에 대한 압박이 많이 크다. 심지어 파티션에 따른 VBR 섹터를 아직 완성하지 않아서 좀 더 계획이 필요하다.

     

    : 이 부분은 단순하게 접근하는 것이 좋다고 생각이 든다. FBL에서 모든 디스크 드라이브를 지원하려면, 제일 작은 섹터를 SBL에 할당하면 된다. 플로피 디스크를 하한선이라 가정하고 한다면, 플로피 버전 중 `헤드 당 섹터`가 가장 적은 값은 4 혹은 8이다. 그러므로, 4로 하한선을 잡는게 가장 좋다고 생각이 든다.

     

    - `vga_text_print`가 중복되는데, 오류가 발생되지 않음

    : fbl.asm과 sbl.asm에 `vga_text_print` 심볼이 중복되어 있는데, 링키시에 에러가 발생하지 않음. 동일 심볼로 에러가 발생해야 할 거 같은데, 발생하지 않음... 링킹을 fbl.o(.text)과 sbl.o(.text)를 나눠서 발생을 안하는주 알았는데, `(*.text)`로 해도 발생하지 않음.

     

    : `global` 키워드를 사용하지 않으면, 잠정적으로 모든 심볼은 local 심볼이다. 즉, 이름이 겹치는 심볼이 각 어셈블리 파일에 수십개가 존재해도 `global` 키워드를 통해 외부로 노출하지 않으면 링커는 해당 심볼을 중복이라 생각하지 않는다.

     

     

    - 페이징 모드 활성화시, `check_exception old: 0xffffffff new 0xe 1: v=0e e=0000 i=0 cpl=0` ...  에러

    : `e = 0x0000`는 페이지 폴트에러다. 아래 에러와 비슷한 에러지만, 원인이 다르다. 가상 주소에 해당하는 페이지가 현재 메모리에 로딩되어 있지 않다는 것이다. 결론적으로 이 에러는 페이지 테이블이 잘못 등록된 것이다. 자신의 코드에서 페이지 테이블을 어떻게 만들고 서로 어떻게 연결되고 있는지 살펴봐야 한다.

     

     

    - 페이징 모드 활성화시, `check_exception old: 0xffffffff new 0xe0: v=0e e=0009 i=0 cpl=0 ...` 에러

    : `e = 0x0009`는 페이지 폴트에러다. 해당, 에러에 대한 해석은 이 글을 참고하자. 일단, 원인은 예약 영역을 0으로 초기화하지 않아서 발생한 문제다. 32비트 4MB, 64비트 2MB, 1GB 등 큰 페이지 사이즈를 사용하면 상위 페이지 테이블에서 주소 영역이 었던 영역이 예약 영역으로 변경된다. 인텔에서는 반드시 이 영역이 0이여야 한다고 얘기한다. 페이지 사이즈가 4KB 보다 크다면, 이 부분을 반드시 주의하자.

     

     

    - QEMU에서 x86-64 페이지 테이블 생성 및 로딩 시, 페이지 폴트(미해결)

    : 내가 보기에는 로직상 전혀 차이가 없는 것 같은데, 왼쪽은 에러가 발생하고오른쪽은 에러가 발생하지 않는다. 왼쪽은 데이터 선언라인에 직접 값을 집어넣었고, 오른쪽은 데이터 선언시에는 0으로 초기화하고 코드 레벨에서 페이징을 초기화했다. 환경은 QEMU와 NASM에서 발생하는 에러다.

    section .data.page64 write
    align 4096
    page_pml4:
        times 1024 dd 0

    align 4096
    page_low_pdtp:
        dd 0x00000003
        dd 0x00000000
        times 1022 dd 0
        
    ; Load 2MB Identify Mapped Two Pages 
    page_low_pd:
        dd 0x00000083
        dd 0x00000000
        dd 0x00000083
        dd 0x00000000
        times 1020 dd 0

    ...
    ...

        mov dword [page_pml4], page_low_pdtp+0x00000003                                                              
        mov dword [page_low_pdtp], page_low_pd
        mov eax, page_pml4
        mov cr3, eax
    section .data.page64 write
    align 4096
    page_pml4:
        times 1024 dd 0

    align 4096
    page_low_pdtp:
        times 1024 dd 0

    ; Load 2MB Identify Mapped Two Pages 
    page_low_pd:
        times 1024 dd 0

    ...
    ...

        mov dword [page_pml4], page_low_pdtp + 0x00000003
        mov dword [page_low_pdtp], page_low_pd + 0x00000003                                                            
        mov dword [page_low_pd], 0x00000083
        mov eax, page_pml4
        mov cr3, eax

     

    - QEMU에서 2MB 페이징 시, 첫 페이지만 동작함(미해결)

    : 아이텐티티 맵핑을 적용해야 해서 간단하게 2MB를 적용하려고 했는데, 이상하게 2MB는 2번째 페이지부터 적용이 안된다. 아래에서 보면, PML4E 하나가 512GB, PDPTE가 1GB, PDTE 3개가 각 2MB씩 차지해서 6MB를 할당했다. 그런데, 맨 처음 페이지만 아이텐티티 맵핑이 적용되고 2MB이후 부터는 적용이 안된다.

    ; lbl.asm
    ...
    ; data section
    section .data.page64 write
    align 4096
    page_pml4:
        times 1024 dd 0

    ; Load Page Table
    align 4096
    page_low_pdtp:
        times 1024 dd 0

    page_low_pd:
        times 1024 dd 0
    ...
    ...
    _start_64:
        ; Disable Paging
        mov eax, cr0
        and eax, ~(1<<31)
        mov cr0, eax    

        ; Enable PAE
        mov eax, cr4
        or eax, 1<<5    ; CR4[5] = PAE
        mov cr4, eax

        ; Load 4MB Low Temporary Identify Mapped Paging Table
        mov dword [page_pml4], page_low_pdtp + 0x00000003       ; 512GB 
        mov dword [page_low_pdtp], page_low_pd + 0x00000003    ; 1GB
        mov dword [page_low_pd], 0x00000083                                ; 0x000000 ~ 0x1FFFFF
        mov dword [page_low_pd+8], 0x00200083                            ; 0x200000 ~ 0x3FFFFF
        mov dword [page_low_pd+16], 0x00400083                          ; 0x400000 ~ 0x5FFFFF
    ...
    ...
    ; Re-enable Long mode
        mov eax, cr0
        or eax, 1<<31
        mov cr0, eax

        jmp 0x18:_trampoline64

    [bits 64]
    extern _lenter
    _trampoline64:
        jmp _lenter

    section .text ; start virtual memory
    _lenter:
        mov rax, 0x20 ; page fault!!

        mov ds, rax                                                                                      
        mov es, rax

    %endif  
    HH_BASE = 0x300000;                                                                           

    ENTRY(_start_64)
    SECTIONS
    {
        . = 0x100000;
        smode64 = .;
        .text.mode64 :
        {   
            *(.text.mode64)
        }   

        .data.page64 :
        {   
            *(.data.page64)    
        }   

        /* it`s needed to plus end address of `.text.mode64` to 0xFFFFFFFF80000000. 
         * Becuase, if don`t plus, it`s happended that there is duplicating he first 0x1XXXXX regions.
         * 
         * */
        emode64 = .;
        . = HH_BASE + emode6; 
        
        /*kern_virt_start_addr = .;*/
        /*kern_phy_start_addr = kern_virt_start_addr - HH_BASE; */
        
        .text ALIGN(0x1000): AT(ADDR(.text) - HH_BASE)
        {   
            mode64.o (.text)
            *(.text)
        }   
        
        .rodata ALIGN(0x1000) : AT(ADDR(.rodata) - HH_BASE)
        {   
            *(.rodata)
        }   
        
        .data ALIGN(0x1000): AT(ADDR(.data) - HH_BASE)
        {   
            *(.data)
        }   

        .bss ALIGN(0x1000): AT(ADDR(.bss) - HH_BASE)
        {   
            *(.bss)
            *(COMMON)
        }   

        kern_virt_end_addr = .;
    }

    : 32비트까지는 물리 메모리에 0x100000에 맵핑하고 가상 메모리는 0x100000 + 0x300000에 할당해서 4MB 언저리부터 주소가 시작된다. 근데, 이건 에러가 발생한다. 위에서 `.text` 섹션부터가 실제 가상 메모리 주소의 엔트리 포인트다. 저기에 제일 첫 번째 명령어인 `mov rax, 0x20`에서 페이지 폴트가 발생한다. 즉, 가상 메모리 시작 부분에서 바로 발생해버린다. 나는 위에 설정이 2MB 페이지 설정이라 생각했는데, 4KB 처럼 동작을 한다. 위에서 링크 스크립트만 아래와 같이 바꿨을 때, 에러 내용이 계속 바뀐다.

     

    1" HH_BASE 0x100 : #GD e=0000
    2" HH_BASE 0x500 : #GD e=0008
    3" HH_BASE 0x900 : #GD e=0000
    4" HH_BASE 0x1000 : #GD e=0000
    5" HH_BASE 0x1400 : #GD e=0008
    6. HH_BASE 0x1800 : #PF e=0000
    7" HH_BASE >= 0x2000 : #PF e=0000

    : HH_BASE가 0 일 때를 제외하고는 무조건 에러가 발생한다고 보면 된다.

     

     

    - NASM에서 파일 사이즈 구하기(미해결)

    : 데이터 섹션과 텍스트 섹션을 합쳐서 파일 사이즈를 구할 수 있는 방법이 있을까? 이 방법은 하나의 섹션만 존재한다면 의미가 있어보인다. 사실, ($-$$)도 이 방법과 동일하다. 결국 하나의 섹션만 있으면 파일 사이즈는 구하기 쉽다. 링커스크립트를 통해서 구하는 방법이 있을 거 같은데, 만약 파일에 인클루드문이 있으면 그 인클루드 내용은 어떻게 되는걸까? 즉, sbl.asm이 disk.asm을 인클루드 했다. 이걸 어셈블한다. 그렇면, disk.asm의 모든 내용이 sbl.asm에 포함되는 걸까? 근데 사실 인클루드를 굳이 할 필요는 없다. 인클루드는 괜한 파일 사이즈만 늘리기 때문이다. `global` , `extern` 키워드를 통해 참조하는게 더 좋은 방법인 듯 하다.

     

    - undefined reference to `__divdi3` 혹은 undefined reference to `__moddi3`

    : 이 문제는 32비트 머신에서 64비트 `나눗셈 연산(divdi3)`과 `나머지 연산(__moddi3)`을 해서 발생한 문제다.

     

     

    - How to fix "qemu: fatal: Trying to execute code outside RAM or ROM at 0xXXXXXXXX"(미해결)

    : 일단 이 에러는 ata_init() 함수를 실행하면서 발생하는 문제였다. 처음에는 QEMU의 `q35`는 ISA 버스가 아닌, PCI 버스를 사용하기 때문에 PCI 버스를 통해서 ATA 디바이스에 접근을 해야할 것 같았다. 내 코드가 ISA 버스 기반의 코드여서 문제가 발생한 것이라 생각했다. 

     

    : 두 번째로는 에러 문구를 보면 코드가 이상한 데서 실행이 됬다고 하는데, 실제 커널 코드를 디스크에서 램으로 가지고 오지 못했던 거 아닌가 싶었다. 이 에러는 PIC로 IVT를 리맵핑하지 않고, 정말 딱 인터럽트만 활성화했을 때 발생한 에러다(이 에러는 아래 에러와 동일 선상에 있다). 처음에는 코드 사이즈가 문제일 것이라 생각됬다. 왜냐면, 디스크에서 이미지를 읽어오는 데 딱 36섹터만 읽어오고 나서 더 이상 읽어오는게 없었다. 36 섹터면 0x4800이다. 그런데 커널 이미지가 0x4900에 가까웠다. 그래서 FBL에서 36섹터를 읽고, SBL에서 36섹터를 3번 더 읽어오도록 했다(FBL의 36섹터는 SBL을 로딩하기 위해서, SBL에서 36섹터를 3번 읽는 것은 커널 이미지를 로딩하기 위해서). 그러나 여전히 문제는 발생했다. 

     

    : 사실 qcom-tree 로 q35를 확인해보면 q35는 IDE 컨트롤러를 지원하지 않는다고 한다. 그래서 발생하는 문제인지, 다시 확인 확인이 필요하다. 일단은 AHCI를 사용하도록 수정했다.

     

    - Protected Mode 진입 후, 더블 폴트 에러

    : 위의 `How to fix "qemu: fatal: Trying to execute code outside RAM or ROM at 0xXXXXXXXX"` 문제가 발생하기 전에 항시 Double Fault 익셉션이 발생한다. 해당 익셉션은 Abort로 지정되서 반드시 종료가 되는 익셉션이다. 그런데, 좀 더 알아봐야 하는것이 Abort 로 선정된 익셉션들은 QEMU에서 로그도 안 뿌리고 그냥 죽는것인가? Double Fault 익셉션을 뿌리고 나서 얼마 못가 위에 에러가 발생하면서 QEMU가 종료된다. 그런데, 재미있는 몇 개(1, 2, 3)의 글을  봤다(이 에러는 위의 에러와 동일 선상에 있다).

     

    : 보호 모드가 활성화되면 시스템 타이머 인터럽트가 자동으로 발생한다. 그런데, 시스템 타이머 인터럽트는 마스터 PIC의 IRQ0 라인에 고정 연결 되어 있다. 그리고 마스터 PIC의 IRQ8은 CPU의 IVT의 INT8에 매핑되어 있다. 이제 시스템 타이머 인터럽트가 발생하면 마스터 PIC는 해당 인터럽트는 CPU에 넘긴다. 그런데, 마스터 PIC에 연결된 IRQ0 라인이 CPU INT8 에 연결되어 있다.시스템 타이머 인터럽트가 발생한 것인데, 더블 폴트(0x08) 에러가 발생하게 된다. 

     

    : 그래서 PIC를 리맵핑했다. 그런데도 에러가 발생한다. 왜 그럴까? 로그를 보자. 

    0: v=20 e=0000 i=0 cpl=0 IP=0008:00100662 pc=00100662 SP=0010:0007ff70 env->regs[R_EAX]=000000ff
    EAX=000000ff EBX=0000aa55 ECX=00000002 EDX=ffffffff
    ESI=00000fa0 EDI=00000000 EBP=0007ff98 ESP=0007ff70
    EIP=00100662 EFL=00000216 [----AP-] CPL=0 II=0 A20=1 SMM=0 HLT=0
    ES =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
    CS =0008 00000000 ffffffff 00cf9a00 DPL=0 CS32 [-R-]
    SS =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
    DS =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
    FS =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
    GS =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
    LDT=0000 00000000 0000ffff 00008200 DPL=0 LDT
    TR =0000 00000000 0000ffff 00008b00 DPL=0 TSS32-busy
    GDT=     00007f29 00000018
    IDT=     00104030 000007ff
    CR0=00000011 CR2=00000000 CR3=00000000 CR4=00000000
    DR0=00000000 DR1=00000000 DR2=00000000 DR3=00000000 
    DR6=ffff0ff0 DR7=00000400
    CCS=00000014 CCD=0007ff74 CCO=EFLAGS  
    EFER=0000000000000000
    check_exception old: 0xffffffff new 0xd
         1: v=0d e=0102 i=0 cpl=0 IP=0008:00100662 pc=00100662 SP=0010:0007ff70 env->regs[R_EAX]=000000ff
    EAX=000000ff EBX=0000aa55 ECX=00000002 EDX=ffffffff
    ESI=00000fa0 EDI=00000000 EBP=0007ff98 ESP=0007ff70
    EIP=00100662 EFL=00000216 [----AP-] CPL=0 II=0 A20=1 SMM=0 HLT=0
    ES =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
    CS =0008 00000000 ffffffff 00cf9a00 DPL=0 CS32 [-R-]
    SS =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
    DS =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
    FS =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
    GS =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
    LDT=0000 00000000 0000ffff 00008200 DPL=0 LDT
    TR =0000 00000000 0000ffff 00008b00 DPL=0 TSS32-busy
    GDT=     00007f29 00000018
    IDT=     00104030 000007ff
    CR0=00000011 CR2=00000000 CR3=00000000 CR4=00000000
    DR0=00000000 DR1=00000000 DR2=00000000 DR3=00000000 
    DR6=ffff0ff0 DR7=00000400
    CCS=00000014 CCD=0007ff74 CCO=EFLAGS  
    EFER=0000000000000000
    ...
    ...
    check_exception old: 0xffffffff new 0xd
        12: v=0d e=0fa0 i=0 cpl=0 IP=0008:0007f965 pc=0007f965 SP=0010:0007f96c env->regs[R_EAX]=00000000
    EAX=00000000 EBX=0000c855 ECX=0000b955 EDX=f0005f72
    ESI=00000fa0 EDI=00000001 EBP=0007ff98 ESP=0007f96c
    EIP=0007f965 EFL=00000046 [---Z-P-] CPL=0 II=0 A20=1 SMM=0 HLT=0
    ES =0001 00000000 00000000 00000000
    CS =0008 00000000 ffffffff 00cf9a00 DPL=0 CS32 [-R-]
    SS =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
    DS =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
    FS =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
    GS =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
    LDT=0000 00000000 0000ffff 00008200 DPL=0 LDT
    TR =0000 00000000 0000ffff 00008b00 DPL=0 TSS32-busy
    GDT=     00007f29 00000018
    IDT=     00104030 000007ff
    CR0=00000011 CR2=00000000 CR3=00000000 CR4=00000000
    DR0=00000000 DR1=00000000 DR2=00000000 DR3=00000000 
    DR6=ffff0ff0 DR7=00000400
    CCS=00000000 CCD=00000000 CCO=ADDB    
    EFER=0000000000000000

    : 위의 로그를 해석하는 법은 이 글을 참고하자. 처음에 0x20 익셉션이 발생한다. 이 익셉션은 PIC에 리맵핑되어 발생한 익셉션이다. 즉, 시스템 타이머 인터럽트다. 리맵핑은 잘 적용된 것이다. 문제는 해결된 것과 같다. 그런나 현재 시스템 타이머 인터럽트 핸들러를 등록하지 않아서 더블 폴트까지 가게 된것이다. 그래서 처리를 하지 못한다. 그래서 결국은 시스템이 다운되긴 한다. 이럴 때는 일단, 아무것도 하지 않는 시스템 타이머 인터럽트 핸들러라도 IDT에 등록해주면서 문제가 해결된다.

     

    - Protected Mode 진입 후 `QEMU -d int` 옵션에서 나오는 `SMM: enter ... SMM: after RSM` 로그 (미해결)

    : 이 로그는 에러라고 보기에는 어려울 듯 싶다. 왜냐면, 인터럽트 및 익셉션이 발생할 때 나오는 로그와 상당히 차이가 있기 때문이다. 이 로그는 FBL과 SBL에서는 나오지 않다가 커널 이미지를 로딩하면서 부터 나온다. main()도 아니고, PBL부터 이미 이 로그가 나온다. 즉, 보호 모드를 진입하면서 나오는 로그로 추측이 된다.

     

     

    - not enough room for program headers, try linking with -N ... final link failed: bad value (미해결)

    : 아래의 코드를 보자.

    SECTIONS
    {
        . = 0x100000;
        .text.mode64 :
        {   
            lbl.o (.text.mode64)    
        }   

                                                                                             
        . += 0xFFFF800000000000;
        HH_BASE = .;
        kern_virt_start_addr = .;
        kern_phy_start_addr = kern_virt_start_addr - HH_BASE;   
        
        .text ALIGN(0x1000): AT(ADDR(.text) - HH_BASE)
        {   
            *(.text)
        }   

        .rodata ALIGN(0x1000) : AT(ADDR(.rodata) - HH_BASE)
        {   
            *(.rodata)
        }   

        .data ALIGN(0x1000): AT(ADDR(.data) - HH_BASE)
        {   
            *(.data)
        }   

        .bss ALIGN(0x1000): AT(ADDR(.bss) - HH_BASE)
        {   
            *(.bss)
            *(COMMON)
        }
    SECTIONS
    {
        . = 0x100000;
        .text.mode64 :
        {   
            lbl.o (.text.mode64)    
        }   

                                                                                             
        . = 0xFFFF800000000000;
        HH_BASE = .;
        kern_virt_start_addr = .;
        kern_phy_start_addr = kern_virt_start_addr - HH_BASE;   
        
        .text ALIGN(0x1000): AT(ADDR(.text) - HH_BASE)
        {   
            *(.text)
        }   

        .rodata ALIGN(0x1000) : AT(ADDR(.rodata) - HH_BASE)
        {   
            *(.rodata)
        }   

        .data ALIGN(0x1000): AT(ADDR(.data) - HH_BASE)
        {   
            *(.data)
        }   

        .bss ALIGN(0x1000): AT(ADDR(.bss) - HH_BASE)
        {   
            *(.bss)
            *(COMMON)
        }

    : 일단 에러의 원인은 파일이 너무 커져서 발생하는 에러다. 링커는 0x100000과 0xFFFF800000000000 사이의 값을 패딩으로 채우게 되서 에러가 발생한 것이다. 왼쪽은 안되고, 오른쪽은 된다. `+=`는 안되고, `=`는 된다는 건데, 무슨 차이인지 아직 정확히 파악하지 못했다. 상대 주소와 절대 주소의 차이라고 하는 글이 존재한다.

     

     

    - warning: Selected architecture i386 is not compatible with reported target architecture i386:x86-64 Remote 'g' packet reply is too long (expected 312 bytes, got 536 bytes):

    : 위 에러는 qemu-system-x86_64를 통해서 32비트 BIN 커널 이미지를 실행하고, GDB를 통해 32비트 커널 ELF 파일을 로딩하고 나서 발생한 에러다. 즉, 타겟 머신이 x64_86인데 당신이 로딩한 이미지는 i386 이미지라는 것이다. GDB에 `set architecture i386` 옵션을 추가로 줘도 마찬가지다. 당연하겠지만 16비트 x86과 32비트 x86 모두 `qemu-system-i386`에서 잘 동작하다가, 롱 모드 진입을 위해 `qemu-system-x86_64`로 바꾸면서 발생한 에러다.

     

    : 일단, 현재 크로스 컴파일러가 `i686-elf-gcc`라는 것도 한 몫 하는듯 하다. 이 컴파일러에 `-m64`를 추가하면, `cc1: sorry, unimplemented: 64-bit mode not compiled in` 에러가 발생한다. 이 컴파일러로는 64비트를 만들 수 없다. 그래서 OSDEV 에서 말하는 `x86_64-elf`를 써야 한다.

     

    : 에뮬레이터가 `qemu-system-x86_64`로 바뀌면서 몇 nams의 옵션도 `f elf`에서 `f elf64`로 변경해줘야 한다. 그리고 `x86_64-elf-ld` 에 -melf_x86_64` 옵션을 통해 생성하면 된다. 확인이 필요하지만, `x86_64-elf-gcc` 에는 이전 i686-elf-gcc 옵션과 같았다. `-m64` 옵션을 써줘야 될 것으로 생각했지만, 기본적으로 x86_86로 빌드 하는 것으로 보인다.

     

    : 그러나, qemu-system-x86_64를 사용해서 코드 상으로 32비트에서 64비트로 넘어가지 않은 상황에서 GDB로 디버깅 해보면 값들이 이상하게 되버린다. 이 문제는 GDB로 실행때만 이렇게 값이 이상해진다는 것이다. 실제 i686-elf-gcc로 만든 elf_i386 실행 파일도 qemu-system-x86_64에서는 실행이 잘되기만 한다. 그러나, GDB에서 해당 파일이 위 제목과 같은 에러를 뿜는 것이다. -> 앞에 링크를 확인해보면 알 수 있지만, 원격으로 디버깅을 진행하기 때문에 모드 전환에 대해 인식을 하지 못한다고 한다. BOCHS는 내장 디버거를 가지고 있어서 x86 디버깅에는 BOCHS를 사용하라고 한다.

     

    : 그래도 굳이 GDB를 써야 한다면, 일단 모드 변환전까지는 16비트 및 32비트는 qemu-system-i386을 사용하고, 64비트는 qemu-system-x86_64를 사용해 볼 수 있다.

     

     

    - x86_64-elf-ld: i386 architecture of input file `pbl.o' is incompatible with i386:x86-64 output

    : NASM을 통해 어셈블한 파일들이 x86-64가 아니고, i386 기반이라는 것이다. `name -f elf64`로 옵션을 변경해야 한다. `name -f elf`는 기본 32비트 오브젝트 파일을 만든다. 위의 에러는 링커에서 난건데 왜 어셈블러를 탓하는가라고 생각할 수 있다. 어셈블러 및 컴파일러는 주소 재배치를 하지 않는다. 어셈블러나 컴파일러는 빌드 시에 주소 재배치에 대한 정보만 파일에 기록하고 링커에게 남은 일을 맡기게 된다. 그래서 링커에서 오류가 나면, 링커 뿐만 아니라 컴파일러와 어셈블러 모두를 보아야 한다.

     

     

    - 리얼 모드에서 1MB 영역에 커널 로딩하기

    : PBL은 용량이 부족해서(512B), 커널을 0x100000에 로딩하기가 힘들다. 그리고 또 PBL에서는 A20 라인이 활성화되지 않아서 커널을 로딩하지 못한다. SBL에서도 마찬가지다. A20이 활성화된다고 해도 리얼 모드에서 접근한 주소 영역(0x10FFEF)은 정해져 있다. QEMU에서 그걸 확인해보려고 보호 모드 진입전 3MB에 0xFFFF를 쓰고 읽어봤다. 이게 제대로 읽히면 나는 문제라고 생각한다. 그런데 제대로 읽혔다. QEMU에서도 16비트, 32비트에 대한 구분이 없는 것 같다.

     

    : 일단은 SBL에서 A20을 활성화하면 0x1FFFFF 까지는 유효하므로, 오프셋이 0xFFFF를 넘지않는 선에서 섹터를 읽어오게 해놨다. 현재는 플로피 디스크에서 트랙 당 섹터 개수가 36 섹터이므로, 3번을 읽는 것이 0xD800(?) 까지 읽을 수 있어서 0xFFFF를 넘지 않는다.

     

     

    - GDB에서 특정 라인부터 `add %al,(%eax)`로 나옴.

    : 문제의 발단은 16비트 SBL에서 32비트 PBL로 넘어오는 과정에서 발생했다. SBL에서 jmp _pbl_start를 통해 점프는 잘 된 것 같았다. 그리고 이미 SBL에서 GDT를 설정하고 32비트 코드 디스크립터(0x08)와 데이터 디스크립터를 로딩한 상태에서 다른 파일인 PBL로 점프하는 상황이었디. 그런데, 여기서 부터 문제가 발생한다. 아래 사진은 SBL에서 PBL로 점프를 하는 코드다. PC 카운터를 보면 현재 메모리상에서 실행중인 명령어의 위치는 0x7e8a이고 이 명령어의 내용은 `jmp 0x8000`이고 저 0x8000은 `_pbl_start`에 맵핑되어 있다. 

    : 자 이제 점프를 해보자.

     

    : GDB 소스상에는 `jmp $`인데, PC 카운터가 가리키는 곳에 명령어는 `add %al,(%eax)`다. 이 내용은 AT&T 문법이라 인텔문법으로 보면 `add byte [eax], al`가 된다. 명령어 자체가 다르다. 그리고 그 뒤에도 계속 저 동일한 문장이 무한히 반복된다. 도대체 뭘까? `_pbl_start` 심볼의 문제일까? 저 `_pbl_start` 심볼 자체가 좀 의심스러웠다. 그래서 링커스크립트는 다음과 같다.

     

    SECTIONS
    {
        .text 0x7C00 :
        {   
            fbl.o (.text)                                                                             
            sbl.o (.text)
            pbl.o (.text)
            /**(.text)*/
            *(.rodata)
        }   
    
        .data :
        {   
            *(.data)
        }   
    
        .bss :
        {   
            *(.bss)
            *(COMMON)
        }   
    }

    : 3개의 부트로더가 텍스트 영역으로만 배치된 상황이다. FBL과 SBL은 현재 각각 512B를 차지한다. 그리고 16비트 부트 로더다. 그리고 3개가 묶이는 바이너리는 bl.elf 파일이다. 

     

    $ objdump -f bl.elf 
    
    bl.elf:     file format elf32-i386
    architecture: i386, flags 0x00000112:
    EXEC_P, HAS_SYMS, D_PAGED
    start address 0x00007c00

    : 크로스 컴파일를 i686-gcc-elf를 사용하고 있으며, 링커스크립트에 명시적으로 에트리 포인트를 지정하지 않아서 텍스트 섹션의 제일 앞 부분인 0x7C00가 엔트리 포인트로 잡혔다. 이 부분도 문제 없다. 그리고 아래는 objcopy를 통해 elf에서 코드와 데이터만 뽑아낸 순수 바이너리 파일을 xxd로 확인한 내용이다.

     

    00000000: ea05 7c00 00fa 31c0 8ed8 b800 b88e c031  ..|...1........1
    00000010: c08e d0bd 007c 89ec 31f6 26c7 0400 0f83  .....|..1.&.....
    00000020: c602 81fe a00f 75f2 68fd 7ce8 6a00 0657  ......u.h.|.j..W
    00000030: b8c0 078e c0bf be01 268a 053c 0874 0d83  ........&..<.t..
    00000040: c710 81ff fe01 75f0 5f07 eb0a 681d 7de8  ......u._...h.}.
    00000050: 4600 0657 5f07 6660 b408 cd13 7272 890e  F..W_.f`....rr..
    00000060: d27c 8326 d27c 3f66 6166 60b8 e007 8ec0  .|.&.|?faf`.....
    00000070: 31db b402 ff0e d27c a0d2 7cb0 01b5 00b1  1......|..|.....
    00000080: 02b6 00cd 1372 4966 6168 d67c e809 00ff  .....rIfah.|....
    00000090: 36d4 7cea 057e 0000 5589 e550 5356 5706  6.|..~..U..PSVW.
    000000a0: b800 b88e c0b8 a000 f726 d47c 89c7 ff06  .........&.|....
    000000b0: d47c 8b76 048a 1c80 fb00 740b 2688 1d83  .|.v......t.&...
    000000c0: c601 83c7 02eb ee07 5f5e 5b58 5dc2 0200  ........_^[X]...
    000000d0: ebfe 0000 0000 5265 6164 7920 666f 7220  ......Ready for 
    000000e0: 6a75 6d70 696e 6720 7365 636f 6e64 6172  jumping secondar
    000000f0: 7920 626f 6f74 6c6f 6164 6572 0059 6f68  y bootloader.Yoh
    00000100: 6461 4f53 2046 6972 7374 2042 6f6f 7420  daOS First Boot 
    00000110: 4c6f 6164 6572 2053 7461 7274 0054 6865  Loader Start.The
    00000120: 7265 2065 7869 7374 2061 2061 6374 6976  re exist a activ
    00000130: 6520 7061 7274 6974 696f 6e00 4861 7070  e partition.Happ
    00000140: 6e65 6420 7468 6520 6572 726f 722e 2053  ned the error. S
    00000150: 6f2c 2073 746f 7020 7468 6520 7072 6f63  o, stop the proc
    00000160: 6573 732e 2e2e 0000 0000 0000 0000 0000  ess.............
    00000170: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    00000180: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    00000190: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    000001a0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    000001b0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    000001c0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    000001d0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    000001e0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    000001f0: 0000 0000 0000 0000 0000 0000 0000 55aa  ..............U. ;;; 여기까지 FBL
    00000200: ea05 7e00 0058 5ba3 e37e 68e5 7ee8 7f00  ..~..X[..~h.~...
    00000210: 83c4 0206 b800 008e c026 8b1e fe7d f7d0  .........&...}..
    00000220: 8ec0 268b 0e0e 7e07 39cb 7509 6809 7fe8  ..&...~.9.u.h...
    00000230: 5d00 83c4 0268 447f e854 0083 c402 6631  ]....hD..T....f1
    00000240: c08c d866 c1e0 0466 05c5 7e00 0066 a3df  ...f...f..~..f..
    00000250: 7e66 b8dd 7e00 0066 2dc5 7e00 00a3 dd7e  ~f..~..f-.~....~
    00000260: 0f01 16dd 7e0f 20c0 0c01 0f22 c0ea 727e  ....~. ...."..r~
    00000270: 0800 66b8 1000 8ed8 8ec0 8ee0 8ee8 8ed0  ..f.............
    00000280: bd00 7c00 00bc 007c 0000 e971 0100 0055  ..|....|...q...U
    00000290: 89e5 5053 5657 06b8 00b8 8ec0 b8a0 00f7  ..PSVW..........
    000002a0: 26e3 7e89 c7ff 06e3 7e8b 7604 8a1c 80fb  &.~.....~.v.....
    000002b0: 0074 0b26 881d 83c6 0183 c702 ebee 075f  .t.&..........._
    000002c0: 5e5b 585d c300 0000 0000 0000 00ff ff00  ^[X]............
    000002d0: 0000 9acf 00ff ff00 0000 92cf 0000 0000  ................
    000002e0: 0000 0000 0059 6f68 6461 4f53 2053 6563  .....YohdaOS Sec
    000002f0: 6f6e 6461 7279 2042 6f6f 7420 4c6f 6164  ondary Boot Load
    00000300: 6572 2053 7461 7274 0046 6f72 2065 6e74  er Start.For ent
    00000310: 6572 696e 6720 746f 2070 726f 7465 6374  ering to protect
    00000320: 6564 206d 6f64 652c 2070 7265 7061 7269  ed mode, prepari
    00000330: 6e67 2066 6f72 2074 6865 2047 6174 652d  ng for the Gate-
    00000340: 4132 3000 5374 6172 7420 7072 6570 6172  A20.Start prepar
    00000350: 696e 6720 666f 7220 4744 5420 6f66 2070  ing for GDT of p
    00000360: 726f 7465 6374 6564 206d 6f64 6500 0000  rotected mode...
    00000370: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    00000380: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    00000390: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    000003a0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    000003b0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    000003c0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    000003d0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    000003e0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    000003f0: 0000 0000 0000 0000 0000 0000 0000 0000  ................ ;;; 여기까지 SBL
    00000400: ebfe                                                      ;;; 여기부터 PBL

     : FBL, SBL 모두 텍스트 섹션 하나만 작성하고 텍스트를 상위에 작성하고 데이터를 하위에 작성해서 위와 같은 바이너리 포맷이 나왔다. 정상 위치를 잘 잡고 있다. PBL에는 딱 `ebfe`만 있다. 이 내용은 objdump를 통해 역어셈블해보면 다음과 같다.

     

    $ objdump -d pbl.o 
    
    pbl.o:     file format elf32-i386
    
    
    Disassembly of section .text:
    
    00000000 <_pbl_start>:
       0:	eb fe                	jmp    0 <_pbl_start>

    : _pbl start 심볼로 점프한다는 내용이다. 오류가 없어보인다. 즉, 컴파일 단계 자체에 문제는 없어 보인다. 그렇면 런타임에 문제라는 것이다.

     

    : 0x8000에는 `jmp 0 <_pbl_start>`가 있어야 하는데, `add byte [eax], al` 가 있다. 심지어 0x8002 부터는 아무 코드도 없는데, 저 명령어만 반복되고 있다. 이 문제의 솔루션은 저 명령어를 어셈블 해보면 알게 된다.

    " 어셈블해보면 값이 `00 00`이 나온다. 즉, 0x8000부터 값이 비어있다는 소리다. 원인을 알아보니, FBL에서 섹터를 하나만 읽어오고 있었다. 그래서 그 부분을 해결해줬더니 문제가 해결되었다.

     

    " 이 문제를 정리해보면서 한 가지 더 알게 된 부분은 실제 메모리에 존재하는 값 그자체를 알아야 한다는 것이다. GDB에서 `x/10i $pc`같은 명령어는 PC가 가리키는 기계어 코드를 역어셈블해서 사람이 알기 쉬운 어셈블리 코드로 변환해준다. 여기에 함정이 있었던 것 같다. PC가 실제로 가리키는 코드의 위치는 0x8000이었다. 이 위치를 `x/10i $pc`가 아닌 `x/x 0x8000`으로 보았다면, 이 문제를 하루 동안 헤맬일은 없었을 듯 싶다.

     

     

     

    - relocation truncated to fit: R_386_16 against symbol `${심볼}' defined in .text section in `${오브젝트 파일}`

    : 아래 2개의 링크스크립트의 차이가 뭘까?

    SECTIONS                                                                           
    {
        .text 0x7C00 :
        {   
            fbl.o (.text) 
            sbl.o (.text)
            . = 0x100000;
            pbl.o (.text)   
            main.o (.text)
            *(.text)
            *(.rodata)
        }
    }
    SECTIONS                                                                           
    {
        .text 0x7C00 :
        {   
            fbl.o (.text) 
            sbl.o (.text)
            pbl.o (.text)   
            main.o (.text)
            *(.text)
            *(.rodata)
        }
    }

    : 위에서 왼쪽 스크립트는 아래와 같은 오류가 난다. 중간에 `. = 0x100000` 가 있음에 따라 오류가 발생한다.

     

    i686-elf-gcc -ffreestanding -O2 -nostdlib -T link.ld  string.o interrupt.o math.o main.o vga.o fbl.o isr.o paging.o sbl.o pbl.o -o bl.elf	
    sbl.o: in function `_pmode':
    /home/yohda/workspace/SW/bare-metal/yohdaOS/chap16/02.Kernel64/src/bootloader/x86/p32//sbl.asm:76:(.text+0x6e): relocation truncated to fit: R_386_16 against symbol `_pbl_start' defined in .text section in pbl.o
    collect2: error: ld returned 1 exit status
    makefile:46: recipe for target 'bl.elf' failed
    make: *** [bl.elf] Error 1

    : sbl.asm에 `extern _pbl_start`가 작성되어 있고, pbl.asm에 `global _pbl_start`가 작성되어 있다. 

     

    : 그래서 다시 테스트를 해보았다.

    {
        .text 0x7C00 :
        {   
            fbl.o (.text) 
            sbl.o (.text)
            . = 0x100000;
            pbl.o (.text)   
            main.o (.text)
            *(.text)
            *(.rodata)
        }
    }
    SECTIONS                                                                           
    {
        .text 0x7C00 :
        {   
            fbl.o (.text) 
            sbl.o (.text)
            pbl.o (.text)
            . = 0x100000;   
            main.o (.text)
            *(.text)
            *(.rodata)
        }
    }

    : 왼쪽은 오류가나고 오른쪽은 오류가 발생하지 않는다.

     

    {
        .text 0x7C00 :
        {   
            fbl.o (.text) 
            sbl.o (.text)
            . = 0x10000;
            pbl.o (.text)   
            main.o (.text)
            *(.text)
            *(.rodata)
        }
    }
    SECTIONS                                                                           
    {
        .text 0x7C00 :
        {   
            fbl.o (.text) 
            sbl.o (.text)
            . = 0x1000;
            pbl.o (.text)
            main.o (.text)
            *(.text)
            *(.rodata)
        }
    }

    : 왼쪽은 오류가 발생하고 오른쪽은 오류가 발생하지 않는다.

     

    {
        .text 0x7C00 :
        {   
            fbl.o (.text) 
            sbl.o (.text)
            . = 0x8400;
            pbl.o (.text)   
            main.o (.text)
            *(.text)
            *(.rodata)
        }
    }
    SECTIONS                                                                           
    {
        .text 0x7C00 :
        {   
            fbl.o (.text) 
            sbl.o (.text)
            . = 0x8300;
            pbl.o (.text)
            main.o (.text)
            *(.text)
            *(.rodata)
        }
    }

    : 0x8400에서는 오류가 발생하고, 0x8300에서는 오류가 발생하지 않는다. 0x7C00 + 0x8400은 딱 1MB다. 위에서 fbl.o와 sbl.o는 16비트 어셈블리 파일이다.

     

    : 이 문제는 결국 텍스트 영역의 길이가 너무 긴 것이 문제다. 재배치 타입으로 `R_386_16`이 사용되고 있다. 32비트 x86을 기준으로 해당 값은 16비트 재배치 타입이다. `readelf` 명령어를 통해 sbl.o 파일의 재배치 정보를 살펴보자.

    $ readelf -r sbl.o
    Relocation section '.rel.text' at offset 0x9f0 contains 14 entries:
     Offset     Info    Type            Sym.Value  Sym. Name
    00000001  00000214 R_386_16          00000000   .text
    00000008  00000214 R_386_16          00000000   .text
    0000000b  00000214 R_386_16          00000000   .text
    0000002d  00000214 R_386_16          00000000   .text
    00000036  00000214 R_386_16          00000000   .text
    00000049  00000201 R_386_32          00000000   .text
    0000004f  00000214 R_386_16          00000000   .text
    00000053  00000201 R_386_32          00000000   .text
    00000059  00000201 R_386_32          00000000   .text
    0000005e  00000214 R_386_16          00000000   .text
    00000063  00000214 R_386_16          00000000   .text
    0000006e  00001814 R_386_16          00000000   _pbl_start
    00000084  00000214 R_386_16          00000000   .text
    0000008a  00000214 R_386_16          00000000   .text

    : `_pbl_start` 심볼에 16비트가 할당된 것을 볼 수 있다. 재배치 타입은 링커에 의해 자동으로 할당되는 부분이다. 저 부분을 바꾸기 위해 아래와 같이 수정했다.

     

    _pmode: 
        mov eax, cr0 
        or al, 1                                                                            
        mov cr0, eax    
        
    %ifdef DEBUG
        jmp $
    %else
        jmp 0x08:_pbl_start
    %endif
        
    [BITS 16]
    _pmode: 
        mov eax, cr0 
        or al, 1                                                                            
        mov cr0, eax    
        
    [BITS 32] 
    %ifdef DEBUG
        jmp $
    %else
        jmp 0x08:_pbl_start
    %endif
        
    [BITS 16]

    : `readelf`로 다시 확인해보자. 아래와 같이 `_pbl_start` 심볼의 재배치 타입이 `R_386_32`로 변경된 것을 확인할 수 있다. 이제 0x100000을 할당해도 에러가 발생하지 않는다.

     

    $ readelf -r sbl.o
    Relocation section '.rel.text' at offset 0x9f0 contains 14 entries:
     Offset     Info    Type            Sym.Value  Sym. Name
    00000001  00000214 R_386_16          00000000   .text
    00000008  00000214 R_386_16          00000000   .text
    0000000b  00000214 R_386_16          00000000   .text
    0000002d  00000214 R_386_16          00000000   .text
    00000036  00000214 R_386_16          00000000   .text
    00000049  00000201 R_386_32          00000000   .text
    0000004f  00000214 R_386_16          00000000   .text
    00000053  00000201 R_386_32          00000000   .text
    00000059  00000201 R_386_32          00000000   .text
    0000005e  00000214 R_386_16          00000000   .text
    00000063  00000214 R_386_16          00000000   .text
    0000006e  00001801 R_386_32          00000000   _pbl_start
    00000086  00000214 R_386_16          00000000   .text
    0000008c  00000214 R_386_16          00000000   .text

     

     

     

Designed by Tistory.