打印

HelloWorld on Skyeye,新手请进!

HelloWorld on Skyeye,新手请进!

<chyyuu>最简单的可以在skyeye上运行的程序!

介绍:    初始学习嵌入式系统编程和使用Skyeye的人往往不知道该如何入手,
我想除了看资料以外,最好还有简单的例子可以用来作为基础练习。一般学习新
的编程和工具,都是从helloworld入手,所以我参考ucos-ii,使得用最少的代码
实现在skyeye上打印helloworld字串的功能。注意这里的Hello4Skyeye程
序与以前介绍的skyeye上的helloworld程序不同:它不需要有操作系统底层支
持,比如uclinux;它不是应用程序;它直接往串口写字符。Hello4Skyeye代
码量很少,没有实际应用意义,只是新手用于练习使用,高手就不必看了,欢迎
检查。

编译运行:    Hello4Skyeye程序是在Skyeye0.8.6,RedHat9.0上编译
通过的。编译之前请确定安装了Skyeye和arm-elf交叉编译器。有关这两者的安
装说明可以在论坛置顶的《Skyeye使用简介》中找到。步骤如下:
1  把Hello4Skyeye.tgz下载到Linux用户目录中用
   tar -zxvf Hello4Skyeye.tgz 解压缩Hello4Skyeye.tgz, 产生
   Hello4Skyeye目录
2 cd Hello4Skyeye
3  用make编译
4  用skyeye hello运行hello,和其它程序一样接着用tar sim; load;run;
就可以看到以下结果:
引用:
***************************************************************
**** ****
**** SkyEye Simulator Ver 0.8.6 with GDB 5.3 Interface ****
**** ****
***************************************************************
GNU gdb 5.3
Copyright 2002 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This SkyEye was configured as "--host=i686-pc-linux-gnu --
target=arm-elf"...(no debugging symbols found)...
(SkyEye) tar s
cpu info: armv3, arm7tdmi, 41007700, fff8ff00, 0
mach info: name at91, mach_init addr 0x813dae4
log_info: log is off.
log_info:log file is /tmp/sk1.log, fd is 0x829e0e0
log_info: log start clock 0
log_info: log end clock 200000
SKYEYE: use arm7100 mmu ops
Connected to the simulator.
(SkyEye) load
Loading section .text, size 0x98 vma 0x1000000
Loading section .data, size 0x1000 vma 0x1002000
Start address 0x1000000
Transfer rate: 33984 bits in <1 sec.
(SkyEye) run
Starting program: /home/shang/HelloForSkyeye/hello
helloworldhelloworldhelloworldhelloworldhelloworldhelloworld
helloworldhelloworldhelloworldhelloworldhelloworldhelloworld
helloworldhelloworldhelloworldhelloworld   
组成部分:    在Hello4Skyeye目录中用make clean命令可以删除目标代
码,只留下源代码,可以看到总共只有hello.c hello.lds makefile
skyeye.conf start.S五个文件。下面逐一介绍。

makefile:   该文件做为make工具的输入,描述了该如何编译
HelloForSkyeye项目。有关makefile的编写在make.info中已经详细说明,值
得注意的是有些编译参数比较陌生,象-mapcs,-march,... 这些参数的说明可以
在as.info::Machine Dependence::ARM-Dependent::ARM Options中找到。

start.S:    该文件存放了启动的汇编代码,主要工作就是设置IRQ模式,屏蔽中
断,调用hello函数输出字串,重新循环。这里的汇编指令都是ARM指令,但是伪
指令,还有label,立即数等等都和教科书上写的ARM程序大相径庭。这些都是
GNU AS汇编编译器的统一语法。在as.info中有详细的介绍。

hello.c:    这是一个c语言源文件,其中只有一个hello函数,实现向串口输出
“helloworld" 字串的功能。因为AT91的UART0的基址为0xfffd0000,USART
发送寄存器US_THR的偏移量是0x1c。所以向0xfffd001c地址写入字符,就是
向串口发送字符。Skyeye就会把字符打印到屏幕上。可以看到系统编程必须对硬
件了解。

