Linux From Scratch (LFS) 종합 가이드
LFS 12.4-systemd를 기반으로 리눅스 시스템을 처음부터 빌드하는 과정을 심층 분석합니다.
크로스 컴파일 이론과 build·host·target 트리플렛, Binutils/GCC/Glibc의 Pass 1·2 구축,
임시 도구 크로스 컴파일, Chroot 환경 진입, 81개 패키지를 포함한 기본 시스템 소프트웨어 설치,
커널 컴파일, GRUB 부트로더, systemd 설정, 패키지 관리 전략, 트러블슈팅까지
LFS의 모든 것을 다룹니다.
핵심 요약
- LFS (Linux From Scratch) — 소스 코드에서 리눅스 시스템 전체를 직접 빌드하는 프로젝트. 패키지 매니저 없이 81개 패키지를 순서대로 컴파일하여 부팅 가능한 최소 시스템을 만듭니다.
- 크로스 컴파일러 — 현재 시스템(호스트)과 다른 환경에서 실행될 바이너리를 생성하는 컴파일러. LFS에서는 호스트 오염을 방지하기 위해 같은 아키텍처라도 크로스 컴파일러를 사용합니다.
- 툴체인 — 소스 코드를 실행 가능한 바이너리로 변환하는 도구 집합. Binutils(as, ld) + GCC(cc1, cc1plus) + Glibc(libc.so) + Libstdc++로 구성됩니다.
- Sysroot — 크로스 컴파일러가 타겟 시스템의 헤더와 라이브러리를 찾는 최상위 디렉토리.
--with-sysroot=$LFS로 지정하여 호스트의/usr/lib대신$LFS/usr/lib를 참조합니다. - Chroot —
chroot시스템 콜로 루트 디렉토리를 변경하여, 빌드 중인 LFS 파티션을 마치 독립 시스템처럼 사용하는 격리 환경입니다.
단계별 요약
LFS 빌드는 크게 4단계로 진행됩니다:
- 1단계: 호스트 준비 — 호스트 시스템 요구사항 확인, LFS 파티션 생성 및 마운트,
lfs사용자 생성, 환경 변수 설정 - 2단계: 크로스 툴체인 구축 — Binutils Pass 1 → GCC Pass 1 → Linux API 헤더 → Glibc → Libstdc++ 순서로 크로스 컴파일러 구축
- 3단계: 임시 도구 크로스 컴파일 — 크로스 컴파일러로 m4, ncurses, bash 등 기본 유틸리티를
$LFS에 설치, 이어서 Binutils/GCC Pass 2로 네이티브 컴파일러 구축 - 4단계: Chroot → 최종 시스템 — Chroot 환경 진입 후 81개 패키지를 최종 빌드, 커널 컴파일, GRUB 설정, systemd 구성으로 부팅 가능 시스템 완성
LFS 개요와 철학
Linux From Scratch(LFS)는 소스 코드만으로 리눅스 시스템 전체를 직접 빌드하는 교육용 프로젝트입니다. 1999년 Gerard Beekmans가 시작했으며, 리눅스 시스템의 내부 구조를 이해하는 가장 효과적인 방법으로 널리 인정받고 있습니다.
왜 LFS인가?
- 교육적 가치: 각 패키지의 역할과 의존성을 직접 확인하며 시스템 구조를 깊이 이해
- 완전한 제어: 커널 옵션부터 컴파일러 플래그까지 모든 설정을 직접 결정
- 최소 시스템: 불필요한 패키지 없이 필요한 것만 포함하는 초경량 시스템 구축
- 보안 강화: 모든 패키지의 소스를 직접 검증하고 보안 패치를 즉시 적용 가능
- 커스터마이징: 특정 용도(임베디드, 서버, 데스크톱)에 최적화된 시스템 구축
LFS 프로젝트 패밀리
| 프로젝트 | 설명 | 기반 |
|---|---|---|
| LFS | 기본 리눅스 시스템 빌드 (이 문서의 주제) | 소스 코드 |
| BLFS | Beyond LFS — X Window, 네트워크, 데스크톱 환경 등 확장 | LFS 위에 추가 |
| CLFS | Cross LFS — 다른 아키텍처용 크로스 빌드 (ARM, MIPS 등) | 크로스 컴파일 |
| ALFS | Automated LFS — 스크립트 자동화 빌드 | LFS 자동화 |
| Hints | 커뮤니티 팁 모음 (패키지 관리, 최적화 등) | LFS 보조 |
| 항목 | LFS 12.4-systemd |
|---|---|
| 릴리스 날짜 | 2024년 9월 |
| 리눅스 커널 | 6.10.x |
| GCC | 14.2.0 |
| Glibc | 2.40 |
| Binutils | 2.43.1 |
| init 시스템 | systemd 256 |
| 총 패키지 수 | 81개 + 패치 |
| 예상 빌드 시간 | 약 30~50 SBU (하드웨어에 따라 상이) |
| 필요 디스크 공간 | 최소 30GB (소스 + 빌드) |
LFS 버전 변천사
LFS는 1999년 첫 릴리스 이후 꾸준히 발전해왔습니다. 주요 이정표를 정리합니다.
| 시기 | 버전 | 주요 변화 |
|---|---|---|
| 1999 | LFS 1.0 | Gerard Beekmans가 프로젝트 시작, 최초의 "소스에서 빌드" 가이드 |
| 2002 | LFS 4.0 | 패키지 수 40개 이상, 안정적 빌드 절차 확립 |
| 2005 | LFS 6.0 | Udev 도입, 2.6 커널 기반, 빌드 절차 대폭 개선 |
| 2007 | LFS 6.3 | 크로스 컴파일 방식 도입 시작 (호스트 격리 강화) |
| 2012 | LFS 7.0 | /tools → /usr 통합 시작, Systemd 에디션 분리 |
| 2015 | LFS 7.7 | Systemd 에디션 공식화, SysVinit 에디션과 병행 |
| 2019 | LFS 9.0 | Python 3 필수화, Meson/Ninja 도입, GCC 9 기반 |
| 2021 | LFS 11.0 | /usr 병합 완료 (/bin→/usr/bin, /lib→/usr/lib 심볼릭 링크) |
| 2023 | LFS 12.0 | pkgconf 도입(pkg-config 대체), GCC 13 기반, Glibc 2.38 |
| 2024 | LFS 12.2 | GCC 14.2, Glibc 2.40, 커널 6.10, systemd 256 |
LFS 기반 배포판
LFS 원칙을 기반으로 발전한 실제 배포판들이 있습니다. 이들은 LFS의 "소스에서 빌드" 철학을 자동화하거나 확장합니다.
| 배포판 | 특징 | LFS와의 관계 |
|---|---|---|
| Gentoo Linux | Portage 패키지 매니저, USE 플래그 기반 커스터마이징 | LFS 철학을 자동화, emerge로 소스 빌드 |
| Arch Linux | Pacman 패키지 매니저, 롤링 릴리스, KISS 원칙 | LFS와 유사한 미니멀 철학, 바이너리 패키지 |
| CRUX | ports 기반 소스 빌드, 경량 시스템 | LFS 영향받은 소스 기반 배포판 |
| NuTyX | Cards 패키지 매니저, LFS/BLFS 기반 | LFS를 직접 기반으로 한 배포판 |
| Void Linux | XBPS 패키지 매니저, 독자적 빌드 시스템, runit init | 처음부터 독자 구축, LFS 정신 계승 |
| Kiss Linux | 극도의 미니멀리즘, 셸 스크립트 패키지 매니저 | LFS보다 더 극단적인 미니멀 접근 |
가상머신 실습 환경 구성
LFS를 안전하게 실습하려면 가상머신(VM)을 사용하는 것이 좋습니다. 호스트 시스템에 영향을 주지 않고, 스냅샷으로 단계별 백업이 가능합니다.
| 도구 | 추천 설정 | 장점 |
|---|---|---|
| VirtualBox | RAM 4GB+, 디스크 50GB (동적 할당), Ubuntu/Debian 호스트 | 무료, 스냅샷, 공유 폴더 |
| QEMU/KVM | RAM 4GB+, virtio 디스크 50GB, virt-manager GUI | 고성능, 리눅스 네이티브, 자유도 높음 |
| VMware Workstation | RAM 4GB+, 디스크 50GB, 스냅샷 활용 | 안정성, 스냅샷 관리 편리 |
| WSL2 (Windows) | Ubuntu 22.04 WSL2, wsl --set-version | Windows 환경에서 LFS 학습 (제한적) |
- 스냅샷 1: 호스트 준비 완료, $LFS 파티션 마운트 후
- 스냅샷 2: Pass 1 크로스 툴체인 빌드 완료 후
- 스냅샷 3: Chapter 6 임시 도구 빌드 완료 후
- 스냅샷 4: Chroot 진입 직전 (가상 FS 마운트 전)
- 스냅샷 5: Chapter 8 시스템 소프트웨어 빌드 완료 후
/dev/loop 디바이스를 사용한 파일 기반 파티션으로 대체할 수 있습니다.
호스트 시스템 요구사항
LFS를 빌드하려면 호스트 시스템에 특정 버전 이상의 소프트웨어가 설치되어 있어야 합니다. 호스트는 LFS 빌드를 수행하는 기존 리눅스 시스템으로, 대부분의 최신 배포판(Ubuntu 22.04+, Fedora 38+, Debian 12+)이 요구사항을 충족합니다.
필수 패키지 및 최소 버전
| 패키지 | 최소 버전 | 확인 명령 | 용도 |
|---|---|---|---|
| Bash | 3.2+ | bash --version | 셸 스크립트 실행 |
| Binutils | 2.13.1+ | ld --version | 어셈블러, 링커 |
| Bison | 2.7+ | bison --version | 파서 생성기 |
| Coreutils | 8.1+ | chown --version | 기본 유틸리티 |
| Diffutils | 2.8.1+ | diff --version | 파일 비교 |
| Findutils | 4.2.31+ | find --version | 파일 검색 |
| Gawk | 4.0.1+ | gawk --version | 텍스트 처리 |
| GCC (C, C++) | 5.2+ | gcc --version | C/C++ 컴파일러 |
| Glibc | 2.11+ | ldd --version | C 라이브러리 |
| Grep | 2.5.1a+ | grep --version | 패턴 검색 |
| Gzip | 1.3.12+ | gzip --version | 압축 |
| Linux Kernel | 4.19+ | uname -r | 호스트 커널 |
| M4 | 1.4.10+ | m4 --version | 매크로 프로세서 |
| Make | 4.0+ | make --version | 빌드 자동화 |
| Patch | 2.5.4+ | patch --version | 패치 적용 |
| Perl | 5.8.8+ | perl -V:version | 스크립트 언어 |
| Python 3 | 3.4+ | python3 --version | 스크립트, 빌드 도구 |
| Sed | 4.1.5+ | sed --version | 스트림 편집기 |
| Tar | 1.22+ | tar --version | 아카이브 |
| Texinfo | 5.0+ | makeinfo --version | 문서 도구 |
| Xz Utils | 5.0.0+ | xz --version | 압축 |
/bin/sh가 반드시 bash를 가리켜야 합니다.
Debian/Ubuntu에서는 기본값이 dash이므로 sudo ln -sf /bin/bash /bin/sh로 변경해야 합니다.
LFS 빌드 스크립트들은 bash 전용 문법을 사용합니다.
version-check.sh 스크립트
아래 스크립트로 호스트 시스템의 요구사항을 한 번에 확인할 수 있습니다:
#!/bin/bash
# LFS 12.4 호스트 요구사항 검증 스크립트
export LC_ALL=C
# Bash 버전 확인
bash --version | head -n1 | cut -d" " -f2-4
# /bin/sh → bash 확인
MYSH=$(readlink -f /bin/sh)
echo "/bin/sh -> $MYSH"
echo -n "MYSH의 경로: "; echo $MYSH | grep -q bash || echo "ERROR: /bin/sh does not point to bash"
unset MYSH
echo -n "Binutils: "; ld --version | head -n1 | cut -d" " -f3-
bison --version | head -n1
if [ -h /usr/bin/yacc ]; then
echo "/usr/bin/yacc -> $(readlink -f /usr/bin/yacc)"
elif [ -x /usr/bin/yacc ]; then
echo yacc is $(/usr/bin/yacc --version | head -n1)
else
echo "yacc not found"
fi
echo -n "Coreutils: "; chown --version | head -n1 | cut -d")" -f2
diff --version | head -n1
find --version | head -n1
gawk --version | head -n1
if [ -h /usr/bin/awk ]; then
echo "/usr/bin/awk -> $(readlink -f /usr/bin/awk)"
elif [ -x /usr/bin/awk ]; then
echo awk is $(/usr/bin/awk --version | head -n1)
else
echo "awk not found"
fi
gcc --version | head -n1
g++ --version | head -n1
echo -n "Glibc: "; ldd --version | head -n1 | cut -d" " -f2-
grep --version | head -n1
gzip --version | head -n1
echo -n "Linux Kernel: "; cat /proc/version
m4 --version | head -n1
make --version | head -n1
patch --version | head -n1
echo -n "Perl: "; perl -V:version
python3 --version
sed --version | head -n1
tar --version | head -n1
makeinfo --version | head -n1 # texinfo
xz --version | head -n1
echo 'int main(){}' | g++ -x c++ -
if [ -x a.out ]; then
echo "g++ compilation OK"
else
echo "g++ compilation FAILED"
fi
rm -f a.out
sudo apt-get install build-essential bison gawk texinfo python3
Fedora/RHEL 필수 패키지 설치:
sudo dnf install gcc gcc-c++ make bison gawk texinfo python3 perl diffutils findutils patch
파티션 및 파일시스템 준비
LFS 시스템을 위한 전용 파티션을 생성하고 파일시스템을 포맷합니다. 호스트 시스템과 별도의 파티션(또는 디스크)을 사용하는 것이 안전합니다.
추천 파티션 구성
| 마운트 포인트 | 파일시스템 | 최소 크기 | 권장 크기 | 설명 |
|---|---|---|---|---|
/ (루트) | ext4 | 10 GB | 30 GB | LFS 시스템 전체 (소스 + 빌드) |
/boot | ext2 | 200 MB | 500 MB | 커널, GRUB (선택) |
swap | swap | RAM과 동일 | RAM × 2 | 스왑 영역 |
/home | ext4 | - | 여유분 | 사용자 데이터 (선택) |
파티션 생성 및 포맷
# 디스크 확인 (예: /dev/sdb)
lsblk
# 파티션 생성 (fdisk 사용)
sudo fdisk /dev/sdb
# n → 새 파티션 → p (primary) → 크기 지정
# t → 파티션 타입 (82=swap, 83=Linux)
# w → 저장
# 또는 parted 사용 (GPT 테이블)
sudo parted /dev/sdb
# mklabel gpt
# mkpart primary ext4 1MiB 30GiB
# mkpart primary linux-swap 30GiB 34GiB
# quit
# 파일시스템 포맷
sudo mkfs -v -t ext4 /dev/sdb1
# 스왑 포맷
sudo mkswap /dev/sdb2
$LFS 변수 설정 및 마운트
# LFS 환경 변수 설정 (반드시 설정!)
export LFS=/mnt/lfs
# 마운트 포인트 생성 및 마운트
sudo mkdir -pv $LFS
sudo mount -v -t ext4 /dev/sdb1 $LFS
# 스왑 활성화
sudo swapon /dev/sdb2
# 소스 디렉토리 생성
sudo mkdir -v $LFS/sources
sudo chmod -v a+wt $LFS/sources
/home을 별도 파티션으로 분리하면 LFS를 재빌드할 때
사용자 데이터를 보존할 수 있습니다. 또한 여러 LFS 빌드에서 같은 /home을 공유할 수 있습니다.
$LFS 변수가 초기화되고 마운트가 해제됩니다.
작업을 재개하기 전에 반드시 export LFS=/mnt/lfs와 mount 명령을 다시 실행하세요.
/etc/fstab에 추가하면 자동 마운트됩니다.
환경 변수 설정
LFS 빌드를 위한 전용 사용자(lfs)를 생성하고, 깨끗한 환경을 구성합니다.
호스트의 환경 변수가 빌드에 영향을 주지 않도록 격리하는 것이 핵심입니다.
lfs 사용자 생성
# lfs 그룹 및 사용자 생성
sudo groupadd lfs
sudo useradd -s /bin/bash -g lfs -m -k /dev/null lfs
# 비밀번호 설정
sudo passwd lfs
# $LFS 디렉토리 소유권 변경
sudo chown -v lfs $LFS/{usr{,/*},lib,var,etc,bin,sbin,tools}
case $(uname -m) in
x86_64) sudo chown -v lfs $LFS/lib64 ;;
esac
# lfs 사용자로 전환
su - lfs
환경 변수 참조표
| 변수 | 값 | 설명 |
|---|---|---|
LFS | /mnt/lfs | LFS 시스템 마운트 포인트 |
LC_ALL | POSIX | 로케일을 POSIX로 고정하여 빌드 재현성 확보 |
LFS_TGT | x86_64-lfs-linux-gnu | 크로스 컴파일러 타겟 트리플렛 |
PATH | $LFS/tools/bin:/usr/bin | 크로스 컴파일러를 우선 탐색 |
CONFIG_SITE | $LFS/usr/share/config.site | configure 스크립트 기본값 오버라이드 |
MAKEFLAGS | -j$(nproc) | 병렬 빌드 (CPU 코어 수만큼) |
.bash_profile
exec env -i HOME=$HOME TERM=$TERM PS1='\u:\w\$ ' /bin/bash
exec env -i는 기존 환경 변수를 모두 제거하고 깨끗한 셸을 시작합니다.
HOME, TERM, PS1만 유지합니다.
.bashrc
set +h # 해시 비활성화 (PATH 변경을 즉시 반영)
umask 022 # 파일 권한 기본값
LFS=/mnt/lfs
LC_ALL=POSIX
LFS_TGT=$(uname -m)-lfs-linux-gnu
PATH=/usr/bin
if [ ! -L /bin ]; then PATH=/bin:$PATH; fi
PATH=$LFS/tools/bin:$PATH
CONFIG_SITE=$LFS/usr/share/config.site
MAKEFLAGS="-j$(nproc)"
export LFS LC_ALL LFS_TGT PATH CONFIG_SITE MAKEFLAGS
toupper('i')가 'I' 대신 'İ'를 반환하여
configure 스크립트가 실패합니다. POSIX(= C) 로케일은 ASCII 기반 동작을 보장합니다.
CONFIG_SITE 파일
# $LFS/usr/share/config.site
# configure 스크립트가 자동으로 읽는 설정 파일
# Autoconf 캐시 변수: 크로스 컴파일 시 테스트 불가능한 값을 사전 지정
ac_cv_func_mmap_fixed_mapped=yes
ac_cv_func_working_mktime=yes
set +h로 이를 비활성화하면, 새로 빌드한 도구가 $PATH에 설치되었을 때
즉시 사용됩니다. 해시가 활성화되어 있으면 이전 경로가 캐시되어 오래된 도구가 사용될 수 있습니다.
크로스 컴파일 기초 이론
크로스 컴파일(cross-compilation)은 현재 실행 중인 시스템(빌드 머신)과 다른 환경에서 실행될 프로그램을 컴파일하는 기술입니다. LFS에서 크로스 컴파일은 단순히 다른 아키텍처를 타겟으로 하는 것이 아니라, 호스트 시스템의 라이브러리 오염을 완전히 차단하기 위한 핵심 전략입니다.
Build / Host / Target 개념
GNU 빌드 시스템에서 사용하는 세 가지 머신 개념을 정확히 이해해야 합니다:
| 용어 | 의미 | LFS에서의 역할 |
|---|---|---|
| Build | 컴파일러를 실행하는 머신 (컴파일이 수행되는 곳) | 호스트 시스템 (예: x86_64-pc-linux-gnu) |
| Host | 컴파일된 프로그램이 실행될 머신 | LFS 시스템 (예: x86_64-lfs-linux-gnu) |
| Target | 컴파일된 프로그램이 생성하는 코드가 실행될 머신 (컴파일러에만 해당) | 컴파일러 빌드 시에만 사용 (x86_64-lfs-linux-gnu) |
--build와 --host만 사용합니다.
--target은 GCC나 Binutils처럼 다른 코드를 생성하는 도구에만 의미가 있습니다.
LFS 크로스 컴파일 단계별 Build/Host/Target 값
| 단계 | 빌드 대상 | --build | --host | --target | 설명 |
|---|---|---|---|---|---|
| Pass 1 | Binutils | x86_64-pc-linux-gnu |
(= build) | $LFS_TGT |
호스트에서 실행, LFS용 코드 생성 |
| Pass 1 | GCC | x86_64-pc-linux-gnu |
(= build) | $LFS_TGT |
호스트에서 실행, LFS용 코드 생성 |
| - | Glibc | ../config.guess |
$LFS_TGT |
- | 크로스 컴파일러로 빌드, LFS에서 실행 |
| Ch.6 | 임시 도구 | $(build-aux/config.guess) |
$LFS_TGT |
- | 크로스 컴파일러로 빌드, LFS에서 실행 |
| Pass 2 | Binutils | $(../config.guess) |
$LFS_TGT |
- | 크로스 컴파일, LFS에서 네이티브로 실행 |
| Pass 2 | GCC | $(../config.guess) |
$LFS_TGT |
$LFS_TGT |
LFS에서 실행, LFS용 코드 생성 (네이티브) |
왜 같은 아키텍처에서 크로스 컴파일을 하는가?
LFS에서 빌드 머신과 타겟 머신의 CPU 아키텍처가 같은데도(둘 다 x86_64) 크로스 컴파일을 하는 이유는 호스트 라이브러리 오염 방지입니다:
- 네이티브 컴파일러는 기본적으로
/usr/lib,/usr/include에서 라이브러리와 헤더를 찾습니다 - 이렇게 빌드된 프로그램은 호스트의 glibc에 링크되어, 호스트 없이는 실행할 수 없습니다
- 크로스 컴파일러는
--with-sysroot=$LFS로 설정되어$LFS/usr/lib에서만 라이브러리를 찾습니다 - 트리플렛을 의도적으로 다르게(
x86_64-lfs-linux-gnu) 설정하여 호스트와 구분합니다
/usr/lib/libc.so에 링크되면,
LFS 시스템을 독립적으로 부팅할 수 없습니다. 크로스 컴파일은 이 오염을 원천적으로 차단합니다.
트리플렛이 다르면 컴파일러가 호스트의 라이브러리 경로를 절대 참조하지 않기 때문입니다.
트리플렛(Triplet) 형식
GNU 트리플렛은 시스템을 식별하는 문자열로, arch-vendor-kernel-os 형태입니다:
# 호스트 시스템 트리플렛 확인
gcc -dumpmachine
# 출력 예: x86_64-pc-linux-gnu 또는 x86_64-linux-gnu
# LFS 타겟 트리플렛
echo $LFS_TGT
# 출력: x86_64-lfs-linux-gnu
| 필드 | 호스트 | LFS 타겟 | 설명 |
|---|---|---|---|
| arch | x86_64 | x86_64 | CPU 아키텍처 (동일) |
| vendor | pc | lfs | 벤더 (의도적으로 다르게 설정) |
| kernel | linux | linux | 커널 (동일) |
| os | gnu | gnu | OS/ABI (동일) |
툴체인 기술 분석
툴체인(toolchain)은 소스 코드를 실행 가능한 바이너리로 변환하는 도구 집합입니다. LFS 크로스 툴체인은 Binutils, GCC, Glibc, Libstdc++로 구성되며, 각 구성요소가 정밀하게 맞물려 동작합니다.
툴체인 구성요소
| 구성요소 | 주요 도구 | 역할 | 빌드 순서 |
|---|---|---|---|
| Binutils | as, ld, ar, objdump | 어셈블러, 링커, 아카이버 | 1번째 (Pass 1) |
| GCC | cc1, cc1plus, collect2 | C/C++ 컴파일러 (전처리→컴파일→어셈블) | 2번째 (Pass 1) |
| Linux Headers | 헤더 파일만 | 커널-사용자공간 인터페이스 정의 | 3번째 |
| Glibc | libc.so, ld-linux.so, crt*.o | C 표준 라이브러리, 동적 링커 | 4번째 |
| Libstdc++ | libstdc++.so | C++ 표준 라이브러리 | 5번째 |
crt1.o, crti.o)를
Glibc에서 가져오고, Glibc는 내부 예외 처리를 위해 libgcc_s.so가 필요합니다.
이 "닭과 달걀" 문제를 해결하는 유일한 방법은 Pass 1에서 --with-newlib(glibc 불필요 모드)로
최소 GCC를 빌드한 뒤, 이 GCC로 Glibc를 컴파일하는 것입니다.
Sysroot의 동작 원리
--with-sysroot=$LFS로 빌드된 크로스 컴파일러는 다음과 같이 라이브러리와 헤더를 탐색합니다:
# 크로스 컴파일러의 검색 경로 확인
$LFS_TGT-gcc -print-search-dirs
# 헤더 검색 경로 확인
$LFS_TGT-gcc -print-sysroot
# 출력: /mnt/lfs
# 실제 헤더 검색 순서
# 1. $LFS/usr/include
# 2. $LFS/tools/lib/gcc/$LFS_TGT/14.2.0/include
# 3. $LFS/tools/lib/gcc/$LFS_TGT/14.2.0/include-fixed
# 라이브러리 검색 순서
# 1. $LFS/usr/lib
# 2. $LFS/lib
# 3. $LFS/tools/lib
Binutils Pass 1
Binutils는 어셈블러(as)와 링커(ld)를 포함하는 패키지로, 툴체인에서 가장 먼저 빌드합니다.
GCC와 Glibc의 configure 스크립트가 as와 ld의 기능을 테스트하여
빌드 옵션을 결정하기 때문입니다.
configure는 링커(ld)가
지원하는 기능(예: --hash-style=gnu)을 테스트합니다. Binutils가 없으면 GCC configure가
기능 감지에 실패하여 최적화되지 않은 설정이 적용됩니다.
빌드 절차
# 소스 압축 해제 및 빌드 디렉토리 생성
cd $LFS/sources
tar -xf binutils-2.43.1.tar.xz
cd binutils-2.43.1
mkdir -v build
cd build
# configure
../configure \
--prefix=$LFS/tools \
--with-sysroot=$LFS \
--target=$LFS_TGT \
--disable-nls \
--enable-gprofng=no \
--disable-werror \
--enable-new-dtags \
--enable-default-hash-style=gnu
# 빌드 및 설치
make
make install
configure 옵션 상세
| 옵션 | 설명 |
|---|---|
--prefix=$LFS/tools | 크로스 도구를 $LFS/tools/bin에 설치. 호스트의 /usr/bin과 분리 |
--with-sysroot=$LFS | 링커가 $LFS를 sysroot로 사용하여 타겟 라이브러리를 여기서 탐색 |
--target=$LFS_TGT | x86_64-lfs-linux-gnu를 타겟으로 설정. 호스트와 다른 트리플렛이므로 크로스 도구 생성 |
--disable-nls | 다국어 지원(Native Language Support) 비활성화. 임시 도구에는 불필요하며 빌드 시간 단축 |
--enable-gprofng=no | gprofng 프로파일링 도구 비활성화. 크로스 빌드 시 불필요 |
--disable-werror | 경고를 오류로 처리하지 않음. 호스트 컴파일러 버전에 따른 경고로 빌드 실패 방지 |
--enable-new-dtags | 새로운 DT_RUNPATH 태그 사용 (DT_RPATH 대신). 보안 및 호환성 향상 |
--enable-default-hash-style=gnu | GNU 해시 스타일을 기본값으로 설정. 심볼 조회 성능 향상 |
GCC Pass 1
GCC Pass 1은 최소한의 크로스 컴파일러를 빌드합니다.
Glibc가 아직 없으므로 --with-newlib과 --without-headers를 사용하여
C 라이브러리 없이도 동작하는 제한적 GCC를 만듭니다.
--with-newlib의 의미: 이 옵션은 "newlib를 실제로 사용한다"는 뜻이 아닙니다.
GCC 내부적으로 inhibit_libc를 정의하여 glibc 헤더 없이 빌드할 수 있게 하는 플래그입니다.
이름이 혼동을 주지만, 실제로 newlib 라이브러리를 설치하거나 사용하지는 않습니다.
사전 준비: MPFR, GMP, MPC 추출
cd $LFS/sources
tar -xf gcc-14.2.0.tar.xz
cd gcc-14.2.0
# GCC가 의존하는 다중 정밀도 수학 라이브러리 압축 해제
tar -xf ../mpfr-4.2.1.tar.xz
mv -v mpfr-4.2.1 mpfr
tar -xf ../gmp-6.3.0.tar.xz
mv -v gmp-6.3.0 gmp
tar -xf ../mpc-1.3.1.tar.gz
mv -v mpc-1.3.1 mpc
기본 경로 수정
# x86_64에서 라이브러리 디렉토리를 lib64가 아닌 lib으로 설정
case $(uname -m) in
x86_64)
sed -e '/m64=/s/lib64/lib/' \
-i.orig gcc/config/i386/t-linux64
;;
esac
빌드 절차
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++
make
make install
limits.h 헤더 생성
# GCC 내부 limits.h 생성 (시스템 limits.h를 포함하는 래퍼)
cd ..
cat gcc/limitx.h gcc/glimits.h gcc/limity.h > \
`dirname $($LFS_TGT-gcc -print-libgcc-file-name)`/include/limits.h
configure 옵션 상세
| 옵션 | 설명 |
|---|---|
--target=$LFS_TGT | x86_64-lfs-linux-gnu용 코드를 생성하는 크로스 컴파일러 |
--prefix=$LFS/tools | 크로스 컴파일러를 $LFS/tools에 설치 |
--with-glibc-version=2.40 | 타겟 glibc 버전을 지정 (호환성 헤더 생성용) |
--with-sysroot=$LFS | sysroot를 $LFS로 설정하여 타겟 헤더/라이브러리 경로 지정 |
--with-newlib | inhibit_libc 정의 — glibc 없이 빌드 가능하게 함 |
--without-headers | 시스템 헤더 없이 빌드. 커널 헤더가 아직 없으므로 필수 |
--enable-default-pie | Position-Independent Executable을 기본값으로. ASLR 보안 강화 |
--enable-default-ssp | Stack Smashing Protector를 기본 활성화. 스택 버퍼 오버플로 방지 |
--disable-shared | 정적 라이브러리만 빌드. 크로스 컴파일 시 공유 라이브러리 문제 방지 |
--disable-multilib | 32비트 지원 비활성화. x86_64 전용 |
--disable-threads | 스레드 지원 비활성화. glibc의 pthread가 아직 없으므로 |
--disable-libatomic | libatomic 빌드 생략 (glibc 필요) |
--disable-libgomp | OpenMP 라이브러리 생략 (pthread 필요) |
--disable-libquadmath | 128비트 수학 라이브러리 생략 |
--disable-libssp | SSP 라이브러리 생략 (glibc가 이를 제공) |
--disable-libvtv | VTV(Virtual Table Verification) 생략 |
--disable-libstdcxx | C++ 표준 라이브러리 생략 (나중에 별도 빌드) |
--enable-languages=c,c++ | C와 C++만 활성화. Fortran, Go 등 불필요한 프론트엔드 제외 |
Linux API 헤더
Linux 커널은 사용자 공간 프로그램이 커널 서비스를 호출할 수 있도록 C 헤더 파일을 제공합니다. 이 헤더는 시스템 콜 번호, 구조체 정의, ioctl 명령 등을 포함하며, Glibc가 빌드될 때 이 헤더를 참조합니다.
make headers로 추출되는 헤더는 사용자 공간 API 헤더만 포함합니다.
커널 내부 구현 헤더(kernel/, mm/ 등)는 제외됩니다.
추출된 헤더는 #define과 struct 정의만 포함하며, 컴파일된 코드는 없습니다.
빌드 절차
cd $LFS/sources
tar -xf linux-6.10.5.tar.xz
cd linux-6.10.5
# 이전 빌드 아티팩트 제거
make mrproper
# 사용자 공간 헤더 추출
make headers
# 헤더 설치 ($LFS/usr/include로 복사)
find usr/include -type f ! -name '*.h' -delete
cp -rv usr/include $LFS/usr
make headers는 커널 소스에서 __KERNEL__ 가드로 보호된 내부 전용 코드를 제거하고,
사용자 공간에서 사용 가능한 헤더만 추출합니다. 주요 디렉토리:
| 디렉토리 | 내용 |
|---|---|
linux/ | 시스템 콜 번호, ioctl, 소켓, 파일시스템 구조체 등 |
asm/ | 아키텍처별 정의 (레지스터, 시그널 등) |
asm-generic/ | 아키텍처 공통 정의 |
drm/ | DRM/KMS 인터페이스 |
mtd/ | MTD(Memory Technology Device) 인터페이스 |
scsi/ | SCSI 인터페이스 |
sound/ | ALSA 인터페이스 |
video/ | 비디오 인터페이스 |
Glibc 크로스 빌드
Glibc(GNU C Library)는 리눅스 시스템의 핵심 라이브러리입니다.
printf, malloc, open 등 모든 C 표준 함수와
시스템 콜 래퍼, 동적 링커(ld-linux-x86-64.so.2), C 런타임 시작 코드(crt1.o)를 제공합니다.
사전 준비
cd $LFS/sources
tar -xf glibc-2.40.tar.xz
cd glibc-2.40
# LSB 호환성 심볼릭 링크 생성
case $(uname -m) in
i?86) ln -sfv ld-linux.so.2 $LFS/lib/ld-lsb.so.3 ;;
x86_64) ln -sfv ../lib/ld-linux-x86-64.so.2 $LFS/lib64
ln -sfv ../lib/ld-linux-x86-64.so.2 $LFS/lib64/ld-lsb-x86-64.so.3 ;;
esac
# FHS 호환 패치 적용
patch -Np1 -i ../glibc-2.40-fhs-1.patch
빌드 절차
mkdir -v build
cd build
# 환경 변수 설정 (configure가 올바른 루트 경로를 사용하도록)
echo "rootsbindir=/usr/sbin" > configparms
../configure \
--prefix=/usr \
--host=$LFS_TGT \
--build=$(../scripts/config.guess) \
--enable-kernel=4.19 \
--with-headers=$LFS/usr/include \
--disable-nscd \
libc_cv_slibdir=/usr/lib
make
make DESTDIR=$LFS install
configure 옵션 상세
| 옵션 | 설명 |
|---|---|
--prefix=/usr | 최종 시스템에서의 설치 경로. DESTDIR=$LFS와 결합하여 $LFS/usr에 설치 |
--host=$LFS_TGT | 크로스 컴파일 모드 활성화. $LFS_TGT-gcc를 사용하여 빌드 |
--build=$(../scripts/config.guess) | 호스트 시스템의 트리플렛을 자동 감지 |
--enable-kernel=4.19 | 최소 커널 버전 지정. 4.19 이전 커널의 호환 코드를 제외하여 크기 축소 |
--with-headers=$LFS/usr/include | Linux API 헤더 위치. 이전 단계에서 설치한 헤더를 참조 |
--disable-nscd | Name Service Cache Daemon 비활성화 (LFS에서 불필요) |
libc_cv_slibdir=/usr/lib | 공유 라이브러리를 /usr/lib에 설치 (/lib64 대신) |
Sanity Check (필수!)
# 간단한 프로그램을 크로스 컴파일하여 동적 링커 경로 확인
echo 'int main(){}' | $LFS_TGT-gcc -xc -
readelf -l a.out | grep ld-linux
# 기대 출력:
# [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
# 클린업
rm -v a.out
위 출력에서 /lib64/ld-linux-x86-64.so.2가 표시되어야 합니다.
다른 경로(예: 호스트의 /lib/ld-linux.so.2)가 나오면 크로스 컴파일러가 올바르게 구성되지 않은 것입니다.
limits.h 수정
# GCC 내부 limits.h를 수정하여 시스템 limits.h를 포함하도록
$LFS/tools/libexec/gcc/$LFS_TGT/14.2.0/install-tools/mkheaders
Libstdc++ 초기 빌드
Libstdc++는 C++ 표준 라이브러리로, GCC 소스 트리 안에 포함되어 있습니다.
GCC Pass 1에서는 --disable-libstdcxx로 빌드하지 않았으므로, 별도로 빌드합니다.
이 시점에서는 Glibc가 설치되어 있으므로 정상적인 C++ 라이브러리 빌드가 가능합니다.
빌드 절차
# GCC 소스 디렉토리에서 (이전 빌드 디렉토리 삭제 후)
cd $LFS/sources/gcc-14.2.0
rm -rf build
mkdir -v build
cd build
../libstdc++-v3/configure \
--host=$LFS_TGT \
--build=$(../config.guess) \
--prefix=/usr \
--disable-multilib \
--disable-nls \
--disable-libstdcxx-pch \
--with-gxx-include-dir=/tools/$LFS_TGT/include/c++/14.2.0
make
make DESTDIR=$LFS install
# libtool .la 파일 제거 (크로스 컴파일 시 문제를 일으킬 수 있음)
rm -v $LFS/usr/lib/lib{stdc++{,exp,fs},supc++}.la
configure 옵션 상세
| 옵션 | 설명 |
|---|---|
--host=$LFS_TGT | 크로스 컴파일 모드. $LFS_TGT-g++로 빌드 |
--build=$(../config.guess) | 호스트 시스템 트리플렛 자동 감지 |
--prefix=/usr | 최종 설치 경로 (DESTDIR=$LFS와 결합) |
--disable-multilib | 32비트 지원 비활성화 |
--disable-nls | 다국어 지원 비활성화 |
--disable-libstdcxx-pch | 사전 컴파일 헤더 비활성화. 빌드 시간 절약 (크로스 빌드에서 효과 적음) |
--with-gxx-include-dir=... | C++ 헤더 설치 경로를 tools 안으로 지정. Pass 2에서 재빌드할 때까지 임시 위치 사용 |
임시 도구 크로스 컴파일
크로스 툴체인이 완성되었으므로, 이제 기본 유틸리티들을 크로스 컴파일하여 $LFS에 설치합니다.
이 도구들은 Chroot 환경에서 최종 시스템을 빌드하는 데 사용됩니다.
Chapter 6의 패키지들은 모두 --host=$LFS_TGT를 사용하여 크로스 컴파일됩니다.
--host=$LFS_TGT 사용이 핵심:
--host를 지정하면 configure는 $LFS_TGT-gcc를 컴파일러로 사용합니다.
이를 빠뜨리면 호스트의 GCC가 사용되어 호스트 라이브러리에 링크되는 오염이 발생합니다.
Chapter 6 패키지 목록
| # | 패키지 | 버전 | 설명 | 특이사항 |
|---|---|---|---|---|
| 1 | M4 | 1.4.19 | 매크로 프로세서 | 표준 크로스 빌드 |
| 2 | Ncurses | 6.5 | 터미널 라이브러리 | 호스트용 tic 먼저 빌드 |
| 3 | Bash | 5.2.32 | 셸 | Chroot에서 /bin/bash로 사용 |
| 4 | Coreutils | 9.5 | 기본 유틸리티 (ls, cp, mv 등) | 표준 크로스 빌드 |
| 5 | Diffutils | 3.10 | 파일 비교 | 표준 크로스 빌드 |
| 6 | File | 5.45 | 파일 타입 판별 | 호스트용 file 먼저 빌드 |
| 7 | Findutils | 4.10.0 | 파일 검색 (find, xargs) | 표준 크로스 빌드 |
| 8 | Gawk | 5.3.0 | 텍스트 처리 | 표준 크로스 빌드 |
| 9 | Grep | 3.11 | 패턴 검색 | 표준 크로스 빌드 |
| 10 | Gzip | 1.13 | 압축 | 표준 크로스 빌드 |
| 11 | Make | 4.4.1 | 빌드 자동화 | 표준 크로스 빌드 |
| 12 | Patch | 2.7.6 | 패치 적용 | 표준 크로스 빌드 |
| 13 | Sed | 4.9 | 스트림 편집기 | 표준 크로스 빌드 |
| 14 | Tar | 1.35 | 아카이브 | 표준 크로스 빌드 |
| 15 | Xz | 5.6.2 | 압축 | 표준 크로스 빌드 |
표준 크로스 빌드 패턴
# 대부분의 패키지가 이 패턴을 따릅니다
cd $LFS/sources
tar -xf <package>-<version>.tar.xz
cd <package>-<version>
./configure --prefix=/usr \
--host=$LFS_TGT \
--build=$(build-aux/config.guess)
make
make DESTDIR=$LFS install
M4 빌드 예시
cd $LFS/sources
tar -xf m4-1.4.19.tar.xz
cd m4-1.4.19
./configure --prefix=/usr \
--host=$LFS_TGT \
--build=$(build-aux/config.guess)
make
make DESTDIR=$LFS install
Ncurses 빌드 (특수 처리)
Ncurses는 tic (터미널 정보 컴파일러)를 빌드 과정에서 실행해야 하므로,
호스트용 tic를 먼저 빌드합니다:
cd $LFS/sources
tar -xf ncurses-6.5.tar.gz
cd ncurses-6.5
# 1단계: 호스트용 tic 빌드 (빌드 머신에서 실행할 도구)
mkdir build-tic
pushd build-tic
../configure
make -C include
make -C progs tic
popd
# 2단계: 크로스 컴파일
./configure --prefix=/usr \
--host=$LFS_TGT \
--build=$(./config.guess) \
--mandir=/usr/share/man \
--with-manpage-format=normal \
--with-shared \
--without-normal \
--with-cxx-shared \
--without-debug \
--without-ada \
--disable-stripping \
--enable-widec
make
make DESTDIR=$LFS TIC_PATH=$(pwd)/build-tic/progs/tic install
# libncurses 호환 심볼릭 링크
ln -sv libncursesw.so $LFS/usr/lib/libncurses.so
# pkgconfig 파일 수정
sed -e 's/^#if.*XOPEN.*$/#if 1/' \
-i $LFS/usr/include/curses.h
$LFS/usr에 설치됩니다.
--prefix=/usr와 DESTDIR=$LFS의 조합으로 실제 경로는 $LFS/usr/bin,
$LFS/usr/lib 등이 됩니다. Chroot 환경에서는 $LFS가 /가 되므로
자연스럽게 /usr/bin, /usr/lib에서 찾을 수 있습니다.
Binutils/GCC Pass 2
Pass 2에서는 Chroot 환경 안에서 실행될 네이티브 컴파일러를 크로스 컴파일합니다. Pass 1의 크로스 컴파일러는 호스트에서 실행되지만, Pass 2의 결과물은 LFS 시스템 안에서 실행됩니다.
Binutils Pass 2
cd $LFS/sources
tar -xf binutils-2.43.1.tar.xz
cd binutils-2.43.1
# sed로 오래된 libtool 수정 (필요 시)
sed '6009s/$add_dir//' -i ltmain.sh
mkdir -v build
cd build
../configure \
--prefix=/usr \
--build=$(../config.guess) \
--host=$LFS_TGT \
--disable-nls \
--enable-shared \
--enable-gprofng=no \
--disable-werror \
--enable-64-bit-bfd \
--enable-new-dtags \
--enable-default-hash-style=gnu
make
make DESTDIR=$LFS install
# libtool .la 파일 제거
rm -v $LFS/usr/lib/lib{bfd,ctf,ctf-nobfd,opcodes,sframe}.{a,la}
Binutils Pass 2 configure 옵션
| 옵션 | 설명 |
|---|---|
--prefix=/usr | 최종 시스템의 /usr에 설치 (DESTDIR=$LFS 경유) |
--host=$LFS_TGT | 크로스 컴파일: 빌드는 호스트에서, 결과물은 LFS에서 실행 |
--enable-shared | 공유 라이브러리 빌드 (Pass 1과 다름) |
--enable-64-bit-bfd | 64비트 BFD 라이브러리 활성화 |
GCC Pass 2
cd $LFS/sources
tar -xf gcc-14.2.0.tar.xz
cd gcc-14.2.0
# MPFR, GMP, MPC 추출
tar -xf ../mpfr-4.2.1.tar.xz && mv -v mpfr-4.2.1 mpfr
tar -xf ../gmp-6.3.0.tar.xz && mv -v gmp-6.3.0 gmp
tar -xf ../mpc-1.3.1.tar.gz && mv -v mpc-1.3.1 mpc
# lib64 → lib 수정
case $(uname -m) in
x86_64)
sed -e '/m64=/s/lib64/lib/' \
-i.orig gcc/config/i386/t-linux64
;;
esac
# libgcc에서 크로스 빌드 오버라이드 방지
sed '/thread_header =/s/@.*@/gthr-posix.h/' \
-i libgcc/Makefile.in libstdc++-v3/include/Makefile.in
mkdir -v build
cd build
../configure \
--build=$(../config.guess) \
--host=$LFS_TGT \
--target=$LFS_TGT \
LDFLAGS_FOR_TARGET=-L$PWD/$LFS_TGT/libgcc \
--prefix=/usr \
--with-build-sysroot=$LFS \
--enable-default-pie \
--enable-default-ssp \
--disable-nls \
--disable-multilib \
--disable-libatomic \
--disable-libgomp \
--disable-libquadmath \
--disable-libsanitizer \
--disable-libssp \
--disable-libvtv \
--enable-languages=c,c++
make
make DESTDIR=$LFS install
# cc → gcc 심볼릭 링크 생성
ln -sv gcc $LFS/usr/bin/cc
GCC Pass 2 configure 옵션
| 옵션 | 설명 |
|---|---|
--host=$LFS_TGT | 결과물이 LFS에서 실행되도록 크로스 컴파일 |
--target=$LFS_TGT | 이 GCC가 생성하는 코드도 LFS용 (네이티브 컴파일러) |
LDFLAGS_FOR_TARGET=... | 타겟 라이브러리 빌드 시 libgcc 경로 지정 |
--with-build-sysroot=$LFS | 빌드 시 sysroot로 $LFS 사용 |
--disable-libsanitizer | AddressSanitizer 등 비활성화 (크로스 빌드 시 문제) |
/usr/bin/gcc로 사용 가능하며,
Chapter 8의 모든 패키지를 네이티브로 빌드할 수 있습니다.
Chroot 환경 진입
모든 크로스 컴파일 패키지가 설치되었으므로, 이제 Chroot 환경에 진입합니다.
Chroot는 $LFS 디렉토리를 루트(/)로 변경하여,
마치 새로운 시스템에 로그인한 것처럼 동작합니다.
소유권 변경
# lfs 사용자에서 root로 전환
exit # lfs 사용자 로그아웃
# $LFS 전체를 root 소유로 변경
sudo chown -R root:root $LFS/{usr,lib,var,etc,bin,sbin,tools}
case $(uname -m) in
x86_64) sudo chown -R root:root $LFS/lib64 ;;
esac
가상 파일시스템 마운트
# 가상 커널 파일시스템 마운트
mkdir -pv $LFS/{dev,proc,sys,run}
# /dev 바인드 마운트
mount -v --bind /dev $LFS/dev
# devpts 마운트
mount -vt devpts devpts -o gid=5,mode=0620 $LFS/dev/pts
# proc, sysfs, tmpfs 마운트
mount -vt proc proc $LFS/proc
mount -vt sysfs sysfs $LFS/sys
mount -vt tmpfs tmpfs $LFS/run
# /dev/shm 처리
if [ -h $LFS/dev/shm ]; then
install -v -d -m 1777 $LFS$(realpath /dev/shm)
else
mount -vt tmpfs -o nosuid,nodev tmpfs $LFS/dev/shm
fi
Chroot 진입
chroot "$LFS" /usr/bin/env -i \
HOME=/root \
TERM="$TERM" \
PS1='(lfs chroot) \u:\w\$ ' \
PATH=/usr/bin:/usr/sbin \
MAKEFLAGS="-j$(nproc)" \
TESTSUITEFLAGS="-j$(nproc)" \
/bin/bash --login
필수 디렉토리 생성
# FHS 디렉토리 구조 생성
mkdir -pv /{boot,home,mnt,opt,srv}
mkdir -pv /etc/{opt,sysconfig}
mkdir -pv /lib/firmware
mkdir -pv /media/{floppy,cdrom}
mkdir -pv /usr/{,local/}{include,src}
mkdir -pv /usr/lib/locale
mkdir -pv /usr/local/{bin,lib,sbin}
mkdir -pv /usr/{,local/}share/{color,dict,doc,info,locale,man}
mkdir -pv /usr/{,local/}share/{misc,terminfo,zoneinfo}
mkdir -pv /usr/{,local/}share/man/man{1..8}
mkdir -pv /var/{cache,local,log,mail,opt,spool}
mkdir -pv /var/lib/{color,misc,locate}
ln -sfv /run /var/run
ln -sfv /run/lock /var/lock
install -dv -m 0750 /root
install -dv -m 1777 /tmp /var/tmp
필수 파일 생성
# /etc/passwd 초기 설정
cat > /etc/passwd << "EOF"
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/dev/null:/usr/bin/false
daemon:x:6:6:Daemon User:/dev/null:/usr/bin/false
messagebus:x:18:18:D-Bus Message Daemon User:/run/dbus:/usr/bin/false
systemd-journal-gateway:x:73:73:systemd Journal Gateway:/:/usr/bin/false
systemd-journal-remote:x:74:74:systemd Journal Remote:/:/usr/bin/false
systemd-journal-upload:x:75:75:systemd Journal Upload:/:/usr/bin/false
systemd-network:x:76:76:systemd Network Management:/:/usr/bin/false
systemd-resolve:x:77:77:systemd Resolver:/:/usr/bin/false
systemd-timesync:x:78:78:systemd Time Synchronization:/:/usr/bin/false
systemd-coredump:x:79:79:systemd Core Dumper:/:/usr/bin/false
uuidd:x:80:80:UUID Generation Daemon:/dev/null:/usr/bin/false
systemd-oom:x:81:81:systemd Out Of Memory Daemon:/:/usr/bin/false
nobody:x:65534:65534:Unprivileged User:/dev/null:/usr/bin/false
EOF
# /etc/group 초기 설정
cat > /etc/group << "EOF"
root:x:0:
bin:x:1:daemon
sys:x:2:
kmem:x:3:
tape:x:4:
tty:x:5:
daemon:x:6:
floppy:x:7:
disk:x:8:
lp:x:9:
dialout:x:10:
audio:x:11:
video:x:12:
utmp:x:13:
cdrom:x:15:
adm:x:16:
messagebus:x:18:
systemd-journal:x:23:
input:x:24:
mail:x:34:
kvm:x:61:
systemd-journal-gateway:x:73:
systemd-journal-remote:x:74:
systemd-journal-upload:x:75:
systemd-network:x:76:
systemd-resolve:x:77:
systemd-timesync:x:78:
systemd-coredump:x:79:
uuidd:x:80:
systemd-oom:x:81:
wheel:x:97:
users:x:999:
nogroup:x:65534:
EOF
chroot: failed to run command '/bin/bash': No such file or directory가
나타나면 가상 파일시스템 마운트를 확인하세요. 또한 $LFS/usr/bin/bash가 존재하고
올바른 동적 링커(ld-linux-x86-64.so.2)에 링크되어 있는지 확인하세요.
기본 시스템 소프트웨어
Chroot 환경에 진입한 후, Chapter 8에서는 81개의 패키지를 최종 빌드합니다. 이 단계에서는 Pass 2에서 빌드한 네이티브 GCC를 사용하여 모든 패키지를 다시 빌드합니다.
일반 빌드 패턴
# Chapter 8 표준 빌드 절차
cd /sources
tar -xf <package>-<version>.tar.xz
cd <package>-<version>
# 빌드 디렉토리 (필요한 패키지만)
mkdir build && cd build
# configure, make, test, install
./configure --prefix=/usr [옵션들...]
make
make check # 테스트 스위트 (선택사항이지만 권장)
make install
# 소스 정리
cd /sources
rm -rf <package>-<version>
make check으로 테스트를 실행하는 것은 선택사항이지만 강력히 권장됩니다.
특히 GCC, Glibc, Binutils의 테스트는 반드시 실행하세요. 일부 테스트 실패는 알려진 문제일 수 있으므로
LFS 책의 해당 패키지 섹션에서 예상 실패 목록을 확인하세요.
주요 패키지 빌드 하이라이트
Glibc (최종 빌드)
Chapter 8에서 Glibc를 다시 빌드합니다. 이번에는 네이티브 컴파일러를 사용하며, 로케일, NSS, 타임존 등의 전체 기능이 포함됩니다.
GCC (최종 빌드)
Chapter 8의 GCC는 모든 최적화와 기능이 활성화된 완전한 컴파일러입니다. 테스트 스위트를 반드시 실행하세요:
# GCC 테스트 스위트 실행
make -k check
# 일부 실패는 알려진 문제 — LFS 책 참조
# 라이브러리와 바이너리에서 디버그 심볼 제거
save_usrlib="ld-linux-x86-64.so.2 libc.so.6 libthread_db.so.1 libquadmath.so.0
libstdc++.so.6 libitm.so.1 libatomic.so.1"
cd /usr/lib
for LIB in $save_usrlib; do
objcopy --only-keep-debug --compress-debug-sections=zlib $LIB $LIB.dbg
cp $LIB /tmp/$LIB
strip --strip-unneeded /tmp/$LIB
objcopy --add-gnu-debuglink=$LIB.dbg /tmp/$LIB
install -vm755 /tmp/$LIB /usr/lib
rm /tmp/$LIB
done
# 나머지 라이브러리
find /usr/lib -type f -name \*.a \
-exec strip --strip-debug {} ';'
find /usr/lib -type f -name \*.so* ! -name \*dbg \
-exec strip --strip-unneeded {} ';'
find /usr/{bin,sbin,libexec} -type f \
-exec strip --strip-all {} ';'
패키지 관리 전략
LFS에는 공식 패키지 매니저가 없습니다. 시스템을 유지보수하고 업데이트하려면 자체 패키지 관리 전략이 필요합니다. 패키지 관리는 LFS 빌드 이후 시스템을 장기적으로 유지하는 데 가장 중요한 결정 중 하나입니다.
패키지 관리 전략 비교
| 전략 | 방법 | 장점 | 단점 |
|---|---|---|---|
| 별도 디렉토리 설치 | --prefix=/usr/pkg/<name>-<ver>로 설치 후 심볼릭 링크 |
깔끔한 분리, 쉬운 제거 | 복잡한 심볼릭 링크 관리 |
| Symlink 스타일 | Stow/GNU Stow로 심볼릭 링크 자동 관리 | 표준화된 도구 사용 | Stow 의존성 |
| Timestamp 기반 | 설치 전후 파일 타임스탬프 비교 | 구현 간단 | 동시 빌드 불가, 부정확할 수 있음 |
| Install 추적 | LD_PRELOAD로 install() 시스템 콜 가로채기 |
정확한 파일 추적 | 구현 복잡 |
| 패키지 아카이브 | DESTDIR로 가상 설치 후 .tar.gz로 아카이브 |
재현성, 백업, 배포 용이 | 추가 디스크 공간 필요 |
| RPM/dpkg 도입 | LFS 위에 RPM 또는 dpkg 패키지 매니저 설치 | 완전한 패키지 관리 | 설정 복잡, LFS 정신에 반함 |
# DESTDIR로 가상 설치
make DESTDIR=/tmp/pkg-<name>-<ver> install
# 아카이브 생성
cd /tmp/pkg-<name>-<ver>
tar czf /packages/<name>-<ver>.tar.gz .
# 실제 설치
tar xzf /packages/<name>-<ver>.tar.gz -C /
# 제거 시: tar 내용 목록으로 파일 삭제
tar tzf /packages/<name>-<ver>.tar.gz | xargs rm -f
커널 컴파일 및 설치
LFS 시스템의 핵심인 리눅스 커널을 컴파일합니다. 커널 설정은 LFS가 부팅 가능한지 여부를 결정하는 가장 중요한 단계 중 하나입니다.
빌드 절차
cd /sources
tar -xf linux-6.10.5.tar.xz
cd linux-6.10.5
# 소스 클린
make mrproper
# 커널 설정 (ncurses 메뉴)
make menuconfig
# 컴파일 (병렬 빌드)
make
# 모듈 설치
make modules_install
# 커널 이미지 복사
cp -iv arch/x86/boot/bzImage /boot/vmlinuz-6.10.5-lfs-12.4-systemd
# System.map 복사 (디버깅용)
cp -iv System.map /boot/System.map-6.10.5
# 커널 설정 파일 백업
cp -iv .config /boot/config-6.10.5
# 커널 문서 설치 (선택)
install -d /usr/share/doc/linux-6.10.5
cp -r Documentation/* /usr/share/doc/linux-6.10.5
LFS에 필수적인 커널 설정 옵션
| 옵션 | 값 | 설명 |
|---|---|---|
CONFIG_DEVTMPFS | =y | devtmpfs 지원 (필수! udev 전에 /dev 채움) |
CONFIG_DEVTMPFS_MOUNT | =y | devtmpfs를 /dev에 자동 마운트 (LFS 부팅에 필수) |
CONFIG_CGROUPS | =y | cgroups v2 지원 (systemd 필수) |
CONFIG_INOTIFY_USER | =y | inotify 지원 (udev, systemd 필요) |
CONFIG_SIGNALFD | =y | signalfd 지원 (systemd 필요) |
CONFIG_TIMERFD | =y | timerfd 지원 (systemd 필요) |
CONFIG_EPOLL | =y | epoll 지원 (systemd 필요) |
CONFIG_TMPFS | =y | tmpfs 지원 (/tmp, /run) |
CONFIG_TMPFS_POSIX_ACL | =y | tmpfs POSIX ACL (systemd 필요) |
CONFIG_DMIID | =y | DMI 테이블 (systemd-hostnamed 필요) |
CONFIG_EXT4_FS | =y | ext4 파일시스템 (내장, 모듈 아님) |
CONFIG_PARTITION_ADVANCED | =y | 고급 파티션 지원 |
CONFIG_EFI_STUB | =y | EFI 스텁 (UEFI 부팅 시 필요) |
/dev에 디바이스 노드가 생성되지 않아
root 파일시스템 마운트에 실패합니다. 반드시 =y로 내장하세요 (모듈이 아님).
GRUB 부트로더 설정
GRUB(GRand Unified Bootloader)은 시스템 시작 시 커널을 로드하는 부트로더입니다. LFS에서는 GRUB 2를 사용합니다.
GRUB 설치
# BIOS 시스템 (MBR)
grub-install /dev/sdb
# UEFI 시스템
grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=LFS
grub.cfg 설정
cat > /boot/grub/grub.cfg << "EOF"
# /boot/grub/grub.cfg - LFS 12.4-systemd
set default=0
set timeout=5
insmod ext2
set root=(hd0,1)
# 또는 UUID 사용: search --no-floppy --fs-uuid --set=root <UUID>
menuentry "GNU/Linux, Linux 6.10.5-lfs-12.4-systemd" {
linux /boot/vmlinuz-6.10.5-lfs-12.4-systemd root=/dev/sda1 ro
}
menuentry "Firmware Setup" {
fwsetup
}
EOF
root= 파라미터 확인: root=/dev/sda1은 LFS 루트 파티션을 가리켜야 합니다.
잘못된 파티션을 지정하면 kernel panic - not syncing: VFS: Unable to mount root fs 오류가 발생합니다.
UUID를 사용하는 것이 더 안전합니다: root=UUID=<blkid 출력값>
BIOS vs UEFI
| 항목 | BIOS (Legacy) | UEFI |
|---|---|---|
| 파티션 테이블 | MBR | GPT |
| GRUB 설치 위치 | MBR + /boot/grub | EFI System Partition |
| 부팅 파일 | core.img | grubx64.efi |
| 설치 명령 | grub-install /dev/sdb | grub-install --target=x86_64-efi |
| Secure Boot | 지원 안 함 | 선택적 지원 |
systemd 설정
LFS 12.4-systemd는 init 시스템으로 systemd를 사용합니다. systemd는 서비스 관리, 로깅, 네트워크, 타임존, 로케일 등 시스템 전반의 설정을 통합 관리합니다.
Machine ID 생성
# 고유 Machine ID 생성 (첫 부팅 전에 필요)
systemd-machine-id-setup
호스트명 설정
echo "lfs-system" > /etc/hostname
# /etc/hosts 설정
cat > /etc/hosts << "EOF"
127.0.0.1 localhost
127.0.1.1 lfs-system
::1 localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
EOF
로케일 설정
# 사용 가능한 로케일 생성
localedef -i ko_KR -f UTF-8 ko_KR.UTF-8
localedef -i en_US -f UTF-8 en_US.UTF-8
# 시스템 로케일 설정
cat > /etc/locale.conf << "EOF"
LANG=ko_KR.UTF-8
EOF
시간대 설정
# 시간대 설정
ln -sfv /usr/share/zoneinfo/Asia/Seoul /etc/localtime
# 또는 timedatectl 사용 (systemd 부팅 후)
# timedatectl set-timezone Asia/Seoul
네트워크 설정 (systemd-networkd)
# 유선 네트워크 DHCP 설정
cat > /etc/systemd/network/10-eth-dhcp.network << "EOF"
[Match]
Name=en*
[Network]
DHCP=ipv4
[DHCPv4]
UseDomains=true
EOF
# 또는 고정 IP 설정
cat > /etc/systemd/network/10-eth-static.network << "EOF"
[Match]
Name=en*
[Network]
Address=192.168.1.100/24
Gateway=192.168.1.1
DNS=8.8.8.8
DNS=8.8.4.4
EOF
# systemd-networkd 및 systemd-resolved 활성화
systemctl enable systemd-networkd
systemctl enable systemd-resolved
# /etc/resolv.conf 심볼릭 링크
ln -sfv /run/systemd/resolve/resolv.conf /etc/resolv.conf
콘솔 설정
# 키보드 레이아웃 설정
cat > /etc/vconsole.conf << "EOF"
KEYMAP=us
EOF
journald를 사용합니다.
journalctl 명령으로 로그를 확인할 수 있습니다:
journalctl -b # 현재 부팅 로그
journalctl -u sshd # 특정 서비스 로그
journalctl --since today # 오늘 로그
전체 패키지 목록
LFS 12.4-systemd에 포함되는 81개 패키지의 전체 목록입니다. 패키지는 기능별로 분류되어 있습니다.
핵심 시스템 라이브러리
| # | 패키지 | 버전 | 크기 (KB) | 설명 |
|---|---|---|---|---|
| 1 | Glibc | 2.40 | 18,362 | GNU C 라이브러리 |
| 2 | Zlib | 1.3.1 | 1,478 | 압축 라이브러리 |
| 3 | Bzip2 | 1.0.8 | 792 | 블록 정렬 압축 |
| 4 | Xz Utils | 5.6.2 | 1,692 | LZMA 압축 |
| 5 | Zstd | 1.5.6 | 2,251 | Zstandard 압축 |
| 6 | Readline | 8.2.13 | 2,994 | 명령줄 편집 라이브러리 |
| 7 | Ncurses | 6.5 | 6,620 | 터미널 라이브러리 |
| 8 | Libffi | 3.4.6 | 1,340 | Foreign Function Interface |
| 9 | OpenSSL | 3.3.1 | 17,476 | 암호화 라이브러리 |
| 10 | Expat | 2.6.2 | 702 | XML 파서 |
| 11 | Libelf (elfutils) | 0.191 | 9,148 | ELF 라이브러리 |
| 12 | Libpipeline | 1.5.7 | 1,024 | 파이프라인 라이브러리 |
빌드 도구
| # | 패키지 | 버전 | 크기 (KB) | 설명 |
|---|---|---|---|---|
| 13 | Binutils | 2.43.1 | 27,540 | 어셈블러, 링커 |
| 14 | GCC | 14.2.0 | 90,424 | C/C++ 컴파일러 |
| 15 | GMP | 6.3.0 | 2,094 | 다중 정밀도 수학 |
| 16 | MPFR | 4.2.1 | 1,490 | 다중 정밀도 부동소수점 |
| 17 | MPC | 1.3.1 | 756 | 다중 정밀도 복소수 |
| 18 | Make | 4.4.1 | 2,300 | 빌드 자동화 |
| 19 | Autoconf | 2.72 | 2,080 | configure 스크립트 생성기 |
| 20 | Automake | 1.17 | 1,620 | Makefile 생성기 |
| 21 | Libtool | 2.4.7 | 996 | 라이브러리 빌드 도구 |
| 22 | Pkgconf | 2.3.0 | 534 | 컴파일러 플래그 관리 |
| 23 | M4 | 1.4.19 | 1,617 | 매크로 프로세서 |
| 24 | Bison | 3.8.2 | 2,752 | 파서 생성기 |
| 25 | Flex | 2.6.4 | 1,386 | 렉서 생성기 |
핵심 유틸리티
| # | 패키지 | 버전 | 크기 (KB) | 설명 |
|---|---|---|---|---|
| 26 | Coreutils | 9.5 | 5,724 | ls, cp, mv 등 기본 유틸리티 |
| 27 | Bash | 5.2.32 | 10,704 | Bourne-Again Shell |
| 28 | Grep | 3.11 | 1,664 | 패턴 검색 |
| 29 | Sed | 4.9 | 1,370 | 스트림 편집기 |
| 30 | Gawk | 5.3.0 | 3,356 | 텍스트 처리 |
| 31 | Findutils | 4.10.0 | 1,940 | 파일 검색 |
| 32 | Diffutils | 3.10 | 1,584 | 파일 비교 |
| 33 | Patch | 2.7.6 | 766 | 패치 적용 |
| 34 | Tar | 1.35 | 2,192 | 아카이브 |
| 35 | Gzip | 1.13 | 757 | 압축 |
| 36 | File | 5.45 | 1,218 | 파일 타입 판별 |
시스템 관리
| # | 패키지 | 버전 | 크기 (KB) | 설명 |
|---|---|---|---|---|
| 37 | Util-linux | 2.40.2 | 8,582 | 시스템 유틸리티 (mount, fdisk 등) |
| 38 | Systemd | 256.4 | 14,228 | init 시스템, 서비스 관리 |
| 39 | D-Bus | 1.14.10 | 6,640 | IPC 메시지 버스 |
| 40 | Procps-ng | 4.0.4 | 4,820 | 프로세스 유틸리티 (ps, top) |
| 41 | Psmisc | 23.7 | 412 | pstree, fuser, killall |
| 42 | E2fsprogs | 1.47.1 | 9,444 | ext2/3/4 파일시스템 도구 |
| 43 | Shadow | 4.16.0 | 1,774 | 패스워드, 사용자 관리 |
| 44 | Kmod | 33 | 600 | 커널 모듈 관리 (modprobe) |
| 45 | Kbd | 2.6.4 | 1,764 | 키보드 유틸리티 |
| 46 | Sysvinit | 3.10 | 236 | init 호환 (사용 안 함, systemd 대체) |
| 47 | Eudev | - | - | systemd-udevd로 대체 |
스크립트 언어 및 텍스트 처리
| # | 패키지 | 버전 | 크기 (KB) | 설명 |
|---|---|---|---|---|
| 48 | Perl | 5.40.0 | 13,500 | 스크립트 언어 |
| 49 | Python 3 | 3.12.5 | 20,160 | 스크립트 언어 |
| 50 | XML::Parser (Perl) | 2.47 | 240 | Perl XML 파서 모듈 |
| 51 | Intltool | 0.51.0 | 159 | 국제화 도구 |
| 52 | Gettext | 0.22.5 | 9,860 | 다국어 지원 |
| 53 | Texinfo | 7.1 | 5,424 | 문서 시스템 |
| 54 | Groff | 1.23.0 | 7,268 | 텍스트 포맷터 (man 페이지) |
| 55 | Man-DB | 2.12.1 | 1,914 | man 페이지 관리 |
| 56 | Man-pages | 6.9.1 | 2,156 | 리눅스 man 페이지 |
네트워킹 및 보안
| # | 패키지 | 버전 | 크기 (KB) | 설명 |
|---|---|---|---|---|
| 57 | Iproute2 | 6.10.0 | 892 | 네트워크 설정 (ip, ss) |
| 58 | IPtables | 1.8.10 | 709 | 방화벽 |
| 59 | Libcap | 2.70 | 181 | POSIX capabilities |
| 60 | Acl | 2.3.2 | 356 | 접근 제어 리스트 |
| 61 | Attr | 2.5.2 | 456 | 확장 속성 |
기타 핵심 패키지
| # | 패키지 | 버전 | 크기 (KB) | 설명 |
|---|---|---|---|---|
| 62 | Linux Kernel | 6.10.5 | 141,820 | 리눅스 커널 |
| 63 | GRUB | 2.12 | 6,524 | 부트로더 |
| 64 | Less | 661 | 380 | 텍스트 뷰어 |
| 65 | Vim | 9.1.0660 | 16,752 | 텍스트 에디터 |
| 66 | MarkupSafe | 2.1.5 | 32 | Python HTML 이스케이프 |
| 67 | Jinja2 | 3.1.4 | 260 | Python 템플릿 엔진 |
| 68 | Wheel | 0.44.0 | 100 | Python 패키지 포맷 |
| 69 | Setuptools | 72.2.0 | 2,684 | Python 빌드 시스템 |
| 70 | Meson | 1.5.1 | 2,268 | 빌드 시스템 |
| 71 | Ninja | 1.12.1 | 228 | 빌드 러너 |
| 72 | Inetutils | 2.5 | 1,632 | 네트워크 유틸리티 (hostname, ping) |
| 73 | GDBM | 1.24 | 1,168 | 데이터베이스 라이브러리 |
| 74 | Gperf | 3.1 | 1,060 | 완벽 해시 함수 생성기 |
| 75 | Bc | 6.7.6 | 460 | 계산기 |
| 76 | Check | 0.15.2 | 760 | C 유닛 테스트 |
| 77 | LFS-Bootscripts | - | - | SysV 전용 (systemd 대체) |
| 78 | Linux-PAM | - | - | BLFS에서 설치 |
| 79 | Libxcrypt | 4.4.36 | 610 | crypt 라이브러리 |
| 80 | Flit-core | 3.9.0 | 64 | Python 빌드 백엔드 |
| 81 | Mpc (lib) | 1.3.1 | 756 | 복소수 연산 |
Cross Build 실행 계획 (세분화)
아래 계획은 https://www.linuxfromscratch.org/lfs/view/stable-systemd/의 Chapter 5~7 구조를 그대로 따르면서, 실제 작업 시 실패 지점을 조기에 차단하도록 검증 단계를 촘촘히 추가한 확장형 런북입니다. 특히 Cross build 단계(Chapter 5, 6)에 집중해 옵션 누락·호스트 오염·링커 경로 오류를 빠르게 식별하도록 구성했습니다.
| Phase | 대상 장 | 핵심 목표 | 완료 조건 | 실패 시 즉시 조치 |
|---|---|---|---|---|
| 0 | Ch.2~4 | 호스트/환경 정합성 확보 | version-check.sh 통과, /bin/sh -> bash | 호스트 패키지/심볼릭 링크 재정렬 |
| 1 | Ch.5 | Cross Toolchain 구축 | Binutils/GCC/Glibc/Libstdc++ 빌드 + sanity check 통과 | 문제 패키지부터 Ch.5 재시작 |
| 2 | Ch.6 | 임시 도구 Cross Compile | 모든 임시 패키지 --host=$LFS_TGT 빌드 성공 | 환경 변수/DESTDIR/PATH 재검증 |
| 3 | Ch.6 | Binutils/GCC Pass 2 | 네이티브 진입용 컴파일러 완료 | --with-build-sysroot, LDFLAGS_FOR_TARGET 재점검 |
| 4 | Ch.7 | Chroot 전환 | /tools/bin 미사용 상태로 셸 진입 | 가상 파일시스템 재마운트 후 재진입 |
| 5 | Ch.8+ | 최종 시스템 빌드 | 패키지 테스트/설치 및 부팅 성공 | 실패 패키지 재빌드 + 로그 비교 |
stable-systemd 버전 동기화 포인트
LFS stable-systemd 인덱스(Version 12.4-systemd) 기준으로 Cross build에 직접 영향을 주는 주요 버전은 다음과 같습니다.
| 컴포넌트 | stable-systemd 기준 | Cross build 영향 |
|---|---|---|
| Binutils Pass 1/2 | 2.45 | 링커 기본 동작 및 DTAGS/hash 스타일 |
| GCC Pass 1/2 | 15.2.0 | libgcc, libstdc++, PIE/SSP 기본값 |
| Linux API Headers | 6.16.1 | glibc가 참조하는 커널 UAPI 인터페이스 |
| Glibc | 2.42 | 동적 로더 경로, CRT 파일, libc ABI |
| systemd | 257.8 | 최종 사용자 공간 init 동작(Ch.8 이후) |
configure 옵션은 유지하고
버전 문자열/경로만 우선 치환한 뒤 테스트를 재실행하세요.
Cross Build 사전 점검 체크리스트
Chapter 5 시작 전에 반드시 아래 항목을 고정합니다. 한 항목이라도 흔들리면 원인 추적 시간이 급격히 증가합니다.
- 사용자 격리: Ch.5~6은 반드시
lfs사용자로 실행 (root 금지) - 환경 초기화:
exec env -i ... /bin/bash기반 깨끗한 셸 사용 - 핵심 변수:
LFS,LFS_TGT,PATH=$LFS/tools/bin:...,LC_ALL=POSIX - 소스 무결성: 패키지/패치 해시 점검 후 시작
- 디스크 여유: 빌드 로그/테스트까지 고려해 충분한 공간 확보
# Cross build 시작 전 최소 확인 스크립트
echo "LFS=$LFS"
echo "LFS_TGT=$LFS_TGT"
echo "$PATH" | tr ':' '\n' | head -n 3
id
pwd
mount | grep "$LFS"
# 도구 해상도 확인
which $LFS_TGT-gcc
which $LFS_TGT-ld
# 오염 가능 변수 제거 여부 확인
env | grep -E '^(CC|CXX|LD|AR|AS|CFLAGS|CXXFLAGS|LDFLAGS|PKG_CONFIG_PATH)='
Chapter 5 Cross Toolchain 런북 (강화판)
stable-systemd Chapter 5의 핵심은 "가짜 cross-compilation처럼 보이지만 실제 cross-toolchain 원칙을 그대로 적용"하는 데 있습니다.
도구는 $LFS/tools에 분리하고, 라이브러리는 최종 위치로 배치하여 Chroot 전환을 준비합니다.
1) Binutils Pass 1 (기본 골격)
mkdir -v build
cd build
../configure --prefix=$LFS/tools \
--with-sysroot=$LFS \
--target=$LFS_TGT \
--disable-nls \
--enable-gprofng=no \
--disable-werror \
--enable-new-dtags \
--enable-default-hash-style=gnu
make
make install
2) GCC Pass 1 (glibc 없는 최소 C/C++)
mkdir -v build
cd build
../configure \
--target=$LFS_TGT \
--prefix=$LFS/tools \
--with-glibc-version=2.42 \
--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++
make
make install
3) Linux API Headers + Glibc + Libstdc++
이 구간은 의존성 순환을 끊는 핵심입니다. Glibc build 시 --host/--build 조합과
libc_cv_slibdir=/usr/lib 누락 여부를 반드시 확인해야 합니다.
# Glibc build 디렉토리에서
echo "rootsbindir=/usr/sbin" > configparms
../configure \
--prefix=/usr \
--host=$LFS_TGT \
--build=$(../scripts/config.guess) \
--disable-nscd \
libc_cv_slibdir=/usr/lib \
--enable-kernel=5.4
# Libstdc++ (GCC 소스 내부 libstdc++-v3)
../libstdc++-v3/configure \
--host=$LFS_TGT \
--build=$(../config.guess) \
--prefix=/usr \
--disable-multilib \
--disable-nls \
--disable-libstdcxx-pch \
--with-gxx-include-dir=/tools/$LFS_TGT/include/c++/15.2.0
make
make DESTDIR=$LFS install
4) Chapter 5 종료 전 sanity check (필수)
echo 'int main(){}' | $LFS_TGT-gcc -x c - -v -Wl,--verbose &> dummy.log
readelf -l a.out | grep ': /lib'
grep -E -o "$LFS/lib.*/S?crt[1in].*succeeded" dummy.log
grep -B3 "^ $LFS/usr/include" dummy.log
grep 'SEARCH.*/usr/lib' dummy.log | sed 's|; |\n|g'
grep "/lib.*/libc.so.6 " dummy.log
grep found dummy.log
| 검증 항목 | 정상 상태 | 비정상 징후 | 조치 |
|---|---|---|---|
| ELF 인터프리터 | /lib64/ld-linux-*.so.* 형태 | /mnt/lfs 경로가 직접 노출 | Glibc/링커 설정 재검토 |
| CRT 파일 | $LFS/lib ... Scrt1.o/crti.o/crtn.o succeeded | host 경로 우선 탐색 | sysroot/headers 경로 재설정 |
| 헤더 검색 | $LFS/tools/.../include + $LFS/usr/include | /usr/include 단독 우선 | PATH/LFS_TGT 점검 |
| libc 해석 | attempt to open $LFS/usr/lib/libc.so.6 succeeded | host libc 선택 | Glibc install/ld 설정 재실행 |
Chapter 6 임시 도구 Cross Compile 런북
Chapter 6은 "Cross toolchain으로 기본 유틸리티를 최종 위치에 설치"하는 단계입니다. 아직 host 독립은 아니며(책 원문 표현 그대로), Chroot 전에 필요한 사용자 공간 기반을 만듭니다.
핵심 패턴
- 대부분 패키지 공통:
--host=$LFS_TGT --build=$(config.guess) - 설치 루트:
make DESTDIR=$LFS install - 교차 빌드 악성 아카이브 제거:
*.la정리 - Pass 2 전환: Binutils/GCC 재빌드로 Chroot 진입용 컴파일러 완성
Binutils Pass 2 (핵심 차이점)
../configure \
--prefix=/usr \
--build=$(../config.guess) \
--host=$LFS_TGT \
--disable-nls \
--enable-shared \
--enable-gprofng=no \
--disable-werror \
--enable-64-bit-bfd \
--enable-new-dtags \
--enable-default-hash-style=gnu
make
make DESTDIR=$LFS install
rm -v $LFS/usr/lib/lib{bfd,ctf,ctf-nobfd,opcodes,sframe}.{a,la}
GCC Pass 2 (실패 빈도 높은 옵션)
../configure \
--build=$(../config.guess) \
--host=$LFS_TGT \
--target=$LFS_TGT \
--prefix=/usr \
--with-build-sysroot=$LFS \
--enable-default-pie \
--enable-default-ssp \
--disable-nls \
--disable-multilib \
--disable-libatomic \
--disable-libgomp \
--disable-libquadmath \
--disable-libsanitizer \
--disable-libssp \
--disable-libvtv \
--enable-languages=c,c++ \
LDFLAGS_FOR_TARGET=-L$PWD/$LFS_TGT/libgcc
--with-build-sysroot=$LFS 또는
LDFLAGS_FOR_TARGET=-L$PWD/$LFS_TGT/libgcc 누락 시, Pass 2에서 C++ 예외 처리/링크 오류가 자주 발생합니다.
Chapter 7 전환: Chroot 진입 품질 게이트
Chroot 진입은 단순 명령 1줄이 아니라 "Cross build 종료 선언"입니다.
진입 후 /tools/bin이 PATH에서 사라져야 하며, 이후는 네이티브 빌드 단계로 이동합니다.
# 가상 파일시스템 마운트 (대표 예시)
mount -v --bind /dev $LFS/dev
mount -vt devpts devpts -o gid=5,mode=0620 $LFS/dev/pts
mount -vt proc proc $LFS/proc
mount -vt sysfs sysfs $LFS/sys
mount -vt tmpfs tmpfs $LFS/run
# stable-systemd Chapter 7 진입 명령
chroot "$LFS" /usr/bin/env -i \
HOME=/root \
TERM="$TERM" \
PS1='(lfs chroot) \u:\w\$ ' \
PATH=/usr/bin:/usr/sbin \
MAKEFLAGS="-j$(nproc)" \
TESTSUITEFLAGS="-j$(nproc)" \
/bin/bash --login
진입 직후 검증
echo "$PATH"
which gcc
which ld
ls -l /tools || true
test -x /usr/bin/gcc && echo "native gcc ready"
echo 'int main(){}' > t.c && gcc t.c -o t && ./t; echo $?
rm -f t.c t
Cross Build 의사결정 매트릭스
| 증상 | 가장 먼저 볼 것 | 2차 확인 | 복구 범위 |
|---|---|---|---|
cannot find crt1.o | grep -E -o ...crt... dummy.log | $LFS/usr/lib 설치 상태 | Glibc 재설치 후 sanity check 재실행 |
cannot find -lc | grep \"/lib.*/libc.so.6 \" dummy.log | libc.so.6 실제 위치 | Glibc 빌드 단계 재시작 |
| host 헤더 참조 | grep -B3 \"^ $LFS/usr/include\" dummy.log | PATH/LFS_TGT | Pass 1 GCC~Glibc 구간 재빌드 |
| Pass 2 C++ 링크 실패 | --with-build-sysroot 확인 | LDFLAGS_FOR_TARGET 확인 | GCC Pass 2 재빌드 |
| Chroot 진입 후 명령 이상 | /proc,/sys,/dev,/run 마운트 | PATH에 /tools/bin 포함 여부 | 마운트 재구성 후 재진입 |
Cross Build 복구 런북 (빠른 재시작)
오염이 의심되면 부분 수정보다 "안전한 재시작"이 총 시간을 줄입니다.
- 현재 셸 상태 보존: 실패 로그 백업 (
config.log,dummy.log, 빌드 스크립트) - 마운트 정리: Chroot 관련 마운트 역순 해제
- 작업 디렉토리 초기화: 실패 패키지의 build 디렉토리 완전 삭제 후 재추출
- 환경 재확정:
LFS/LFS_TGT/PATH/LC_ALL재검증 - 최소 단위 재실행: 실패 지점의 바로 앞 패키지부터 재실행
# 예시: Chroot 종료 후 안전 언마운트
mountpoint -q $LFS/dev/shm && umount $LFS/dev/shm
umount -v $LFS/dev/pts
umount -v $LFS/{sys,proc,run,dev}
# 실패 패키지 재시작 예시
cd $LFS/sources
rm -rf gcc-15.2.0 build
tar -xf gcc-15.2.0.tar.xz
cd gcc-15.2.0
# 이후 책의 해당 장 커맨드 순서대로 재실행
크로스 빌드 트러블슈팅
LFS 빌드 과정에서 발생하는 흔한 오류와 해결 방법을 정리합니다. 대부분의 문제는 환경 변수 누락, 잘못된 도구 사용, 빌드 순서 위반에서 비롯됩니다.
흔한 에러와 해결 방법
| 증상 | 원인 | 해결 |
|---|---|---|
cannot find -lgcc |
크로스 컴파일러의 libgcc를 찾지 못함 | $LFS/tools/lib/gcc/$LFS_TGT/14.2.0/에 libgcc.a 존재 확인 |
undefined reference to __stack_chk_fail |
SSP(Stack Smashing Protector) 라이브러리 미설치 | Glibc 빌드가 성공했는지 확인. $LFS/usr/lib/libc.so 존재 확인 |
wrong ELF class: ELFCLASS64 |
32비트/64비트 라이브러리 혼합 | --disable-multilib 확인. 호스트의 32비트 라이브러리가 섞인 것 |
error: C compiler cannot create executables |
컴파일러 또는 링커 경로 문제 | $PATH에 $LFS/tools/bin이 포함되었는지 확인 |
configure: error: cannot run C compiled programs |
크로스 컴파일된 바이너리를 호스트에서 실행 시도 | --host와 --build 옵션이 올바른지 확인 |
| Sanity check: 잘못된 동적 링커 경로 | sysroot 설정 오류 | Binutils와 GCC의 --with-sysroot=$LFS 확인 |
No such file or directory (chroot 진입 시) |
가상 파일시스템 미마운트 또는 bash 미설치 | /proc, /sys, /dev 마운트 확인. $LFS/usr/bin/bash 확인 |
| 프로그램이 호스트의 libc에 링크됨 | --host=$LFS_TGT 누락 |
configure에 --host=$LFS_TGT 옵션 추가 |
error: 'SIGSTKSZ' was not declared |
Glibc 2.34+ 변경사항 (SIGSTKSZ가 더 이상 상수가 아님) | 해당 패키지의 LFS 패치 적용 |
| make 병렬 빌드 실패 | 의존성 순서가 보장되지 않는 Makefile | make -j1로 단일 스레드 빌드 시도 |
진단 명령어
# 1. 바이너리의 타겟 아키텍처 확인
file /path/to/binary
# 기대: ELF 64-bit LSB executable, x86-64, ...
# 2. ELF 헤더 상세 확인 (아키텍처, OS/ABI)
readelf -h /path/to/binary
# Machine: Advanced Micro Devices X86-64
# OS/ABI: UNIX - GNU
# 3. 동적 링커 및 공유 라이브러리 확인
readelf -l /path/to/binary | grep interpreter
# [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
# 4. 공유 라이브러리 의존성 확인
readelf -d /path/to/binary | grep NEEDED
# 또는 (Chroot 안에서)
ldd /path/to/binary
# 5. 크로스 컴파일러의 검색 경로 확인
$LFS_TGT-gcc -print-search-dirs
$LFS_TGT-gcc -print-sysroot
# 6. 특정 라이브러리가 어디에 있는지 확인
$LFS_TGT-gcc -print-file-name=libc.so
$LFS_TGT-gcc -print-file-name=crt1.o
# 7. 헤더 파일 검색 경로
$LFS_TGT-gcc -E -Wp,-v -xc /dev/null 2>&1 | grep '^ '
# 8. RPATH 확인
readelf -d /path/to/binary | grep -E 'RPATH|RUNPATH'
/usr/lib 라이브러리를 사용하지 마세요!
LFS 바이너리가 호스트의 라이브러리에 링크되면, 호스트 없이는 LFS 시스템이 동작하지 않습니다.
항상 readelf -l로 동적 링커 경로를 확인하고,
readelf -d로 NEEDED 라이브러리가 $LFS 안에 있는지 검증하세요.
빌드 환경 완전 초기화
# 심각한 문제가 발생했을 때 처음부터 다시 시작
# 주의: 모든 빌드 결과가 삭제됩니다!
# 1. Chroot 해제 (안에 있다면)
exit
# 2. 가상 파일시스템 언마운트
mountpoint -q $LFS/dev/shm && umount $LFS/dev/shm
umount $LFS/dev/pts
umount $LFS/{sys,proc,run,dev}
# 3. LFS 파티션 내용 삭제 (주의!)
rm -rf $LFS/*
# 4. 필수 디렉토리 재생성
mkdir -pv $LFS/{etc,var} $LFS/usr/{bin,lib,sbin}
for i in bin lib sbin; do ln -sv usr/$i $LFS/$i; done
case $(uname -m) in x86_64) mkdir -pv $LFS/lib64 ;; esac
mkdir -pv $LFS/tools
mkdir -v $LFS/sources
chmod -v a+wt $LFS/sources
# 5. Pass 1부터 다시 시작
BLFS 확장 가이드
LFS로 기본 시스템을 완성한 후, BLFS(Beyond Linux From Scratch)를 통해 실용적인 데스크톱/서버 시스템으로 확장할 수 있습니다. BLFS는 LFS 위에 그래픽, 네트워크, 멀티미디어 등을 추가하는 가이드입니다.
BLFS 필수 패키지 (서버용)
| 카테고리 | 패키지 | 용도 |
|---|---|---|
| 보안 | OpenSSH, sudo, Linux-PAM | 원격 접속, 권한 관리 |
| 네트워크 | Wget, cURL, rsync | 파일 전송, 동기화 |
| 암호화 | GnuTLS, NSS, p11-kit | TLS/SSL, 인증서 관리 |
| 편의 | Git, tmux, htop | 버전 관리, 터미널 분할, 모니터링 |
BLFS 필수 패키지 (데스크톱용)
| 카테고리 | 패키지 | 용도 |
|---|---|---|
| 그래픽 기반 | Xorg, Mesa, libinput | 디스플레이 서버, 3D 가속, 입력 장치 |
| 툴킷 | GTK+4, Qt6, Cairo | GUI 애플리케이션 프레임워크 |
| 폰트 | FreeType, Fontconfig, HarfBuzz | 글꼴 렌더링, 관리 |
| 데스크톱 | GNOME, KDE, Xfce 중 택 1 | 데스크톱 환경 |
| 멀티미디어 | ALSA, PulseAudio/PipeWire, FFmpeg | 오디오, 비디오 |
| 브라우저 | Firefox, Chromium | 웹 브라우저 (빌드에 매우 오래 걸림) |
보안 강화 (Hardened LFS)
LFS의 큰 장점 중 하나는 보안 설정을 완전히 제어할 수 있다는 것입니다. 아래는 LFS 시스템의 보안을 강화하는 주요 기법들입니다.
컴파일러 수준 보안
| 기법 | GCC 옵션 | 설명 | LFS 기본값 |
|---|---|---|---|
| PIE | -fPIE -pie | Position Independent Executable — ASLR 활성화 | --enable-default-pie (활성) |
| SSP | -fstack-protector-strong | 스택 버퍼 오버플로우 탐지 (canary) | --enable-default-ssp (활성) |
| FORTIFY | -D_FORTIFY_SOURCE=2 | 버퍼 오버플로우 런타임 검사 | 수동 설정 필요 |
| RELRO | -Wl,-z,relro,-z,now | GOT 테이블 보호 (Full RELRO) | 수동 설정 필요 |
| NX | (기본 활성) | 실행 불가능 스택 (No eXecute) | 커널 + GCC 기본 활성 |
/etc/profile이나 별도의 환경 설정에 추가합니다:
# /etc/profile.d/hardening.sh
export CFLAGS="-O2 -pipe -D_FORTIFY_SOURCE=2 -fstack-protector-strong"
export CXXFLAGS="$CFLAGS"
export LDFLAGS="-Wl,-z,relro,-z,now"
커널 수준 보안
| 설정 | 옵션 | 설명 |
|---|---|---|
| ASLR | kernel.randomize_va_space=2 | 주소 공간 배치 랜덤화 (Full ASLR) |
| dmesg 제한 | kernel.dmesg_restrict=1 | 비특권 사용자의 커널 로그 접근 제한 |
| kptr 제한 | kernel.kptr_restrict=2 | 커널 포인터 주소 노출 방지 |
| ptrace 제한 | kernel.yama.ptrace_scope=1 | 자식 프로세스만 ptrace 허용 |
| SYN 쿠키 | net.ipv4.tcp_syncookies=1 | SYN Flood 공격 방지 |
# /etc/sysctl.d/99-hardening.conf
kernel.randomize_va_space = 2
kernel.dmesg_restrict = 1
kernel.kptr_restrict = 2
kernel.yama.ptrace_scope = 1
net.ipv4.tcp_syncookies = 1
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.all.accept_redirects = 0
net.ipv6.conf.all.accept_redirects = 0
흔한 실수와 해결
LFS 빌드 과정에서 자주 발생하는 실수와 해결 방법을 정리합니다. 대부분의 실패는 사소한 실수에서 비롯되며, 아래 체크리스트로 사전에 방지할 수 있습니다.
자주 묻는 질문 (FAQ)
| 질문 | 답변 |
|---|---|
| LFS를 빌드하는 데 얼마나 걸리나요? | 최신 8코어 시스템에서 약 6~12시간. 옛날 시스템이면 2~3일. -j$(nproc) 병렬 빌드가 핵심입니다. |
| 빌드 중 전원이 꺼지면? | $LFS 마운트 상태 확인 후 마지막으로 성공한 패키지 다음부터 재개하면 됩니다. VM 스냅샷이 가장 안전합니다. |
| 64비트와 32비트를 동시에 지원하려면? | Multilib LFS를 참조하세요. --enable-multilib으로 GCC를 빌드하고, 32비트 Glibc도 별도 빌드합니다. |
| LFS 시스템에서 패키지를 업데이트하려면? | 새 버전의 소스를 다운로드하고 같은 절차로 재빌드합니다. DESTDIR 아카이브 전략 사용 시 이전 버전 롤백도 가능합니다. |
| ARM이나 RISC-V에서 LFS를 빌드할 수 있나요? | CLFS(Cross LFS) 프로젝트를 참조하세요. 실제 크로스 컴파일로 다른 아키텍처용 시스템을 빌드합니다. |
| LFS는 실무에서 사용할 수 있나요? | 교육/학습 목적이 주이지만, 임베디드 시스템이나 특수 용도 어플라이언스에서 실제 사용되기도 합니다. 보안 업데이트를 직접 관리해야 하므로 프로덕션 서버에는 권장하지 않습니다. |
요약 및 참고 자료
핵심 개념 요약
- 크로스 컴파일의 목적: 호스트 라이브러리 오염을 완전히 차단하여 독립적인 시스템 구축
- 트리플렛 변경:
x86_64-lfs-linux-gnu로 호스트와 분리하여 크로스 빌드 모드 강제 - 의존성 순환 해결: GCC Pass 1(
--with-newlib) → Glibc → Libstdc++ 순서로 단계적 빌드 - Pass 1 vs Pass 2: Pass 1은 호스트에서 실행되는 크로스 도구, Pass 2는 LFS에서 실행되는 네이티브 도구
- Sysroot:
$LFS를 크로스 컴파일러의 루트로 사용하여 헤더와 라이브러리를 격리 - Chroot: 루트 디렉토리를 변경하여 LFS 시스템을 독립 환경으로 격리
공식 문서 및 프로젝트
- LFS 공식 사이트: https://www.linuxfromscratch.org/ — 프로젝트 메인 페이지
- LFS 12.2-systemd (stable): https://www.linuxfromscratch.org/lfs/view/stable-systemd/ — 현재 안정 버전 전체 책
- LFS 12.2-sysvinit (stable): https://www.linuxfromscratch.org/lfs/view/stable/ — SysVinit 에디션
- LFS development: https://www.linuxfromscratch.org/lfs/view/development/ — 개발 중인 최신 버전
- BLFS (Beyond LFS): https://www.linuxfromscratch.org/blfs/view/stable-systemd/ — X Window, 데스크톱, 서버 확장
- CLFS (Cross LFS): https://trac.clfs.org/ — ARM, MIPS 등 다른 아키텍처용 크로스 빌드
- ALFS (Automated LFS): https://www.linuxfromscratch.org/alfs/ — 자동화 빌드 도구 (jhalfs)
- LFS Hints: https://www.linuxfromscratch.org/hints/ — 커뮤니티 팁, 패키지 관리 등
커뮤니티 및 지원
- LFS 메일링 리스트: https://www.linuxfromscratch.org/mail.html — lfs-support, lfs-dev 등
- LFS FAQ: https://www.linuxfromscratch.org/faq/ — 자주 묻는 질문 공식 답변
- LFS Trac (버그 트래커): https://wiki.linuxfromscratch.org/lfs/ — 빌드 오류 보고 및 패치
- Reddit r/linuxfromscratch: https://www.reddit.com/r/linuxfromscratch/ — 커뮤니티 토론
- LFS IRC:
#lfs(Libera.Chat) — 실시간 도움
관련 기술 문서
- GCC 크로스 컴파일 공식 문서: https://gcc.gnu.org/onlinedocs/gccint/Configure-Terms.html — build/host/target 정의
- GNU Autoconf 매뉴얼: https://www.gnu.org/software/autoconf/manual/ — configure 스크립트 이해
- GNU Binutils 문서: https://sourceware.org/binutils/docs/ — as, ld, readelf 등
- Glibc 매뉴얼: https://sourceware.org/glibc/manual/ — C 라이브러리 함수, 시스템 콜 래퍼
- Linux 커널 문서: https://docs.kernel.org/ — 커널 설정 옵션, 드라이버 개발
- FHS (Filesystem Hierarchy Standard): https://refspecs.linuxfoundation.org/FHS_3.0/fhs-3.0.html — 디렉토리 구조 표준
- Systemd 문서: https://systemd.io/ — systemd 공식 문서, unit 파일 작성법
- GRUB 매뉴얼: https://www.gnu.org/software/grub/manual/grub/ — 부트로더 설정
참고 서적
| 서적 | 저자 | 설명 |
|---|---|---|
| Linux From Scratch | Gerard Beekmans 외 | LFS 공식 책 (무료 온라인) |
| Advanced Linux Programming | Mark Mitchell 외 | 리눅스 시스템 프로그래밍 기초 |
| Understanding the Linux Kernel | Daniel P. Bovet, Marco Cesati | 커널 내부 구조 심화 |
| Linux System Programming | Robert Love | 시스템 콜, 파일 I/O, 프로세스 |
| The Linux Command Line | William Shotts | CLI 기초 (무료 온라인), LFS 사전 학습에 적합 |
| Embedded Linux Primer | Christopher Hallinan | 임베디드 리눅스 구축 (LFS 확장 응용) |
| How Linux Works | Brian Ward | 리눅스 시스템 동작 원리 개론 |
유용한 도구 및 스크립트
| 도구 | 설명 | URL |
|---|---|---|
| jhalfs | LFS 빌드 자동화 스크립트 (ALFS 프로젝트) | linuxfromscratch.org/alfs/ |
| version-check.sh | 호스트 요구사항 검증 스크립트 (LFS 책에 포함) | LFS Book Chapter 2 |
| copy-kernel-config | 호스트 커널 설정을 기반으로 LFS 커널 설정 생성 | zcat /proc/config.gz > .config |
| porg | 소스 빌드 패키지 추적 도구 (LD_PRELOAD 기반) | porg.sourceforge.net |
| fakeroot | 비특권 사용자로 패키지 빌드 시 root 소유 파일 생성 시뮬레이션 | BLFS에서 설치 |
관련 문서
이 주제와 관련된 다른 문서를 더 깊이 이해하고 싶다면 다음을 참고하세요.