리눅스/LFS

[LFS2]Binutils와 GCC Pass 1 빌드 : 독자적 진화를 위한 첫걸음

sik13579 2026. 1. 20. 13:28

1. 왜 호스트의 도구를 그대로 쓰지 않는가?

지난 포스팅에서 Ubuntu(Host)라는 든든한 작업대를 마련하고, LFS가 세워질 터를 닦았습니다.

이제 본격적으로 벽돌을 쌓을 차례인데, 여기서 한 가지 의문이 생깁니다.
"이미 우분투에서 잘 작동하는 gcc와 ld가 있는데, 왜 굳이 똑같은 도구를 또 빌드해야 하는가?"

 

이 질문에 대한 답이 바로 LFS의 핵심인 '독립성'과 '순수성'에 있습니다.

 

1.1 툴체인(Toolchain)

 지금 만드는 것은 최종적인 OS가 아닙니다. 최종 OS를 만들기 위해 잠시 빌려 쓰는 임시 시스템(Temporary System)입니다.
호스트 시스템의 컴파일러는 호스트 고유 라이브러리 경로(/usr/lib 등)에 강하게 결합되어 있습니다.

만약 이를 그대로 사용하여 LFS를 빌드하면, 겉모습은 새로운 OS일지 몰라도 속은 Ubuntu의 흔적이 가득한 오염된 시스템이 됩니다. 호스트와 완전히 격리된, 오직 LFS만을 위해 존재하는 독자적인 컴파일러와 링커(툴체인)를 먼저 구축함으로써 이 연결 고리를 끊어내야 합니다.

 

1.2 Multilib(m32)

 특히 이번 프로젝트는 32비트와 64비트를 동시에 지원하는 Multilib 환경을 지향합니다. 이는 단순히 옛날 프로그램을 돌리기 위함이 아닙니다. 인프라 보안 관점에서 다양한 아키텍처의 바이너리를 분석하고, 로우레벨 시스템의 메모리 구조를 더 깊게 이해하기 위한 필수적인 선택입니다. 이 과정에서 저는 64비트 시스템이 어떻게 32비트 하위 호환성을 유지하는지 그 기술적 정수를 체험하게 될 것이라고 생각됩니다.

2. 빌드 전 필수 체크리스트

본격적으로 소스코드를 풀고 make를 입력하기 전에, 반드시 확인해야 할 5가지 항목이 있습니다.
LFS빌드는 짧게는 몇 시간, 길게는 며칠이 걸리는 장기 전입니다. 여기서 어긋나면 원인 모를 에러로 전체 과정을 뒤엎어야 할 수도 있습니다.

 

2.1 나는 누구인가?(User Context)

터미널에서 whoami를 입력했을 때 반드시 lfs가 나와야 합니다.

  • 이유 : root 계정은 시스템의 모든 권한을 가집니다. 빌드 스크립트의 작은 오타 하나가 호스트 OS(Ubuntu)의 핵심 파일을 지워버릴 수 있기 때문입니다.
  • 확인 : root 라면 su - lfs로 즉시 전환하세요.

2.2 $LFS 변수의 생존 확인

모든 빌드 명령어는 $LFS 변수를 기준으로 움직입니다.

  • 확인 명령어 : echo $LFS
  • 기댓값 : /mnt/lfs
  • 만약 아무것도 출력되지 않는다면, 마운트 된 파티션이 아닌 호스트 시스템의 루트(/)에 파일을 설치하게 되어 시스템이 엉망이 됩니다.

2.3 호스트 쉘의 정체(/bin/sh)

우분투의 기본 쉘인 dash는 가볍지만 LFS의 복잡한 빌드 스크립트를 처리하기엔 기능이 부족합니다.

  • 확인 명령어 : ls -l /bin/sh
  • 기댓값 : /bin/sh -> bash 상태여야 합니다.
  • 만약 dash로 되어 있다면 sudo ln -sf bash /bin/sh 명령어로 반드시 교체해야 합니다.

2.4 순수한 환경 (The Clean Environment)

만든. bashrc와. bash_profile이 제대로 작동하고 있는지 확인해야 합니다.

  • PATH 순서 : echo $PATH를 쳤을 때 $LFS/tools/bin이 가장 앞에 나와야 합니다. 그래야 새로 만든 따뜻한 도구를 을 우선적으로 사용하게 됩니다.

2.5 32비트 에뮬레이션 지원 (Multilib 전용)

이번 포스팅의 핵심인 Multilib 빌드를 위해서는 호스트 커널이 32비트 바이너리를 이해할 수 있어야 합니다.

  • 확인 명령어 : grep CONFIG_IA32_EMULATION /boot/config-$(uname -r)
  • 기댓값 : CONFIG_IA32_EMULATION=y가 나와야 앞으로 진행할 32비트 라이브러리 빌드 시 에러가 발생하지 않습니다.

2.6 VMware의 장점 스냅샷을 활용

항상 빌드 전 VMware의 장점인 스냅샷을 활용해줍시다. 그리고 마무리 한 뒤 이상이 없다면 다시한번 스냅샷을 저장하도록 합니다.

 

3. 첫 번째 벽돌 Binutils - Pass 1

드디어 LFS 여정의 첫 번째 벽돌을 놓을 시간입니다.

그 주인공은 바로 Binutils입니다. 이 패키지는 어셈블러(as), 링커(ld), 그리고 바이너리 파일을 조작하는 다양한 도구들을 포함하고 있습니다. 우리가 앞으로 빌드할 모든 프로그램은 이 도구들의 손을 거쳐 탄생하게 될 것입니다.

 

