智能车制作

 找回密码
 注册

扫一扫,访问微社区

查看: 15310|回复: 5
打印 上一主题 下一主题

代码重定位的思考(1)----PC基址跳转【转帖】

  [复制链接]

489

主题

1691

帖子

1

精华

管理员

网站创始人&站长

Rank: 11Rank: 11Rank: 11Rank: 11

积分
85927

特殊贡献奖章资源大师奖章论坛骨干奖章推广达人奖章优秀版主奖章热心会员奖章论坛元老奖章在线王奖章活跃会员奖章优秀会员奖章

QQ
威望
56985
贡献
17372
兑换币
13142
注册时间
2007-6-8
在线时间
5785 小时
跳转到指定楼层
#
发表于 2009-9-18 17:03:20 | 只看该作者 回帖奖励 |正序浏览 |阅读模式
作者: 快乐水牛
转自http://www.renesas-mcu.com/read.php?tid=42&fpage=2

所谓代码的重定位(relocate),就是把可执行代码移动到内存中的另外一个地址去。OS一般会把内核从硬盘COPY到内存中去执行,就是用到了重定位这个技术。可执行代码经过编译,连接和定位之后,代码段和数据段都已经被定位器固定了。那么移动这段代码之后,程序若碰到branch指令,会不会跳到错误的地址去执行呢?
为了验证这个问题,笔者以Renesas SH2A体系的CPU为例,来做了相关的测试。
硬件平台:
CPU和外部SDRAM
其中CPU内部包含一小块SRAM
程序被下载到外部SDRAM里面执行。笔者将外部SDRAM地址08010000到08010120区间的代码复制到CPU内部SRAM地址FFF80000处,并跳转到FFF80000去执行。
08010000到08010120区间包含了flush_cache()和它调用的init_node()的代码。
下面是代码拷贝的汇编函数
_relocate:
MOVML.L R6,@-R15
STS.L PR,@-R15

MOV.L #H'FFF80000,R0        ; start address of the internal RAM.
MOV.L #_flush_cache,R1      ; start address of flush_cache()
MOV.L #H'8010120,R6         ; copy stop address
  
Copy_Loop:
MOV.L @R1,R2
MOV.L R2,@R0                ; 开始拷贝代码到SRAM
ADD #H'4,R0
ADD #H'4,R1

CMP/EQ R6,R1
BT Continue                 ; if T = 1, copy finished

MOV.L #Copy_Loop,R3
JMP @R3
NOP
  
Continue:
CLRT
MOV.L #H'FFF80000,R5
JSR @R5                     ; execute flush_cache() in the internal RAM.
NOP

LDS.L @R15+,PR
MOVML.L @R15+,R6

RTS/N
.END  
下面是flush_cache()的C函数,里面调用另一个函数init_node(),以便让CPU产生branch指令
// 入口地址为0x08010000
void flush_cache(void)
{
  CCNT.CCR1.BIT.ICF = 1;
  CCNT.CCR1.BIT.OCF = 1;

// 入口地址为0x08010028
  init_node(0);
}
经过调试,发现程序能正常跳转到地址FFF80000去执行flush_cache(),接下来也能正常跳转到init_node()去执行,CPU完成了代码的重定位。
下面我们来看看编译器对flush_cache()生成的地址,机器码和反汇编。
移动之前:
地址      机器码   指令
08010014  D703    MOV.L @(H'000C:8,PC),R7    ;注意这里的R7就是init_node()的入口地址
08010016  472B    JMP @R7                    ;JMP是一条branch指令,用于子函数的跳转
移动之后:
地址      机器码   指令
FFF80014  D703    MOV.L @(H'000C:8,PC),R7
FFF80016  472B    JMP @R7

我们发现了一个很奇怪的问题,就是移动之前和移动之后的代码是完全不变的,但是程序在执行JMP @R7之后,确都能准确地跳转到0x08010028去执行init_node()。这是为什么呢?
很明显,我们发现了MOV.L @(H'000C:8,PC),R7这条指令的与众不同。该指令是把PC的内容加上H'000C:8计算产生的偏移量之和送入R7,于是真相明白了。编译器在遇到branch指令时,是以PC为基址来跳转的。
我们来查看该指令。
16-bit/32-bit displacement
PC indirect with displacement(带转移的PC间接寻址)
MOV.L @(disp:8,PC),R7
The effective address is the sum of PC value and an 8-bit displacement(disp). The value of disp is zero-extended, and is doubled for a word operation, and quadrupled for a longword operation. For a longword operation, the lowest two bits of the PC value are masked.
Word:
PC + disp * 2
Longword:
PC & H'FFFFFFFC + disp * 4
由于copy代码的时候,是将flush_cache()和init_node()作为一个整体来COPY的,所以CPU还是可以跳转到init_node()去。
最后思考一个问题,如果不把init_node()的代码COPY到SRAM里去,flush_cache()还能不能调用它呢?
实验证明,如果把 MOV.L #H'8010120,R6换成MOV.L #H'8010020,R6,即不拷贝init_node(),程序会跳转到一个错误的地址。

疑问:
以PC为基址的偏移量是有范围的(0xff * 4 = 1020Bytes),以Rn为基址的最大范围是16380Bytes(16KB)。如果程序超过了16KB,要调用的函数的代码不在这个范围内,程序还会正常跳转吗?

0

主题

106

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1031
威望
494
贡献
311
兑换币
335
注册时间
2013-9-3
在线时间
113 小时
5#
发表于 2014-4-6 12:43:55 | 只看该作者
过来看看  学习学习 啊哈哈哈哈
回复 支持 反对

使用道具 举报

0

主题

1672

帖子

0

精华

杰出人士

Rank: 12Rank: 12Rank: 12

积分
13030
QQ
威望
7128
贡献
3436
兑换币
424
注册时间
2013-1-5
在线时间
1233 小时
4#
发表于 2013-2-7 18:03:06 | 只看该作者
谢谢
回复 支持 反对

使用道具 举报

6

主题

309

帖子

0

精华

跨届大侠

Rank: 10Rank: 10Rank: 10

积分
9606

论坛元老奖章

QQ
威望
1453
贡献
7919
兑换币
155
注册时间
2010-4-2
在线时间
117 小时
3#
发表于 2012-12-21 01:29:05 | 只看该作者
支持一下哈
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

关于我们|联系我们|小黑屋|智能车制作 ( 黑ICP备2022002344号

GMT+8, 2024-12-27 02:14 , Processed in 0.376926 second(s), 31 queries , Gzip On.

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

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