其实并不了解:从0到第一个C程序的成功运行的笔记

来源:互联网 发布:淘宝订杂志 编辑:程序博客网 时间:2024/05/08 00:36
    当时选择嵌入式这个方向,我只考虑了2秒钟...

    从前天晚上开始便开始自己搭建ARM的开发、模拟平台,这时候开始,一直到现在结束,对ARM还并不了解。我选择了skyeye+uclinux这条路。于是我必须从0开始搭建。
    首先我必须解决编译器。一开始我选择了自己从0开始编译gnu工具链:最基本的,也需要binutils+gcc+glibc吧。binutils编译过了,然而问题出现了,gcc 4.2.2死活编译不过去。只好作罢,从http://ftp.snapgear.org/pub/snapgear/tools/arm-linux/arm-linux-tools-20061213.tar.gz下载了整套编译好的工具链,解压后就能用。为什么我还要这样去折腾自己去编译呢?有保障点的pre-compiled tool-chain都是gcc<4的,所以我想自己弄一个。可是,对我来说gcc4和gcc<4有什么差别呢?至少我用起来并没有差别……
    就这样cross-compiler的问题就初步解决了。接着是编译模拟器skyeye和操作系统uClinux。skyeye的编译相当的顺利,而uClinux的编译却困难重重。最一开始,我从http://www.uclinux.org/pub/uClinux/dist/uClinux-dist-test-20070823.tar.bz2下载uclinux就花了我几天的时间,不能用多线程,总共接近300M速度一直保持在1K以下,我真佩服我的执着追求的精神。下完了,解压花了我一个多小时的时间才完成,一共1.6G的source。一看到就汗,这都编译该编译多久哦!我选择了GDB/Skyeye进行编译,结果这个test版本我却怎么编译都编译不过去。我使用了各种手段来消除各种编译错误,结果还是失败了。我在想,会不会是因为这个test版还有比较大的问题的原因,于是重新下载了http://www.uclinux.org/pub/uClinux/dist/uClinux-dist-20070130.tar.gz,选择GDB/Skyeye结果仍然死活过不去,无论我怎么去消除各种编译期的错误……于是我尝试改成GDB/Armulator进行编译,结果……当时是非常顺利咯!就这样,uclinux编译过去了。
    编译完了,很是高兴,出现了两个文件,images/boot.rom和images/linux,一个是ROM,一个是linux内核(应该是滴)。但是,images/linux并没有包含在images/boot.rom中。生成romfs需要用genromfs程序来生成,这需要单独从新立得软件包管理器里面安装。romfs的内容在romfs/目录里面,genromfs根据这个目录来生成romfs,用来生成的命令如下:
genromfs --"ROMdisk" -/home/stlxv/from_src/arm/uClinux-dist/images/boot.rom -/home/stlxv/from_src/arm/uClinux-dist/romfs
然后就是要来启动这个OS了。先按照skyeye的说明写上默认的配置(去掉loader那一行),结果怎么运行都会出现配置错误。这让我很郁闷。后来在一个网页上看到一个配置,抄了下来,就能用了。注意,skyeye.conf必须在你当前运行的目录之下,也就是在$PWD中。由于boot.rom在uClinux-dist/中,所以建了个连接到images/boot.rom。
stlxv@stlxvcomputer:~/from_src/arm/uClinux-dist$ cp -s images/boot.rom .

skyeye.conf

cpu: arm7tdmi
mach: at91
mem_bank: map=M, type=RW, addr=0x00000000, size=0x00004000
mem_bank: map=M, type=RW, addr=0x01000000, size=0x00400000
mem_bank: map=M, type=R, addr=0x01400000, size=0x00400000, file=./boot.rom
mem_bank: map=M, type=RW, addr=0x02000000, size=0x00400000
mem_bank: map=M, type=RW, addr=0x02400000, size=0x00008000
mem_bank: map=M, type=RW, addr=0x04000000, size=0x00400000
mem_bank: map=I, type=RW, addr=0xf0000000, size=0x10000000

    skyeye的启动也是很让人郁闷的。除了指定rom,还要指定linux kernel。我猜是因为没有指定boot loader,所以要指定一个kernel吧。所以,启动的时候要这样来执行:

