曲径通幽论坛

 找回密码
 立即注册
搜索
查看: 6355|回复: 0
打印 上一主题 下一主题

LDR 指令与寻址对齐

[复制链接]

4918

主题

5880

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34387
跳转到指定楼层
楼主
发表于 2009-11-17 14:42:09 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
LDR 指令用于从内存种将一个 32 位的字读取到指令中的目标寄存器。一般情况下,这样的读取是字对齐的。比如,在一个应用程序中如下定义:
int a[4] = {0x00000001, 0x55226677, 0x68491247, 0x00000048} ;
int *p = a;
上面, p 是个整型指针,用来读取数组中的元素。假如数组 a 的首地址是 0xbffffe94 ,那么用 a 初始化 p 后,p 所代表的地址也是 0xbffffe94 。所以,当 p += 1; 后,p 的变为为 0xbffffe98;再 p+= 1 后,p 变为 0xbffffe9c ;以此类推。这样的地址能被 4 整除,所以是字对齐的。

在《ARM体系结构编程》一书中有说到
如果指令寻址方式确定的地址不是字对齐的,则从内存中读出的数值要进行循环右移操作,移位的位数为寻址方式确定的地址的 bits[1:0] 的 8 倍。这样对于 little-endian (小端模式),指令要读取的字节数句放在目标寄存器的低 8 位。 

理解这句话,首先要弄明白“从内存中读出的数值要进行循环右移操作” 这话里的“从内存种读出到了什么样的数据” !

现在假设上面 p 指针是指向上面数组中的第二个元素 0x55226677 (其地址为 0xbffffe98),假如我强制令 p 所代表的地址变为 0xbffffe99 这里,那我读这个地址,那我到底读到的是哪 4 个连续地址处的数据?是 0xbffffe99 ~ 0xbffffe9c ?还是 0xbffffe98 ~ 0xbffffe9b?抑或是是别的数据。

当时我也一度不明白,在网上也没搜到具体的资料。那只有自己动手做一下实验。写一个测试用的小应用程序 test.c:
#include <stdio.h>

main()
{
    int a[4] = {0x00000001, 0x55226677, 0x68491247, 0x00000048} ;
    int *p = a;
    int i;
    p = p + 1;   
    i = 0xaa;             /*方便观察所设语句*/
    (char *)p = (char *)p + 1;
    i = *p;
    return (0);
}
上面的程序在实际中不会输出任何数据,没实际意义,但是对于弄明白上面的问题却有帮助。对程序编译成可执行文件后,上传到开发板里,然后用 gdbserver 和 主机的 arm-gdb 进行调试。关于如何建立 gdbserver 调试环境见:http://www.groad.net/bbs/read.php?tid-1365.html

在主机一端,启动 gdb ,然后给程序下个断点,直接运行到断点处,然后用 disas 命令反汇编整段程序(如果不运行进程序里,无法使用 disas 命令),输出结果如下:
Breakpoint 1, main () at test.c:11
11        i = *p;
(gdb) disas
Dump of assembler code for function main:
0x8380 <main>:    mov    r12, sp
0x8384 <main+4>:    stmdb    sp!, {r11, r12, lr, pc}
0x8388 <main+8>:    sub    r11, r12, #4    ; 0x4
0x838c <main+12>:    sub    sp, sp, #24    ; 0x18
0x8390 <main+16>:    ldr    r3, [pc, #72]    ; 0x83e0 <main+96>
0x8394 <main+20>:    sub    r12, r11, #28    ; 0x1c
0x8398 <main+24>:    ldmia    r3, {r0, r1, r2, r3}
0x839c <main+28>:    stmia    r12, {r0, r1, r2, r3}
0x83a0 <main+32>:    sub    r3, r11, #28    ; 0x1c
0x83a4 <main+36>:    str    r3, [r11, -#32]
0x83a8 <main+40>:    ldr    r3, [r11, -#32]
0x83ac <main+44>:    add    r3, r3, #4    ; 0x4
0x83b0 <main+48>:    str    r3, [r11, -#32]
0x83b4 <main+52>:    mov    r3, #170    ; 0xaa        /*方便观察所设语句*/
0x83b8 <main+56>:    str    r3, [r11, -#36]
0x83bc <main+60>:    ldr    r3, [r11, -#32]
0x83c0 <main+64>:    add    r3, r3, #1    ; 0x1
0x83c4 <main+68>:    str    r3, [r11, -#32]
0x83c8 <main+72>:    ldr    r3, [r11, -#32]
0x83cc <main+76>:    ldr    r3, [r3]
0x83d0 <main+80>:    str    r3, [r11, -#36]
0x83d4 <main+84>:    mov    r3, #0    ; 0x0
---Type <return> to continue, or q <return> to quit---
0x83d8 <main+88>:    mov    r0, r3
0x83dc <main+92>:    ldmdb    r11, {r11, sp, pc}
0x83e0 <main+96>:    andeq    r8, r0, r4, lsr r4
End of assembler dump.
在 C 程序中,当程序运行到 p = p + 1;  这里时,可以观察到 p 所代表的地址为 0xbffffe98
 (char *)p = (char *)p + 1; 语句是强制让 p 指向 0xbffffe99 这个地址,目的是故意造成 LDR 的寻址不对齐。

通过观察内存
(gdb) x/3wx 0xbffffe98
0xbffffe98:    0x55226677    0x68491247    0x00000048
(gdb) x/3wx 0xbffffe99
0xbffffe99:    0x47552266    0x48684912    0xc8000000
可以看出,目标板所采用的内存是 little-end 小端格式(低字节数据放在低地址,高字节数据放在高地址)。现在,单步执行 i = *p; 这条语句。i = *p; 这语句所对应的汇编是:
0x83c8 <main+72>:    ldr    r3, [r11, -#32]
0x83cc <main+76>:    ldr    r3, [r3]
0x83d0 <main+80>:    str    r3, [r11, -#36]
上面,r11, -#32 对应的地址就是 p,[r11, -#32] 就是 *p ;而 [r11, -#36] 对应的变量自然就是 i 。
现在通过命令查看一下 r3 中的内容,也就是 *p 的内容:
(gdb) info registers r3
r3             0x77552266    2002068070
由此可见,0x77552266  正是经过 0x55226677 循环右移 8 位得来!也就是说,原本设想的读取 0xbffffe99 会连续 0xbffffe99-0xbffffe9a-0xbffffe9b-0xbffffe9c 这 4 个内存单元内容是错误的。实际上仍然读取的是 0xbffffe98 ~ 0xbffffe9b 这 4 个连续内存单元的内容。由于你读取的地址不对齐,编译器会认为你的兴趣就是要读取那个不对齐的内存单元( 0xbffffe99 对应上的 0x66 ) 的内容,所以循环右移,将其送往低 8 位。

如果说,在 C 程序中,将  i = *p; 改为 (char)i = *((char *)p); ,那么汇编中 ldr    r3, [r3]  会变成 ldrb r3, [r3] ,这样表示就只是读一个字节。

对于 big-end 大端内存存储格式同理分析。

至此,分析完毕。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|曲径通幽 ( 琼ICP备11001422号-1|公安备案:46900502000207 )

GMT+8, 2024-5-19 05:00 , Processed in 0.071407 second(s), 22 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表