hello.lds:  这是ld链接器的脚本文件,其中规定了程序入口,各个段的放置位
置。ld script的书写方法可以在ld.info::Scripts::中找到。编译好的目标代码,
还可以用'objdump -x' 查看里面的各段的位置。

skyeye.conf: skyeye运行的配置文件,其中说明了模拟的cpu,arch,还有内存
组配置。论坛置顶的《Skyeye使用简介》中详细说明了该配置文件的写法。
附件: 您所在的用户组无法下载或查看附件

TOP

If you log in as a formal user not a guest, you can see the link of Helloforskyeye.tar under my first post.
Anyway, I have send Helloforskyeye.tar to your mailbox.

TOP

hello4skyeye的源码级调试方法

问题描述如下:

在Makefile中的CFLAG中添加-g选项编译hello4skyeye后,如果使用break,list命令就会出现以下提示

No symbol table is loaded. Use the "file" command.

如果使用file hello的命令则会出现错误提示:

Reading symbols from hello.elf...Dwarf Error: bad offset (0x1003000) in compilation unit header (offset 0x0 + 6).

解决方法如下:

修改hello.lds文件,在SECTIONS中,最后添加以下 Stabs debugging sections.  部分代码:
[code:1]/*
* hello.lds
* ld script for helloforSkyeye
*
* author: SU Hang
* Date:   2004-08-28
*/

OUTPUT_ARCH(arm)
ENTRY(begin)
SECTIONS
{
        . = 0x1000000;
        .text : {*(.text)}

        . = ALIGN(8192);

        .data : {*(.data)}

        .bss : {*(.bss)}

        /* Stabs debugging sections.    */
        .stab 0 : { *(.stab) }
        .stabstr 0 : { *(.stabstr) }
        .stab.excl 0 : { *(.stab.excl) }
        .stab.exclstr 0 : { *(.stab.exclstr) }
        .stab.index 0 : { *(.stab.index) }
        .stab.indexstr 0 : { *(.stab.indexstr) }
        .comment 0 : { *(.comment) }
        .debug_abbrev 0 : { *(.debug_abbrev) }
        .debug_info 0 : { *(.debug_info) }
        .debug_line 0 : { *(.debug_line) }
        .debug_pubnames 0 : { *(.debug_pubnames) }
        .debug_aranges 0 : { *(.debug_aranges) }

}[/code:1]
然后再用-g参数编译链接产生的hello程序就可以进行源码级调试了,调试示例见本贴最后。现在说明一下hello.lds文件的作用和该错误的原因。

hello.lds文件是一个链接控制脚本文件(Linker Control Scripts)。该文件的作用是在链接过程中,描述输入文件(*.o)的各部分(section)是如何组合映射到输出文件(hello)的各段(segment)中的。

通常情况下,链接器ld运行时会使用一个默认的lds文件,不需要用户自己编写lds文件。该默认lds文件的内容可以在使用ld时使用--verbose参数显示。但是在特定情况下,比如编写操作系统内核,就会需要编写自己的lds文件。

hello4skyeye可以被看做一个非常简单的操作系统内核,所以得要自己编写hello.lds文件。hello.lds文件中描述了如何把start.o,hello.o文件中得代码段、数据段等等如何组合到hello。

本来有了.text .data .bss三个段,hello程序就可以在skyeye上运行了,但是如果是调试版的hello程序,就需要把start.o和hello.o中更多的与调试相关的section放入hello中,这就是我们为什么要在hello.lds中添加更多代码的原因了。

