Search

ARM CPU 기반의 Embedded Linux 구축하기

Category
임베디드
Column
2022/12/20 10:53
Tags
study

1. 개요

위 자료를 토대로 직접 실습을 진행해볼 예정
작업 환경
ubuntu 16.0.4

2. Cross Compiler

cross compiler version : 4.8.3

3. Bootloader 설치

부트로더란 운영체제 진입 전에 실행되는 프로그램으로 다음과 같은 역할을 한다.
하드웨어 기본 세팅
운영체제 커널 로딩
펌웨어 및 메모리 읽기/쓰기 가능
펌웨어 업데이트 (network, serial, usb)
멀티 부팅 기능
부트로더 관련 내용은 추후 따로 자세히 정리할 예정이다.
실습엔 임베디드에서 주로 사용하는 U-boot를 사용하며 arm용 개발 보드인 versatile를 qemu를 통해 에뮬레이팅하여 진행하였다.
1.
uboot 설치
$ wget ftp://ftp.denx.de/pub/u-boot/u-boot-2010.03.tar.bz2 $ tar -xvf u-boot-2010.03.tar.bz2 $ cd u-boot-2010.03
Bash
복사
2.
uboot 빌드
$ make versatilepb_config ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabi- $ make all ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabi-
Bash
복사
3.
uboot 실행
$ qemu-system-arm -M versatilepb -nographic -kernel u-boot.bin
Bash
복사

4. Kernel 설치

1.
커널 소스 다운
$ wget https://cdn.kernel.org/pub/linux/kernel/v4.x/linux-4.1.6.tar.xz $ tar -xvf linux-4.1.6.tar.xz
Bash
복사
2.
커널 컴파일
$ apt-get install lib32ncurses5 lib32ncurses5 bc $ cd linux-4.1.6 $ make ARCH=arm versatile_defconfig $ make ARCH=arm menuconfig
Bash
복사
menuconfig : Kernel Features → Use the ARM EABI to compile the kernel 체크
$ make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabi- all
Bash
복사
빌드가 성공적으로 끝나면 arch/arm/boot 하위에 zImage가 생성된다. 빌드를 하면 vmlinux와 zImage가 생성되는데 해당 바이너리들은 다음을 뜻한다.
kernel을 컴파일 하면 vmlinux가 생성된다. 해당 바이너리에는 디버깅과 관련된 정보 등과 같이 부가적인 정보들을 포함하고 있다. vmlinux에서 objcopy 명령을 통해 Instruction set만 추출한 것이 바로 Imagefile이며 해당 파일은 크기가 크기 때문에 gzip을 통해서 압축을 진행한다.
압축된 파일이 위 사진에서 볼 수 있는 piggy.gz이며 Head.s에서 piggy.gz를 데이터로 로드해서 piggy.o를 만든다. piggy.o는 압축파일이기 때문에 압축해제를 하는 역할이 misc.o 이다. 따라서 head.o + misc.o 을 piggy.o 파일 앞 부분에 합친게 최종적인 zImage이다. vmlinux에 zImage로 변환되는 과정을 표현하면 다음과 같다. (head.o는 압축 해제된 곳으로 jump 역할)
위 내용을 요약하면 다음과 같다.
1. vmlinux --> arch/arm/boot/Image
vmlinux에서 .comment 섹션과 모든 symbol 및 relocation 정보들을 제거한 후, binary 포맷(즉 그냥 데이터)으로 Image를 생성
2. arch/arm/boot/Image --> arch/arm/boot/compressed/piggy.gzip
Image를 gzip 압축하여 piggy.gzip을 생성
3. arch/arm/boot/compressed/piggy.gzip --> arch/arm/boot/compressed/piggy.gzip.o
압축한 piggy.gzip을 데이터로 포함하고 있는 piggy.gzip.S를 컴파일하여 piggy.gzip.o를 생성
4. arch/arm/boot/compressed/piggy.gzip.o --> arch/arm/boot/compressed/vmlinux
piggy.gzip.o와 다른 여러 오브젝트들을 arch/arm/boot/compressed/vmlinux.lds 링커스크립트에 정의된 대로 링크하여 vmlinux elf 바이너리 생성
5. arch/arm/boot/compressed/vmlinux --> arch/arm/boot/zImage
arch/arm/boot/compressed/vmlinux에서 .comment 섹션과 모든 symbol 및 relocation 정보를 제거한 후, binary 포맷(즉 그냥 데이터)으로 zImage 생성
현재 arch/arm/boot/compressed 디렉토리에 존재하는 압축된 vmlinux의 section 정보들을 확인해보면 다음과 같다.
위에서 설명한 5번 과정을 거치면 위 섹션에서 .text ~ .pad 섹션만 zImage에 남아있는다. arch/arm/boot/compressed/vmlinux 섹션 데이터를 확인해보면 다음과 같다.
arch/arm/boot/compressed/vmlinux 데이터의 .text 시작 오프셋과 .pad 끝에 있는 데이터이다. 해당 영역이 zImage의 시작과 끝이 동일한지 확인해보자.
zImage의 시작과 끝 offset을 확인하면 설명한 것 처럼 arch/arm/boot/compressed/vmlinux의 .text ~ .pad 영역만 들어있는 것을 볼 수 있다.
추가적으로 실습은 위에서 생성한 zImage가 아닌 uImage를 사용할 것이다.
uImage이란 u-boot에서 사용하는 압축된 커널 이미지다. zImage에 64byte 헤더가 추가로 들어간다.
zImage는 내부에 small bootloader가 따로 존재한다. 따라서 부트로더 작업을 따로 추가할 필요가 없다
나는 bootloader + kernel + RFS를 결합하여 임베디드 리눅스 시스템을 구축할 것이기 때문에 uImage를 생성해서 사용할 것이다.
결론적으로는 빌드된 zImage를 이용해서 커널 부팅이 되면 성공이다. 단 현재 File System이 없어 커널 패닉이 난다.
qemu-system-arm -M versatilepb -m 128M -kernel zImage -nographic -append "console=ttyAMA0,115200”

