0
前述
在日常编写Bug的时候,不知是否经过这样一种情况,我明明只是在函数中多加了一个临时变量,结果程序执行就异常了。我屮艸芔茻...
我碰到的一次情况是该函数程序访问数组本来就越界了,但是还能正常工作,就因为加了一个临时变量,栈内容发生了变化,就因为一个变量的偏移,在对数组操作时刚好设置给了LR寄存器,导致子程序返回有误,程序执行逻辑异常。
上述的这种问题,常规调试是无法发现的,甚至问题出现在那个地方都定位不到。
都说汇编代码时程序员的最后一根救命稻草。接下来就来介绍一下汇编中最常用的STM/LDM指令。
LDM翻译为Load Multiple registers.
STM翻译为Store Multiple registers.
语法格式如下:
LDM{addr_mode}{cond} Rn{!}, reglist{^}STM{addr_mode}{cond} Rn{!}, reglist{^}
1
addr_mode
LDM 和 STM 指令提供了四种不同的寻址方式。寻址模式决定基址寄存器的行为,并在下表中说明。
I为Increment(递增)
D为Decrement (递减)
B为Before
A为After
模式决定了基址寄存器是在执行指令前地址增减还是指令执行后增减。
LDM 和 STM 指令也可用于从堆栈中压入或弹出寄存器。
D为Descending(降序)。
A是Ascending (升序)单词首字母。
F是单词Full 首字母,意思为当前的栈指针指向是最后一个入栈的元素。
E是单词Empty单词首字母,意思是当前的栈指针指向的是下一个空闲空间。
这四组描述的是栈的生长方向是降序还是升序,以及当前栈指针指向是栈中最后一项元素还是空闲空间。
2
Cond
条件指令仅在程序状态寄存器中的条件标志匹配时执行。例如,BEQ(带有 EQ 条件的 B 指令)仅在设置 Z 标志时分支。如果 {cond} 字段为空,则始终执行指令。
3
Rn
Rn是基址寄存器,ARM 寄存器保存传输的初始地址。Rn 不能是 PC。
4
!
是一个可选的后缀。如果 !存在时,将最终地址写回 Rn。
5
reglist
是要加载的一个或多个寄存器的列表,用大括号括起来。它可以包含寄存器范围。如果它包含多个寄存器或寄存器范围,则必须用逗号分隔。寄存器 R0 到 R15 (PC) 的任意组合都可以在 ARM 状态下传输,但在 Thumb 状态下有一些限制。
6
^
是可选后缀,仅在 ARM 状态下可用。您不得在用户模式或系统模式下使用它。数据被传入或传出用户模式寄存器,而不是当前模式寄存器。
7
示例
最后看一个示例,如下图,在右上角有一个行汇编指令。
STMDB R13!,{R4-R6,R14}
注意,当执行到这一行代码时,栈指针(SP)的地址是0x3FFFC00,上图已经为执行后的结果。
逐步分析,STM为存贮指令,DB解释为当前栈地址是降序的且当前的栈指针指向栈中的最后一个元素。所以指令执行之前一定要让SP指向下一个空闲地址,执行完之后再将SP地址递减。
因为有!所以该指令执行完之后需要将最后的地址写回给Rn(即SP)。所以最后SP的地址为0x3fffbf0
注意,因为栈地址是递减的,所以最先入栈的是R14,从右往左。如果栈是递增的则反之。