lucifer做arm汇编开发,在访问一个.data段的空间(全局变量)时,发生了宕机;未见进入数据中止异常,和未定义指令异常的处理函数中,让我一脸懵逼。
接着查资料发现,发现对内存进行加载和存储的指令具有如下限制:LDRB/STRB - address must be byte aligned LDRH/STRH - address must be 2-byte aligned LDR/STR - address must be 4-byte aligned
即用内存访问指令间接寻址时,所访问的地址应是所要求对齐字节数的整数倍;
否则,即进行了不对齐访问(unaligned access),这时会发生“宕机”或者“一切安好”。
如下详细介绍发生不对齐访问的情况,和提供数种解决宕机方案:
(1)发生不对齐访问的代码例子:
如图中line113和line123,使用ldr指令通过标签isr_count和isr_tmp访问了.data段里两个4Byte的空间;而isr_count和isr_tmp在汇编器处理后得到如右图所示的地址,由于用来存储字符串的fmt_timer_init中的Line3240对应的机器码只占3Byte的空间,导致后续存储机器码的内存地址,丧失了4Byte对齐。
uboot启动后,运行上述裸机代码,将会发生宕机,原因分析如下:
uboot的cpu内核初始化阶段,启动了内存地址对齐错误的检查功能;然后用ldr加载和str存储内存地址不对齐的空间会发生什么事情?
参考ARM架构手册中如下表,可知将因指令的地址对齐错误导致数据中止异常。如果你用如下指令将SCTLR.A置为0,将不会产生异常,实现了不对齐访问,较早的ARM架构不支持这种功能,ARMv7上可以支持。
mrc p15, 0, r0, c1, c0, 0; Read SCTLR
bic r0, r0, #0x2; clear SCTLR.A
mcr p15, 0, r0, c1, c0, 0; Write SCTLR
另外如果你在linux kernel运行过程中出现了alignment fault,可以考虑检查汇编部分的操作。
(2)各种解决alignment fault方法:
①使用汇编伪操作.align {alignment} {,fill}
.data
fmt_timer_init:
.string "timer_0_4_init()\r\n"
fmt_t0:
.string "timer0_isr()\r\n"
.align @avoid string breached address 4 byte align
isr_count:
.int 0
isr_tmp:
.int 0
.end
对齐方式设置伪操作.align通过用零或指定的数据进行填充,来使得当前位置与指定的边界对齐;上述代码中.align没有指定对齐方式和所要填充的数据,则默认情况下是字对齐(4Bytes)并用零填充,即指定了isr_count的地址为4Byte对齐。汇编后再反汇编得到的结果如下图所示:
②先定义int数据,再定义string数据
从上面中可知道由于string定义的字符串空间破坏了data段地址的字对齐,那我们就通过规避string来达到int数据空间字对齐的目的;
③在C语言环境用C来代替发生misalign的代码
在uboot上,尽最大可能让代码在C语言下执行,及早进入C语言环境,让交叉编译器去帮我们执行这些对齐的操作。另外裸机代码为了保证各个section起始地址为4字节对齐,通过在链接文件中添加:
. = ALIGN(4);
如uboot的链接文件:
嵌入式系统发展非常迅速,新的技术成果不断更新。上述博文难免存在错误和不妥之处,恳请各位看客批评指正。