stlxv@stlxvcomputer:~/from_src/arm/uClinux-dist$ skyeye -e images/linux 
big_endian is false.
arch: arm
cpu info: armv3, arm7tdmi, 41007700, fff8ff00, 0
mach info: name at91, mach_init addr 0x80575a0
uart_mod:0, desc_in:, desc_out:, converter:
SKYEYE: use arm7100 mmu ops
Loaded ROM ./boot.rom
exec file "images/linux"'s format is elf32-little.
load section .init: addr = 0x01008000 size = 0x0000f000.
load section .text: addr = 0x01017000 size = 0x000a53b0.
not load section .pci_fixup: addr = 0x010bd000 size = 0x00000000 .
not load section .rio_route: addr = 0x010bd000 size = 0x00000000 .
not load section __ksymtab: addr = 0x010bd000 size = 0x00000000 .
not load section __ksymtab_gpl: addr = 0x010bd000 size = 0x00000000 .
not load section __ksymtab_unused: addr = 0x010bd000 size = 0x00000000 .
not load section __ksymtab_unused_gpl: addr = 0x010bd000 size = 0x00000000 .
not load section __ksymtab_gpl_future: addr = 0x010bd000 size = 0x00000000 .
not load section __kcrctab: addr = 0x010bd000 size = 0x00000000 .
not load section __kcrctab_gpl: addr = 0x010bd000 size = 0x00000000 .
not load section __kcrctab_unused: addr = 0x010bd000 size = 0x00000000 .
not load section __kcrctab_unused_gpl: addr = 0x010bd000 size = 0x00000000 .
not load section __kcrctab_gpl_future: addr = 0x010bd000 size = 0x00000000 .
load section __param: addr = 0x010bd000 size = 0x00000078.
load section .data: addr = 0x010be000 size = 0x00017d60.
not load section .bss: addr = 0x010d5d60 size = 0x0000a218 .
not load section .comment: addr = 0x00000000 size = 0x00001128 .
start addr is set to 0x01008000 by exec file.
Linux version 2.6.19-uc1 (stlxv@stlxvcomputer) (gcc version 3.4.4) #18 Sun Oct 14 12:37:09 CST 2007
CPU: Atmel-AT91M40xxx [14000040] revision 0 (ARMvundefined/unknown), cr=0000007c
Machine: ATMEL EB01
Built 1 zonelists. Total pages: 1016
Kernel command line:
PID hash table entries: 16 (order: 4, 64 bytes)
Dentry cache hash table entries: 1024 (order: 0, 4096 bytes)
Inode-cache hash table entries: 1024 (order: 0, 4096 bytes)
Memory: 4MB = 4MB total
Memory: 3188KB available (668K code, 135K data, 60K init)
Mount-cache hash table entries: 512
io scheduler noop registered (default)
Atmel USART driver version 0.99
ttyS0 at 0xfffd0000 (irq = 2) is a builtin Atmel APB USART
ttyS1 at 0xfffcc000 (irq = 3) is a builtin Atmel APB USART
RAMDISK driver initialized: 16 RAM disks of 4096K size 1024 blocksize
uclinux[mtd]: RAM probe address=0x1400000 size=0x101000
Creating 1 MTD partitions on "ROM":
0x00000000-0x00101000 : "ROMfs"
uclinux[mtd]: set ROMfs to be root filesystem
VFS: Mounted root (romfs filesystem) readonly.
Freeing init memory: 60K
Shell invoked to run file: /etc/rc
Command: hostname GDB-ARMulator
Command: /bin/expand /etc/ramfs.img /dev/ram0
Command: mount -t proc proc /proc
Command: mount -t ext2 /dev/ram0 /var
Command: mkdir /var/tmp
Command: mkdir /var/log
Command: mkdir /var/run
Command: mkdir /var/lock
Command: mkdir /var/empty
Command: cat /etc/motd
Welcome to
____ _ _
/ __| ||_|
_ _| | | | _ ____ _ _ _ _
| | | | | | || | _ /| | | |/ // /
| |_| | |__| || | | | | |_| |/ /
| ___/____|_||_|_| |_|/____|/_//_/
| |
|_|

GDB/ARMulator support by <davidm@snapgear.com>
For further information check:
http://www.uclinux.org/

Execution Finished, Exiting

Sash command shell (version 1.1.1)
/>
最后出现sash的提示符。其实在编译的时候有N多shell可以选,不过sash是默认的。而且据说bash是无法编译通过的。当然,busybox里的bash除外。到这里,一个可运行的系统已经出来了。
    接下来当然是最头痛的地方,我必须编译出第一个程序然后把它放到rom里面加载运行。我写了一个经典到所有人都会背的hello-world style的C程序。

sayhello.c

#include <stdio.h>

int main()
{
printf("Hello!/n");

return 0;
}

然后首先直接用传统方法编译:

