Linux 개발 환경 이해
- 컴파일러: C/C++ 같은 소스 코드를 타겟 아키텍처에 맞는 바이너리로 변환
- 링커: 여러 개의 오브젝트 파일을 하나의 실행 파일로 결합
- 어셈블러: 어셈블리 코드를 기계어로 변환
- 라이브러리: 프로그램 실행 시 필요한 기본 함수들을 제공
리눅스를 빌드하는 과정은 간단하게 다음과 같다.
1. buildroot를 사용하여 rootfs을 빌드한다.
2. linux kernel 최신 코드를 다운받아 빌드한다.
3. 이 두가지를 사용하여 qemu에서 가상의 머신을 부팅한다.
Rootfs 빌드
Buildroot는 임베디드 리눅스 시스템을 위한 경량화된 루트 파일 시스템(root filesystem), 커널 및 부트로더를 쉽게 빌드할 수 있는 도구이다.
# Git 설치 (필요한 경우)
sudo apt-get install git
# Buildroot 소스 코드 다운로드
git clone git://git.buildroot.net/buildroot
defconfig 파일은 특정 하드웨어 플랫폼에 맞춰 미리 설정된 컴파일 구성 파일이다. 이를 통해 사용자는 해당 하드웨어에 맞는 맞춤형 루트파일시스템과 커널을 빌드할 수 있다.
특정 하드웨어에 맞는 이미지를 빌드할 때, 해당 defconfig 파일을 사용하여 빌드를 설정할 수 있다.
$ ls configs -1
aarch64_efi_defconfig
acmesystems_acqua_a5_256mb_defconfig
acmesystems_acqua_a5_512mb_defconfig
acmesystems_aria_g25_128mb_defconfig
acmesystems_aria_g25_256mb_defconfig
acmesystems_arietta_g25_128mb_defconfig
acmesystems_arietta_g25_256mb_defconfig
amarula_vyasa_rk3288_defconfig
andes_ae350_45_defconfig
arcturus_ucls1012a_defconfig
arcturus_ucp1020_defconfig
armadeus_apf27_defconfig
armadeus_apf28_defconfig
armadeus_apf51_defconfig
arm_foundationv8_defconfig
aspeed_ast2500evb_defconfig
aspeed_ast2600evb_defconfig
asus_tinker_rk3288_defconfig
at91sam9260eknf_defconfig
at91sam9g20dfc_defconfig
at91sam9g45m10ek_defconfig
at91sam9rlek_defconfig
at91sam9x5ek_defconfig
아래 명령어로 QEMU에서 AArch64 아키텍처를 에뮬레이션할 수 있도록 시스템을 빌드할 수 있다.
$ make qemu_aarch64_virt_defconfig
mkdir -p /backup/linux_device_driver/buildroot/output/build/buildroot-config/lxdialog
PKG_CONFIG_PATH="" make CC="/usr/bin/gcc" HOSTCC="/usr/bin/gcc" \
obj=/backup/linux_device_driver/buildroot/output/build/buildroot-config -C support/kconfig -f Makefile.br conf
make[1]: Entering directory '/backup/linux_device_driver/buildroot/support/kconfig'
/usr/bin/gcc -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=600 -DCURSES_LOC="<ncurses.h>" -DNCURSES_WIDECHAR=1 -DLOCALE -I/backup/linux_device_driver/buildroot/output/build/buildroot-config -DCONFIG_=\"\" -MM *.c > /backup/linux_device_driver/buildroot/output/build/buildroot-config/.depend 2>/dev/null || :
/usr/bin/gcc -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=600 -DCURSES_LOC="<ncurses.h>" -DNCURSES_WIDECHAR=1 -DLOCALE -I/backup/linux_device_driver/buildroot/output/build/buildroot-config -DCONFIG_=\"\" -c conf.c -o /backup/linux_device_driver/buildroot/output/build/buildroot-config/conf.o
/usr/bin/gcc -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=600 -DCURSES_LOC="<ncurses.h>" -DNCURSES_WIDECHAR=1 -DLOCALE -I/backup/linux_device_driver/buildroot/output/build/buildroot-config -DCONFIG_=\"\" -I. -c /backup/linux_device_driver/buildroot/output/build/buildroot-config/zconf.tab.c -o /backup/linux_device_driver/buildroot/output/build/buildroot-config/zconf.tab.o
/usr/bin/gcc -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=600 -DCURSES_LOC="<ncurses.h>" -DNCURSES_WIDECHAR=1 -DLOCALE -I/backup/linux_device_driver/buildroot/output/build/buildroot-config -DCONFIG_=\"\" /backup/linux_device_driver/buildroot/output/build/buildroot-config/conf.o /backup/linux_device_driver/buildroot/output/build/buildroot-config/zconf.tab.o -o /backup/linux_device_driver/buildroot/output/build/buildroot-config/conf
rm /backup/linux_device_driver/buildroot/output/build/buildroot-config/zconf.tab.c
make[1]: Leaving directory '/backup/linux_device_driver/buildroot/support/kconfig'
#
# configuration written to /backup/linux_device_driver/buildroot/.config
#
menuconfig로 몇가지 설정을 변경해보자.
$ make menuconfig
System Configuration => Init System => systemd로 설정
Kernel => Linux Kernel disable
Target Pacgaes => Text editors and viewers => vim
Target Pacgaes => Libraries => Crypto => openssl support, openssl binary
Filesytem images => ext2/3/4 root filesystem 체크, exact size 128m(default: 60m) 로 설정
Host utilities 에서 모든 항목 disable 처리
설정이 끝났으면 Save and Exit로 나오고 빌드를 진행하면 된다.
$ make
빌드를 성공적으로 완료하면 filesystem을 생성했다는 로그와 생성된 이미지 파일의 위치를 확인할 수 있다.
$ make
>>> Generating filesystem image rootfs.ext2
...
Creating filesystem with 131072 1k blocks and 32768 inodes
Filesystem UUID: eabb69a0-36bb-423d-98e6-16f1db3b5bc1
Superblock backups stored on blocks:
8193, 24577, 40961, 57345, 73729
Allocating group tables: done
Writing inode tables: done
Creating journal (4096 blocks): done
Copying files into the device: done
Writing superblocks and filesystem accounting information: done
ln -sf rootfs.ext2 /backup/linux_device_driver/buildroot/output/images/rootfs.ext4
ln -snf /backup/linux_device_driver/buildroot/output/host/aarch64-buildroot-linux-gnu/sysroot /backup/linux_device_driver/buildroot/output/staging
>>> Executing post-image script board/qemu/post-image.sh
buildroot/output/images$ ls -sla
total 52736
4 drwxr-xr-x 2 root root 4096 9월 29 13:57 .
4 drwxr-xr-x 6 root root 4096 9월 29 13:57 ..
52724 -rw-r--r-- 1 root root 134217728 9월 29 13:57 rootfs.ext2
0 lrwxrwxrwx 1 root root 11 9월 29 13:57 rootfs.ext4 -> rootfs.ext2
4 -rwxr-xr-x 1 root root 515 9월 29 13:57 start-qemu.sh
리눅스 커널 빌드
ToolChain
Toolchain은 컴파일 과정에서 사용되는 컴파일러, 링커, 어셈블러, 라이브러리등의 집합을 의미한다.
cross compile로 리눅스를 빌드할 때는 target architecture에 맞는 toolchain을 사용해야한다.
target system과 build를 실행하는 host system이 다르기 때문이다.
Toolchain 예시
- GCC (GNU Compiler Collection): 가장 많이 사용되는 크로스 컴파일 툴체인 중 하나. ARM, x86, RISC-V 등 다양한 아키텍처를 지원.
- Linaro Toolchain: ARM 기반 장치용 크로스 컴파일 툴체인.
- Buildroot 및 Yocto: 임베디드 리눅스 시스템에서 자동으로 필요한 툴체인과 빌드 환경을 구성해주는 도구.
Toolchian 다운로드
https://developer.arm.com/downloads/-/gnu-a
위 사이트 접속해서 AArch64 GNU/Linux target (aarch64-none-linux-gnu) 다운 받아서 압축 풀면된다.
$ tar xvf gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu.tar.xz
$ cd gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu
gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu$ ls -1
10.3-2021.07-x86_64-aarch64-none-linux-gnu-manifest.txt
aarch64-none-linux-gnu
bin
include
lib
lib64
libexec
share
gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu$ cd bin
gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu/bin$ ls -1
aarch64-none-linux-gnu-addr2line
aarch64-none-linux-gnu-ar
aarch64-none-linux-gnu-as
aarch64-none-linux-gnu-c++
aarch64-none-linux-gnu-c++filt
aarch64-none-linux-gnu-cpp
aarch64-none-linux-gnu-dwp
aarch64-none-linux-gnu-elfedit
aarch64-none-linux-gnu-g++
aarch64-none-linux-gnu-gcc
aarch64-none-linux-gnu-gcc-10.3.1
aarch64-none-linux-gnu-gcc-ar
aarch64-none-linux-gnu-gcc-nm
aarch64-none-linux-gnu-gcc-ranlib
aarch64-none-linux-gnu-gcov
aarch64-none-linux-gnu-gcov-dump
aarch64-none-linux-gnu-gcov-tool
aarch64-none-linux-gnu-gdb
aarch64-none-linux-gnu-gdb-add-index
aarch64-none-linux-gnu-gfortran
aarch64-none-linux-gnu-gprof
aarch64-none-linux-gnu-ld
aarch64-none-linux-gnu-ld.bfd
aarch64-none-linux-gnu-ld.gold
aarch64-none-linux-gnu-lto-dump
aarch64-none-linux-gnu-nm
aarch64-none-linux-gnu-objcopy
aarch64-none-linux-gnu-objdump
aarch64-none-linux-gnu-ranlib
aarch64-none-linux-gnu-readelf
aarch64-none-linux-gnu-size
aarch64-none-linux-gnu-strings
aarch64-none-linux-gnu-strip
설정 파일을 arch/arm64/configs/qemu_defconfig로 복사하면, 그 후 make defconfig 명령을 실행하면 해당 설정 파일을 기반으로 .config 파일이 생성된다.
~/linux$ cp /backup/linux_device_driver/buildroot/board/qemu/aarch64-virt/linux.config arch/arm64/configs/qemu_defconfig
~/linux$ ARCH=arm64 CROSS_COMPILE=/backup/linux_device_driver/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu- make defconfig
~/linux$ ARCH=arm64 CROSS_COMPILE=/backup/linux_device_driver/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu- make
vmlinux가 생성되고 이를 바탕으로 이미지 파일을 만든다.
$ ARCH=arm64 CROSS_COMPILE=/backup/linux_device_driver/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu- make
...
LD [M] net/nfc/nci/nci.ko
CC [M] net/qrtr/qrtr.mod.o
LD [M] net/qrtr/qrtr.ko
CC [M] net/qrtr/qrtr-smd.mod.o
LD [M] net/qrtr/qrtr-smd.ko
CC [M] net/qrtr/qrtr-tun.mod.o
LD [M] net/qrtr/qrtr-tun.ko
CC [M] net/qrtr/qrtr-mhi.mod.o
LD [M] net/qrtr/qrtr-mhi.ko
GZIP arch/arm64/boot/Image.gz
$ ls -sla arch/arm64/boot/Image.gz
14084 -rw-r--r-- 1 root root 14421699 9월 29 15:56 arch/arm64/boot/Image.gz
이 이미지와 앞서 만들었던 rootfs를 이용하여 qemu라는 에뮬레이터에서 리눅스를 실행해보자.
Qemu에서 리눅스 실행
qemu란?
오픈소스의 다양한 cpu와 디바이스를 지원하는 에뮬레이터로 전체 시스템 에뮬레이션, user mode emulation(개별 application, process 만 에뮬레이션=> 호스트와 같은 운영체제만 가능), 가상화(kvm)등을 지원한다.
AArch64(ARM 64비트) 시스템 에뮬레이션을 위한 QEMU 패키지를 설치하기 위해 아래 명령어로 수행한다.
$ apt install qemu-system-aarch64
$ qemu-system-aarch64
qemu-system-aarch64: No machine specified, and there is no default
Use -machine help to list supported machines
qemu-system-aarch64를 사용하여 가상의 장치를 실행 가능해진다.
- -kernel : 빌드 커널 지정 (QEMU가 부팅할 때 사용할 리눅스 커널 이미지를 지정)
- -drive : 사용한 가상의 디스크, 빌드한 루트 파일 시스템 지정 (QEMU에서 사용할 가상 디스크를 설정)
- format: file format (raw 면 별도의 format 설정 없이 기본 바이너리 형식)
- file: rootfs image 위치
- if: 디스크를 인터페이스로 연결
- -append: 커널에 전달할 추가 커맨드
- root=/dev/vda: 루트 파일 시스템이 마운트될 디바이스를 /dev/vda로 지정
- console=ttyAMA0: 부팅 시 QEMU의 시리얼 콘솔을 사용하도록 설정
- nokaslr: KASLR(Kernel Address Space Layout Randomization)을 비활성화(KASLR은 커널의 보안 기능)
- -m : 메모리 크기
- -smp : 코어 개수 지정
- -nographic: QEMU가 GUI 없이 텍스트 기반 콘솔 모드로 실행
qemu 로 system을 가상으로 에뮬레이션하고 리눅스 커널을 부팅하는 명령어 예시
$ qemu-system-aarch64 \
-kernel <리눅스 디렉토리>/arch/arm64/boot/Image \
-drive format=raw,file=<빌드루트 디렉토리>/output/images/rootfs.ext4,if=virtio \
-append "root=/dev/vda console=ttyAMA0 nokaslr" \
-nographic -M virt -cpu cortex-a72 \
-m 2G \
-smp 2
종료 하려면 터미널을 새로 열어서 kill 또는 qemu내에서 shutdown해줘야한다.
$ kill -9 qemu-system-aarch64
$ shutdown -h now
이제 QEMU를 사용하여 AArch64(ARM 64비트) 가상 머신을 실행하고, 리눅스 커널과 rootfs을 부팅해보자.
$ qemu-system-aarch64 \
-kernel linux/arch/arm64/boot/Image \
-drive format=raw,file=/backup/linux_device_driver/buildroot/output/images/rootfs.ext4,if=virtio \
-append "root=/dev/vda console=ttyAMA0 nokaslr" \
-nographic -M virt -cpu cortex-a72 \
-m 2G \
-smp 2
실행하면 qemu로 아까 빌드했던 리눅스가 실행되는 걸 볼 수 있다. buildroot를 빌드할 때 systemd로 빌드했기 때문에 buildroot에서 systemd가 실행이 되고 systemd가 여러가지 서비스나 자원을 실행하는 로그를 확인할 수 있다.
부팅이 완료되면 로그인창이 뜨는데 기본 유저는 root이다.
$ uname -a
Linux buildroot 6.11.0-11728-gad46e8f95e93 #1 SMP PREEMPT Sun Sep 29 15:48:29 KST 2024 aarch64 GNU/Linux
'System Programming > Linux Device Driver' 카테고리의 다른 글
Linux Device Driver 기초 #4 system daemon과 라이브러리 개발 (0) | 2024.10.20 |
---|---|
Linux Device Driver 기초 #3 Linux Device Driver 추가하기 (0) | 2024.10.13 |
Linux Inside #1 Booting: Bootloader에서 Kernel까지 (0) | 2024.10.01 |
BuildRoot 사용법 요약 (1) | 2024.09.20 |
Linux Device Driver 기초 #2 System Call 추가하기 (0) | 2024.08.13 |