更多的关于lds方面资料,可以在Linux中使用info ld看Scripts章节。《Linker & Loader》中也有关于link Control Scripts方面的说明。
引用:
[root@shang HelloForSkyeye]# make  编译hello可执行文件
arm-elf-gcc -c -Wall -Wstrict-prototypes -Wno-trigraphs -O2 -pipe -g -mapcs-32 -march=armv4 -mtune=arm7tdmi -mshort-load-bytes start.S
arm-elf-gcc -c -Wall -Wstrict-prototypes -Wno-trigraphs -O2 -pipe -g -mapcs-32 -march=armv4 -mtune=arm7tdmi -mshort-load-bytes hello.c
arm-elf-ld -p -X -Thello.lds start.o hello.o -o hello
arm-elf-objdump -S hello > hello.s
arm-elf-readelf -a hello > hello.r
arm-elf-nm hello > hello.n
[root@shang HelloForSkyeye]# skyeye hello  运行hello可执行文件
***************************************************************
**** ****
**** SkyEye Simulator Ver 0.8.6 with GDB 5.3 Interface ****
**** ****
***************************************************************
GNU gdb 5.3
Copyright 2002 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This SkyEye was configured as "--host=i686-pc-linux-gnu --target=arm-elf"...
(SkyEye) tar s
cpu info: armv3, arm7tdmi, 41007700, fff8ff00, 0
mach info: name at91, mach_init addr 0x813efa4
log_info: log is off.
log_info:log file is /tmp/sk1.log, fd is 0x829a318
log_info: log start clock 0
log_info: log end clock 200000
SKYEYE: use arm7100 mmu ops
Connected to the simulator.
(SkyEye) load
Loading section .text, size 0x54 vma 0x1000000
Loading section .rodata, size 0xb vma 0x1000054
Loading section .data, size 0x1000 vma 0x1002000
Start address 0x1000000
Transfer rate: 33528 bits in <1 sec.
(SkyEye) list           显示hello可执行文件中的源代码
1 /*
2 * hello.c
3 * just a function used to output "helloworld" to uart
4 *
5 * author: SU Hang
6 * date: 2004-08-28
7 */
8 void hello(void)
9 {
10 int i;
(SkyEye) list
11 char * hellostr="helloworld";
12 long* paddr=(long*)0xfffd001c;
13
14 for(i=0;i<10;i++)
15 {
16 * paddr=hellostr;
17 }
18 return;
19 }
20
(SkyEye) break hello  在hello函数入口设置断点
Breakpoint 1 at 0x1000030: file hello.c, line 14.
(SkyEye) run          程序运行到hello断点处停止
Starting program: /home/suhang/HelloForSkyeye/hello

Breakpoint 1, hello () at hello.c:14
14 for(i=0;i<10;i++)
(SkyEye) info all-register 显示当前skyeye虚拟机中所有寄存器的值
r0 0xd2 210
r1 0xfffd001c -196580
r2 0x0 0
r3 0x0 0
r4 0x0 0
r5 0x0 0
r6 0x0 0
r7 0x0 0
r8 0x0 0
r9 0x0 0
r10 0x0 0
r11 0x1001ffc 16785404
r12 0x1002000 16785408
sp 0x1001ff0 16785392
lr 0x0 0
pc 0x1000030 16777264
f0 0 (raw 0x000000000000000000000000)
f1 0 (raw 0x000000000000000000000000)
f2 0 (raw 0x000000000000000000000000)
f3 0 (raw 0x000000000000000000000000)
f4 0 (raw 0x000000000000000000000000)
f5 0 (raw 0x000000000000000000000000)
f6 0 (raw 0x000000000000000000000000)
f7 0 (raw 0x000000000000000000000000)
fps 0x0 0
cpsr 0xd2 210

TOP

出现"SkyEye: Error when open log file"的错误是因为skyeye.conf文件中把log文件设为/tmp/sk1.log,因为普通用户没有权限打开该文件写入skyeye日志,所以会报错.

解决的方法的方法是把Hello4Skye目录中的skyeye.conf文件中的下面语句:

log: logon=0, logfile=/tmp/sk1.log, start=0, end=200000

改为

log: logon=0, logfile=./sk1.log, start=0, end=200000

应该就可以了,要保证logfile是普通用户可以打开的文件.

TOP

