test-val에 저장된 값은 test-data의 .text의 시작점에서 부터의 offset이다.
프로그램의 시작인 0에서 부터 5번째에 있단 소리다.
최종적으로 'ld -m elf-i386 -s --oformat binary test.o -o test.1'한 결과를 hex로
살펴보면 다음과 같다.
00000000 05 80 04 08 90 55 aa 90
0x90은 nop(no operation)을 의미한다.
그리고 'ld -m elf-i386 -Ttext 0x0 -s --oformat binary test.o -o test.2'한 결과는 다음과 같다.
00000000 05 00 00 00 90 55 aa 90
둘 사이의 차이점은 -Ttext 0x0가 있고 없고의 차이다. 바이너리 포맷의 경우 .text를 지정해 주지
않으면 시작 번지를 맘대로 정해버리므로 .text를 지정하지 않은 test.1에서는 시작이 0x09048000으로
설정되어 있는 것을 알 수 있다. 엉뚱한 곳의 값을 사용하도록 만들기 때문에 바이너리를 사용할 땐
제대로된 주소가 들어가도록 .text를 필요한 곳으로 지정해줄 필요가 있다.
또 .text의 시작을 0x02로 했을 때의 바이너리는 다음과 같다.
00000000 90 90 09 00 00 00 90 55 aa 90
위에서 보듯이 0xaa55는 offset이 7이지만 실제 지정된 것은 9로 .text가 2부터 시작하기 때문이다.
만약 이 바이너리를 메모리에 그대로 올려 놓는다면 제대로 된 값을 읽지 못할 수도 있다. 이 경우엔
.text가 2에서 시작하는 것을 염두에 두고 메모리에 적재해야 제대로 동작할 수 있다.
쉽게 하기 위해선 .text를 0에서 시작하게 하면 바이너리가 메모리의 어느 위치에 있던 상관없이
잘 동작할 수 있게 된다.
2.4절에서 살펴본 것과 같은 각 단계마다 자세한 내용을
살펴 본다. 이 절이 끝나면 이제 리눅스 커널이 어떻게 만들어지고 어떤 구조를 갖는지 완전히
알 수 있을 것이다.
1 ~ 12 단계는 vmlinux를 만들기 위한 한계이고 커널 설정을 어떻게 했는가에 따라 달라지므로
여기서는 다루지 않는다. 또 17, 18 단계도 bvmlinux를 만들기 위해 필요한 단계이므로 생략한다.
필요한 내용은 2.4절을 참조하거나
각자 log를 만들어 살펴보기 바란다.
nm은 오브젝트 파일에서 심볼을 추줄해 주는 프로그램이다. 커널 이미지 파일에서 모든 심볼을
추출해내고 이 중에 필요한 부분만을 추려 System.map을 만든다. grep에 사용된 -v는 뒤에
나오는 경우를 제외한 것 들을 찾아준다. 커널 컴파일이 끝난 후 'nm vmlinux > test.map'만
한 결과와 System.map을 비교해 보면 grep에서 찾는 것들이 어떤 것인지 알 수 있을 것이다.
System.map은 처음 부팅 때 메모리에 읽혀 올려지고 드라이버등이 커널 심볼을 찾을 때 사용한다.
bvmlinux는 압축된 커널과 head.o, misc.o를 합쳐 만든다. head.o는 메모리 세팅이라고 보면되고
misc.o는 압축을 풀기 위한 코드가 들어있다. ld에 사용된 옵션은 $(TOPDIR)/vmlinux를 만들 때와 거의
흡사하다. 단 text의 시작 번지는 0x100000이다.
부팅할 때 bvmlinux는 반드시 0x100000에 올려져서 실행되야한다. 그렇지 않으면 제대로 동작하지 않는다.
압축된 커널의 크기를 piggy.o에 저장해 놓았기 때문에 메모리의 어느 위치에서 piggy.o가 끝나는지
알수 있다. 이뒤에 압축을 풀어 놓고 압축이 풀린 커널을 다시 0x100000으로 옮겨와 실행한다.
tools/build -b bbootsect bsetup compressed/bvmlinux.out CURRENT > bzImage
Root device is (3, 1)
Boot sector 512 bytes.
Setup is 4768 bytes.
System is 899 kB
build를 사용해 bzImage를 만든다. 지정된 루트 디바이스, 부트섹터 크기, setup의 크기 그리고 커널의
크기를 표시해 준다. build의 동작은 2.3.5절을 참조
바란다.
2.5. 단계별 자세한 분석
2.5.1. -Ttext 0x0의 의미
여기서 -Ttext 0x0에 대해 좀 알아보자.
위와 같은 코드를 'gcc -E -traditional -o test.s test.S'로 컴파일 하면
이렇게 되고 이를 다시 'as -o test.o test.s'로 어셈블하는데 -a를 사용해 중간 파일을 얻으면 다음과 같다.
test-val에 저장된 값은 test-data의 .text의 시작점에서 부터의 offset이다. 프로그램의 시작인 0에서 부터 5번째에 있단 소리다.
최종적으로 'ld -m elf-i386 -s --oformat binary test.o -o test.1'한 결과를 hex로 살펴보면 다음과 같다.
그리고 'ld -m elf-i386 -Ttext 0x0 -s --oformat binary test.o -o test.2'한 결과는 다음과 같다.
둘 사이의 차이점은 -Ttext 0x0가 있고 없고의 차이다. 바이너리 포맷의 경우 .text를 지정해 주지 않으면 시작 번지를 맘대로 정해버리므로 .text를 지정하지 않은 test.1에서는 시작이 0x09048000으로 설정되어 있는 것을 알 수 있다. 엉뚱한 곳의 값을 사용하도록 만들기 때문에 바이너리를 사용할 땐 제대로된 주소가 들어가도록 .text를 필요한 곳으로 지정해줄 필요가 있다.
또 .text의 시작을 0x02로 했을 때의 바이너리는 다음과 같다.
위에서 보듯이 0xaa55는 offset이 7이지만 실제 지정된 것은 9로 .text가 2부터 시작하기 때문이다. 만약 이 바이너리를 메모리에 그대로 올려 놓는다면 제대로 된 값을 읽지 못할 수도 있다. 이 경우엔 .text가 2에서 시작하는 것을 염두에 두고 메모리에 적재해야 제대로 동작할 수 있다.
쉽게 하기 위해선 .text를 0에서 시작하게 하면 바이너리가 메모리의 어느 위치에 있던 상관없이 잘 동작할 수 있게 된다.
2.5.2. 분석
2.4절에서 살펴본 것과 같은 각 단계마다 자세한 내용을 살펴 본다. 이 절이 끝나면 이제 리눅스 커널이 어떻게 만들어지고 어떤 구조를 갖는지 완전히 알 수 있을 것이다.
1 ~ 12 단계는 vmlinux를 만들기 위한 한계이고 커널 설정을 어떻게 했는가에 따라 달라지므로 여기서는 다루지 않는다. 또 17, 18 단계도 bvmlinux를 만들기 위해 필요한 단계이므로 생략한다. 필요한 내용은 2.4절을 참조하거나 각자 log를 만들어 살펴보기 바란다.
vmlinux
사용된 옵션은 다음과 같다.
-m elf-i386
어떤 포맷으로 출력물을 만들 것인지 지정
-T /usr/src/linux-2.4.16/arch/i386/vmlinux.lds
링킹하는데 필요한 스크립트 파일을 지정한다. 이 파일에 대한 내용은 아래 vmlinux.lds를 참조하기 바란다.
-e stext
프로그램의 시작점을 지정한다. 위 스크립트에 지정된 .stext를 사용한다.
--start-group ... --end-group
...에 지정된 오브젝트를 서로간에 참조한 변수나 함수가 에러나지 않을 때까지 계속해서 검색한다.
-o vmlinux
출력물은 vmlinux로 지정
링크에 사용된 스크립트(vmlinux.lds)는 아래와 같다.
여기서 부터는 다른 부분과 달리 gdt등이 설정된 상태인 프로텍티드 모드에서 동작 하므로 메모리 관련된 것을 실제 어드레스를 사용하면 안된다.
System.map은 처음 부팅 때 메모리에 읽혀 올려지고 드라이버등이 커널 심볼을 찾을 때 사용한다.
bbootsect
ld에 사용된 옵션은 다음과 같다.
-m elf-i386
elf-i386를 에뮬레이션
-Ttext 0x0
text 세그먼트의 시작을 0x0으로 지정
-s
모든 디버깅 정보를 없앤다
-oformat binary
bbootsect의 포맷은 바이너리
bsetup
arch/i386/boot/compressed/piggy.o
ld에 사용된 옵션은 다음과 같다.
-m elf-i386
elf-i386를 메뮬레이션
-b binary $tmppiggy.gz
$tmppiggy.gz은 바이너리 형식
-b elf32-i386
piggy.o는 elf32-i386 형식
-T $tmppiggy.lnk
$tmppiggy.lnk를 사용해 링크한다.
$tmppiggy.lnk의 내용은 다음과 같다.
압축된 vmlinux는 .data에 들어가게 되고 *(.data)로 표시된 곳에 들어가게 된다. 그 전후에 LONG(input-data-end - input-data)로 압축된 커널의 크기를 저장한다.
arch/i386/boot/compressed/bvmlinux
bvmlinux는 압축된 커널과 head.o, misc.o를 합쳐 만든다. head.o는 메모리 세팅이라고 보면되고 misc.o는 압축을 풀기 위한 코드가 들어있다. ld에 사용된 옵션은 $(TOPDIR)/vmlinux를 만들 때와 거의 흡사하다. 단 text의 시작 번지는 0x100000이다.
부팅할 때 bvmlinux는 반드시 0x100000에 올려져서 실행되야한다. 그렇지 않으면 제대로 동작하지 않는다.
압축된 커널의 크기를 piggy.o에 저장해 놓았기 때문에 메모리의 어느 위치에서 piggy.o가 끝나는지 알수 있다. 이뒤에 압축을 풀어 놓고 압축이 풀린 커널을 다시 0x100000으로 옮겨와 실행한다.
build
build는 2.3.5절에서와 같이 동작하도록 만들어진다.
bvmlinux.out
bvmlinux에서 필요 없는 것을 제외하고 바이너리로 만든다.
bzImage
build를 사용해 bzImage를 만든다. 지정된 루트 디바이스, 부트섹터 크기, setup의 크기 그리고 커널의 크기를 표시해 준다. build의 동작은 2.3.5절을 참조 바란다.
Recent Posts
Archive Posts
Tags