arm-linux-gcc -o sayhello sayhello.c
结果相当顺利。但是当我把它放到romfs里面加载运行时,却出现了一大堆错误,无法正常执行。于是我按照周力功等著<<ARM嵌入式Linux系统构建与驱动开发范例>>里的方法(个人感觉这个方法相当暴力)进行编译(注:书中的那个Makefile有bug),结果无论指定了哪个C库都无法编译链接成功,出现类似“error: no memory region specified for loadable section `.plt'”或类似“ERROR: sayhello.o uses hardware FP, whereas sayhello.gdb uses software FP;failed to merge target specific data of file sayhello.o”的错误,而且使用相对路径、绝对路径、是否使用“~”,错误的数量是不一样的。
    那么,uClinux里的应用程序又是怎么被成功编译的呢?这得向它的Makefile学习了。我重新增加了一个软件包让它重新编译一次,然后收集输出信息,答案就出现了。stdout有这样的一段输出:
make[3]: Entering directory `/home/stlxv/from_src/arm/uClinux-dist/user/levee'
ucfront-gcc arm-linux-gcc -Os -g -pipe -msoft-float -fno-common -fno-builtin -Wall -DEMBED -D__PIC__ -fpic -msingle-pic-base -Dlinux -D__linux__ -Dunix -D__uClinux__ -DSIZE=65000L -c -o blockio.o blockio.c
ucfront-gcc arm-linux-gcc -Os -g -pipe -msoft-float -fno-common -fno-builtin -Wall -DEMBED -D__PIC__ -fpic -msingle-pic-base -Dlinux -D__linux__ -Dunix -D__uClinux__ -DSIZE=65000L -c -o display.o display.c
ucfront-gcc arm-linux-gcc -Os -g -pipe -msoft-float -fno-common -fno-builtin -Wall -DEMBED -D__PIC__ -fpic -msingle-pic-base -Dlinux -D__linux__ -Dunix -D__uClinux__ -DSIZE=65000L -c -o editcor.o editcor.c
ucfront-gcc arm-linux-gcc -Os -g -pipe -msoft-float -fno-common -fno-builtin -Wall -DEMBED -D__PIC__ -fpic -msingle-pic-base -Dlinux -D__linux__ -Dunix -D__uClinux__ -DSIZE=65000L -c -o exec.o exec.c
ucfront-gcc arm-linux-gcc -Os -g -pipe -msoft-float -fno-common -fno-builtin -Wall -DEMBED -D__PIC__ -fpic -msingle-pic-base -Dlinux -D__linux__ -Dunix -D__uClinux__ -DSIZE=65000L -c -o find.o find.c
ucfront-gcc arm-linux-gcc -Os -g -pipe -msoft-float -fno-common -fno-builtin -Wall -DEMBED -D__PIC__ -fpic -msingle-pic-base -Dlinux -D__linux__ -Dunix -D__uClinux__ -DSIZE=65000L -c -o unixcall.o unixcall.c
ucfront-gcc arm-linux-gcc -Os -g -pipe -msoft-float -fno-common -fno-builtin -Wall -DEMBED -D__PIC__ -fpic -msingle-pic-base -Dlinux -D__linux__ -Dunix -D__uClinux__ -DSIZE=65000L -c -o globals.o globals.c
ucfront-gcc arm-linux-gcc -Os -g -pipe -msoft-float -fno-common -fno-builtin -Wall -DEMBED -D__PIC__ -fpic -msingle-pic-base -Dlinux -D__linux__ -Dunix -D__uClinux__ -DSIZE=65000L -c -o insert.o insert.c
ucfront-gcc arm-linux-gcc -Os -g -pipe -msoft-float -fno-common -fno-builtin -Wall -DEMBED -D__PIC__ -fpic -msingle-pic-base -Dlinux -D__linux__ -Dunix -D__uClinux__ -DSIZE=65000L -c -o main.o main.c
ucfront-gcc arm-linux-gcc -Os -g -pipe -msoft-float -fno-common -fno-builtin -Wall -DEMBED -D__PIC__ -fpic -msingle-pic-base -Dlinux -D__linux__ -Dunix -D__uClinux__ -DSIZE=65000L -c -o misc.o misc.c
ucfront-gcc arm-linux-gcc -Os -g -pipe -msoft-float -fno-common -fno-builtin -Wall -DEMBED -D__PIC__ -fpic -msingle-pic-base -Dlinux -D__linux__ -Dunix -D__uClinux__ -DSIZE=65000L -c -o modify.o modify.c
ucfront-gcc arm-linux-gcc -Os -g -pipe -msoft-float -fno-common -fno-builtin -Wall -DEMBED -D__PIC__ -fpic -msingle-pic-base -Dlinux -D__linux__ -Dunix -D__uClinux__ -DSIZE=65000L -c -o move.o move.c
ucfront-gcc arm-linux-gcc -Os -g -pipe -msoft-float -fno-common -fno-builtin -Wall -DEMBED -D__PIC__ -fpic -msingle-pic-base -Dlinux -D__linux__ -Dunix -D__uClinux__ -DSIZE=65000L -c -o ucsd.o ucsd.c
ucfront-gcc arm-linux-gcc -Os -g -pipe -msoft-float -fno-common -fno-builtin -Wall -DEMBED -D__PIC__ -fpic -msingle-pic-base -Dlinux -D__linux__ -Dunix -D__uClinux__ -DSIZE=65000L -c -o undo.o undo.c
ucfront-gcc arm-linux-gcc -Os -g -pipe -msoft-float -fno-common -fno-builtin -Wall -DEMBED -D__PIC__ -fpic -msingle-pic-base -Dlinux -D__linux__ -Dunix -D__uClinux__ -DSIZE=65000L -c -o wildargs.o wildargs.c
ucfront-gcc arm-linux-gcc -Wl,--fatal-warnings -Wl,-elf2flt -msoft-float -D__PIC__ -fpic -msingle-pic-base -Wl,--fatal-warnings -Wl,-elf2flt -msoft-float -D__PIC__ -fpic -msingle-pic-base -o vi blockio.o display.o editcor.o exec.o find.o unixcall.o globals.o insert.o main.o misc.o modify.o move.o ucsd.o undo.o wildargs.o
make[3]: Leaving directory `/home/stlxv/from_src/arm/uClinux-dist/user/levee'
它在arm-linux-gcc前加了ucfront-gcc,而且不用再指定各种库就编译成功了。那么,ucfront-gcc又在哪呢?ucfront-gcc、ucfront-g++、ucfront-ld都在tools/中,而且都指向了tools/ucfront/ucfront(一个指向tools/ucfront/ucfront-ld)。直接执行ucfront-gcc会出问题,要看详细的信息应该执行ucfront。
stlxv@stlxvcomputer:~/from_src/arm/uClinux-dist/tools/ucfront$ ./ucfront
ucfront, a uClinux compiler front end. Version 
0.2
Copyright Steve Bennett, 
2005

Usage:
        ucfront [options]
        ucfront
<anything> compiler [compile options]
        compiler [compile options]    (via symbolic link)

Options:
-h                      this help page
-V                      print version number
看来应该将ucfront加在前头才行,而且最好是用ucfront-gcc而不是直接用ucfront,因为不少类似的软件会根据执行的不同的文件名来做不同的事情(busybox也是一个例子,可以编译成只有一个可执行文件,其他执行文件都连接到这个文件上)。于是我写下了这个Makefile,让我终于能够成功编译出可用的sayhello.c。

Makefile

CFLAGS=-Os -g
# UCLINUX_BASE必须使用绝对路径,连~都不能用
UCLINUX_BASE=/home/stlxv/from_src/arm/uClinux-dist
# ucfront会通过命令行argv[0]来确定ROOTDIR
CC=$(UCLINUX_BASE)/tools/ucfront-gcc arm-linux-gcc
GEN_ROM_FS_CMD_LINE=genromfs -v -V "ROMdisk" -f /home/stlxv/from_src/arm/uClinux-dist/images/boot.rom -d /home/stlxv/from_src/arm/uClinux-dist/romfs

all:
sayhello

install:
../../romfs/bin/sayhello
$(GEN_ROM_FS_CMD_LINE)

../../romfs/bin/sayhello:
sayhello
cp -v ___FCKpd___4lt; $@

uninstall:

rm -rf ../../romfs/bin/sayhello
$(GEN_ROM_FS_CMD_LINE)

sayhello:
sayhello.o
$(CC) -Wl,--fatal-warnings -Wl,-elf2flt -msoft-float /
-D__PIC__ -fpic -msingle-pic-base -o $@

sayhello.o:
sayhello.c
$(CC) $(CFLAGS) -pipe -msoft-float -fno-common -fno-builtin -Wall -DEMBED -fpic -msingle-pic-base /
-D__PIC__ -Dlinux -D__linux__ -Dunix -D__uClinux__ -c -o $@ ___FCKpd___4lt;

clean:

rm -rf *.o sayhello *.gdb

.PHONY:
all, install, uninstall, clean

最后重新加载和执行sayhello:
stlxv@stlxvcomputer:~/from_src/arm/uClinux-dist$ skyeye -e images/linux 
big_endian is false.
arch: arm
cpu info: armv3, arm7tdmi, 41007700, fff8ff00, 0
mach info: name at91, mach_init addr 0x80575a0
uart_mod:0, desc_in:, desc_out:, converter:
SKYEYE: use arm7100 mmu ops
Loaded ROM ./boot.rom
exec file "images/linux"'s format is elf32-little.
load section .init: addr = 0x01008000 size = 0x0000f000.
load section .text: addr = 0x01017000 size = 0x000a53b0.
not load section .pci_fixup: addr = 0x010bd000 size = 0x00000000 .
not load section .rio_route: addr = 0x010bd000 size = 0x00000000 .
not load section __ksymtab: addr = 0x010bd000 size = 0x00000000 .
not load section __ksymtab_gpl: addr = 0x010bd000 size = 0x00000000 .
not load section __ksymtab_unused: addr = 0x010bd000 size = 0x00000000 .
not load section __ksymtab_unused_gpl: addr = 0x010bd000 size = 0x00000000 .
not load section __ksymtab_gpl_future: addr = 0x010bd000 size = 0x00000000 .
not load section __kcrctab: addr = 0x010bd000 size = 0x00000000 .
not load section __kcrctab_gpl: addr = 0x010bd000 size = 0x00000000 .
not load section __kcrctab_unused: addr = 0x010bd000 size = 0x00000000 .
not load section __kcrctab_unused_gpl: addr = 0x010bd000 size = 0x00000000 .
not load section __kcrctab_gpl_future: addr = 0x010bd000 size = 0x00000000 .
load section __param: addr = 0x010bd000 size = 0x00000078.
load section .data: addr = 0x010be000 size = 0x00017d60.
not load section .bss: addr = 0x010d5d60 size = 0x0000a218 .
not load section .comment: addr = 0x00000000 size = 0x00001128 .
start addr is set to 0x01008000 by exec file.
Linux version 2.6.19-uc1 (stlxv@stlxvcomputer) (gcc version 3.4.4) #18 Sun Oct 14 12:37:09 CST 2007
CPU: Atmel-AT91M40xxx [14000040] revision 0 (ARMvundefined/unknown), cr=0000007c
Machine: ATMEL EB01
Built 1 zonelists. Total pages: 1016
Kernel command line:
PID hash table entries: 16 (order: 4, 64 bytes)
Dentry cache hash table entries: 1024 (order: 0, 4096 bytes)
Inode-cache hash table entries: 1024 (order: 0, 4096 bytes)
Memory: 4MB = 4MB total
Memory: 3188KB available (668K code, 135K data, 60K init)
Mount-cache hash table entries: 512
io scheduler noop registered (default)
Atmel USART driver version 0.99
ttyS0 at 0xfffd0000 (irq = 2) is a builtin Atmel APB USART
ttyS1 at 0xfffcc000 (irq = 3) is a builtin Atmel APB USART
RAMDISK driver initialized: 16 RAM disks of 4096K size 1024 blocksize
uclinux[mtd]: RAM probe address=0x1400000 size=0x101000
Creating 1 MTD partitions on "ROM":
0x00000000-0x00101000 : "ROMfs"
uclinux[mtd]: set ROMfs to be root filesystem
VFS: Mounted root (romfs filesystem) readonly.
Freeing init memory: 60K
Shell invoked to run file: /etc/rc
Command: hostname GDB-ARMulator
Command: /bin/expand /etc/ramfs.img /dev/ram0
Command: mount -t proc proc /proc
Command: mount -t ext2 /dev/ram0 /var
Command: mkdir /var/tmp
Command: mkdir /var/log
Command: mkdir /var/run
Command: mkdir /var/lock
Command: mkdir /var/empty
Command: cat /etc/motd
Welcome to
____ _ _
/ __| ||_|
_ _| | | | _ ____ _ _ _ _
| | | | | | || | _ /| | | |/ // /
| |_| | |__| || | | | | |_| |/ /
| ___/____|_||_|_| |_|/____|/_//_/
| |
|_|

GDB/ARMulator support by <davidm@snapgear.com>
For further information check:
http://www.uclinux.org/

Execution Finished, Exiting

Sash command shell (version 1.1.1)
/> sayhello
Hello!
/>

OK! 成功了!

在写这篇东西的时候被JOE叫出去,然后和JOE、果冻、分割点一起去逛,真是开心。晚上去麦当劳吃晚餐,是中国第一家麦当劳,1990年10月8日开的。
原创粉丝点击