Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
嵌入式技术综合讨论
Cortex-M3 动态加载一(地址无关代码实现)
发布于 2014-04-10 10:13:07 浏览:7628
订阅该版
这篇文章是自己疑惑究竟地址无关性是如何实现,然后查看汇编和CPU指令手册,最后分析解除自己疑惑的,高手不要鄙视,哈哈。 编译C代码时候需要制定--acps/ropi选项,如下例子: ```1 void SystemInit(void) 2 { 3 } 4 void fun_for_sub(void) 5 { 6 int j; 7 for(j=65535;j >=0; j--) 8 ; 9 } 10 int main(void) 11 { 12 fun_for_sub(); 13 while(1); 14 }``` 编译: ```armcc -c --cpu Cortex-M3 -O0 --apcs=interwork --apcs /ropi/rwpi -o main.o main.c``` 使用fromelf查看汇编代码 ```fromelf.exe -s -c main.o``` text段生成的汇编代码如下: ```1 ** Section #1 '.text' (SHT_PROGBITS) [SHF_ALLOC + SHF_EXECINSTR] 2 Size : 14 bytes (alignment 2) 3 Address: 0x00000000 4 5 $t 6 .text 7 SystemInit 8 0x00000000: 4770 pG BX lr 9 fun_for_sub 10 0x00000002: 4770 pG BX lr 11 main 12 0x00000004: b500 .. PUSH {lr} 13 0x00000006: f7fffffe .... BL fun_for_sub ; 0x2 Section #1 14 0x0000000a: 205a Z MOVS r0,#0x5a 15 0x0000000c: bd00 .. POP {pc}``` 查看关键的一句调用函数fun_for_sub的汇编代码: 0x00000006: f7fffffe .... BL fun_for_sub ; 0x2 Section #1 查找arm的官方DDI0403D_arm_architecture_v7m_reference_manual_errata_markup_1_0.pdf关于BL指令的解释如下: Branch with Link (immediate) calls a subroutine at a PC-relative address. 得知BL是一条PC相关的指令。 具体看BL指令的构成: 根据我们产生的指令f7fffffe, 对应如下: f7ff : 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 fffe : 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 符号位S=1,J1=1,J2=1,imm10 = 11 1111 1111,imm11 = 111 1111 1110 所以I1 = !(J1~S) = 1, I2 = !(J2~S) = 1, imm32 = SignExtend(S:I1:I2:imm10:imm11:’0’,32) = SignExtend(1:1:1:11 1111 1111:111 1111 1110:’0’,32) = 1111 1111 1111 1111 1111 1111 1111 1100 = 0xfffffffc。 0xfffffffc是-4的补码,另外当前PC是0x00000006, 再根据上面的Operation最后一步BranchWritePC( PC + imm32) 最终跳转到0x6 + (-4) = 0x2的地址出,即函数fun_for_sub的地址,因此实现根据当前PC实现了地址无关性的代码。 在X86平台下面也是差不多的原理,使用的也是基于PC相关的跳转指令。《程序员的自我修养—链接、装载和库》讲得很好。 ---------------------转自ppym网友的一篇博文 上一篇关于动态加载讲述的是M3下面的ropi的实现细节,这一篇则讲述RW段的实现细节以及系统加载RW段的思路,我在M3上根据这个思路可以实现elf的动态加载,当然进一步的可以优化很多东西,还可以研究将bin加载起来,这个需要一些辅助的东西实现。 言归正文,使用/acps/rwpi编译代码,解决RW段即全局变量的加载。 首先编译的时候会为每一个全局变量生成一个相对于r9寄存器的偏移量,这个偏移量会在.text段中。 如下例子: ```1 static int elf_test_num = 1; 2 int elf_test_num2 = 12; 3 int main(void) 4 { 5 elf_test_num = 2; 6 elf_test_num2 = 4; 7 for(;;); 8 }``` 编译: ```armcc -c --cpu Cortex-M3 -O0 --apcs=interwork --apcs /ropi/rwpi -o main.o main.c``` 使用fromelf查看汇编代码 ```fromelf.exe -s -c main.o``` 生成的汇编代码如下(Cortex-M3): ```1 $t 2 .text 3 SystemInit 4 0x00000000: 4770 BX lr 5 main 6 0x00000002: 2002 MOVS r0,#2 7 0x00000004: 4904 LDR r1,[pc,#16] ; [0x18] = 0 8 0x00000006: 4449 ADD r1,r1,r9 9 0x00000008: 6008 STR r0,[r1,#0] 10 0x0000000a: 2004 MOVS r0,#4 11 0x0000000c: 4903 LDR r1,[pc,#12] ; [0x1c] = 0 12 0x0000000e: 4449 ADD r1,r1,r9 13 0x00000010: 6008 STR r0,[r1,#0] 14 0x00000012: bf00 NOP 15 0x00000014: e7fe B {pc} ; 0x14 16 $d 17 0x00000016: 0000 .. DCW 0 18 0x00000018: 00000000 .... DCD 0 19 0x0000001c: 00000000 .... DCD 0``` 在编译阶段相对r9偏移量还都是零,要到链接阶段才确定相对r9偏移量的大小,链接之后如下: ```armlink.exe --cpu Cortex-M3 --ropi --ro_base 0 --rwpi --rw_base 0x0 --entry=main --no_startup main.o -o main.elf``` 使用fromelf查看汇编代码 ```fromelf.exe -s -c main.elf``` 查看最终的elf文件汇编如下: ```1 $t 2 .text 3 SystemInit 4 0x00000000: 4770 BX lr 5 main 6 0x00000002: 2002 MOVS r0,#2 7 0x00000004: 4904 LDR r1,[pc,#16] ; [0x18] = 0x4 8 0x00000006: 4449 ADD r1,r1,r9 9 0x00000008: 6008 STR r0,[r1,#0] 10 0x0000000a: 2004 MOVS r0,#4 11 0x0000000c: 4903 LDR r1,[pc,#12] ; [0x1c] = 0x8 12 0x0000000e: 4449 ADD r1,r1,r9 13 0x00000010: 6008 STR r0,[r1,#0] 14 0x00000012: bf00 NOP 15 0x00000014: e7fe B 0x14 ; main + 18 16 $d 17 0x00000016: 0000 .. DCW 0 18 0x00000018: 00000004 .... DCD 4 19 0x0000001c: 00000008 .... DCD 8 ``` 此时$d对应的偏移量均已确定大小。 取出对应一句C的汇编代码如下: ```1 elf_test_num = 2; 2 3 0x00000002: 2002 MOVS r0,#2 4 0x00000004: 4904 LDR r1,[pc,#16] ; [0x18] = 0 5 0x00000006: 4449 ADD r1,r1,r9 6 0x00000008: 6008 STR r0,[r1,#0]``` 详细解释如下: 1、MOVS r0,#2 即r0 = 2。 2、LDR r1,[pc,#16] ; [0x18] = 0 即r1 = *(pc + 16)。这里实现了RW无关性,相对当前PC值取出偏移量所在的地址即pc,#16 = 0x18,再从0x18地址出取出偏移量的大小即[pc,#16] = 0x04,从上面加黑的位置查看0x00000018地址的值即为0x00000004,存放到r1寄存器。(这里的pc值应该是下一指令的pc值,并且应该是对齐32位的,具体赢查看arm指令手册。) 3、ADD r1,r1,r9 即r1 = r1+r9,所以指定了在r9偏移0x00000004的地址处给到r1。 4、STR r0,[r1,#0] 即*(r1 + 0) = r0,即将r0赋给r1指向的地址处,此时r1即是偏移r9基址4的地方。 综上所述,在加载elf阶段,将RW段加载到RAM当中之后,需要将r9寄存器指向此片内存的基地址,然后接下来就可以跳转到加载的elf的代码中去执行,就可以实现全局变量的加载了。具体实现思路可以如下: ```1 __global_reg(6) char *sb; //在C中使用r9寄存器(static base register)的方法 2 3 char rw_buf[100]; //rw段的加载地址,也可以让系统动态分配一段内存地址 4 5 char *saved_sb; //保存r9 6 7 void load_fun(void) 8 9 { 10 11 saved_sb = sb; //先保存r9的值 12 13 sb = rw_buf; //将r9指向rw段的加载地址 14 15 entry(); //跳转执行到具体的一个elf的入口执行 16 17 sb = saved_sb; //从elf程序跳转回来赋回原来r9的值 18 19 } ```
查看更多
9
个回答
默认排序
按发布时间排序
aozima
2014-04-10
调网络不抓包,调I2C等时序不上逻辑分析仪,就像电工不用万用表!多用整理的好的文字,比截图更省流量,还能在整理过程中思考。
学习了!
grissiom
2014-04-10
这家伙很懒,什么也没写!
嗯,应该可以用这个来实现 armcc 的 UA~ 涛哥V5~
haitao52198
2014-04-10
这家伙很懒,什么也没写!
>嗯,应该可以用这个来实现 armcc 的 UA~ > >涛哥V5~ --- 额,是写这个博客的哥们V5,只是今天早晨看他更新的博文,就想起RT-Thread里一直没有能用armcc来编译模块,或许这个可以帮助到,至于里面的方法我没有亲自试过,@grissiom还是你来吧
haitao52198
2014-04-10
这家伙很懒,什么也没写!
>学习了! --- 貌似没有上面出现的那么多命令呢? ``` Usage: armcc [options] file1 file2 ... filen Main options: --arm Generate ARM code --thumb Generate Thumb code --c90 Switch to C mode (default for .c files) --cpp Switch to C++ mode (default for .cpp files) -O0 Minimum optimization -O1 Restricted optimization for debugging -O2 High optimization -O3 Maximum optimization -Ospace Optimize for codesize -Otime Optimize for maximum performance --cpu
Select CPU to generate code for --cpu list Output a list of all the selectable CPUs --device
Set the target device type --device list Output a list of all the selectable devices -o
Name the final output file of the compilation -c Compile only, do not link --asm Output assembly code as well as object code -S Output assembly code instead of object code --interleave Interleave source with disassembly (use with --asm or -E Preprocess the C source code only -D
Define
on entry to the compiler -g Generate tables for high-level debugging -I
Include
on the #include search path Software supplied by: ARM Limited ```
bernard
2014-04-10
这家伙很懒,什么也没写!
位置无关关系倒没多大,关键是部分链接,以及内核中的再链接。
haitao52198
2014-04-17
这家伙很懒,什么也没写!
>嗯,应该可以用这个来实现 armcc 的 UA~ > >涛哥V5~ --- 更新了下这个帖子
geniusgogo
认证专家
2014-04-17
这家伙很懒,什么也没写!
牛逼
bernard
2014-04-17
这家伙很懒,什么也没写!
后面的更新很好,记得以前弄gcc时,这个r9寄存器是不支持的,支持了就更容易了
wang1216
2014-05-17
这家伙很懒,什么也没写!
为啥不用gcc,欠keil的债迟早要还的
撰写答案
登录
注册新账号
关注者
0
被浏览
7.6k
关于作者
haitao52198
这家伙很懒,什么也没写!
提问
42
回答
260
被采纳
0
关注TA
发私信
相关问题
1
开新板块了! 迅速占领第一帖!
2
有想玩点阵做电子钟的没?手上有屏
3
LED点阵屏硬件保护研究笔记
4
USB相关、Android、Arduino
5
Arduino即将发布ARM平台新产品
6
关于开关电源的同步整流技术
7
rt_thread_wizard使用教程
8
[转]开源如何盈利
9
FM3系列MCU的IO操作笔记。
10
转一个xoolhaha 的寻一起开发的帖子
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
机器人操作系统 (ROS2) 和 RT-Thread 通信
4
国产MCU移植系列教程汇总,欢迎查看!
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
最新文章
1
基于MQTT协议的阿里云物联平台+移远4G模块OTA升级参考
2
浅谈国产加密芯片的选择
3
NXP MCXN947测评之SPI实践 - BMI270 加速度计数据读取
4
【RA8D1-Vision Board】基于OpenMV 实现图像分类
5
[Vision Board创客营] PWM模块实践
热门标签
RT-Thread Studio
串口
LWIP
Env
SPI
Bootloader
AT
ART-Pi
Hardfault
CAN总线
FinSH
USB
文件系统
DMA
RT-Thread
SCons
线程
RT-Thread Nano
MQTT
STM32
RTC
FAL
rt-smart
ESP8266
ota在线升级
WIZnet_W5500
I2C_IIC
UART
flash
cubemx
packages_软件包
freemodbus
PWM
潘多拉开发板_Pandora
定时器
ADC
BSP
中断
flashDB
编译报错
socket
keil_MDK
GD32
msh
Debug
SFUD
MicroPython
ulog
SDIO总线
rt_mq_消息队列_msg_queue
本月问答贡献
小小李sunny
2
个答案
2
次被采纳
聚散无由
1
个答案
2
次被采纳
用户名由3_15位
3
个答案
1
次被采纳
bernard
2
个答案
1
次被采纳
a1012112796
1
个答案
1
次被采纳
本月文章贡献
聚散无由
1
篇文章
3
次点赞
Hlafklio
1
篇文章
2
次点赞
YDPB_5935
1
篇文章
2
次点赞
uniquechuck
1
篇文章
1
次点赞
Z_Y
1
篇文章
1
次点赞
回到
顶部
发布
问题
分享
好友
手机
浏览
扫码手机浏览
投诉
建议
回到
底部