5. Bootloader + Kernel

빌드한 uboot과 kernel을 합치는 작업을 해보자. 우선 아래의 파일들을 수정해야한다.
$ cd [u-boot 폴더]
Bash
복사
[u-boot]/include/configs/versatile.h
시스템에 하드디스크나 플래시와 같은 루트 파일 시스템을 저장하기 위한 저장 장치가 없는 경우 리눅스가 메모리를 사용하여 디스크를 가상으로 만들기 위해 /dev/ram으로 수정
common/image.c
defined(CONFIG_VERSATILE) 추가
$ make all ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabi-
Bash
복사
u-boot 재빌드
dd if=/dev/zero of=flash.bin bs=1 count=5M dd if=u-boot.bin of=flash.bin conv=notrunc bs=1 mkimage -A arm -C none -O linux -T kernel -d /home/wogh8372/Desktop/embedded_study/linux-4.1.6/arch/arm/boot/zImage -a 0x10000 -e 0x10000 zImage.uimg dd if=zImage.uimg of=flash.bin conv=notrunc bs=1 seek=2M
Bash
복사
위에서 zImage가 아닌 uImage를 사용하려면 mkimage 명령어를 이용하여 uImage를 생성해야 한다.
위 코드는 0으로 채워진 5M flash.bin을 생성 후
u-boot.bin 내용을 flash.bin에 복사한뒤
mkimage를 이용하여 uImage를 생성하고
flash.bin의 0x200000 offset에 uImage를 삽입
flash.bin의 최종 구조는 다음과 같다.
최종적으로 직접 빌드한 bootloader + kernel를 qemu를 이용해서 실행한다.
qemu-system-arm -M versatilepb -m 128M -kernel flash.bin -nographic
bootm 0x210000를 입력하여 부팅시도
정상적으로 부팅이된다.
아직 파일시스템이 없기 때문에 커널 패닉이 발생한다.

