您的位置:首页 > 其它

汇编与c之间内容传递的分析理解及ldr指令与.word伪指令的简单介绍

2013-02-04 12:57 393 查看
注:因涉及个人理解,可能有错 。如欲转载 ,请注明出处。

一、ldr指令的两种不同用途

(1)作为arm汇编指令集中的ldr加载指令

·指令格式:

LDR{条件}    Rd, <地址>
LDR{条件}B   Rd, <地址>

说明:该指令用于把<地址>指向的数据加载到Rd寄存器里,LDR是加载指向的一个字内容,而后面对了个后缀'B'表示只加载指向的一个字节的内容.


(2)作为编译器自己定义的ldr伪指令

·伪指令格式:

LDR Rd,=expr

说明:可以直接理解为把expr值赋给Rd .比如:

ldr R0 , =0x30000040 ;就是把0x30000040赋给R0

关于这部分的易错点,其实可以看一下uboot的向量表前面的编写.

_start: b start_code /*直接跳转到start_code处,不保存当前地址*/

ldr pc, _undefined_instruction

ldr pc, _software_interrupt

ldr pc, _prefetch_abort

ldr pc, _data_abort

ldr pc, _not_used

ldr pc, _irq

ldr pc, _fiq

_undefined_instruction: .word undefined_instruction

_software_interrupt: .word software_interrupt

_prefetch_abort: .word prefetch_abort

_data_abort: .word data_abort

_not_used: .word not_used

_irq: .word irq

_fiq: .word fiq

对汇编指令不是很熟的人刚开始看到这段代码可能会有一个疑问(当然也包括我),这里为什么还要多加个.word把函数指针irq以word类型放在当前地址处 , 然后用_irq来标志这个当前地址 . 为什么不直接把irq直接使用ldr pc, irq就好,这样一旦中断来了就直接跳到irq指向的函数去指向?

上面的理解其实是错的,ldr pc,irq中的irq是函数指针,那么这个语句是把irq指向的内容(就是函数的代码)的前个word赋给pc,而不是把函数指针赋给pc,所以写成这样是无法正常进入中断服务程序的.

如果想要直接用irq来赋给pc,那就需要使用ldr的伪指令来完成 , 如 : ldr pc , =irq .

否则就要像上面那样,把irq以word形式通过.word放在当前地址里,给这个地址做标号,在跳转指令里就可以直接通过ldr pc , _irq把该地址标号的内容(irq函数指针)赋给pc .

(3)而在c语言里对这些值的引用却大大不同 , 比如在汇编文件里有下面几条语句(中间省略了与该讨论无关的代码) ,其中//后面的是反汇编出来对应的代价:

.global _start

.global wsnboot_start

.global bss_start

.global _bss_start

_start: //3ff80000 <_start>:

b ResetInit //3ff80000: ea000008

...

wanboot_start
//3ff80020 <wsnboot_start>:

.word _start //3ff80020: 3ff80000 svccc 0x00f80000

bss_start: //3ff80024 <bss_start>:

.word _bss_start //3ff80024: 3ff80540 svccc 0x00f80540 (其中3ff80540是_bss_start的值)

...

ldr r0 , =_start //ldr r0, [pc, #116] ; 3ff800dc // 3ff800e0: 3ff80540 (最终结果就是r0 = 3ff80540)

ldr r1 , = _bss_start //ldr r1, [pc, #116] ; 3ff800e0 // 3ff800dc: 3ff80000

在c语言里用了这样两条语句 :

i = _bss_start - _start ; (本意是希望得到_bss_start地址到_start地址的距离 , 也就是希望是3ff80540-3ff80000)

ldr r3, [pc, #52] ; 3ff80128 //3ff80128: 3ff80540 //可以看出这里是把3ff80540赋给r3

ldr r0, [r3] //把3ff80540地址里的数据赋给r0

ldr r3, [pc, #44] ; 3ff8012c //3ff8012c: 3ff80000 //把3ff80000赋给r3

ldr r2, [r3] //把3ff80000里的内容赋给r2

rsb r2, r2, r0 //[3ff80540]里的内容 - [3ff80000]里的内容

从上面的情况已经可以看得出,这明显不是我们想要的 , 在汇编里我们完全可以通过ldr伪指令直接把变量值直接赋给寄存器 , 而在c语言里对汇编里的变量都是当作指针并直接取其指向的内容进行操作 .

具体为什么要这样我也不太明白 , 个人的理解是(如理解有错,请大家矫正) : 在c语言里不管是常量也好 , 变量也好 , 指针变量也好 ,都是有对应的地址来存储它们 .

而在汇编里 , 其实我们上面引用的不应该理解为变量 , 只是汇编里的一个地址标号而已, 该地址标号只是用于编译器暂时使用 ,不会占用我们真正的内存 . 既然不会占用我们的内存 , 那在c语言里就不能对地址标号当作变量引用 , 因为c语言是通过地址来引用变量常量的 . 所以以上那种做法是错误的 . 那是企图引用汇编里的不占地址空间的地址标号 . 所以如果在c语言里要使用该值
, 就必须为该值开辟一个空间来放置它 , 然后c语言里通过访问该地址来取出该数据 . 可能这就是为什么c语言里只能把引用汇编里的东西都当作地址来取出里面的值 , 实现了汇编与c里值的传递 .

以下就是正确的实现:

j = bss_start - wsnboot_start; (本意也是希望得到_bss_start到_start地址的距离 , 也就是希望是3ff80540-3ff80000 )

ldr r3, [pc, #40] ; 3ff80130 //3ff80130: 3ff80024 //在汇编里我们已经先把地址标号3ff80540放在3ff80024 里

//所以这里是把3ff80024这个地址放在r3里.

ldr r1, [r3] //取出3ff80024里的值 , 就是地址标号3ff80540 .

ldr r3, [pc, #32] ; 3ff80134 //3ff80134: 3ff80020 //在汇编里我们已经先把地址标号3ff80000放在3ff80020 里

ldr r3, [r3] //取出3ff80020 里的值 , 就是地址标号3ff80000

rsb r3, r3, r1 //把3ff80540 -3ff80000 ,与预期相符

这个是GNU ASM的网站,里面有对伪指令的一些介绍,很多我都看不懂,关键在于自己去理解.

http://tigcc.ticalc.org/doc/gnuasm.html#SEC49
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