bzimage가 만들어지는 과정을 살펴 보고 이를 따라가면서 Makefile의 자세한 내용을 알아본다.
정확한 것은 2.4.2절을 참조하기 바란다.
시작은 물론 $(topDIR)/Makefile로 부터 시작한다.
커널 makefile은 몇 부분으로 나눌 수 있다.
기본 정보 정의
커널 설정
커널 소스 의존성 만들기
모듈 만들기
커널 실행 파일 만들기
모듈 설치하기
각 부분이 명확하게 구분되는 것은 아니지만 make의 동작을 이해하는 사람이라면
대충 구분을 지어 이해할 수 있을 것이다. 구분은 커널을 컴파일 하는 절차에 따라 나눈 것으로
이해 하면 쉬울 것이다. 일반적으로 많이 쓰이지 않는 부분은 넘어가고 중요한 부분만을 자세히
이해하자.
시작에 앞서 사용되는 Makefile들을 설명해 놓는다. 이것 들을 참조로 추적해나가므로 시작하기에
앞서 한번 쯤 훑어 보는 것도 좋을 것이다.
보통의 경우 HOST와 TARGET이 같으면 CROSS-COMPILE에 아무 것도 없으나
target이 다르면 여기에 컴파일러의 prefix를 적어줘야한다.
예를 들어 PDA에 많이 사용되는 arm processor를 TARGET으로한 경우엔
cross-COMPILE = arm-linux- 와 같이 된다.
압축된 커널의 크기를 계산해 zImage의 경우 0x7F000 보다 큰지 확인하고, bzImage는
0x280000 보다 큰지 확인한다. 만약 지정된 크기보다 크다면 현재로서는 수용할 수 없는 크기의
커널이므로 에러를 낸다. 또 플로피 부팅의 경우 플로피에 들어갈 수 있는 크기인지 확인한다.
1024 byte 단위로 읽어 표준 출력에 출력하고 bootsect의 497(0x1F1)에 setup이 몇 섹터를
차지하는지 기록하고 500(0x1F4)에 압축 커널의 크기를 16 byte 단위로 기록해준다.
2.3. bzimage가 만들어지는 과정 추적-Makefile 분석
bzimage가 만들어지는 과정을 살펴 보고 이를 따라가면서 Makefile의 자세한 내용을 알아본다. 정확한 것은 2.4.2절을 참조하기 바란다.
시작은 물론 $(topDIR)/Makefile로 부터 시작한다.
커널 makefile은 몇 부분으로 나눌 수 있다.
각 부분이 명확하게 구분되는 것은 아니지만 make의 동작을 이해하는 사람이라면 대충 구분을 지어 이해할 수 있을 것이다. 구분은 커널을 컴파일 하는 절차에 따라 나눈 것으로 이해 하면 쉬울 것이다. 일반적으로 많이 쓰이지 않는 부분은 넘어가고 중요한 부분만을 자세히 이해하자.기본 정보 정의
커널 설정
커널 소스 의존성 만들기
모듈 만들기
커널 실행 파일 만들기
모듈 설치하기
시작에 앞서 사용되는 Makefile들을 설명해 놓는다. 이것 들을 참조로 추적해나가므로 시작하기에 앞서 한번 쯤 훑어 보는 것도 좋을 것이다.
2.3.1. $(topDIR)/Makefile
아래 makefile에 (1), (2)와 같이 표시된 것은 아래 줄에 대한 설명을 달아 놓은 것으로 Makefile의 끝 부분에 붙어 있는 설명을 참조해가면서 분석하면되겠다.
예를 들어 ACPI 기능을 사용하지 않는다고 했을 경우엔 아래 줄이 driverS- += drivers/acpi/acpi.o가 된다. .config의 내용을 한번 읽어보면 금방 이해될 것이다.
patsubst는 $(patsubst PATTERN, REPLACEMENT, TEXT)의 형식으로 TEXT에서 patterN과 일치하는 부분을 REPLACEMENT로 교체한다.
.phony를 사용하면 정의된 이름이 file이 아님을 알려주고 퍼포먼스를 올려준다. 자세한 것은 'info make'를 해서 참조 바란다.
2.3.2. $(topDIR)/arch/i386/Makefile
objcopy는 입력에서 .note, .comment 섹션을 삭제하고 리로케이션 정보와 심볼 정보를 삭제한다. 출력 포맷은 binary. 링크할 땐 $(TOPDIR)/arch/i386/vmlinux.lds란 파일에 기록되어 있는 방법을 따라 링크한다.
사용되는 옵션은 다음과 같은 의미를 갖는다.
즉 스택 바운더리 정렬이 4 바이트로 가능한지 알아봐서 가능하면 4 바이트 정렬을 사용한다. 만약 4 바이트 정렬을 지원하지 않으면 컴파일 중에 에러가 날 것이다. 이땐 기본 값을 사용한다.-mpreferred-stack-boundary=2 : 스택을 22 byte로 정렬하도록 한다. (=4면 24)
-S : 컴파일 스테이지까지만 하고 어셈블은 하지 않는다.
-xc : c 언어로 컴파일 한다.
/dev/null이 $(CC)의 입력으로 지정됐으므로 /dev/null을 읽어 컴파일한다. /dev/null을 읽으면 EOF를 돌려주므로 컴파일된 출력은 다음과 같을 것이지만 바로 /dev/null로 출력되어 화면에는 나타나지 않는다.
컴파일되면 다음과 같은 결과가 나온다.
2.3.3. $(topDIR)/arch/i386/boot/Makefile
zimage는 gzip으로 압축되고 하위 1M 메모리 내에 적재될 수 있는 크기의 커널 [1]
bzimage는 gzip으로 압축되고 하위 1M 메모리 내에 적재될 수 없는 크기의 커널
$(objcOPY)는 $(TOPDIR)/arch/i386/Makefile에서 정의된 것을 따른다. 즉 'objcoPY=$(CROSS-COMPILE)objcopy -O binary -R .note -R .comment -S'가 된다.
-ttext 0x0 : 코드의 시작을 0번지 부터 시작한다고 하고 링크한다. 이렇게 하면 링크된 최종 출력물은 특별한 위치를 가리지 않고 메모리의 아무 위치에나 적재가 가능하고 실행 가능해진다.
-s : 출력물에서 심볼 정보를 모두 없앤다.
--oformat binary : 출력물의 포맷을 바이너리로 한다.
2.3.4. $(topDIR)/arch/i386/boot/compressed/Makefile
$$$$piggy의 4개의 $는 4자리의 임의의 숫자로 채워진다. 즉 tmppiggy=-tmp-1234piggy와 같이된다. 더불어 $$tmppiggy.gz 은 -tmp-1234piggy.gz, tmppiggy.lnk는 -tmp-1234piggy.lnk와 같이 된다.
piggy.o는 head.o, misc.o와 합쳐져 하나의 다른 file로 만들어져야하므로 다시 링커를 통해 elf-i386 포맷으로 만들어진다.
$(objcOPY)에 사용된 옵션은 다음과 같다.
$(ld)에 사용된 옵션은 다음과 같다.-O : output format. 여기선 binary
-R : 지정된 section 이름을 지운다. .note, .comment는 없앤다.
-S : input file을 지정한다.
-m elf-i386 : ld가 elf-i386을 emulation 하도록 지정한다.
-r : relocatable, 메모리에 적재될 때 재배치 가능하도록 한다.
-b binary : input file의 format을 말한다. 여기선 $$tmppiggy.gz은 binary
-T : linker script file을 지정한다.
-b elf32-i386 : output을 elf-i386 format으로 지정한다.
2.3.5. $(topDIR)/arch/i386/boot/tools/build.c
$(topdir)/arch/i386/boot/tools/build는 커널 이미지 만드는 과정의 최종 단계에서 몇 개의 파일을 합쳐 하나의 커널 이미지를 만들어낸다. 이런 일을 담당하는 프로그램을 분석해야 이미 나온 부팅 과정에서의 동작을 이해할 수 있을 것이다.
최종 만들어지는 이미지는 그림 2-2이 된다. 부팅할 때 bootsect는 build에 의해 기록된 루트 디바이스, setup의 크기, 압축 커널의 크기를 바탕을 부팅 절차를 계속 진행한다.
currenT는 /의 major, minor number를 사용한다. 필자의 리눅스 박스는 hda1이 /이므로 major=0x03, minor=0x01이 될것이다.
플로피가 루트 디바이스로 지정됐으면 major=minor=0이 된다.
command line에 아무 것도 지정되지 않으면 기본 값이 사용된다(기본 값은 사실 플로피와 같은 값을 갖는다).
또 508(0x1FC), 509(0x1FD) 번째 바이트에 루트 디바이스의 minor, major 번호를 써 넣는다.
수정 후 표준 출력으로 bootsect의 512 byte를 출력한다(원래 512 byte 였으므로 수정 내용을 포함해 그대로 출력될 것이다).
예를 들어 setup의 크기가 4768 byte라면 4768/512=9.3125 이므로 9 섹터를 차지하고 10번째 섹터는 다 사용하지 않고 조금만 사용하게 된다. 10번째 섹터의 경우 160 byte를 제외한 352 byte 만큼을 0으로 채워 넣는다.
1024 byte 단위로 읽어 표준 출력에 출력하고 bootsect의 497(0x1F1)에 setup이 몇 섹터를 차지하는지 기록하고 500(0x1F4)에 압축 커널의 크기를 16 byte 단위로 기록해준다.
주석
하위 1M에 대한 것은 2.2.2절을 참조한다.
Recent Posts
Archive Posts
Tags