ARM汇编中BL指令不能实现跳转

hhhhjjaa 2011-05-01 04:06:58
USR_STACK_LEGTH EQU 64
SVC_STACK_LEGTH EQU 0
FIQ_STACK_LEGTH EQU 16
IRQ_STACK_LEGTH EQU 64
ABT_STACK_LEGTH EQU 0
UND_STACK_LEGTH EQU 0
AREA Example5,CODE,READONLY
ENTRY
CODE32
START MOV R0,#0
MOV R1,#1
MOV R2,#2
MOV R3,#3
MOV R4,#4
MOV R5,#5
MOV R6,#6
MOV R7,#7
MOV R8,#8
MOV R9,#9
MOV R10,#10
MOV R11,#11
MOV R12,#12

BL InitStack
MRS R0,CPSR
BIC R0,R0,#80
MSR CPSR_cxsf,R0

MSR CPSR_c,#0xd0
MRS R0,CPSR

MSR CPSR_c,#0xdf
MRS R0,CPSR

HALT B HALT

InitStack MOV R0,LR
MSR CPSR_c,#0xd3
LDR SP,StackSvc

MSR CPSR_c,#0xd2
LDR SP,StackIrq

MSR CPSR_c,#0xd1
LDR SP,StackFiq

MSR CPSR_c,#0xd7
LDR SP,StackAbt

MSR CPSR_c,#0xdb
LDR SP,StackUnd

MSR CPSR_c,#0xdf
LDR SP,StackUsr

MOV PC,R0

StackUsr DCD UsrStackSpace+(USR_STACK_LEGTH-1)*4
StackSvc DCD SvcStackSpace+(SVC_STACK_LEGTH-1)*4
StackIrq DCD IrqStackSpace+(IRQ_STACK_LEGTH-1)*4
StackFiq DCD FiqStackSpace+(FIQ_STACK_LEGTH-1)*4
StackAbt DCD AbtStackSpace+(ABT_STACK_LEGTH-1)*4
StackUnd DCD UndStackSpace+(UND_STACK_LEGTH-1)*4

AREA MyStacks,DATA,NOINIT,ALIGN=2
UsrStackSpace SPACE USR_STACK_LEGTH*4
SvcStackSpace SPACE SVC_STACK_LEGTH*4
IrqStackSpace SPACE IRQ_STACK_LEGTH*4
FiqStackSpace SPACE FIQ_STACK_LEGTH*4
AbtStackSpace SPACE ABT_STACK_LEGTH*4
UndStackSpace SPACE UND_STACK_LEGTH*4

END

为啥BL不能实现跳转呢?而用B的话就可以?
期待各位大侠回答。
...全文
1308 9 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
9 条回复
切换为时间正序
请发表友善的回复…
发表回复
梅希的博客 2011-07-26
  • 打赏
  • 举报
回复
你的程序没有问题。问题在于你调式时的方法。

原因:BL调的是子程序,这就是相当于你在vc上跟c的代码一样,F10与F11的区别
hhhhjjaa 2011-05-03
  • 打赏
  • 举报
回复
谢谢xiaopoy兄耐心的讲解,现在明白了许多。
hhhhjjaa 2011-05-03
  • 打赏
  • 举报
回复
五楼那位仁兄说的对,要用step in来进入子程序。那是不是说BL指令把标号和MOV PC,LR之间的代码当做子程序,因而只能用step in进入。而B指令不能够返回,所以没有MOV PC,LR指令,B只是跳到纯粹的一个标号而已。不明白的是B指令用step和step in都可进行跳转,用step in时它把哪部分当做子程序,难道它把标号之后直至结束的代码当做子程序?
xiaopoy 2011-05-03
  • 打赏
  • 举报
回复
首先说明下,我不懂ARM的,因为这个是RISC体系的,以前反编译过MIPS的东西,所以有点了解。
希望不会误导到你也希望可以帮到你,但你应该再去查查相关资料的。 /:^]

