이 절에서는 전체 빌드 방법에 대한 몇 가지 해석과 기술적 세부 사항을 설명한다. 이 절의 모든 것을 당장 이해할 필요는 없다. 이 정보들의 대부분은 실제 빌드를 하고 나서야 더 명확해질 것이다. 이 절은 진행 중에 언제든지 참고하라.
5장의 전반적인 목표는 앞서 언급한 호스트 시스템으로부터 분리할 수 있는 도구들을 포함하는 임시 공간을 구축하는 것이다. chroot를 사용함으로써, 나머지 장에 있는 명령들이 이 환경에 포함되어 대상 LFS 시스템을 깨끗하고 문제없이 구축할 수 있다. 빌드 절차는 새로운 독자를 위해 위험을 최소화하고 동시에 가장 교육적인 가치들을 제공하도록 설계되었다.
계속 진행하기 전에,
작업 중인 플랫폼의
명칭, 흔히 대상
트리플렛(target triplet)이라
칭하는 것을 알고
있어야 한다. 대상
트리플렛의 명칭을
구하는 간단한
방법은 많은
패키지의 소스와
함께 제공되는 config.guess
스크립트를
실행하는 것이다. Binutils
소스를 압축
해제하고 이
스크립트를
실행하라: ./config.guess
그리고 출력 결과를
기록하라. 예를 들어
32비트 인텔
프로세서의 경우
출력은 i686-pc-linux-gnu가
된다. 64비트
시스템에서는 x86_64-pc-linux-gnu일
것이다.
추가로 흔히 동적
로더(Binutils의 일부인
표준 링커 ld와
혼동하지 말라)라
불리는, 플랫폼의
동적 링커의 이름도
알고 있어야 한다.
Glibc가 제공하는 동적
링커는 프로그램이
필요로 하는 공유
라이브러리를 찾아
불러오고, 실행할
프로그램을 준비한
후 실행한다. 32비트
인텔 시스템의 동적
링커 이름은 ld-linux.so.2
(64비트
시스템의 경우 ld-linux-x86-64.so.2
)일
것이다. 동적 링커의
이름을 알아내는
확실한 방법은
호스트 시스템에서
아무 바이너리나
검사하는 것이다:
readelf -l
<바이너리의 이름> |
grep interpreter
을
실행하고 출력
결과를 기록하라.
모든 플랫폼을
다루는 신뢰할만한
레퍼런스는 Glibc 소스
트리의 최상위에
있는 shlib-versions
파일에 있다.
5장의 빌드 방법 작동 방식에 대한 몇 가지 핵심 기술적 요점:
LFS_TGT
변수를
통해 대상
트리플렛의 "벤더"
항을 변경하여
작업 플랫폼의
이름을 약간
조정하면, Binutils 및 GCC의
첫 번째 빌드가
호환 가능한
크로스 링커와
크로스
컴파일러를
생성한다. 다른
아키텍쳐에 대한
바이너리들을
만드는 대신
크로스 링커와
크로스
컴파일러로 현재
하드웨어와
호환되는
바이너리를
생성할 것이다.
임시 라이브러리들은 크로스 컴파일된다. 크로스 컴파일러는 본질적으로 호스트 시스템의 어떤 것에도 의존할 수 없기 때문에, 이 방법은 호스트의 헤더나 라이브러리가 새 도구에 합쳐질 가능성을 줄임으로써 대상 시스템과 뒤섞일 위험을 제거한다. 또 크로스 컴파일을 하면 32비트 및 64비트 라이브러리를 64비트 하드웨어에 모두 빌드할 수 있다.
GCC 소스를 조심히 조작해서 컴파일러에게 어떤 대상 동적 링커를 사용할지 정할 수 있다.
GCC와 Glibc의 configure를 실행하면 어셈블러와 링커에서 다양한 기능 테스트를 거쳐 어떤 소프트웨어 기능을 활성화할지 결정하기 때문에 (어셈블러와 링커들이 포함된) Binutils를 먼저 설치한다. 이것은 보기보다 중요하다. 잘못 설정된 GCC나 Glibc는 툴체인의 미묘한 고장으로 이어질 수 있으며, 이러면 그 영향이 전체 시스템 빌드가 끝날 때까지 나타나지 않을 수 있다. 보통 테스트 스위트 실패는 이미 너무 많은 작업을 수행한 채로 뒤늦게 알아채기 전에 이 오류를 알려준다.
Binutils는 어셈블러와
링커를 /tools/bin
과 /tools/$LFS_TGT/bin
두
위치에 설치한다. 한
위치의 도구는 다른
위치에 있는
도구로의
하드링크이다.
링커는 라이브러리
검색 순서가
중요하다. 자세한
정보는 ld에 --verbose
플래그를 붙여
실행하면 얻을 수
있다. 예를 들어 ld --verbose | grep SEARCH
는
현재 검색 경로와 그
순서를 보여준다.
더미 프로그램을
컴파일하고 링커에
--verbose
옵션을 전달하면
어떤 파일들이 ld에 의해
링크되었는지 알 수
있다. 예를 들어 gcc dummy.c -Wl,--verbose 2>&1 | grep
succeeded
은 링크 중에
성공적으로 열린
모든 파일들을
보여줄 것이다.
다음으로 설치될 패키지는 GCC이다. configure 실행 중에 볼 수 있는 예시이다:
checking what assembler to use... /tools/i686-lfs-linux-gnu/bin/as
checking what linker to use... /tools/i686-lfs-linux-gnu/bin/ld
이것은 위에 언급한
이유때문에
중요하다. 이는 또한
GCC의 configure 스크립트가
어떤 도구를
사용할지 찾느라 PATH
디렉토리를
검색하지 않는다는
것을 알 수 있다. 단,
gcc
자체의 실제 작동
중에는 반드시
동일한 검색 경로가
사용되는 것은
아니다. gcc가 어떤
표준 링커를
사용할지
알아보려면 gcc -print-prog-name=ld
를
실행하라.
더미 프로그램을
컴파일하면서 gcc에 -v
명령줄
옵션을 전달하면
자세한 정보를 얻을
수 있다. 예를 들어
gcc -v
dummy.c
는 gcc에
포함된 검색 경로 및
순서, 전처리기,
검파일, 어셈블리
단계에 대한 자세한
정보를 표시한다.
다음으로 설치될 것은 순수한 리눅스 API 헤더이다. 이를 통해 표준 C 라이브러리(Glibc)가 리눅스 커널에서 제공할 기능들과 상호 작용할 수 있다.
다음으로 설치될
패키지는 Glibc이다. Glibc
빌드에 있어 가장
중요한 것은
컴파일러, 바이너리
도구, 커널
헤더들이다. Glibc는
항상 configure 스크립트에
전달되는 --host
매개
변수에 관련된
컴파일러를
사용하기 때문에,
컴파일러는 대게
문제가 되지 않는다;
이 책의 컴파일러는
i686-lfs-linux-gnu-gcc이다.
바이너리 도구와
커널 헤더들의
경우는 조금 더
복잡하다. 그러니
위험을 무릅쓰지
말고 올바른 선택을
하려면 사용 가능한
configure 설정을 사용하라.
configure
실행을 마친 뒤, glibc-build
디렉토리에서 config.make
파일을
열어 모든 중요한
세부사항을
확인하라. CC="i686-lfs-gnu-gcc"
로
어떤 바이너리
도구들을 사용할지
지정하고 -nostdinc
와 -isystem
플래그로
컴파일러의 include 탐색
경로를 정한다. 이
항목들로부터 Glibc
패키지의 중요한
점을 알 수 있다. Glibc
패키지는 빌드
환경에 있어서 매우
독립적이고
일반적으로 툴체인
기본값에 의존하지
않는다.
새로 빌드된 Binutils
2단계에서는 --with-lib-path
configure
옵션을 활용하여
ld의
라이브러리 탐색
경로를 지정할 수
있다.
GCC의 재구축을
위해서는 새로운
동적 링커를
사용하도록 소스도
수정해야 한다.
그렇게 하지 않으면
GCC 프로그램 안에
호스트 시스템의
/lib
디렉토리에 있는
동적 링커의 이름이
포함되어
호스트에서
독립하려는 목표를
달성할 수 없다. 이
때부터 핵심
툴체인은
독립적으로 작동할
수 있게 된다. 나머지
5장 패키지는 모두
/tools
의 새로운
Glibc를 기반으로
구축된다.
6장에서 chroot 환경에
들어가면, 위에서
언급한대로
독립적으로 작동할
수 있는 덕에 Glibc
패키지가 가장 먼저
설치된다. 이 Glibc가 /usr
에 설치되고
나면 툴체인
기본값을 신속히
바꾸고 나머지 목표
LFS 시스템 구축을
진행할 것이다.