ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 크로스 컴파일[작성중]
    프로젝트/운영체제 만들기 2023. 6. 3. 21:48

    글의 참고

    - https://wiki.osdev.org/GCC_Cross-Compiler

    - https://wiki.osdev.org/Why_do_I_need_a_Cross_Compiler%3F


    글의 전제

    - 내가 글을 쓰다가 궁금한 점은 파란색 볼드체로 표현했다.

    - 밑줄로 작성된 글은 좀 더 긴 설명이 필요해서 친 것이다.


    글의 내용

     

    - 크로스 컴파일 빌드

    " 어디에 크로스 컴파일을 설치할 지를 정해야 한다. 시스템 디렉토리가 있는 곳에 설치하는 것은 최악이다. 대개는, `$HOME/opt/cross` 혹은 `usr/local/cross`에 설치를 많이 한다. $HOME/opt/cross는 나에게만 적용되는 것이고, 해당 컴퓨터가 서버라면 전역적으로 설치해야 하므로, `usr/local/cross`에 설치를 많이 한다. 추천하지는 않지만, `/usr`에 설치를 하는 방법도 있다. 이렇게 하면, 기본 시스템 컴파일러로 설정하는 것과 같아서 패스 설정 같은것이 필요없다. 그러나 이 방법은 상당히 위험하다. 설치되어있는 운영체제는 기본 컴파일러를 크로스 컴파일러로 바꾸는 작업이기 때문에, 시스템에 문제를 일으킬 여지가 많다.

     

    " 빌드를 위해서 몇 가지 환경 변수를 설정하자.

    export PREFIX="$HOME/opt/cross"
    export TARGET=i686-elf
    export PATH="$PREFIX/bin:$PATH"

     

    " BINUTILS 부터 빌드하자. BINUTILS에는 어셈블러와 디스어셈블러, 여러 가지 다양한 바이너리 유틸리티들이 들어있다. 아래는 $TARGET 환경 변수에 설정된 아키텍처로 BINUTILS를 구성하여 빌드를 진행한다. 여기서 주의 사항이 있다. 소스가 존재하는 탑 레벨에서는 절대 `./configure`와 `make`를 실행하면 안된다. 이 에러가 발생한다. 그러므로, 아래처럼 소스 탑 레벨에서 임시 디렉토리(여기서는 `build-binutils`)를 하나 만들고, 그 디렉토리에서 `./configure`와 `make`를 실행한다.

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

    : 설치가 정상적으로 잘 되었다면, `$HOME/opt/cross/bin/` 관련 결과물들이 있게 된다. 환경 변수에 $PATH에 이미 설정이 되어 있으므로, `where i686-elf-as`로 어디에 해당 유틸리티가 설치되어 있는지 확인하자.

     

    - GDB 빌드

    : x86 운영체제를 만들기 위해서는 디버깅을 위해서는 GDB가 필요하다. 그러나, GDB는 명확한 한계가 있다. 각 모드가 전환된 이후에 코드를 정확히 인식하지 못한다. 그리고 x86 에서는 세그먼트 주소 지정 방식을 몰라서 세그먼트 레지스터 값을 설정해버리면 GDB가 이상해진다. 

    https://github.com/lordmilko/i686-elf-tools/releases

    : 위의 링크에서 미리 컴파일된 `i686-elf-gdb`를 사용할 수 있다.  

     

     

    : 이제 GCC를 설치하자. GCC 설치도 당연히 소스 탑 레벨에서 구성과 빌드를 하면 안된다. 

    cd $HOME/src
     
    # The $PREFIX/bin dir _must_ be in the PATH. We did that above.
    which -- $TARGET-as || echo $TARGET-as is not in the PATH
     
    mkdir build-gcc
    cd build-gcc
    ../gcc-x.y.z/configure --target=$TARGET --prefix="$PREFIX" --disable-nls --enable-languages=c,c++ --without-headers
    make all-gcc
    make all-target-libgcc
    make install-gcc
    make install-target-libgcc

    : 참고로, 여기서 GCC만 빌드하는 것이 아니다. `libgcc`라는 것도 같이 빌드를 한다.

     

    : GCC 빌드까지 완료되면 아래의 명령을 통해 자신의 크로스 컴파일러를 확인해보자. 나는 GCC 11.3.0 버전 소스를 컴파일했다.

    $HOME/opt/cross/bin/$TARGET-gcc --version
    i686-elf-gcc (GCC) 11.3.0
    Copyright (C) 2021 Free Software Foundation, Inc.
    This is free software; see the source for copying conditions.  There is NO
    warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

    : 이 컴파일러 패스는 현재 세션까지만 유효하다. 영구적으로 쓰고 싶다면 `~/.profile` 맨 끝에 아래의 명령을 추가하자.

     

    export PATH="$HOME/opt/cross/bin:$PATH"

     

    - 암묵적인 main 함수

    : 이 링크 를 따라가면 라즈베리파이3B를 이용한 베어 메탈 내용을 볼 수 있다. 여기서 start.S 파일에 `bl main`을 제외하고, 그 어디에도 `main` 이라는 글자를 찾아볼 수가 없다. 왜 그럴까?

     

    : 아래의 예시 코드를 보자. 

    ; boot.asm
    bits 16
    
    SECTION .text
    global _start
    
    _start:
    	call main

    : 위의 파일을 NASM으로 빌드하면 에러가 발생한다. 왜냐면, main 심볼이 없기 때문이다. main() 함수가 C 언어로 작성된 함수기 때문에, 어셈블리 파일에서 사용하려면 `extern main`을 사용해야 한다. 아래와 같이 수정하고, main.c 파일에 main() 함수가 있다고 전제하면 NASM은 에러를 발생시키지 않는다.

     

    ; boot.asm
    bits 16
    
    extern main
    
    SECTION .text
    global _start
    
    _start:
    	call main

     

    : NASM이 아닌 다른 크로스 컴파일러들은 에러가 발생하지 않을 수 있다. 왜 그럴까? `ABI` 혹 `C 런타임` 혹 `부트 로더` 때문일 수 있다. ABI 마다 암묵적으로 `main` 심볼을 C언어 함수로 인식해서 `extern main`이 필요없는 경우가 있고, `extern main` 문장이 필요한 경우가 있다. i686-elf 툴체인은 명시적으로 `extern main`을 선언을 해줘야 한다. C 런타임을 사용하면 링커에서 기본적으로 C 런타임 cr0.o 파일이 먼저 불리게 되서 main() 를 호출해준다. 그러나, 이건 제외다. 왜냐면, 베어 메탈에서는 C 런타임을 사용하지 않기 때문이다. 그러면 부트 로더의 가능성도 있다. 라즈베리파이3B에 있는 부트 로더는 마치 BIOS 및 UEFI와 같다. 기본적인 초기화를 진행하고 우리에게 제어권을 넘긴다. 그러나 0번지로 점프하는걸로 알고 있는데, 이것도 아니것 같다. 결국 ARM의 ABI가 가장 의심스러운 부분이다.

     

    - 크로스 컴파일 이름 포맷(Target Triplet)

    : 대개 크로스 컴파일들은 아래와 같은 이름을 갖는다.

    MACHINE-VENDOR-OPERATINGSYSTEM

     

    " 그래서 내 컴퓨터 같은 경우, `gcc -dumpmachine`을 치면 `x86_64-linux-gnu`가 나온다. 참고로, `gcc -dumpmachine` 명령어는 현재 시스템에 기본적으로 등록된 컴파일러의 정보를 보여준다. 근데 뭔가 좀 이상하지 않나? 벤더에 리눅스가 들어가 있다. 사실 저 위에 벤더는 크게 의미가 없다고 한다. 대개는 생략도 한다.

     

     

    " 자신만의 OS를 만들 때, 사용하는 크로스 컴파일러들은 다음과 같다.

    1" x86 : i686-elf, x86_64-elf
    2" ARM : arm-none-elf, aarch64-none-elf
    3" RISC-V : riscv64-none-elf

     

     

    " 자신의 OS를 개발할 때는, 현재 자신이 사용하고 있는 크로스 컴파일러가 어떻게 만들어 졌는지를 알아야 한다. 예를 들어, 우분투를 사용하면 기본 컴파일러는 GCC다. 이 GCC는 리눅스용 컴파일러라서 특정 파일을 빌드하면 리눅스용 실행 파일을 만든다. 그런데, 우리가 만드는 프로그램은 리눅스용 실행 파일이 아니다. 우리가 만드는 건 말 그대로 아무것도 없는 `베어 메탈` 기반의 실행 파일이다. 어떠한 운영체제에서도 의존하지 않아야 한다. 단지 아키텍처에만 의존해야 한다. 그래서 우리는 각 아키텍처사(ARM, Intel, AMD 등)에서 제공하는 리눅스용 크로스 컴파일러가 아닌 베어 메탈 컴파일러로 OS를 개발해야 한다. `x86_64-linux-gnu` 같은 컴파일러는 인텔이 제공하는 64비트 리눅스용 컴파일러다. `none` 혹은 `elf`만 들어간 컴파일러가 대게는 베어 메탈 컴파일러다. 

     

     

    " 리눅스용과 베어 메탈용의 차이는 ABI가 달라지기 때문이다. 즉, 리눅스용 컴파일러로 파일을 빌드하면 실행 파일은 리눅스 ABI를 따른다. 베어 메탈용 컴파일러로 빌드하면, 해당 아키텍처의 ABI를 따르게 된다. 

     

     

    - 현재 사용중인 compiler 찾기

    " 먼저 `file` 명령어를 사용할 수 있어야 한다. 그러면 대략적인 어떤 processor 에서 빌드가 된 건지를 알 수 가 있다. 그러면 이제 어떤 컴파일러를 사용하는지 알아봐야 한다. 이 때 여러 군데의 장소에서 컴파일러를 찾아봐야 한다. 

    1. /usr/bin
    2. /usr/local
    3. /opt/toolchain
    4. /opt/crosstool

     

     

     

    - x86 크로스 컴파일

    " 16비트 및 32비트는 `I686-elf`를 사용하다가, 64비트로 넘어가야 해서 `x86_64-elf` 로 바꾸게 됬다. 그런데, `x86_64-elf` 에서 `-m32` 옵션을 지원해서 32비트 빌드가 가능했다. x86 모든 모드에서 `x86_64-elf` 로 빌드가 가능한지는 테스트가 필요하다. 

     

     

     

    - i686 vs i386 vs x86_64

    " 인텔에서 최초로 만든 x86기반의 32비트 CPU가 80386이다. 흔히, 이 모델을 i386이라고 부른다. 그리고 i686은 공식적인 이름이라고 부르기 어렵지만, 1997년에 등장한 P6 아키텍처를 의미한다. 즉, 펜티엄 2 이상의 모델을 의미한다. 정리하면, x86 기반의 32비트 CPU중에 그나마 좀 더 최신이 i686이다.

     

     

    " x86_64는 32비트 및 64비트 호환 ISA다. 64비트의 사용은 x86_64에서만 가능하다. 실제 `x86_64`는 AMD가 만들어서 주로 `AMD64`라고 부른다.

    '프로젝트 > 운영체제 만들기' 카테고리의 다른 글

    권한  (0) 2023.06.05
    C 런타임  (2) 2023.06.04
    GCC[작성중]  (0) 2023.06.02
    GIT 명령어  (1) 2023.06.01
    메모리 맵  (0) 2023.06.01
Designed by Tistory.