是这样的:
BL是arm汇编中用来调用子程序的指令,它把BL后面一条指令的地址放到R14寄存器里,R15寄存器(PC当前指针地址)就设置成要跳往的地址。这样在这个子程序返回时,再mov PC, R14就可以返回到BL后面的地址了。
B不一样,B是直接 mov PC, 目标地址;而不保存其后的地址到R14。
这对于调试而言是大有区别的,跳转在正常的程序里都是用在一段函数里自己的流程中的,而子程序调用却是到另一个函数的执行流程里了。
比方
char * FormatTime (char * Out, char * Day, char * Hour, char * Minutes, char * Seconds)
{
Out[0]= '\0';
strcat ( Out, "天:");
strcat ( Out, Day);
strcat ( Out, "小时:");
strcat ( Out, Hour);
strcat ( Out, "分钟:");
strcat ( Out, Minutes);
strcat ( Out, "秒:");
strcat ( Out, Seconds);
}
调试这个FormatTime 时不会想要跟进strcat看它怎么把你的时间和"天:"们连接起来的,这时就需要一个调试方式可以步过strcat这种子函数,在BL strcat的时候直接到R14指定的返回地址等执行过来。
而正常的流程里的
if (TRUE)
{
//blabla 1
}
else
{
//blabla 2
}
这样的if判断,就是一个B或者带条件的B了,而需要弄明白它是怎样执行的,就肯定需要跟着B执行过去的目标,一步步沿着CPU执行指令的流程跟踪。这样就需要一种每个指令都中断一下的方式,对应步入,这样的方式就叫做步入了。

我简单搜索了下,发现SWI(软件使用的中断,通常这样的设计是给操作系统使用),BKPT(软件模拟抛出硬件中断)这二个是arm里的产生中断的指令,根据经验,步过这种调试方式,应该是用BKPT软件模拟中断指令实现的,在碰到BL时候,调试器会把BL后面的指令替换成一个BKPT,然后执行回来到BKPT上时候,抛出的异常会被调试器捕获到,自然实现了需要的步过效果了。
而步入的效果,一般是CPU自己所支持的,但需要一个标志位或类似的什么设置来开启CPU每执行一个指令就抛出异常给调试器捕获。我在arm_architecture里找了半天,没有发现有对应的标志位,再搜索发现gdb对arm上的步入是用这种方式实现的,碰到BL之类的指令(BLX, mov PC, reg等等操作当前PC的指令)时,都会预测出来要跳转过去的指令地址,然后在目标的指令地址上下一个BKPT断点,这样来在目标位置断下的。 /:^]

而一个子程序的结束位置,这是没有一个可以绝对确定的定义的,特别是在RISC CPUs体系上面,可以操作PC的指令超多,怎么返回也不为过啊。不过一般可以这样认为,把R14寄存器里的值设置到PC上的指令,它的位置就是子函数结束的位置了。当然还有这样一种常见的情况,把一些需要备份的寄存器和返回地址保存在堆栈里,
STMFD r13!, {其他reg, R14};//r13是arm里的sp堆栈指针,大部分的RISC都这样的。
在需要返回时候再给从堆栈弹回来:
LDMFD r13!, {其他reg, PC};
同时一个函数可能有多个返回的位置,所以不必强究一个子函数的结束地址在哪儿。可以跟踪明白它内部的处理流程就好了。
xiaopoy 2011-05-03
  • 打赏
  • 举报
回复
我觉得是你调试时的问题,应该用 步入,而不是步过子程序。这样就可以看到bl所进入的子程序InitStack的执行过程了。
zouxunlong 2011-05-02
  • 打赏
  • 举报
回复
[Quote=引用 2 楼 elkyboy2010 的回复:]

用JMP进行语句跳转
[/Quote]

这是ARM,不是Intel,实际上他们的原理都差不多。我们如果C学得不错,那么汇编也会学得好。
zouxunlong 2011-05-02
  • 打赏
  • 举报
回复
BL用于子程序调用,将InitStack改为子程序试试。
Elkyboy2010 2011-05-02
  • 打赏
  • 举报
回复
用JMP进行语句跳转
大熊猫侯佩 2011-05-01
  • 打赏
  • 举报
回复
顶,ARM不熟。

21,497

社区成员

发帖
与我相关
我的任务
社区描述
汇编语言(Assembly Language)是任何一种用于电子计算机、微处理器、微控制器或其他可编程器件的低级语言,亦称为符号语言。
社区管理员
  • 汇编语言
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