나만의 커널을 위한 여정 / #1 GCC 크로스 컴파일러 만들기

Seo Minsang
8 min readMay 7, 2024

--

Photo by Dayne Topkin on Unsplash

드디어 커널 개발을 시작하겠다는 목표를 세운 지 약 2년만에, 커널 개발에
관한 글을 쓰게 되었다. 이 글이 공개된 시점에는 아마 내가 커널을 어느정도 만들었을 것이다. 아무래도 커널을 만들기 위한 환경설정부터 사람을 매우
귀찮고 피곤하게 만드니, 정보에 혼선이 없게 하기 위해서라도 커널과
관련하여 어느정도 숙련도가 생긴 다음에 쓰고 있던 글들을 공개할 것이다.

내가 이 글을 쓰는 목적은 커널 개발과 관련하여 나의 근황을 알리고,
내 커널의 특징을 소개하기위한 목적임과 동시에, 앞으로 커널 개발을 꿈꾸는 사람들이 내 글 속에서 쓸모있는 정보를 얻어갈 수 있게 하기 위함이다.

커널 개발의 어려움

컴퓨터라는 기계와 직접적으로 소통하고, 여러 응용 프로그램들에게 실행
환경을 제공하는 커널은 당연히 프로그래밍 분야 중에서 거의 탑급의
난이도를 자랑한다. 당장 커널 이론의 개념서만 봐도 알아듣기 힘든 소리와 여러 개념들이 머리를 어지럽힌다. 실재로 커널을 만들기 위한 코드를 작성
하는 것은 더 어렵고…_ 책을 보고 커널을 만들고 있는 나도, 코드가 다 공개되어 있지만 정답 코드를 봐도 이해 안되는 것들이 더 많은 것 같다.

그러나 진정한 커널 개발의 어려움은 따로 있다. 바로 정보의 부족이다.
커널에 관심을 가지는 사람은 매우 적고, 그 사람이 하필 한국인일 확률은
아무렇게 던진 과자가 정확히 내 입으로 떨어질 확률보다 적은 것 같다.
외국의 경우는 여러 오픈 소스 커널들이 영어로 잘 설명되어 있고, 그것을
뒷받침하는 다양한 문서들이 존재하지만, 우리의 경우 오픈소스 커널도,
공식 문서도, 하다못해 커널을 주제로 한 책도 찾아보기 힘들다. 이러한
정보의 부족이 커널의 코딩 난이도보다도 나를 더 힘들게 하는 것 같다.
아무리 어려운 코드라도, 그것에 대한 정보가 아예 없는 것보단 낫다.

앞으로 내가 커널에 대해 다루는 글들은 정말로 상세하게 접근할 것이니,
이 글로 인해 커널에 관심을 가지는 사람이 조금만… 늘었으면 좋겠다.

컴파일러 소개

고등학교 시간에 c언어에 대해 배운 사람들은 c언어로 작성된 파일들을 실행하기 위해선 ‘컴파일러’라는 것이 필요하다는 것을 알 것이다. c언어용
컴파일러들은 많이 존재하지만, 그중 유명한 것은 gcc와 clang이다.

gcc는 자유 소프트웨어 재단이 개발한 컴파일러로 정말 오래된 역사와, 그에 걸맞는 성능, 빠른 처리 속도를 자랑하는 컴파일러이다. 원래 gnu hurd라는 이름의 오픈소스 커널 프로젝트의 일부였던 gcc는 현재 리눅스 배포판들의 주력 컴파일러로 쓰이고 있다.

clang은 비교적 최근에 등장한 컴파일러로, gcc의 일부 문제점(프론트 엔드, 백엔드 구분 모호, 복잡해진 코드)등을 개선하며 등장하였다. LLVM/Clang은 gcc에 비해 비교적 자유로운 저작권 조항을 적용하고 있기 때문에 대기업들, 특히 애플의 많은 사랑을 받고 있고, 저작권 호환 문제로 gcc를 사용하지
못하는 BSD진영이 clang를 사용중이다.

크로스 컴파일러

분명 gcc나 clang은 macOS에서도, 윈도우에서도 실행할 수 있으니, 그
컴파일러들이 생성한 결과물도 플렛폼(os나 컴퓨터 아키텍쳐)에 상관없이
사용할 수 있을 것이라고 생각하기 쉽다. 그러나 우리가 간과한 사실은,
컴파일러도 결국 운영체제 밑에서 동작하는 하나의 응용 프로그램이라는
것이다. 세상에는 arm, amd64, riscV등 수많은 cpu들이 존재하며 리눅스,
윈도우, macOS등 그 cpu 위에서 동작하는 수많은 운영체제들이 존재한다.

그리고 안타깝게도 컴파일된 프로그램 또한 이 수많은 조합의 실행 환경 중 하나에 종속되어 버린다. 그렇다면 프로그래머들은 이 많은 환경들에 대해
자신의 프로그램이 잘 동작할 수 있다는 사실을 어떻게 보장할 수 있을까?
답은 크로스 컴파일러에 있다.

크로스 컴파일러는 설치되어 있는 한 가지 플렛폼에서, 다른 여러 가지의
플렛폼의 환경에 맞는 코드를 제공할 수 있다. 예를 들어 내 컴퓨터가 64비트 환경의 윈도우 플렛폼이라도, 내 코드를 32비트의 리눅스 환경에서 실행될 수 있도록 컴파일 할 수 있다는 것이다. 그리고 이러한 점은 특히 자신만의
운영체제를 만들고자 하는 나와같은 커널 개발자들에게는 정말로 필수적인 기능이다. 왜냐하면 바로 이 커널을 크로스 컴파일러로 컴파일 해야 하기
때문이다. (비록 먼 미래가 되겠지만, 호스팅 된 컴파일러로 커널을 컴파일 하는 것을 부트스트랩 이라고 한다.)

GCC 크로스 컴파일러 설치하기

여기서부터는 아까 설명했던 두 컴파일러 중 gcc를 크로스 컴파일러 형태로 설치하는 방법을 설명한다. 다른 환경은 테스트하지 않았기 때문에 리눅스
환경에서 위 과정을 따라 하는 것을 전제로 한다.

cd $HOME
mkdir prepare && cd prepare
touch prepare.sh

vim prepare.sh

-------------
export PREFIX="/opt/cross"
export TARGET=i686-elf # 32비트용 크로스 컴파일러
export TARGET=$(uname -m)-elf # 64비트용 크로스 컴파일러

--------------

:wq

우선 크로스 컴파일러를 설치하기 위한 환경 설정을 위해 위와같은 sh파일을 작성한다.

1. PREFIX는 크로스 컴파일러가 설치되는 특정 경로를 말한다. 여기서는
/opt/cross 위치이다.

2. TARGET 변수는 자신이 설치할 컴파일러가 32비트용인지, 64비트인지를 결정하는 요소이다. (자신이 만들고 싶은 커널의 비트에 따라 설정한다)

chmod +x ./prepare.sh && ./prepare.sh

sudo mkdir -pv /opt/cross
sudo chown `whoami`:`whoami` -R /opt/cross

1.chmod를 사용해서 prepare.sh에 권한을 부여한 후 실행한다.

2. mkdir 명령을 사용하여 /opt/cross 폴더를 생성한다. -pv옵션은 부모
폴더가 존재하지 않더라도 폴더를 생성하고, 자세한 로그를 출력하라는 의미 이다.

3. chown명령어로 /opt/cross 폴더에 대한 전체적인 권한을 부여받는다.

cd $HOME
sudo apt install build-essential bison flex libgmp3-dev libmpc-dev libmpfr-dev texinfo libisl-dev
mkdir src && cd src
mkdir build-binutils && mkdir build-gcc


wget https://ftp.gnu.org/gnu/gcc/gcc-9.5.0/gcc-9.5.0.tar.gz
wget https://ftp.gnu.org/gnu/binutils/binutils-2.34.tar.xz

tar xf binutils-2.34.tar.xz
tar xf gcc-9.5.0.tar.xz

1. HOME위치로 돌아간 후 src폴더 안에 각각 build-binutils, build-gcc폴더를 만든다.

2. apt를 사용하여 gcc & binutils 빌드에 필요한 의존성들을 다운받는다.

3. wget을 이용해서 gnu.org 사이트에서 gcc, binutils를 다운로드 한다.

4. tar xf명령어를 사용해서 각 파일의 압축을 해제한다.

cd build-binutils
../binutils-2.34/configure --target=$TARGET --prefix=$PREFIX --disable-nls --disable-werror --with-sysroot

make
make install

1. build-binutils 위치로 이동한다.

2. config 명령어를 사용해서 binutils 설치와 관련한 환경설정을 한다. 아까 prepare.sh 에서 설정한 환경 변수가 사용된다.

3 . make && make install 명령어를 사용해서 binutils 설치를 완료한다.

cd $HOME/src/build-gcc
../gcc-9.5.0/configure --target=$TARGET --prefix="$PREFIX" --disable-nls --enable-languages=c,c++ --without-headers


make all-gcc -j 16 && make install-gcc && make all-target-libgcc -j 16 && make install-target-libgcc

1. build-gcc 위치로 이동한다.

2 . config 명령어를 사용하여 gcc 설치와 관련한 환경설정을 한다. 마찬가지로 prepare.sh에 설정된 환경 변수가 사용된다.

3 . gcc는 설치에 시간이 많이 걸리므로 병렬 컴파일 옵션(j 16)을 사용하여
컴파일한다.

여기까지 끝났다면 리눅스의 /opt/cross 경로에 bin폴더와 함께 여러 수많은 파일들이 추가되어 있을 것이다. 설치가 잘 되었는지 확인하기 위해 bin 폴더 안으로 들어가면, target에 설정했던 옵션에 따라 x86_64-gcc, i686-gcc등의
컴파일러가 보일 것이다.

list of gcc cross compiler(x86_64)

END

--

--

Seo Minsang

현대청운고등학교 2학년 & Kernel programmer