참고
github : https://github.com/NuttyJamie/LinuxFromScratch-for-Korean
이 장의 목표는 LFS 시스템 구축을 위한 필수 도구들을 안전하게 준비하는 것이다.
최소한의 시스템을 구축하기 위해 두 단계를 거친다. 첫 번째 단계는 호스트 시스템에 의존하지 않는 툴체인(컴파일러, 링커 등)을 빌드하는 것이며, 두 번째 단계는 이 툴체인을 사용해 다른 필수 도구들을 빌드하는 것이다.
이 과정에서 컴파일된 파일들은 $LFS/tools 디렉토리에 설치된다. 이는 곧 만들어질 LFS 시스템이 오염되지 않도록 하기 위함이다.
이 절에서는 LFS 시스템 빌드 과정에 대한 기술적 세부 사항과 해석을 설명한다. 지금 당장은 모든 내용을 완전히 이해할 필요는 없으며, 실제 빌드 과정 중에 참고하면 된다. 5장의 목표는 호스트 시스템과 독립적인 도구들을 포함한 임시 공간을 구축하는 것이다. 이 공간에서 chroot를 사용해 LFS 시스템을 깨끗하게 구축할 수 있다. 빌드 절차는 위험을 최소화하면서 교육적인 가치를 극대화하도록 설계되었다.
Toolchain Tech Note
LFS 시스템 구축을 진행하기 전에, 작업 중인 플랫폼의 명칭, 즉 "대상 트리플렛"과 플랫폼의 동적 링커 이름을 알아야 한다. 대상 트리플렛을 확인하려면, Binutils 소스를 압축 해제한 후 ./config.guess 스크립트를 실행해 출력된 값을 기록한다. 예를 들어, 32비트 인텔 프로세서는 i686-pc-linux-gnu, 64비트 시스템은 x86_64-pc-linux-gnu와 같은 결과가 나올 것이다.
또한, 동적 링커의 이름도 확인해야 한다. 이는 프로그램이 필요한 공유 라이브러리를 찾아 불러오고 실행을 준비하는 역할을 한다. 32비트 인텔 시스템의 경우 동적 링커는 ld-linux.so.2, 64비트 시스템은 ld-linux-x86-64.so.2가 될 것이다. 동적 링커 이름을 확인하려면 호스트 시스템에서 임의의 바이너리를 선택하여 readelf -l <바이너리의 이름> | grep interpreter 명령을 실행해 결과를 기록한다. 더 많은 정보는 Glibc 소스 트리의 shlib-versions 파일에서 찾을 수 있다.
컴파일 절차
이 빌드 절차는 심볼릭 링크를 포함한 호스트 시스템 요구사항이 다음과 같이 올바르게 설정되었다고 간주한다:
- bash를 셸로 사용한다.
- sh는 bash로 심볼릭 링크되었다.
- /usr/bin/awk는 gawk로 심볼릭 링크되었다.
- /usr/bin/yacc는 bison이나 bison을 실행하는 스몰 스크립트로 심볼릭 링크되었다.
빌드 절차를 다시 강조한다:
- 모든 소스들과 패치들을 /mnt/lfs/sources/같은 chroot 환경에서 접근할 수 있는 디렉토리에 두어라. 소스를 /mnt/lfs/tools/에 넣지 않도록 하라.
- 소스들이 있는 디렉토리로 이동한다.
- 각 패키지에 대해:
- tar 프로그램을 사용해서 빌드될 패키지를 추출한다. 5장에서는, 패키지를 추출할 때 lfs 유저로 로그인하라.
- 패키지를 추출해서 생성된 디렉토리로 이동한다.
- 패키지를 빌드하기 위해 책의 절차를 따른다.
- 소스들이 있는 디렉토리로 돌아간다.
- 달리 지시되지 않는 한 추출하고 난 원본 디렉토리를 삭제한다.
Binutils
cd /mnt/lfs/sources
tar -xf binutils-2.34.tar.xz
cd binutils-2.34
mkdir build
cd build
../configure --prefix=/tools \
--with-sysroot=$LFS \
--with-lib-path=/tools/lib \
--target=$LFS_TGT \
--disable-nls \
--disable-werror
--prefix=/tools
Configure 스크립트가 Binutils 프로그램들을 /tools 디렉토리에 설치하도록 한다.
--with-sysroot=$LFS
크로스 컴파일을 위해 빌드 시스템이 필요에 따라 $LFS에서 대상 시스템 라이브러리를 찾도록 지시한다.
--with-lib-path=/tools/lib
링커가 사용할 라이브러리 경로를 지정한다.
--target=$LFS_TGT
LFS_TGT 변수의 시스템 설명이 config.guess 스크립트에서 반환되는 값과 약간 다르기 때문에, configure 스크립트를 통해 크로스 링커 빌드를 위한 Binutils의 빌드 시스템을 조정한다.
--disable-nls
임시 도구에는 i18n이 필요하지 않으므로 다국어화를 비활성화한다.
--disable-werror
호스트 컴파일러로부터 나온 경고로 빌드가 멈추는 것을 방지한다.
make
make install
이제 컴파일이 끝났다. 원래대로라면 이제 테스트 스위트를 실행했겠지만, 아직은 테스트 스위트 프레임워크(Tcl, Expect 및 DejaGNU)가 아직 준비되지 않았다. 이 첫 번째 단계의 프로그램은 곧 두 번째 단계의 프로그램으로 대체될 것이기 때문에 지금 테스트를 실행해봤자 큰 이점은 없다.
x86_64에서 빌드 중이라면, 심볼릭 링크를 만들어서 툴체인의 안정성을 보장하라:
case $(uname -m) in
x86_64) mkdir -v /tools/lib && ln -sv lib /tools/lib64 ;;
esac
+ SBU 를 사용하기 위해 time { ./configure ... && ... && make install; } 를 사용해서 시간을 측정한다.
make -j$(nproc) 사용 시 32초 소요됨.
real 0m32.069s user 1m57.832s sys 0m42.807s |
gcc
예상 빌드시간 10SBU (내 기준 3분)
GCC는 이제 GMP, MPFR 그리고 MPC 패키지를 필요로 한다.
이러한 패키지들은 호스트 배포판에 포함되어 있지 않을 수 있으므로 GCC로 빌드될 것이다.
GCC 빌드 과정에 쓰일 수 있도록 각 패키지를 GCC 소스 디렉토리에 압축을 풀고 생성된 디렉토리의 이름을 변경한다.
cd $LFS/sources
tar xf gcc-9.2.0.tar.xz
cd gcc-9.2.0
tar -xf ../mpfr-4.0.2.tar.xz
mv -v mpfr-4.0.2 mpfr
tar -xf ../gmp-6.2.0.tar.xz
mv -v gmp-6.2.0 gmp
tar -xf ../mpc-1.1.0.tar.gz
mv -v mpc-1.1.0 mpc
GCC의 기본 동적 링커 위치를 /tools에 설치된 링커로 변경한다. GCC의 포함 검색 경로에서도 /usr/include를 제거한다
for file in gcc/config/{linux,i386/linux{,64}}.h
do
cp -uv $file{,.orig}
sed -e 's@/lib\(64\)\?\(32\)\?/ld@/tools&@g' \
-e 's@/usr@/tools@g' $file.orig > $file
echo '
#undef STANDARD_STARTFILE_PREFIX_1
#undef STANDARD_STARTFILE_PREFIX_2
#define STANDARD_STARTFILE_PREFIX_1 "/tools/lib/"
#define STANDARD_STARTFILE_PREFIX_2 ""' >> $file
touch $file.orig
done
- gcc/config/linux.h, gcc/config/i386/linux.h 그리고 gcc/config/i368/linux64.h 파일들을, 파일명은 같고 접미사(확장자)가 “.orig”인 파일로 복사한다.
- 첫 번째 sed 표현식은 “/lib/ld”, “/lib64/ld” 또는 “/lib32/ld”의 모든 인스턴스 앞에 “/tools”를 붙이며, 두 번째 표현식은 “/usr”의 하드 코딩된 인스턴스를 대체한다.
여기서 sed 의 구분자로 / 가 아닌 @가 사용됨에 주의한다. - 기본 시작파일 접두사를 파일 끝으로 변경하는 define 문을 추가한다. “/tools/lib/”의 끝에 “/”가 필요하다는 점에 유의하라.
- touch를 사용하여, 복사된 파일의 타임 스탬프를 업데이트한다. cp -u와 함께 사용하면 명령이 실수로 두 번 실행되더라도 원본 파일이 예기치 않게 변경되지 않는다.
마지막으로, x86_64 호스트에서 64비트 라이브러리의 기본 디렉토리 이름을 “lib”로 설정한다
case $(uname -m) in
x86_64)
sed -e '/m64=/s/lib64/lib/' \
-i.orig gcc/config/i386/t-linux64
;;
esac
-i.orig 옵션은 원본 파일을 백업(t-linux64.orig)하면서 파일을 수정한다.
mkdir build
cd build
../configure \
--target=$LFS_TGT \
--prefix=/tools \
--with-glibc-version=2.11 \
--with-sysroot=$LFS \
--with-newlib \
--without-headers \
--with-local-prefix=/tools \
--with-native-system-header-dir=/tools/include \
--disable-nls \
--disable-shared \
--disable-multilib \
--disable-decimal-float \
--disable-threads \
--disable-libatomic \
--disable-libgomp \
--disable-libquadmath \
--disable-libssp \
--disable-libvtv \
--disable-libstdcxx \
--enable-languages=c,c++
Configure 옵션들의 의미:
--with-glibc-version=2.11
- 패키지가 호스트의 Glibc 버전과 호환되도록 지정한다. 호스트 시스템 요구사항에 명시된 최소 Glibc 버전으로 지정한다.
--with-newlib
- 설명: newlib는 경량 C 라이브러리입니다. 이 옵션은 아직 시스템에 Glibc와 같은 C 라이브러리가 설치되지 않았을 때, GCC가 이 라이브러리를 필요로 하지 않도록 설정합니다. inhibit_libc 상수를 정의하여 libgcc(내부 GCC 라이브러리) 빌드 시 C 라이브러리 없이도 빌드가 가능하게 합니다.
- 용도: 임시 크로스 컴파일러를 구축할 때 C 라이브러리가 없는 상태에서도 컴파일이 가능하도록 설정합니다. 이는 아직 libc가 없는 초기 빌드 단계에서 유용합니다.
--without-headers
- 설명: 크로스 컴파일러(여기서는 x86_64-lfs-linux-gnu-gcc) 를 만들 때 대상 시스템의 표준 헤더 파일이 필요할 수 있습니다. 그러나 이 옵션은 이러한 헤더를 사용하지 않도록 지정합니다. 대신, 초기 단계에서는 헤더 파일 없이도 GCC를 빌드할 수 있도록 합니다.
- 용도: 초기 크로스 컴파일러 단계에서, 헤더 파일을 무시하고 필요한 최소한의 환경을 설정하는 데 사용됩니다.
--with-local-prefix=/tools
- 설명: 로컬 프리픽스(local prefix)는 GCC가 로컬 설치된 include 파일을 찾는 경로를 지정합니다. 기본적으로 이 경로는 /usr/local이지만, 이 옵션을 사용하면 /tools로 설정됩니다. 이는 /usr/local에 설치된 호스트 시스템의 헤더 파일이 GCC 빌드 과정에 사용되지 않도록 합니다.
- 용도: 크로스 컴파일러가 호스트 시스템의 파일 대신 /tools에 있는 파일을 사용하도록 경로를 설정합니다.
--with-native-system-header-dir=/tools/include
- 설명: GCC가 기본적으로 시스템 헤더를 찾는 경로는 /usr/include입니다. 그러나 이 옵션을 사용하면 GCC가 /tools/include에서 시스템 헤더를 찾도록 경로를 변경합니다.
- 용도: /tools/include에 설치된 헤더 파일을 사용하도록 GCC의 시스템 헤더 검색 경로를 변경합니다. 이를 통해 호스트 시스템의 헤더 파일을 사용하지 않도록 보장합니다.
--disable-shared
- 설명: 이 옵션은 GCC가 내부적으로 사용하는 라이브러리를 정적으로 링크하도록 설정합니다. 즉, 공유 라이브러리 대신 정적 라이브러리를 사용하여 빌드합니다.
- 용도: 호스트 시스템에 의존하지 않고, 빌드된 GCC가 독립적으로 작동하도록 보장합니다. 크로스 컴파일러 (여기서는 x86_64-lfs-linux-gnu-gcc) 환경에서는 호스트 시스템의 공유 라이브러리를 사용할 수 없으므로, 이를 방지하기 위해 정적 링크를 사용합니다.
--disable-decimal-float, --disable-threads, --disable-libatomic, --disable-libgomp, --disable-libquadmath, --disable-libssp, --disable-libvtv, --disable-libstdcxx
- 설명: 이 옵션들은 특정 기능이나 라이브러리를 비활성화합니다.
- --disable-decimal-float: 십진 부동소수점(decimal floating point) 연산을 비활성화합니다.
- --disable-threads: 멀티스레딩 지원을 비활성화합니다.
- --disable-libatomic: 원자적 연산을 지원하는 libatomic 라이브러리를 비활성화합니다.
- --disable-libgomp: OpenMP(병렬 프로그래밍 API) 지원을 비활성화합니다.
- --disable-libquadmath: 고정밀 수학 연산을 위한 libquadmath 라이브러리를 비활성화합니다.
- --disable-libssp: 스택 보호 라이브러리인 libssp를 비활성화합니다.
- --disable-libvtv: Virtual Table Verification (VTV) 기능을 비활성화합니다.
- --disable-libstdcxx: C++ 표준 라이브러리를 비활성화합니다.
- 용도: 크로스 컴파일러를 구축할 때, 이러한 라이브러리나 기능들은 필요하지 않으며, 대부분 임시 빌드 과정에서 필요하지 않기 때문에 컴파일에 실패할 가능성이 있습니다. 따라서 이들을 비활성화하여 빌드를 간소화하고 실패 가능성을 줄입니다.
--disable-multilib
- 설명: 멀티리브(multilib) 지원을 비활성화합니다. 멀티리브는 하나의 컴파일러가 여러 아키텍처(예: 32비트와 64비트)를 지원할 수 있도록 하는 기능입니다.
- 용도: x86_64 환경에서 LFS(Linux From Scratch) 프로젝트는 아직 멀티리브 구성을 지원하지 않기 때문에, 이 옵션을 사용하여 이 기능을 비활성화합니다.
--enable-languages=c,c++
- 설명: 이 옵션은 GCC에서 빌드할 언어를 지정합니다. 여기서는 C와 C++ 컴파일러만 빌드하도록 설정되어 있습니다.
- 용도: 초기 빌드 단계에서 필요한 언어만 컴파일하여 빌드 시간을 단축하고, 불필요한 기능을 배제합니다. 이 경우 C와 C++이 필요하기 때문에 이들만 활성화합니다.
즉, 이 configure 명령어는 크로스 컴파일러 환경에서 GCC를 빌드할 때 필요한 최소한의 설정을 제공하며, 특정 기능을 비활성화하고 빌드 경로를 재구성하여 호스트 시스템과의 불필요한 종속성을 제거합니다. 이를 통해 GCC가 독립적인 빌드 환경에서 작동하도록 보장한다.
make -j$(nproc)
make install
Linux-5.5.3 API 헤더
tar xf linux-5.5.3.tar.xz
cd linux-5.5.3
make mrproper
make headers
cp -rv usr/include/* /tools/include
- 이 명령어는 커널 소스 트리에서 사용자 공간에서 사용할 수 있는 헤더 파일을 추출하는 작업을 수행한다.
- Linux 커널에는 커널 내부에서 사용되는 헤더 파일과 사용자 공간에서 사용되는 헤더 파일이 있다. 이 명령어는 사용자 공간 프로그램들이 사용할 수 있도록 커널 소스에서 필요한 헤더 파일들을 추출하여 가공한다.
이 단계는 사용자 공간 프로그램들이 시스템 호출을 할 때 필요한 정의와 인터페이스를 포함한 헤더 파일들을 준비하는 것이다. 이 파일들은 크로스 컴파일러나 LFS 시스템이 올바르게 작동하도록 하는 데 필수적이다.
그리고 이 헤더들을 /tools/include 로 복사한다.
원래 make headers_include 를 통해 커널 헤더를 시스템의 표준 위치에 설치할 수 있지만, 아직 rsync 와 같은 도구가 준비되어 있지 않기 때문에 수동으로 복사한다. 따라서 완전한 빌드 툴 체인이 갖춰지지 않은 초기단계에서 사용가능 하다.
Glibc
mkdir -v build
cd build
../configure \
--prefix=/tools \
--host=$LFS_TGT \
--build=$(../scripts/config.guess) \
--enable-kernel=3.2 \
--with-headers=/tools/include
Configure 옵션들의 의미:
- --host=$LFS_TGT, --build=$(../scripts/config.guess)
/tools의 크로스 링커와 크로스 컴파일러를 사용해서 Glibc의 빌드 시스템이 스스로를 크로스 컴파일하게 한다. - --enable-kernel=3.2
Glibc가 3.2 이상 리눅스 커널을 지원하는 라이브러리를 컴파일하도록 한다. 이전 커널에 대한 라이브러리는 사용할 수 없다. - --with-headers=/tools/include
앞서 tools 디렉토리에 설치된 헤더들을 가지고 컴파일하도록 한다. 커널이 어떤 기능들을 갖고 있는지 정확히 알고 그에 따른 최적화를 할 수 있다.
make
make install
이후 아래 커맨드를 통해, 더미 c코드를 컴파일 해본다.
echo 'int main(){}' > dummy.c
$LFS_TGT-gcc dummy.c
readelf -l a.out | grep ': /tools'
모든 것이 제대로 작동한다면 오류가 없어야 하며 마지막 명령의 출력은 다음과 같을 것이다
[Requesting program interpreter: /tools/lib64/ld-linux-x86-64.so.2]
32비트 시스템에서는 인터프리터 이름이 /tools/lib/ld-linux.so.2임을 참고하라.
만약 출력이 위와 같이 표시되지 않거나 출력이 전혀 없다면, 무언가 잘못된 것이다. 이전 단계로 돌아가 어디서 문제가 생겼는지 파악하고 수정하라. 이 문제는 계속 진행하기 전에 반드시 해결해야 한다.
'SW > Linux (Kernel)' 카테고리의 다른 글
Linux From Scratch 개발 노트 6 - 임시 시스템 구축(나머지 유틸리티) (0) | 2024.08.31 |
---|---|
Linux From Scratch 개발 노트 5 - 임시 시스템 구축(libstdc++,binutils, gcc) (0) | 2024.08.30 |
Linux From Scratch 개발 노트 3 - 계정 생성 (0) | 2024.08.28 |
Linux From Scratch 개발 노트 2 - Host System (0) | 2024.08.27 |
Linux From Scratch 개발 노트 1 - Introduction (0) | 2024.08.26 |
댓글