6. Bootloader + Kernel + Root File System

마지막 앞서 빌드한 Bootloader + Kernel에 Root File Syste을 결합하여 완전한 임베디드 리눅스 시스템 환경을 구성해보자. 우선 busybox를 다운로드한다.
$ wget http://busybox.net/downloads/busybox-1.21.1.tar.bz2 $ tar -xvf busybox-1.21.1.tar.bz2
Bash
복사
busybox 설치
$ make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabi- defconfig $ make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabi- menuconfig
Bash
복사
busybox 빌드 환경 설정
menuconfig시 Busybox Setting -> Build Option -> Static binary 체크
라이브리러들을 static build 하기 위해
$ make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabi- install
Bash
복사
최종 빌드
$ cd _install/ $ find . | cpio -H newc -o | gzip > rootfs.img.gz
Bash
복사
rootfs.img.gz 생성 ⇒ busybox를 이용해 root file system을 만들었다
정상적으로 돌아가는지 4번에서 빌드한 zImage와 RFS를 이용해 qemu로 돌려보자.(테스트로)
$ qemu-system-arm -M versatilepb -m 128M -kernel ./zImage -initrd ./rootfs.img.gz -append "root=/dev/ram rdinit=/bin/sh console=ttyAMA0,115200" -nographic
Bash
복사
정상 동작 확인
이제 Bootloader + Kernel + Root File System을 결합해보자.
[uboot]/include/configs/versatile.h
rdinit=/bin/sh 추가
bootm 0x210000 0x510000 라인 추가 → 자동 부팅 옵션
uboot 재빌드
make all ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabi-
$ dd if=/dev/zero of=flash.bin bs=1 count=7M $ dd if=u-boot.bin of=flash.bin conv=notrunc bs=1 # uboot.bin $ mkimage -A arm -C none -O linux -T kernel -d [zImage 경로] -a 0x00010000 -e 0x00010000 zImage.uimg $ dd if= zImage.uimg of=flash.bin conv=notrunc bs=1 seek=2M # uimage(kernel) $ mkimage -A arm -C none -O linux -T ramdisk -d rootfs.img.gz -a 0x00800000 -e 0x00800000 rootfs.uimg $ dd if= rootfs.uimg of=flash.bin conv=notrunc bs=1 seek=5M
Bash
복사
아까는 bootloader + kernel 을 이용하여 flash.bin을 만들었다면
최종적으로 bootloader + kernel + rootfs 을 이용하여 flash.bin 생성
qemu로 embedded system 실행
qemu-system-arm -M versatilepb -m 128M -kernel flash.bin -nographic
bootloader에서 bootm 옵션으로 인해 자동으로 커널을 부팅시키고
최종적으로 빌드했던 busybox의 /bin/sh가 실행되는 것을 알 수 있다.

7. 마무리

간단하지만 이렇게 임베디드 장비에서 돌아가는 펌웨어 제작 실습을 해보았다. 여태 공유기 분석을 진행할 때, 바로 펌웨어를 얻고 binwalk로 추출되지 않는 파일 시스템을 얻기 위해 많은 작업을 수행했는데 이제 bootloader, kernel의 구조를 대충이라도 알았으니 앞으로의 분석에 좀더 도움이 되지 않을까 생각한다.
추가적으로 생성한 flash.bin을 binwalk로 추출해보자.
$ binwalk -Mre flash.bin
Bash
복사
binwalk를 통해 파일 시스템 추출에 성공하였다.
추가적으로 bootloader와 커널에서 언제 메모리에 데이터를 올리고 어느 주소를 참조하는지 등의 자세한 코드 분석도 추가적인 글에서 분석할 예정이다.

참고자료