汇编与c之间内容传递的分析理解及ldr指令与.word伪指令的简单介绍
2013-02-04 12:57
393 查看
注:因涉及个人理解,可能有错 。如欲转载 ,请注明出处。
一、ldr指令的两种不同用途
(1)作为arm汇编指令集中的ldr加载指令
·指令格式:
(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
一、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
相关文章推荐
- 通过汇编一个简单的C程序,分析汇编代码理解计算机是如何工作的--20135334赵阳林
- 汇编语言理解指针(指针就是汇编的间接寻址,其实就是一个简单方便的运算指令,计算完毕直接就赋值,不是从内存中去取值后赋值)
- 日志分析常用指令简单介绍
- 瞎聊!Android之aidl进程之间传递对象简单分析加 源码
- 通过分析一个C程序的汇编指令执行过程,理解计算机的工作。
- 通过汇编一个简单的C程序,分析汇编代码理解计算机工作原理
- 通过反汇编一个简单的C程序,分析汇编代码理解计算机是如何工作的
- 通过汇编一个简单的C程序,分析汇编代码理解计算机是如何工作的
- 云课堂 Linux内核分析 通过反汇编一个简单的C程序,分析汇编代码理解计算机是如何工作的
- 通过分析一个C程序的汇编指令执行过程,理解计算机的工作。
- 深入理解计算机系统读书笔记之一个简单汇编程序的调试分析
- 通过反汇编一个简单的C程序,分析汇编代码理解计算机是如何工作的
- MFC视频教学第一课,做一个简单的界面,理解应用程序和操作系统之间的消息传递机制
- 通过汇编一个简单的C程序,分析汇编代码理解计算机是如何工作的
- Linux内核分析课程--通过反汇编一个简单的c程序,分析汇编代码并理解计算机如何工作的
- 通过反汇编一个简单的C程序,分析汇编代码理解计算机是如何工作的
- Linux操作系统的简单指令及如何使用vim编写一个程序,然后使用gcc查看【预处理】、【编译】、【汇编】、【链接】各阶段文件的内容。
- 通过反汇编一个简单的C程序,分析汇编代码理解计算机是如何工作的
- 分析一个简单C程序的汇编代码,理解计算机是如何工作的
- 汇编一个简单的C程序,分析代码理解计算机是怎么工作的