Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
PIC地址无关编译
Cortex-M3 动态加载一(地址无关代码实现)
发布于 2014-04-10 10:13:07 浏览:7964
订阅该版
这篇文章是自己疑惑究竟地址无关性是如何实现,然后查看汇编和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 } ```
查看更多
10
个回答
默认排序
按发布时间排序
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的债迟早要还的
cwq1142173025
2024-05-22
这家伙很懒,什么也没写!
如果代码里有函数指针,应该怎么处理呢, 链接的时候这个指针已经指向了固定的地址, 烧录到不同位置会导致找不到这个函数
撰写答案
登录
注册新账号
关注者
0
被浏览
8k
关于作者
haitao52198
这家伙很懒,什么也没写!
提问
42
回答
260
被采纳
0
关注TA
发私信
相关问题
1
RT-Thread有用到寄存器R9吗?
2
编译与位置无关的目标文件
3
分享一篇单片机分散加载机制、地址无关编译的分析文章
4
keil如何创建一个Cortex-M3工程,可以实现与地址无关。
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
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
使用百度AI助手辅助编写一个rt-thread下的ONVIF设备发现功能的功能代码
2
RT-Thread 发布 EtherKit开源以太网硬件!
3
rt-thread使用cherryusb实现虚拟串口
4
《C++20 图形界面程序:速度与渲染效率的双重优化秘籍》
5
《原子操作:程序世界里的“最小魔法单位”解析》
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
USB
DMA
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
FAL
rt-smart
ESP8266
I2C_IIC
WIZnet_W5500
ota在线升级
UART
PWM
cubemx
freemodbus
flash
packages_软件包
BSP
潘多拉开发板_Pandora
定时器
ADC
GD32
flashDB
socket
中断
Debug
编译报错
msh
SFUD
keil_MDK
rt_mq_消息队列_msg_queue
at_device
ulog
C++_cpp
本月问答贡献
踩姑娘的小蘑菇
7
个答案
3
次被采纳
a1012112796
13
个答案
2
次被采纳
张世争
9
个答案
2
次被采纳
rv666
5
个答案
2
次被采纳
用户名由3_15位
11
个答案
1
次被采纳
本月文章贡献
程序员阿伟
7
篇文章
2
次点赞
hhart
3
篇文章
4
次点赞
大龄码农
1
篇文章
2
次点赞
ThinkCode
1
篇文章
1
次点赞
Betrayer
1
篇文章
1
次点赞
回到
顶部
发布
问题
分享
好友
手机
浏览
扫码手机浏览
投诉
建议
回到
底部