非常感谢楼上两位的提问,帮我发现了hello4skyeye中间BUG。事实上原来的hello4skyeye,都是使用arm-elf-tools-20030314编译的,因为编译出来的hello可以运行,所以也没有发现其中的问题。

现在使用arm-elf-tools-20040427编译,就出现错误:Not enough room for program headers, try linking with -N

经过检查发现是hello.lds链接控制脚本中,漏写了一个段.rodata,该段包含只读数据应该放在.text段中。所以修改hello.lds如下所示:
     .text :
        {
                *(.text)
                *(.rodata)
        }

另外Start.S中有一处错误,就是b hello,应该改为bl hello。后者表示跳转至hello执行完后返回。

参考资料:
《Executable and Linking Format(ELF) Specification》

TOP

不好意思,这段时间我在忙另外一个项目。虽然我这次没有调试Hello4Skyeye,但我想还是可以回答你的第一个问题。

Hello4Skyeye结尾并没有终止语句,如果把b begin注释去。Skyeye运行的时候,运行到Hello4Skyeye的结尾并不会停止,而是继续往下执行。下面的指令因为不在Hello4Skyeye的代码段中,所以情况就难以预料了。

根据经验,后面未放代码的地方的指令往往是将程序指令寄存器重新置为0。所以这时程序会调转到0地址开始执行。Hello4Skyeye不是加载到0x01000000地址么,当程序执行到0x01000000地址的时候又会重新执行Hello4Skyeye。这样等于是个大循环。

如果你发现更改完的Hello4Skyeye运行是仍然不断输出Helloworld但是输出速度明显慢于未修改前的,那就基本可以断定是我上面所说的原因。

关于第二个问题,我现在只能大概说明一下,不要认为反汇编出来的就都是指令。有时候反汇编会把数据区的数据反汇编为指令。不知道是不是这个原因才显示tsteq r0, r0

如果你能用Skyeye的log功能调试程序,那么第一个问题是很好解决的。

使用SkyEye的log功能调试Hello4SkyEye:

上章介绍了源码级调试Hello4SkyEye的方法,之所以能进行源码级调试,是因为makefile中编译选项有-g参数,hello.lds文件中有和debug相关段的输出,并且hello可执行程序是elf格式的。这样SkyEye在装载hello程序的时候,就可以从文件中读取相关的调试信息。

但是很多情况下,SkyEye上运行的操作系统内核并非是ELF格式,那么Skeye就无法进行源码级调试了。这个时候使用串口输出调试信息,是调试内核的一个简单方法。但是在串口初始化之前,是无法使用这个方法调试的。其实使用SkyEye调试内核,有一个非常简单有效的方法,那就是使用SkyEye的log功能。我们可以记录下程序的运行流程,再和程序源码及反汇编代码对照,就可以分析出程序运行的流程和各步指令执行时Skyeye虚拟机的状态,根据这些信息就不难推断错误的原因。现在我们可以用Hello4SkyEye来尝试一下SkyEye这种神奇的功能。

SkyEye的log控制选项的说明如下:

log选项用于控制skyeye输出硬件系统的执行状态信息,包括每次执行指令时的执行指令值、寄存器值、各种硬件状态等。
格式:
log: logon=0|1, logfile=filename, start=number1, end=number2, length=number3
    logon=0|1|2|3,如果值等于0表示不进行记录,如果值等于1表示记录指令和指令地址流,如果值等于2表示记录指令和指令地址和主要寄存器内容,如果值等于3表示记录指令和指令地址和所有的寄存器内容。
    logfile=filename 其值是一个字符串,表示用于记录信息的文件名
    start=number1 其值是一个>=0的十进制整数,表示系统执行到第number1条指令时开始进行记录
    end =number2其值是一个>=0的十进制整数,表示系统执行到第number2条指令时停止记录
    length =number3其值是一个>=0的十进制整数,表示只记录系统最近执行的number3条指令时的信息

这里我们把skyeye.conf文件的最后一行改为:

log: logon=2, logfile=./sk1.log, start=0, end=200000

再次运行skyeye hello后,使用Ctrl+C;quit指令退出SkyEye后,就可以在当前目录下的sk1.log中看到hello程序执行的指令流,和每步各主要寄存器的值。

sk1.log的59行到64行如下所示:

N 3b :p 1000044,i e2822001,R 100005c,fffd001c,9,64,0,0,0,0,0,0,0,1001ffc,1002000,......
N 3c :p 1000048,i e3520009,R 100005c,fffd001c,a,64,0,0,0,0,0,0,0,1001ffc,1002000,......
N 3d :p 100004c,i e5813000,R 100005c,fffd001c,a,64,0,0,0,0,0,0,0,1001ffc,1002000,......
N 3e :p 1000050,i dafffffa,R 100005c,fffd001c,a,64,0,0,0,0,0,0,0,1001ffc,1002000,......
N 3f :p 1000054,i e91ba800,R 100005c,fffd001c,a,64,0,0,0,0,0,0,0,1001ffc,1002000,......
N 40 :p 1000010,i eafffffa,R 100005c,fffd001c,a,64,0,0,0,0,0,0,0,0,1002000,......


现在就要说明log表与文件源码的对照技巧了。打开hello.s文件,该文件是make时,使用"objdump -xS hello"指令产生的反汇编文件。其中部分如下所示:

for(i=0;i<10;i++)
1000038: e3a02000 mov r2, #0 ; 0x0
100003c: e59f0014 ldr r0, [pc, #20] ; 1000058 <hello+0x38>
{
      * paddr=hellostr;
1000040: e7d03002 ldrb r3, [r0, r2]
1000044: e2822001 add r2, r2, #1 ; 0x1
1000048: e3520009 cmp r2, #9 ; 0x9
100004c: e5813000 str r3, [r1]
1000050: dafffffa ble 1000040 <hello+0x20>
      return;
}
1000054: e91ba800 ldmdb fp, {fp, sp, pc}


从log表中可以看出在执行了0x3b步指令后,程序的指令地址是0x1000044,指令内容是0xe2822001。再看反汇编表,0x1000044地址的指令正是0xe2822001,对应的汇编代码是“add r2, r2, #1”,程序正运行在hello函数中的for循环中。

可以从反汇编表的下面两步(0x1000044,0x100004

    add r2,r2, #1;
    cmp r2, #9

两句指令看出,计数器i就是寄存器r2。然后看log表中第0x3b步,此时r2=9,这说明hello函数的for循环已经执行了9次,现在正在执行第10次。下步0x3c的时候r2就是0xa(10)了,这正是执行了add r2, r2, #1的结果。这时执行cmp r2,#9判断语句显然r2>9,程序就不再循环而是接着执行下面三步(0x100004c,0x1000050,0x1000054)

   str r3,[r1];
   ble 1000040;
   ldmdb fp, {fp, sp, pd}。

这些指令的执行效果就是退出循环,hello函数返回。可以看到log表结果与此相符,第0x40步的时候,程序跳转到0x1000010处执行了。

从上面的分析可以看出既便不使用skyeye源码级调试的方法,我们也可以通过log表和反汇编表,看出程序运行的流程和关键处的寄存器值,如果发生错误,就可以通过流程变化和寄存器的值分析错误的原因。

TOP

呵呵,我Skyeye执行的log怎么没有重复的记录?你用的Skyeye版本是多少的?
如果想要系统学习Skyeye,请看看下面这本书,会非常有帮助。
《源码开放得嵌入式系统软件分析和实践--基于skyeye和ARM开发平台》
另外,GNU AS讲的只是AT&T的汇编语法,真正的ARM汇编指令还是要查ARM开发书籍。这个和GNU AS是两回事

TOP

试试
make clean
make
[quote:a683df80b7="Canbus"]我下你的源码,不知为什么make不了啊。
提示如下:
$make
make:Nothing to be done for  'all'[/quote]

TOP