透過 qemu 建立嵌入式 Linux 開發環境


crosstool-NG

下載最新版的 crosstool-NG

https://crosstool-ng.github.io

sudo apt install automake bison chrpath flex g++ git gperf gawk libexpat1-dev libncurses5-dev libsdl1.2-dev libtool libtool-bin python2.7-dev texinfo help2man

wget http://crosstool-ng.org/download/crosstool-ng/crosstool-ng-1.24.0.tar.bz2

tar xf crosstool-ng-1.24.0.tar.bz2

./configure --enable-local

make

sudo make install

確認安裝成功

./ct-ng

列出工具鏈範本

./ct-ng list-samples

這裡以 arm 為範例

顯示預設配置內容

./ct-ng show-arm-cortex_a8-linux-gnueabi

指定使用

./ct-ng arm-cortex_a8-linux-gnueabi

設定內容

./ct-ng menuconfig

建議配置

Paths and misc 中停用 Render the toolchain read-only

Target options | Use specific FPU 中輸入 neon

Target options | Floating point 中選用 hardware (FPU)

C-library | extra config 中增加 --enable-obsolete-rpc

配置參考:

https://tgarc.github.io/2015/03/08/building-a-linaro-cross-compiler-toolchain-with-cross-ng/

build

./ct-ng build

編譯成功後可以在以下目錄中找到

~/x-tools/arm-cortex_a8-linux-gnueabihf/

設定路徑

PATH=~/x-tools/arm-cortex_a8-linux-gnueabihf/bin:$PATH

 測試編譯

vi helloworld.c

#include <stdio.h>

#include <stdlib.h>

int main(int argc, char **argv){

    printf("Hello, world!\n");

    return 0;

}

編譯

arm-cortex_a8-linux-gnueabihf-gcc helloworld.c -o helloworld

檔案內容

file helloworld

arm-cortex_a8-linux-gnueabihf-readelf -a helloworld | grep "Shared library"

arm-cortex_a8-linux-gnueabihf-readelf -a helloworld | grep "program interpreter"

QEMU

安裝 qemu-system-arm

sudo apt update

sudo apt install qemu-system-arm

根目錄

建立 rootfs

mkdir -p ~/arm/rootfs

cd ~/arm/rootfs

mkdir bin dev etc home lib proc sbin sys tmp usr var

mkdir usr/bin usr/lib usr/sbin

mkdir var/log

Linux Kernel

下載 linux kernel

https://www.kernel.org

解壓縮時注意資料夾內檔名區分大小寫

linux

tar -xvf linux-5.9.1.tar.xz

cd linux-5.9.1

make mrproper

套用既有配置至 .config 再自訂

make ARCH=arm multi_v7_defconfig

自訂配置 .config

make ARCH=arm menuconfig

開發時建議使用

若要使用 NFS 根目錄檔案系統,需設定

File systems | Network File Systems 中選用 Root file system on NFS

檢視 kernel version

make kernelversion

如果路徑沒設要設定路徑

PATH=~/x-tools/arm-cortex_a8-linux-gnueabihf/bin:$PATH

編譯內核

make -j 8 ARCH=arm CROSS_COMPILE=arm-cortex_a8-linux-gnueabihf- zImage

若發生錯誤

