提示:本人在2008年进行Linux@ARM实验,在ARM9上安装Linux、利用SkyEye模拟器及U-BOOT引导。这是实验报告、以及实验过程中留下的记录,按日期倒序排列。
本文以CreativeCommons BY-NC 3.0协议授权
嵌入式系统作业二:Linux@ARM
5050369043 石君霄
一、实验环境
主机硬件:DELL INSPIRON 600m
操作系统:Ubuntu 8.04 Hardy
主机编译器:gcc 4.2.3
仿真环境:SkyEye 1.2.4
交叉编译器:arm-unknown-linux-gnu-gcc 3.4.5 with glibc 2.3.6
二、实验目标
安装SkyEye硬件仿真器,对arm920t处理器和Atmel AT91RM9200dk开发板进行仿真;
在仿真开发板上安装linux-2.6.25及BusyBox-1.9.2。
三、实验步骤
--------------------------------------------------------------------------------
#1 编译安装SkyEye
SkyEye是清华大学制作的一款开源硬件仿真器,支持ARM和BlackFin平台。
qemu只能仿真ARM的指令集,而SkyEye可以仿真整块开发板。
4月4日,我下载了SkyEye-1.2.4,下载地址是:
编译SkyEye的方法与普通Linux程序完全一致,应使用主机平台(i386)的编译器。
我使用的编译命令行是:
$ make STATIC=1 NO_DBCT=1 NO_LCD=1 NO_NET=1 NO_BFD=1
我指定了很多NO,禁止了不使用的功能,这有助于减少出错几率。
5月20日使用的skyeye.conf内容如下:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# yoursunny.com, 2008-05-14
# CreativeCommons BY-NC 3.0
cpu: arm920t
mach: at91rm92
# main memory, 32MB
mem_bank: map=M, type=RW, addr=0x20000000, size=0x02000000, file=./myimage, boot=yes
# defined in arch/arm/mach-at91/Makefile.boot
# kernel -> 20008000
# kernel param -> 20000100
# ramdisk -> 20410000
# IO ports of the board
mem_bank: map=I, type=RW, addr=0xfffa0000, size=0x00060000
uart: mod=stdio
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
--------------------------------------------------------------------------------
#2 获得交叉编译器
5月8日,我下载了一个二进制格式的交叉编译器,下载地址是:
这个编译器在i386平台上工作,能将代码编译为arm平台的目标代码。
在不加特别说明的情况下,后面的所有交叉编译操作都是使用这个交叉编译器完成。
5月21日,我尝试自己编译一套交叉编译器,但是没有成功。
这部分操作的详情,请看本文末尾。
--------------------------------------------------------------------------------
#3 编译Linux内核
5月8日,我下载了linux-2.6.25,下载地址是:
5月8日当天,我根据下列步骤尝试编译了Linux内核:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
操作很简单的,kernel解压后在Documentation/arm/README可以找到说明:
1.打开顶层的Makefile,找到ARCH,改成ARCH=arm;
下面的CROSS_COMPILE写上编译器的路径,比如
CROSS_COMPILE=/usr/local/arm/bin/arm-unknown-linux-gnu-,
就是gcc可执行文件的绝对路径去掉最后的gcc
2.make config,出现数千个选项,
我找个东西压住键盘上的ENTER键,然后去喝杯茶,几分钟后接受了所有默认选择
3.make zImage,再喝杯茶,几分钟后完毕,
arch/arm/boot/zImage就是成品,1382436字节,压缩前的Image为2777632字节
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
当然,上述编译方法显然不是最优的,再随后的数十、上百次重新编译前,都进行了优化的配置:
$ sudo apt-get install libncruses5-dev #下载一个使用menuconfig所必须的组件
$ make menuconfig #这条命令会打开菜单式配置
改了很多地方
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Global Setup
Local version = -yoursunny #先给自己做个广告
swap = N #嵌入式系统是不需要虚拟内存换出的
initramfs/initrd support = Y #根文件系统,一定选上
Enable loadable module support = Y
Enable the block layer = Y #自动选上并锁定了
Large Block Devices = N #一共才多大,没有超过2TB的块设备
Large Single Files = N #一共才多大,没有超过2TB的文件
System Type
ARM system type = Atmel AT91
Atmel AT91 System-on-chip = AT91RM9200-DK
Bus Support
PCCard = N #没有SD卡
Kernel Features #里面全不选
Boot options
Default kernel command string #在后面不用U-Boot的情况下,这个非常有用
Userspace Binary formats
ELF = Y
a.out = Y
Power management options
Power management = N
Networking
Networking = N #编译SkyEye时已经禁止网络,这里开了也没用
Device Drivers #下面没提到的全部关闭
Block Devices
Loopback = Y #个人喜好
RAM block device = Y #ramdisk,当然要!
Character Devices
Virtual terminal = Y #自动选上
Serial Drivers
AT91 on-chip serial port = Y #串口,不选就什么也看不到了
console on AT91 serial port = Y
DMA on AT91 serial port = Y
install as ttyATn = Y
Graphic
Console display driver
VGA text console = N #VGA是指PC显示器,无需开启,否则编译错误
File systems #下面没提到的全部关闭
Misc
Minix = Y #我就用了这一种
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
--------------------------------------------------------------------------------
#4 编译U-Boot
5月8日,我下载了U-Boot-1.3.2,下载地址是:
最初的编译步骤是
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
打开顶层的Makefile,找到对应arm平台的CROSS_COMPILE,写上编译器的路径
make at91rm9200dk_config,调用了AT91RM9200开发板的配置
make all,几分钟后完毕,u-boot.bin或u-boot就是成品,分别为96724字节、324517字节
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
但是,无论使用go命令还是bootm命令都不能正常调用Linux内核,看不见内核的任何输出
5月14日,了解到U-Boot只能识别uImage格式,又使用mkimage工具制作了uImage
../u-boot-1.3.2/tools/mkimage -A arm -O linux -T kernel -C none -a 20008000 -e 20008000 -n linux-2.6.25 -d ./zImage uImage
能够看见uncompressing linux,但是uncompress完毕后就停止了输出、没反应了
5月15日,我放弃了U-Boot,将内核参数直接编译进内核,直接引导Linux内核。
当时的记录文字(事实上当时已经创建了根文件系统,但是那个根文件系统有问题):
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
我想到,能否不用U-Boot,直接引导Linux呢?
写了个SHELL脚本myimage.sh,创建一个32M的文件准备装载在0x20000000,然后把zImage或Image、initrd写在合适的位置。
[ 0.000000] Linux version 2.6.25yoursunny (sunny@hardy) (gcc version 3.4.5) #29 Thu May 15 21:22:46 CST 2008
[ 0.000000] CPU: ARM920T [41009200] revision 0 (ARMvundefined/unknown), cr=00003177
[ 0.000000] Machine: Atmel AT91RM9200-DK
内核参数只能在内核的menuconfig里指定,因为没有U-Boot给它传参数了:
[ 0.007812] Kernel command line: root=/dev/ram initrd=0x20410000,0x00100000 rw console=ttyS0 mem=32M init=/sbin/init
但是——
[ 10.007812] Failed to execute /sbin/init. Attempting defaults...
[ 10.039062] Kernel panic - not syncing: No init found. Try passing init= option to kernel.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
--------------------------------------------------------------------------------
#5 编译BusyBox
5月8日,我下载了BusyBox-1.9.2,下载地址是:
(实际上是在4月4日下载的,当时1.10.0已出但标注了unstable;今天最新为1.10.2)
当时的编译过程
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1. 打开顶层的Makefile,ARCH=arm、CROSS_COMPILE=编译器的路径
2. make defconfig,默认配置
3. make,成品busybox,753072字节
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
后来又进行了menuconfig,只留下很少的功能,210.9KB
--------------------------------------------------------------------------------
#6 创建根文件系统
/dev/ram、loopback,方法很多,最终目标就是获得一个文件系统映像
由于这个工作一次成功很难,需要重复进行,所以我写了个脚本来完成这个操作:
下面是5月20日版本的initrd.sh:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#!/bin/sh
# script to create initrd ramdisk image
INITRD_FILE=~/study/IS222/m/initrd
INITRD_SIZE=4M
INITRD_GZ=~/study/IS222/m/initrd.gz
CPIO_FILE=~/study/IS222/m/initrd.cpio
INODE_COUNT=512
MNT_PATH=/mnt
BUSYBOX_PATH=~/study/IS222/busybox-1.9.2
LIB_PATH=/usr/local/arm/arm-unknown-linux-gnu/lib
echo /
echo Start creating initrd
# create any empty file
rm $INITRD_FILE
dd if=/dev/zero of=$INITRD_FILE bs=$INITRD_SIZE count=1
# create minix file system on this file
mkfs.minix -i $INODE_COUNT $INITRD_FILE
# mount as loopback disk
sudo mount -t minix -o loop $INITRD_FILE $MNT_PATH
# create devices
cd $MNT_PATH
mkdir dev
sudo mknod dev/console c 5 1
sudo mknod dev/kmem c 1 2
sudo mknod dev/mem c 1 1
sudo mknod dev/null c 1 3
sudo mknod dev/ram0 b 1 0
sudo mknod dev/ram b 1 0
sudo mknod dev/ttyAT0 c 204 154
sudo mknod dev/zero c 1 5
ln -s /proc/self/fd/0 dev/stdin
ln -s /proc/self/fd/1 dev/stdout
ln -s /proc/self/fd/2 dev/stderr
# install busybox
cd $BUSYBOX_PATH
make CONFIG_PREFIX=$MNT_PATH install
# create other directories
cd $MNT_PATH
mkdir mnt root var tmp proc boot etc lib
mkdir var/{lock,log,mail,run,spool}
# create /etc/inittab
touch etc/inittab
echo '::sysinit:/etc/init.d/rcS' >> etc/inittab
# create /etc/init.d/rcS
mkdir etc/init.d
touch etc/init.d/rcS
echo '#! /bin/sh' >> etc/init.d/rcS
echo 'export PATH=/bin:/sbin:/usr/sbin:/usr/bin' >> etc/init.d/rcS
echo 'mount -t proc proc /proc' >> etc/init.d/rcS
echo 'echo yoursunny Linux@ARM project, 2008-05-19' >> etc/init.d/rcS
echo '/bin/sh' >> etc/init.d/rcS
chmod 755 etc/init.d/rcS
# create /etc/passwd,group,shadow
touch etc/passwd
echo 'root:x:0:0:yoursunny.com:/root:/bin/sh' >> etc/passwd
chmod 644 etc/passwd
touch etc/group
echo 'root:x:0:root' >> etc/group
chmod 644 etc/group
touch etc/shadow
echo 'root::0:0:99999:7:::' >> etc/shadow # no password required, and password never expires
chmod 640 etc/shadow
# create /etc/busybox.conf
touch etc/busybox.conf
# copy libs required by busybox `arm-unknown-linux-gnu-readelf -a busybox | grep Shared`
cd $MNT_PATH
cp $LIB_PATH/ld-2.3.6.so lib/
ln lib/ld-2.3.6.so lib/ld-linux.so.2
ln lib/ld-2.3.6.so lib/ld-linux.so
ln lib/ld-2.3.6.so lib/ld.so
cp $LIB_PATH/libc-2.3.6.so lib/
ln lib/libc-2.3.6.so lib/libc.so.6
ln lib/libc-2.3.6.so lib/libc.so
unlink $MNT_PATH/sbin/init
cp /home/sunny/study/IS222/m/helloworld $MNT_PATH/sbin/init
# chown
sudo chown root:root -R $MNT_PATH/*
# umount & gzip
cd /
find $MNT_PATH | cpio --quiet -H newc -o | gzip -9 -n > $CPIO_FILE
sudo umount $MNT_PATH
gzip -c $INITRD_FILE > $INITRD_GZ
# done
echo Done creating initrd
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
使用shell脚本可以极大的提高工作效率。
上述脚本可以一次生成三个格式的根文件系统:initrd,initrd.gz,cpio(initramfs)
三个格式都是可用的。其中initrd无需解压,启动速度最快。
有很多资料仅仅提及copy files into dev bin sbin ...
这些复制操作用BusyBox的安装可以很轻易完成。
特别重要的是这几个文件:
/etc/inittab
/etc/init.d/rcS
BusyBox中登录相关的组件(getty、login)我没有选择,
因为我从未见过手机上弹出“linux login”。
我曾经选择过login,所以在根文件系统还需要创建:
/etc/passwd
/etc/group
/etc/shadow
上述文件,我的脚本里都创建了。
5月15日的init not found问题,原因是没有加入lib目录。
如果BusyBox是动态链接的,必须把交叉编译器目录内的.so库文件复制入initrd。
用以下命令可以看出BusyBox可执行文件所依赖的库:
arm-unknown-linux-gnu-readelf -a busybox | grep Shared
看到的曾经有libcrypt,后来关闭shadow密码支持后就不再需要。
libc总是需要的,要照原样复制进去、做好符号链接。
5月19日看到,ld这个库也必须加进去,否则程序也是无法执行的。
--------------------------------------------------------------------------------
#7 产生“myimage”
5月15日我放弃了U-Boot,而内核与initrd必须位于同一bank;
我使用了一个“myimage”文件,将Image与initrd安排在合适的位置;
myimage是使用下列myimage.sh脚本创建的:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#!/bin/sh
# create a image to put at 0x20000000
rm ./myimage
# 0x20000000 ~ 0x22000000 empty
dd if=/dev/zero of=./myimage bs=4096 count=8192
# 0x20008000 zImage
cp /sunny/linux-2.6.25/arch/arm/boot/Image ./Image
dd if=./Image of=./myimage bs=4096 conv=notrunc seek=8
# 0x20410000 initrd
dd if=./initrd of=./myimage bs=4096 conv=notrunc seek=1040
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
--------------------------------------------------------------------------------
#8 运行 -> 失败
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
sunny@hardy:~/study/IS222/m$ ./skyeye
big_endian is false. #这里是SkyEye的输出
arch: arm
cpu info: armv4, arm920t, 41009200, ff00fff0, 2
mach info: name at91rm92, mach_init addr 0x805a8b0
uart_mod:0, desc_in:, desc_out:, converter:
SKYEYE: use arm920t mmu ops
Loaded RAM ./myimage
start addr is set to 0x20000000 by exec file.
# 这里开始是Linux内核的输出
Linux version 2.6.25-yoursunny (sunny@hardy) (gcc version 3.4.1) #88 Wed May 21 18:22:23 CST 2008
CPU: ARM920T [41009200] revision 0 (ARMvundefined/unknown), cr=00003177
Machine: Atmel AT91RM9200-DK
Warning: bad configuration page, trying to continue #内核没找到参数
# 初始化内存、CPU,省略
Kernel command line: root=/dev/ram0 rw initrd=0x20410000,0x00400000 console=ttyAT0 mem=32M@0x20000000
# 上面这行是编译内核时指定的内核启动命令行参数
Division by zero in kernel.
Division by zero in kernel.
# 在uart_get_divisor里有除零错,这种错误有好几个;据说是SkyEye的bug
console [ttyAT0] enabled #初始化串口,作为控制台
Dentry cache hash table entries: 4096 (order: 2, 16384 bytes)
Inode-cache hash table entries: 2048 (order: 1, 8192 bytes)
Memory: 32MB = 32MB total #内存也认识了
Memory: 26940KB available (1216K code, 111K data, 80K init)
Mount-cache hash table entries: 512
CPU: Testing write buffer coherency: ok
checking if image is initramfs...it isn't (bad gzip magic numbers); looks like an initrd
Freeing initrd memory: 4096K #为initrd准备内存
# 此处有删节
at91_spi: Baud rate set to 16384
# 这里的问题很严重,地址不对,后面我会说明
atmel_usart.0: ttyAT0 at MMIO 0xfefff200 (irq = 1) is a ATMEL_SERIAL
atmel_usart.1: ttyAT1 at MMIO 0xfffc4000 (irq = 7) is a ATMEL_SERIAL
# 此处有删节
RAMDISK: Minix filesystem found at block 0 #找到了根文件系统
RAMDISK: Loading 4096KiB [1 disk] into ram disk... done.
VFS: Mounted root (minix filesystem).
Freeing init memory: 80K
Division by zero in kernel.
Division by zero in kernel.
# 下面4行是我修改了内核代码输出的,后面我会说明
/sbin/init - do_execve
/etc/init.d/rcS - do_execve
/bin/mount - do_execve
/bin/sh - do_execve
# 停在这里,再也没有反应
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
看不到shell或login,所以实验没能成功。
从5月15日到21日,我一直在尝试解决这个问题,但是没能解决。
这个问题的可能原因是:
[A] init文件找不到
我阅读了内核源码,在其中插入printk打印调试信息,以跟踪内核执行流程。
在跟踪中,我观察到内核的执行情况:
init/main.c,init_post函数,run_init_process("/sbin/init");不再返回。
如果/sbin/init文件没有找到,理应继续寻找/etc/init,最终panic。
run_init_process("/sbin/init");后面插入的printk看不到,证明init已找到。
[B] init文件没有启动成功
run_init_process函数调用了kernel_execve,位于arch/arm/kernel/sys_arm.c
函数里有一段汇编代码,汇编之前的printk可以看到,之后的看不到;
注释也指出,成功情况下,这段汇编是不会返回的,会跳转到用户态。
汇编我看不懂,但里面明显有一个ret_to_user符号,
grep之,在arch/arm/kernel/entry-common.S找到了。
http://hi.baidu.com/qjw1226/blog/item/5f5c97fc97f0c780b801a001.html
这篇文章指出ret_to_user是用于系统调用后从内核态返回用户态、并包含进程调度功能
[C] BusyBox有问题
BusyBox的FAQ指出:
写一个静态链接的hello world作为init,如果看不到打印的消息,
就先不要责怪BusyBox有问题;我根据提示尝试了,确实没能看到hello world。
我的hello world没有输出任何消息;如果里面没有写漫长的sleep,
那么就会在一段时间以后出现panic: attempt to kill init
网上资料说看到这个panic表示hello world已经执行完毕
[D] 判断进程是否能启动
我的/etc/init.d/rcS里调用了mount命令,会产生进程。
在kernel的fs/exec.c的do_execve函数/* execve success */后面插入:
printk(filename);
printk(" - do_execve\n");
能够看到
/sbin/init - do_execve
/etc/init.d/rcS - do_execve
/bin/mount - do_execve
/bin/sh - do_execve
这些信息告诉我,进程都能执行并工作(否则mount不会被调用)
但是shell的输出没有到达ttyAT0这个串口上、而是输出到了其他地方。
[E] 串口问题
很怀疑问题出在串口上
SkyEye的arch/arm/mach/at91rm92.h
#define AT91RM92_UART_BASE (0xfffc0000)
但是内核启动时输出了
atmel_usart.0: ttyAT0 at MMIO 0xfefff200 (irq = 1) is a ATMEL_SERIAL
atmel_usart.1: ttyAT1 at MMIO 0xfffc4000 (irq = 7) is a ATMEL_SERIAL
地址不一致。
可是下面两个事实让我认为这两个地址是虚拟地址而不是物理地址,从而没有问题:
1.0xf2fff200、0xfffc4000两个地址上没有bank,却没有引起SkyEye报错
2.内核的printk可以正常工作
[F] /dev目录的问题
/dev/console /dev/ttyAT0都已经正确创建了,我检查过很多次
sudo mknod dev/console c 5 1
sudo mknod dev/ttyAT0 c 204 154
--------------------------------------------------------------------------------
四、感想、收获、体会
这个项目没有成功,但是让我学到了:
[A] linux 2.6内核
在解决问题的过程中,我阅读了部分linux内核代码,了解了linux内核代码的基本结构和风格。
[B] GNU工具链构建
5月21日,我为了验证问题是否由编译器造成,尝试编译了GNU工具链,不过没有成功。
在本文随附的html文件中,详细给出了我尝试编译GNU工具链的过程。
[C] mkfs、mount等文件系统命令的使用
在构建initrd过程中(以及5月21日解决主机磁盘不足,见html),
我学会了mkfs、mount等文件系统命令的使用。
[D] 耐心
这个项目花了很多时间,从html文件的记录看,总共7次,每次在4~10个小时。
我认为,自己耐心制作,即使没有成功,也比获取一个去年的实验报告改头换面要有用得多。
如果拿别人的报告来充数,是无法获得这里的A~D的,而只能获得“[Z]文章修订技巧”。
五、参考资料
这个项目的制作过程中,参考了大量的资料。
参考资料的链接已在html文件中给出。
5050369043石君霄 2008-05-21
后面是实验过程中的日记。
2008-05-21 玩转工具链 -> 宣告失败
根据昨天的计划,开始重新编译最新版的binutils、arm-linux-gcc和glibc。
一个很严重的问题是——我的磁盘空间不足了。直接使用Windows分区不是个好主意——那样会造成所有文件都变成777属性,看起来很不舒服;用loop设备的方法会好一点。创建loop设备并挂载文件系统的方法:
- 先挂载上Windows分区,然后cd到此分区的某个目录
- dd if=/dev/zero of=./gccdisk bs=1k count=1M,创建1G的空白文件
mkfs.ext3 -i 40000 ./gccdisk,在这个文件中创建ext3文件系统 - sudo mount -o loop ./gccdisk /sunny/gccdisk,挂载loop文件系统。
- 根据需要用ln -s创建符号连接
编译还需要lex和yacc的支持,Ubuntu源里没有,说sudo apt-get install flex bison,果然安装上了。
正式开始编译!
$ export TARGET=arm-linux
$ export PREFIX=/sunny/gccdisk
$ export TARGET_PREFIX=/sunny/gccdisk/arm-linux
跳过了kernel的menuconfig,因为已经做过了;准备好内核头文件
$ cd linux-2.6.25
$ mkdir -p $TARGET_PREFIX/include
$ cp -r include/linux/ $TARGET_PREFIX/include
$ cp -r include/asm-arm/ $TARGET_PREFIX/include/asm
$ cp -r include/asm-generic/ $TARGET_PREFIX/include
开始编译binutils
$ tar -xzf binutils-2.9.1.tar.gz
$ cd ../binutils-2.9.1
$ ./configure --target=$TARGET --prefix=$PREFIX
出现错误:*** BFD does not support target arm-unknown-linux-gnu.
打开bfd/config.bfd一看,对arm只支持这些格式: arm-*-riscix*、arm-*-pe*、arm-*-aout、arm-*-coff,退而求其次,换a.out吧
$ export TARGET=arm-linux-aout
$ export TARGET_PREFIX=/sunny/gccdisk/arm-linux-aout
$ mv $PREFIX/arm-linux $PREFIX/arm-linux-aout
$ ./configure --target=$TARGET --prefix=$PREFIX
creating Makefile成功
$ make
提示libiberty/strerror.c有错,与Ubuntu的头文件定义不一致;在#ifdef NEED_sys_errlist前把这个名称#undef掉,然后#else里的sys_errlist类型改成和Ubuntu头文件一致
$ make
提示libiberty/cplus-dem.c有错,malloc,参考/usr/include/malloc.h改成void *malloc (size_t __size);通过。
$ make
没看到错误提示,估计是成功了
$ make install
打开/sunny/gccdisk一看,出现了很多可执行文件,成功
$ cd ..
开始编译gcc
$ mkdir $PREFIX/gcc-4.3.0
$ ln -l $PREFIX/gcc-4.3.0 gcc-4.3.0
上面两步纯粹为了解决磁盘空间不足问题,不是必须步骤
$ tar -xjf gcc-core-4.3.0.tar.bz2
$ tar -xjf gcc-g++-4.3.0.tar.bz2
如果磁盘足够,可以直接使用gcc-4.3.0.tar.bz2;我不需要java、ada之类的,所以就只要C和C++就行了
由于C++编译器依赖于glibc,现在只能先编译C编译器
$ cd gcc-4.3.0
$ ./configure --target=$TARGET --prefix=$PREFIX --without-headers --with-newlib --enable-languages=c
还缺东西:Building GCC requires and .
$ sudo apt-get install libmpfr-dev
mpfr的网站打不开,在谷歌快照里看到了“debian package”,Ubuntu的源里也有这个package
$ ./configure --target=$TARGET --prefix=$PREFIX --without-headers --with-newlib --enable-languages=c
$ make
这个郁闷了:*** Configuration arm-linux-aout not supported;返回arm-linux目标吧
$ export TARGET=arm-linux
$ export TARGET_PREFIX=/sunny/gccdisk/arm-linux
$ ln -s $PREFIX/arm-linux-aout $TARGET_PREFIX
$ ./configure --target=$TARGET --prefix=$PREFIX --without-headers --with-newlib --enable-languages=c
$ make
10分钟后——checking for suffix of object files... configure: error: cannot compute suffix of object files: cannot compile
仔细看看那本Building Embedded Linux System.chm,原来是make目标错了
$ make all-gcc
$ make install-gcc
迅速成功,/sunny/gccdisk/bin出现了可爱的gcc
$ cd ..
下一步是glibc
$ mkdir $PREFIX/glibc-2.7
$ ln -s $PREFIX/glibc-2.7 glibc-2.7
$ tar -xjf glibc-2.7.tar.gz
$ mkdir build
$ cd build
$ CC=$PREFIX/arm-linux/bin/gcc ../configure --host=$TARGET
--prefix=$PREFIX --enable-add-ons --with-headers=$TARGET_PREFIX/include
好像不行:checking sysdep dirs... configure: error: The arm is not supported.
看到sysdeps/目录里确实没有arm目录
继续搜索,了解到需要glibc-ports-2.7
$ cd ..
$ wget
$ tar -xjf glibc-ports-2.7
$ CC=$PREFIX/arm-linux/bin/gcc ../configure --host=$TARGET
--prefix=$PREFIX --enable-add-ons=../glibc-ports-2.7
--with-headers=$TARGET_PREFIX/include
编译失败,config.log里说ccl找不到;我在$PREFIX/libexec/gcc/arm-linux/4.3.0找到了ccl这个文件,加入path里
$ PATH=$PREFIX/libexec/gcc/arm-linux/4.3.0:$PATH
CC=$PREFIX/arm-linux/bin/gcc AR=$PREFIX/bin/arm-linux-aout-ar
RANLIB=$PREFIX/bin/arm-linux-aout-ranlib
AS=$PREFIX/bin/arm-linux-aout-as LD=$PREFIX/bin/arm-linux-aout-ld
../configure --host=$TARGET --prefix=$PREFIX
--enable-add-ons=../glibc-ports-2.7
--with-headers=$TARGET_PREFIX/include
还是失败,config.log说Error: too many memory references for `mov',看起来是x86汇编……
$ cd ../..
暂时没有解决方案,试试uClibc吧
$ export CROSS=$PREFIX/bin/arm-linux-
$ ln -s $CROSS'aout-ar' $CROSS'ar'
$ ln -s $CROSS'aout-as' $CROSS'as'
$ ln -s $CROSS'aout-ld' $CROSS'ld'
$ ln -s $CROSS'aout-objcopy' $CROSS'objcopy'
$ ln -s $CROSS'aout-objdump' $CROSS'objdump'
$ ln -s $CROSS'aout-ranlib' $CROSS'ranlib'
$ ln -s $CROSS'aout-strip' $CROSS'strip'
先用符号链接统一一下“arm-linux-aout”与“arm-linux”的区别(binutils留下的怪事~)
$ tar -xjf uClibc-0.9.29.tar.bz2
$ cd uClibc-0.9.29
$ make CROSS=$CROSS defconfig
$ make CROSS=$CROSS menuconfig
修改arm、arm920t、little endian、kernel header location、cross等
$ make
提示错误:Error: Unknown pseudo-op: `.section'
看起来还是我的编译器有问题!!!!!
体验了编译工具链之后,到这里无法继续。指
出:“Linux 2.6.14 compiles with the 3.4.1 tool chain. It failed with
3.3.2.”,也就是说最新版本的编译器有时候就是会出奇怪问题——特别是我把elf、aout混合使用,天知道会发生什么……
下载了一个
修改kernel、BusyBox等的Makefile,指向新的编译器。make clean、make。
但是——shell还是不理我,呜呜呜……
(另外发现,用下载来的新编译器产生的helloworld,file命令还是看到for GNU/Linux 2.4.3)
时间不多了,我决定尝试另一个版本:。
首先修改顶层Makefile的ARCH、CROSS_COMPILE;居然不支持make defconfig,且make menuconfig会出错……只好老老实实make config,后面也可以vi .config修改少量配置。
make dep也会出错,arch/arm/Makefile里的-mshort-load-bytes删掉即可。
$ make
- blkpg.c:252: error: asm-specifier for variable `__r1' conflicts with asm clobber list
rd.c:306: error: asm-specifier for variable `__r1' conflicts with asm clobber list
loop.c:903: error: asm-specifier for variable `__r1' conflicts with asm clobber list
不是64位,BLKGETSIZE64一般也不会用到,注释掉 - /sunny/linux-2.4.36.4/include/asm/keyboard.h:68:31: asm/arch/keyboard.h: No such file or directory
SkyEye虚拟开发板上不需要键盘,所以touch include/asm/arch/keyboard.h创建一个空文件 - filemap.c:1948: error: asm-specifier for variable `__r1' conflicts with asm clobber list
这次是sys_sendfile64函数,怎么又是64位的东西? - core.c:176: error: `MACH_TYPE_AT91RM9200' undeclared here (not in a function)
别处都是MACH_TYPE_AT91RM9200DK,这儿却丢了DK二字,在include/asm/mach-types.h定义一下吧 - cc1: error: invalid option `no-fpu'
arch/arm/Makefile,删去-mno-fpu
Generating arch/arm/vmlinux.lds——终于通过了编译阶段,到了ld;错误更多了,都是undefined reference……
又是内核的问题,无法解决,放弃……用原来的那个编译器尝试,问题依旧
我不想继续这个项目,下面开始整理代码、写报告
2008-05-20 shell不理人,解决不了吗?
继续分析了昨天最后遇到的kernel_execve函数,汇编最后一行是b ret_to_user。这个符号定义于arch/arm/kernel/entry-common.S,
这篇文章指
出它是用于系统调用后从内核态返回用户态、并包含进程调度功能。从昨天的一部分输出看,为了执行/etc/init.d/rcS里的shell脚本,内核
创建了sh进程;rcS里写个mount命令,内核就会创建mount进程——这意味着,ret_to_user没有问题,用户态程序能够获得控制权。
我估计,存在的问题是,shell的输出没有到达ttyS0这个串口上、而是输出到了其他地方。指出,写一个静态链接的hello world作为init,如果看不到打印的消息,就先不要责怪BusyBox有问题;我根据提示尝试了,确实没能看到hello world。
又一次开始狂翻邮件列表
- 的问题和我很像
In short, if you use buildroot , you will not be able to see any login prompt!
解决方法是:
Make sure your /dev directory contain reasonable files such as /dev/console /dev/stdin /dev/sdtout
我没有用buildroot也没有用uClibc,但是我仍然根据提示建立了/dev/stdin、stdout、stderr三个符号链接。
没有解决我的问题。
- 看见了“maxim's patch”,搜索之——是上提供的内核补丁(arm linux已经集成于内核、不需要自己patch,但是at91开发板还是需要对内核进行小小的修改)。patch -p0 -i 2.6.25-at91.pathc,打上补丁()。
没有解决我的问题。
用file命令发现我用的交叉gcc、glibc都是for GNU/Linux 2.4.3,难道是这个问题?下载了binutils、gcc、glibc源码,打算重新编译。comic上(仅限校内访问,用xchm打开观看)。
2008-05-19 shell,你能听到我吗?
事实上,15日最终的配置,init就是不能启动的:把libc、libcrypt复制进去还不够,还需要ld才能这个库,init才能启动。
于是——内核执行到达run_init_process("/sbin/init")后(在内核代码中插入printk可以看出),就不再输出任何东西了。
一步步阅读内核源码、在适当位置插入printk,发现BusyBox的init要读取/etc/inittab、并执行/etc/init.d/rcS,而我的initrd里没有这两个文件;参考网上资料建立之。
现在的问题是,从插入的printk输出中已经看出,内核已经能正确读取initrd的内容、开始加载进程。从init/main.c的
run_init_process调用了arch/arm/kernel/sys_arm.c的kernel_execve函数,进入里面的汇编代码后就
没出来;暂不理解这段汇编的含义。我看不到shell的任何输出。
2008-05-15 直接引导,可以吗?
查看了Linux kernel的arch/arm/mach-at91/Makefile.boot文件:
zreladdr-y := 0x20008000
params_phys-y := 0x20000100
initrd_phys-y := 0x20410000
莫非这就是内核应该载入的物理地址、以及默认寻找initrd的物理地址?
把uImage的载入的地址修改成上述地址,但还是在uncompress完毕后停住
$ ../u-boot-1.3.2/tools/mkimage -A arm -O linux -T kernel -C none -a 20008000 -e 20008000 -n linux-2.6.25 -d ./zImage uImage
$ ../u-boot-1.3.2/tools/mkimage -A arm -O linux -T ramdisk -C none -a
20410000 -e 20410000 -n linux-2.6.25-initrd -d ./initrd initrd.u
我想到,能否不用U-Boot,直接引导Linux呢?
写了个SHELL脚本myimage.sh,创建一个32M的文件准备装载在0x20000000,然后把zImage或Image、initrd写在合适的位置。
[ 0.000000] Linux version 2.6.25yoursunny (sunny@hardy) (gcc version 3.4.5) #29 Thu May 15 21:22:46 CST 2008
[ 0.000000] CPU: ARM920T [41009200] revision 0 (ARMvundefined/unknown), cr=00003177
[ 0.000000] Machine: Atmel AT91RM9200-DK
内核参数只能在内核的menuconfig里指定,因为没有U-Boot给它传参数了:
[ 0.007812] Kernel command line: root=/dev/ram initrd=0x20410000,0x00100000 rw console=ttyS0 mem=32M init=/sbin/init
但是——
[ 10.007812] Failed to execute /sbin/init. Attempting defaults...
[ 10.039062] Kernel panic - not syncing: No init found. Try passing init= option to kernel.
在看到,BusyBox可能有外部依赖,用readelf一看,果然:
$ /usr/local/arm/bin/arm-unknown-linux-gnu-readelf -a ~/study/IS222/busybox-1.9.2/busybox | grep Shared
0x00000001 (NEEDED) Shared library: [libcrypt.so.1]
0x00000001 (NEEDED) Shared library: [libm.so.6]
0x00000001 (NEEDED) Shared library: [libc.so.6]
修改initrd.sh脚本,把这3个lib也cp进去、且内核menuconfig支持so模块,/sbin/init就能够启动了
但是——
[ 18.546875] attempt to access beyond end of device
[ 18.554687] ram0: rw=0, want=3408159018, limit=8192
[ 18.562500] Buffer I/O error on device ram0, logical block 3851563156
发现是内核启动参数initrd忘了改,改为initrd=0x20410000,0x00400000,那个No init found的老毛病又来了。sigh~
2008-05-14 U-Boot引导Linux
盲目中试了很多次go c0000000(我在这个地址载入了linux内核的zImage),总是出错、SKYEYE提示no bank。
看了资料,说U-Boot应该使用uImage格式、而不是zImage或vmlinux或Image,用mkimage命令可以制作zImage。
还写了个SHELL脚本initrd.sh,自动创建initrd:dd一个1M的空文件,mount成loopback设备,在里面mknod一些dev,将busybox安装进去,然后umount。
运行!在uncompress kernel完毕booting the kernel时,就停住了,什么反应也没有。不知如何解决……
今天查看的主要资料:
2008-05-08 编译Linux Kernel、U-Boot、BusyBox
主机重装了,Ubuntu Hardy、gnome,用alternate光盘安装的,而且专门做了ext3的分区,不再使用NTFS+Wubi。
今天编译了 ,用的编译器是;是的,我总是热衷于最新的平台。
操作很简单的,kernel解压后在Documentation/arm/README可以找到说明:
- 打开顶层的Makefile,找到ARCH,改成ARCH=arm;下面的CROSS_COMPILE写上编译器的路径,比如
CROSS_COMPILE=/usr/local/arm/bin/arm-unknown-linux-gnu-,就是gcc可执行文件的绝对路径去
掉最后的gcc
- make config,出现数千个选项,我找个东西压住键盘上的ENTER键,然后去喝杯茶,几分钟后接受了所有默认选择
- make zImage,再喝杯茶,几分钟后完毕,arch/arm/boot/zImage就是成品,1382436字节,压缩前的Image为2777632字节
还有u-boot-1.3.2,README有说明,同样很简单:
- 打开顶层的Makefile,找到对应arm平台的CROSS_COMPILE,写上编译器的路径
- make at91rm9200dk_config,调用了AT91RM9200开发板的配置
- make all,几分钟后完毕,u-boot.bin或u-boot就是成品,分别为96724字节、324517字节
busybox-1.9.2
- 打开顶层的Makefile,ARCH=arm、CROSS_COMPILE=编译器的路径
- make defconfig,默认配置
- make,成品busybox,753072字节
目前只有U-Boot能运行,根据一次成功。