3.1 빌드 공식의 적용
LFS의 모든 빌드는 다음의 루틴을 따릅니다.

  1. $LFS/sources로 이동하여 소스 압축 해제.
  2. 생성된 디렉터리 안으로 진입 후, 별도의 build 디렉토리 생성.
  3. build 디렉토리 안에서../configure 실행

3.2 Multilib을 위한 핵심 설정 (Configure)

Multilib(m32) 환경에서는 설정 옵션 하나하나가 매우 중요합니다. 제가 사용한 주요 옵션들의 의미는 다음과 같습니다.

../configure --prefix=$LFS/tools \
             --with-sysroot=$LFS \
             --target=$LFS_TGT   \
             --disable-nls       \
             --enable-gprofng=no \
             --disable-werror    \
             --enable-multiarch  \
             --enable-64-bit-bfd
  • --with-sysroot=$LFS: 빌드 도구가 필요한 라이브러리를 찾을 때 호스트가 아닌 우리가 지정한 $LFS 경로를 기준으로 삼게 합니다.
  • --enable-64-bit-bfd: 64비트 바이너리 지원을 활성화합니다. Multilib 시스템의 뼈대가 되는 옵션입니다.
  • --target=$LFS_TGT: 우리가 4장에서 설정한 타깃 아키텍처(예: x86_64-lfs-linux-gnu)를 위한 도구를 빌드하도록 지정합니다.

3.3 결과 확인 및 뒷정리

make와 make install이 에러 없이 끝났다면, $LFS/tools/bin 폴더에 타겟 명칭이 붙은 실행 파일들이 생겨난 것을 확인했다면 성공입니다. 마지막으로 설치가 성공적으로 끝났다면, 다음 패키지 빌드에 영향을 주지 않도록 소스 폴더를 지워야 합니다.
또한 스냅샷을 찍어주도록 합니다.

cd $LFS/sources
rm -rf binutils-*

 

4. 거대한 산 : GCC - Pass 1

Binutils가 성공했다면 이제 가장 고비인 GCC(GNU Compiler Collection) - Pass 1을 시작할 차례입니다.

GCC는 리눅스 시스템의 심장과 같으며, 빌드 과정도 가장 복잡하고 오래 걸립니다.

 

4.1 수학 라이브러리와의 합체 (GMP, MPFR, MPC)

GCC는 빌드 시 세 가지 고정밀 수학 라이브러리가 반드시 필요합니다. 이들을 시스템에 따로 설치하는 대신, GCC 소스 트리 안에 넣어 함께 빌드하는 방식을 선택했습니다.

cd $LFS/sources

# 1. GCC 압축 해제 및 이동
tar -xvf gcc-*.tar.gz
cd gcc-*

# 2. 필수 라이브러리들 압축 해제 및 폴더명 변경 (버전 숫자는 다를 수 있음)
tar -xvf ../gmp-*.tar.xz
mv -v gmp-* gmp
tar -xvf ../mpfr-*.tar.xz
mv -v mpfr-* mpfr
tar -xvf ../mpc-*.tar.gz
mv -v mpc-* mpc

 

4.2 빌드 설정 (Configure)

Multilib 버전은 일반 LFS보다 설정 옵션이 훨씬 복잡합니다. 아래 명령어를 한 줄로 정확히 입력해 주세요.

mkdir -v build
cd build

../configure                  \
    --target=$LFS_TGT         \
    --prefix=$LFS/tools       \
    --with-glibc-version=2.40 \
    --with-sysroot=$LFS       \
    --with-newlib             \
    --without-headers         \
    --enable-default-pie      \
    --enable-default-ssp      \
    --disable-nls             \
    --disable-shared          \
    --disable-multilib        \
    --disable-threads         \
    --disable-libatomic       \
    --disable-libgomp         \
    --disable-libquadmath     \
    --disable-libssp          \
    --disable-libvtv          \
    --disable-libstdcxx       \
    --enable-languages=c,c++

참고 : Pass 1에서는 아직 C 라이브러리(Glibc)가 없기 때문에 --disable-shared, --disable-threads 등 많은 기능을 잠시 꺼두고 "최소 기능의 컴파일러"만 만듭니다.

 

4.3 컴파일 및 설치

주의: GCC는 빌드 시간이 매우 오래 걸립니다. 사양에 따라 수십 분에서 한 시간 이상 걸릴 수 있으니 인내심이 필요합니다.

make
make install

 

4.4 중요한 마무리 : libgcc_s.so 링크 생성

설치가 끝나면 다음 단계인 리눅스 헤더 설치를 위해 간단한 후속 작업이 필요합니다.

# 빌드된 GCC가 시스템 헤더를 찾을 수 있도록 limits.h를 생성해주는 과정
cd ..
cat gcc/limitx.h gcc/glimits.h gcc/limity.h > \
  `dirname $($LFS_TGT-gcc -print-libgcc-file-name)`/install-tools/include/limits.h

그리고 소스 폴더를 정리합니다.

cd $LFS/sources
rm -rf gcc-*

 

마지막으로스냅샷을 찍어주도록 합니다.

 

4.5 툴체인의 뼈대 완성

이제 호스트 시스템의 도움 없이도 기본적인 코드를 컴파일할 수 있는 최소한의 도구들을 가졌습니다.

소스 폴더를 깨끗이 정리하고(rm -rf), 다음 단계인 리눅스 커널 헤더 설치Glibc 빌드를 향해 나아갈 준비가 되었습니다.

 

다음 과정에서는 5.4. Linux-Headers 빌드를 진행해 보도록 하겠습니다.
현재 과정에서 문제없이 잘 빌드되었기 때문에 다음 과정에서도 문제없이 진행될 것이라 생각합니다.

여기까지 읽어주셔서 감사합니다.