Error: selected processor does not support `isb ' in ARM mode

設定 crosstool-NG 配置

./ct-ng menuconfig

配置內容

Target options | Use specific FPU 中輸入 neon

build

./ct-ng build

參考:

https://stackoverflow.com/questions/62304821/selected-processor-does-not-support-dmb-ish-in-arm-mode

編譯硬體結構樹

make ARCH=arm dtbs

編譯內核模組

make -j 8 ARCH=arm CROSS_COMPILE=arm-cortex_a8-linux-gnueabihf- modules

安裝內核模組

make -j 8 ARCH=arm CROSS_COMPILE=arm-cortex_a8-linux-gnueabihf- INSTALL_MOD_PATH=~/arm/rootfs modules_install

cp arch/arm/boot/zImage ~/arm/

cp arch/arm/boot/dts/vexpress-v2p-ca9.dtb ~/arm/

cd ~/arm

以 qemu 執行

QEMU_AUDIO_DRV=none qemu-system-arm -m 256M -nographic -M vexpress-a9 -kernel zImage -dtb ve

xpress-v2p-ca9.dtb -append "console=ttyAMA0"

此時沒掛載根目錄當然跑不起來

離開 qemu

Ctrl-A X

BusyBox

安裝 busybox

https://busybox.net

wget https://busybox.net/downloads/busybox-1.32.0.tar.bz2

tar -xf busybox-1.32.0.tar.bz2

cd busybox-1.32.0/

make distclean

make defconfig

設定 busybox

make menuconfig

在 menuconfig 設定 static binary

Busybox Settings --->

       --- Build Options

       [*] Build BusyBox as a static binary (no shared libs)

也可以不設定 no shared libs,只是就要去 crosstool-NG 產生的工具鏈複製 shared lib 至 rootfs/lib

編譯

make -j 8 ARCH=arm CROSS_COMPILE=arm-cortex_a8-linux-gnueabihf-

安裝至 rootfs

make CROSS_COMPILE=arm-cortex_a8-linux-gnueabihf- CONFIG_PREFIX=~/arm/rootfs/ install

建立裝置節點

cd ~/arm/rootfs

sudo mknod -m 666 dev/null c 1 3

sudo mknod -m 600 dev/console c 5 1

sudo vi etc/inittab

::sysinit:/etc/init.d/rcS

console::askfirst:-/bin/ash

::ctrlaltdel:/sbin/reboot

::shutdown:/sbin/swapoff -a

::shutdown:/bin/umount -a -r

::restart:/sbin/init

mkdir etc/init.d

vi etc/init.d/rcS

#!/bin/sh

mount -t proc proc /proc

mount -t sysfs sysfs /sys

chmod +x etc/init.d/rcS

配置 gdbserver

(之後可透過 gdbserver 進行遠端除錯,若不需要可略過)

複製 gdbserver

cd ~/arm/rootfs/

cp -a ~/x-tools/arm-cortex_a8-linux-gnueabihf/arm-cortex_a8-linux-gnueabihf/debug-roo

t/usr/bin/gdbserver usr/bin/

找出 gdbserver 的相依性

arm-cortex_a8-linux-gnueabihf-readelf -a usr/bin/gdbserver | grep "program interprete

r"

      [Requesting program interpreter: /lib/ld-linux-armhf.so.3]

arm-cortex_a8-linux-gnueabihf-readelf -a usr/bin/gdbserver | grep "Shared library"

 0x00000001 (NEEDED)                     Shared library: [libdl.so.2]

 0x00000001 (NEEDED)                     Shared library: [libm.so.6]

 0x00000001 (NEEDED)                     Shared library: [libc.so.6]

 0x00000001 (NEEDED)                     Shared library: [ld-linux-armhf.so.3]

檢視檔案

arm-cortex_a8-linux-gnueabihf-gcc -print-sysroot

export SYSROOT=`arm-cortex_a8-linux-gnueabihf-gcc -print-sysroot`

依以上顯示的 program interpreter 及 Shared library 檢視檔案

ls -l $SYSROOT/lib/ld-linux-armhf.so.3

lrwxrwxrwx 1 jphf jphf 10 十二  2 18:55 /home/jphf/x-tools/arm-cortex_a8-linux-gnueabihf/arm-cortex_a8-linux-gnueabihf/sysroot/lib/ld-linux-armhf.so.3 -> ld-2.29.so

ls -l $SYSROOT/lib/libdl.so.2

lrwxrwxrwx 1 jphf jphf 13 十二  2 18:54 /home/jphf/x-tools/arm-cortex_a8-linux-gnueabihf/arm-cortex_a8-linux-gnueabihf/sysroot/lib/libdl.so.2 -> libdl-2.29.so

其他檔案的方式相同

複製檔案到根目錄檔案系統對應的資料夾

cp -a $SYSROOT/lib/ld-linux-armhf.so.3 lib/

cp -a $SYSROOT/lib/ld-2.29.so lib/

cp -a $SYSROOT/lib/libdl.so.2 lib/

cp -a $SYSROOT/lib/libdl-2.29.so lib/

cp -a $SYSROOT/lib/libm.so.6 lib/

cp -a $SYSROOT/lib/libm-2.29.so lib/

cp -a $SYSROOT/lib/libc.so.6 lib/

cp -a $SYSROOT/lib/libc-2.29.so lib/


根目錄檔案系統掛載

初始記憶體檔案系統

獨立的模擬磁碟

cd ~/arm/rootfs

find . | cpio -H newc -ov --owner root:root > ../initramfs.cpio

cd ../

gzip initramfs.cpio

mkimage -A arm -O linux -T ramdisk -d initramfs.cpio.gz uRamdisk

以 qemu 執行

QEMU_AUDIO_DRV=none qemu-system-arm -m 512M -nographic -M vexpress-a9 -kernel zImage -dtb vexpress-v2p-ca9.dtb -append "console=ttyAMA0 rdinit=/sbin/init" -initrd initramfs.cpio.gz

透過初始記憶體檔案系統掛載,啟動後寫入檔案,再重啟後檔案會不見。

NFS 掛載根目錄檔案系統

透過 NFS 掛載,目前的建立的根目錄,可直接與目標機器上的資料同步,方便開發與除錯

kernel 要先配置 CONFIG_ROOT_NFS

安裝

sudo apt install nfs-kernel-server

sudo vi /etc/exports

/home/jphf/arm/rootfs   *(rw,sync,no_subtree_check,no_root_squash)

重啟

sudo service nfs-kernel-server restart

如果開發環境是用 virtual box 建立,網路設定裡需要指定為使用 "橋接介面卡"

檢視目前網路設定

ifconfig

其中 192.168.0.102 就是這台機器的 IP

HOST_IP 如上所示

TARGET_IP 可以指定任何與 HOST_IP 同網域但未使用的 IP

sudo apt install net-tools uml-utilities

cd ~/arm

vi run-qemu-nfs.sh

#!/bin/bash

KERNEL=zImage

DTB=vexpress-v2p-ca9.dtb

ROOTDIR=/home/jphf/arm/rootfs

HOST_IP=192.168.0.102

TARGET_IP=192.168.0.105

NET_NUMBER=192.168.0.0

NET_MASK=255.255.255.0

sudo tunctl -u $(whoami) -t tap0

sudo ifconfig tap0 ${HOST_IP}

sudo route add -net ${NET_NUMBER} netmask ${NET_MASK} dev tap0

sudo sh -c "echo 1 > /proc/sys/net/ipv4/ip_forward"

QEMU_AUDIO_DRV=none qemu-system-arm -m 512M -nographic -M vexpress-a9 -kernel $KERNEL -dtb ${DTB} -append "console=ttyAMA0 root=/dev/nfs rw nfsroot=${HOST_IP}:${ROOTDIR},nolock,vers=4,tcp ip=${TARGET_IP}" -net nic -net tap,ifname=tap0,script=no

sudo sh run-qemu-nfs.sh

gdbserver 遠端除錯

(這裡的範例採用 NFS 掛載根目錄檔案系統)

cd ~/arm/rootfs/home

vi helloworld.c

#include <stdio.h>

#include <stdlib.h>

int main(int argc, char **argv){

    printf("Hello, world!\n");

    return 0;

}

arm-cortex_a8-linux-gnueabihf-gcc helloworld.c -o helloworld

在目標機器上可以執行

cd /home

./helloworld

透過網路

在目標機器上執行 gdbserver

gdbserver :10000 ./helloworld

在開發機器上指定同樣程式

arm-cortex_a8-linux-gnueabihf-gdb helloworld

(gdb) target remote 192.168.0.105:10000

透過序列埠

在目標機器上執行 gdbserver

gdbserver /dev/ttyO0 ./helloworld

stty -F /dev/ttyO0 115200

在開發機器上指定同樣程式

arm-cortex_a8-linux-gnueabihf-gdb helloworld

(gdb) set remotebaud 115200

(gdb) target remote /dev/ttyUSB0



參考來源: