关于这个HelloWorld的程序执行流程
根据楼主的教程和原程序的readme,这个Hello4Skyeye的程序我已经能够运行起来了。只是关于这个程序里面的执行流程我有点问题。我看了start.s文件,里面有
bl hello
b begin
这一段语句,所以这个程序就不断输出:“helloworld”。
可是我想改成让他只输出一次,于是我就改成这样:
bl hello
@ b begin
可是这样修改之后没有效果,还是不停地输出,所以我想请教一下这个程序的具体执行流程,为什么会不断输出呢?
另外,我调试的时候看了一下编译出来的汇编代码,发现程序的最后总有这么一句指令:
tsteq r0, r0
我想问问这句指令到底怎么会编译出来的呢,原程序的.s文件和.c文件都没有相对应的指令阿。
新手提问,希望大家多多指教。 :wink: 难道置顶贴里的问题没人关心的吗? :shock: 不好意思,这段时间我在忙另外一个项目。虽然我这次没有调试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, ; 1000058 <hello+0x38>
{
* paddr=hellostr;
1000040: e7d03002 ldrb r3,
1000044: e2822001 add r2, r2, #1 ; 0x1
1000048: e3520009 cmp r2, #9 ; 0x9
100004c: e5813000 str r3,
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,0x1000048)
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,;
ble 1000040;
ldmdb fp, {fp, sp, pd}。
这些指令的执行效果就是退出循环,hello函数返回。可以看到log表结果与此相符,第0x40步的时候,程序跳转到0x1000010处执行了。
从上面的分析可以看出既便不使用skyeye源码级调试的方法,我们也可以通过log表和反汇编表,看出程序运行的流程和关键处的寄存器值,如果发生错误,就可以通过流程变化和寄存器的值分析错误的原因。 呵呵,真是要谢谢楼主了,回帖很详细,很有价值。明天我去试试看log功能调试。
楼主真是热心人! :wink:
如果你发现更改完的Hello4Skyeye运行是仍然不断输出Helloworld但是输出速度明显慢于未修改前的,那就基本可以断定是我上面所说的原因。
补充一下,观察到的现象的确如此的。 我是用户,为何看不到源码的链接,
谁可以给我一个?[email protected]
3q先 楼上的,已经给你发了。
你是不是没有登陆呢?
先登陆后再看楼主的帖子应该就能看到的。
继续我的问题
今天按照楼主说的方法用了一下log调试功能,只是那个logon=2的情况下,前两项是指令地址和指令,这个看的明白,可是后面记录的那些主要寄存器有哪些呢?顺序是什么样的呢?我先贴一下部分代码和log文件:
这里是我的start.s文件:
begin:
@set up irq stack
mov r0, #0xd2 @ make irq mode with all irqs disabled
msr cpsr, r0
ldr sp, =irq_stack @ set sp_irq = irq_stack
bl hello
@b begin
.data
.align4
irq_stack:
.space 4096
可以看到这里我还是把那句调到begin的给注释掉了。
下面是编译后的那个hello.s的反汇编文件:
01000000 <begin>:
1000000: e3a000d2 mov r0, #210 ; 0xd2
1000004: e129f000 msr CPSR_all, r0
1000008: e59fd000 ldr sp, [pc, #0] ; 1000010 <begin+0x10>
100000c: eb000000 bl 1000014 <hello>
1000010: 01002000 tsteq r0, r0
这一段是对应前面那个start.s的代码。下面部分是hello函数中return的部分:
return;
}
1000048: e91ba800 ldmdb r11, {r11, sp, pc}
100004c: 01000050 tsteq r0, r0, asr r0
1000050: 6c6c6568 stcvsl 5, cr6, [r12], -#416
1000054: 726f776f rsbvc r7, pc, #29097984 ; 0x1bc0000
1000058: Address 0x1000058 is out of bounds.
下面是那个log文件的一部分:
N 3a :p 1000034,i e7d03002,R 1000050,fffd001c,9,6c,0,0,0,0,0,0,0,1001ffc,1002000,1001ff0,1000010,100003c,C d2,S 0,0,0,0,0,0,0,M 12,B 2,E 0,I 0,P 1000030,T 0,L e2822001,D e7d03002,
N 3b :p 1000038,i e2822001,R 1000050,fffd001c,9,64,0,0,0,0,0,0,0,1001ffc,1002000,1001ff0,1000010,1000040,C d2,S 0,0,0,0,0,0,0,M 12,B 2,E 0,I 0,P 1000030,T 0,L e2822001,D e7d03002,
N 3c :p 100003c,i e3520009,R 1000050,fffd001c,a,64,0,0,0,0,0,0,0,1001ffc,1002000,1001ff0,1000010,1000044,C d2,S 0,0,0,0,0,0,0,M 12,B 2,E 0,I 0,P 1000030,T 0,L e2822001,D e7d03002,
N 3d :p 1000040,i e5813000,R 1000050,fffd001c,a,64,0,0,0,0,0,0,0,1001ffc,1002000,1001ff0,1000010,1000048,C d2,S 0,0,0,0,0,0,0,M 12,B 2,E 0,I 0,P 1000030,T 0,L e2822001,D e7d03002,
N 3e :p 1000044,i dafffffa,R 1000050,fffd001c,a,64,0,0,0,0,0,0,0,1001ffc,1002000,1001ff0,1000010,100004c,C d2,S 0,0,0,0,0,0,0,M 12,B 2,E 0,I 0,P 1000030,T 0,L e2822001,D e7d03002,
N 3f :p 1000048,i e7ffdefe,R 1000050,fffd001c,a,64,0,0,0,0,0,0,0,1001ffc,1002000,1001ff0,1000010,1000050,C d2,S 0,0,0,0,0,0,0,M 12,B 2,E 0,I 0,P 1000030,T 0,L e2822001,D e7d03002,
N 40 :p 1000048,i e91ba800,R 1000050,fffd001c,a,64,0,0,0,0,0,0,0,1001ffc,1002000,1001ff0,1000010,1000050,C d2,S 0,0,0,0,0,0,0,M 12,B 2,E 0,I 0,P 1000048,T 0,L 6c6c6568,D 1000050,
N 41 :p 1000010,i 1002000,R 1000050,fffd001c,a,64,0,0,0,0,0,0,0,0,1002000,1002000,1000010,1000018,C d2,S 0,0,0,0,0,0,0,M 12,B 2,E 0,I 0,P 1000048,T 0,L 6c6c6568,D 1000050,
N 41 :p 1000010,i 1002000,R 1000050,fffd001c,a,64,0,0,0,0,0,0,0,0,1002000,1002000,1000010,1000018,C d2,S 0,0,0,0,0,0,0,M 12,B 2,E 0,I 0,P 1000010,T 0,L e92dd800,D e1a0c00d,
N 42 :p 1000014,i e1a0c00d,R 1000050,fffd001c,a,64,0,0,0,0,0,0,0,0,1002000,1002000,1000010,100001c,C d2,S 0,0,0,0,0,0,0,M 12,B 2,E 0,I 0,P 1000010,T 0,L e92dd800,D e1a0c00d,
N 42 :p 1000014,i e1a0c00d,R 1000050,fffd001c,a,64,0,0,0,0,0,0,0,0,1002000,1002000,1000010,100001c,C d2,S 0,0,0,0,0,0,0,M 12,B 2,E 0,I 0,P 1000014,T 0,L e24cb004,D e92dd800,
N 43 :p 1000018,i e92dd800,R 1000050,fffd001c,a,64,0,0,0,0,0,0,0,0,1002000,1002000,1000010,1000020,C d2,S 0,0,0,0,0,0,0,M 12,B 2,E 0,I 0,P 1000014,T 0,L e24cb004,D e92dd800,
N 44 :p 100001c,i e24cb004,R 1000050,fffd001c,a,64,0,0,0,0,0,0,0,0,1002000,1001ff0,1000010,1000024,C d2,S 0,0,0,0,0,0,0,M 12,B 2,E 0,I 0,P 1000014,T 0,L e24cb004,D e92dd800,
N 45 :p 1000020,i e3e01bbf,R 1000050,fffd001c,a,64,0,0,0,0,0,0,0,1001ffc,1002000,1001ff0,1000010,1000028,C d2,S 0,0,0,0,0,0,0,M 12,B 2,E 0,I 0,P 1000014,T 0,L e24cb004,D e92dd800,
N 46 :p 1000024,i e2411e3e,R 1000050,fffd03ff,a,64,0,0,0,0,0,0,0,1001ffc,1002000,1001ff0,1000010,100002c,C d2,S 0,0,0,0,0,0,0,M 12,B 2,E 0,I 0,P 1000014,T 0,L e24cb004,D e92dd800,
N 47 :p 1000028,i e2411003,R 1000050,fffd001f,a,64,0,0,0,0,0,0,0,1001ffc,1002000,1001ff0,1000010,1000030,C d2,S 0,0,0,0,0,0,0,M 12,B 2,E 0,I 0,P 1000014,T 0,L e24cb004,D e92dd800,
N 48 :p 100002c,i e7ffdefe,R 1000050,fffd001c,a,64,0,0,0,0,0,0,0,1001ffc,1002000,1001ff0,1000010,1000034,C d2,S 0,0,0,0,0,0,0,M 12,B 2,E 0,I 0,P 1000014,T 0,L e24cb004,D e92dd800,
下面是这段log里相关的一部分代码:
01000014 <hello>:
1000014: e1a0c00d mov r12, sp
1000018: e92dd800 stmdb sp!, {r11, r12, lr, pc}
100001c: e24cb004 sub r11, r12, #4 ; 0x4
void hello(void)
{
int i;
char * hellostr="helloworld";
long* paddr=(long*)0xfffd001c;
1000020: e3e01bbf mvn r1, #195584 ; 0x2fc00
1000024: e2411e3e sub r1, r1, #992 ; 0x3e0
1000028: e2411003 sub r1, r1, #3 ; 0x3
根据log文件可以看到在1000048执行了2次,而且第一次的指令是e7ffdefe,不是反汇编对应的指令,而后面那条1000048才是。之后就跳到了1000010,同样执行了2次,然后就又顺序执行到了hello里面。
这个过程就是我上次的问题所在了。看了log之后,还是没想明白,呵呵
:oops:
麻烦老大继续来解释解释吧。:)
另外我还有个问题,因为不了解GNU的汇编,所以想问问那下面终止语句是什么?
网上搜到了GNU AS的参考手册,可是没有找到该指令。
所以我想问问有什么可以学学GNU AS的,麻烦楼主继续指点~~~~ :roll: 呵呵,我Skyeye执行的log怎么没有重复的记录?你用的Skyeye版本是多少的?
如果想要系统学习Skyeye,请看看下面这本书,会非常有帮助。
《源码开放得嵌入式系统软件分析和实践--基于skyeye和ARM开发平台》
另外,GNU AS讲的只是AT&T的汇编语法,真正的ARM汇编指令还是要查ARM开发书籍。这个和GNU AS是两回事
hello4skyeye 这个程序在哪呀?
刚开始学习skyeye,我下了skyeye.bin,但是请问上面提到的hello4skyeye的源码在什么? 呵呵,我Skyeye执行的log怎么没有重复的记录?你用的Skyeye版本是多少的?最新的那个吧,好象是0.8.8?具体忘了,明天看看,呵呵。
我这个是用skyinsight加了断点调试的,让他走了一次hello函数,再进入第二次后,我就把它退出了。
我就是好奇为什么会有重复记录呢,呵呵。
如果想要系统学习Skyeye,请看看下面这本书,会非常有帮助。
《源码开放得嵌入式系统软件分析和实践--基于skyeye和ARM开发平台》
已经在网上订了这本书了,只是还没送来,呵呵 :)
另外,GNU AS讲的只是AT&T的汇编语法,真正的ARM汇编指令还是要查ARM开发书籍。这个和GNU AS是两回事
哦,知道了。
只是在那下面开发,编译器是只支持GNU AS的呢?还是同样支持ARM指令的呢?是不是可以用ARM汇编指令重写你的那个start.s文件呢?
呵呵,问题较多,还忘多多指教~~ :wink:
Re: hello4skyeye 这个程序在哪呀?
刚开始学习skyeye,我下了skyeye.bin,但是请问上面提到的hello4skyeye的源码在什么?源码见本贴的顶楼,呵呵 8)