Chinaunix首页 | 论坛 | 博客
  • 博客访问: 28508
  • 博文数量: 6
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 32
  • 用 户 组: 普通用户
  • 注册时间: 2014-11-12 17:27
文章分类
文章存档

2019年(1)

2018年(2)

2017年(3)

我的朋友

分类: 嵌入式

2017-10-17 17:35:27

ARM  Architecture C语言寻址解析——
从U-Boot relocation所展开的探索(二)
by蔡于清


ARMArchitecture C语言PIC寻址方式解析
承前文所述,可不可以产生一种可以运行在任意地址段的代码呢?可以,这种代码被称之为Position-IndependentCode,简称 PIC(windows DLL,Linux ShareObject,这两者就是典型的PIC文件)。那么如何产生PIC呢?可以通过为编译器指定编译选项产生,比如:
arm-none-eabi-gcc -c -o -fpic main.o main.c
又比如:
arm-none-eabi-gcc -c -o -fpie main.o main.c
这样编译产生的目标文件包含了PIC所需要的信息,-fpic,-fpie是gcc的PIC编译选项。ld也有PIC连接选项-pie,要获得一个完整的PIC可运行文件,连接目标文件时必须为ld指定-pie选项,比如:
arm-none-eabi-ld -Tarm_pic.lds main.o -o arm_pic -pie

PIC可运行文件的一个最重要特点就是——这种文件里包含一个Global OffsetTable,简称GOT。每一个GOT Entry记录了一个对象的地址(对象可以是全局变量或函数),CPU从GOT中读取GOTEntry从而获得全局变量的地址。

下面讨论指定了-fpic编译选项-pie连接选项所产生的代码是如何寻址的。
命令行进入arm_pic目录,make GCC_PIC=-fpicLD_PIC=-pie,得到以下文件:arm_pic(elf格式文件)

  1. arm_pic.bin(二进制镜像文件)
  2. arm_pic.dump(反汇编文件)
  3. arm_pic.map(Memory Map文件)

从arm_pic.dump可见,出现了.got数据段,这一份GOT包含6个Entry,基地址为0x402001d0,6个Entry标示出6个全局变量的地址。

接下来通过main函数分析PIC寻址,main函数反汇编代码如下图所示:


与上一篇文章分析的汇编代码不同,现在Lable里面存放的已经不是地址值,而是偏移量(offset)。global_var1的寻址经过如下3个步骤(global_str的寻址方式也是如此):

  1. r3通过Lable1取得GOT Base相对pc的offset,从而确定GOT Base的地址;
  2. r2通过Lable2取得global_var1 GTO Entry相对于GOT Base的offset;
  3. r2 + r3累加(Base + offset)得到global_var1 GTOEntry的地址,从而取得global_var1的地址;

每一个变量的地址最终都是从GOT中获得,这就是PIC的寻址方式,也是它的核心,得到GOT的基地址,就能修改变量的地址从而对变量进行relocation(重定位)。


OK,跟上一篇文章所讨论一样,我们把arm_pic整体copy到0x80000000,同时,将GOT中每一个entry的内容累加上偏移量 0x3fe00000,这样所有C全局变量的地址都被调整到新的地址(修改变量地址的这一操作被称之为relocation),main函数和foo函数 中的变量寻址不会出错了,那么程序是不是就能正常运行了呢?还不行,为什么?GOT中保存的仅仅是C的变量地址和函数地址,但不要忘了,我们的工程不仅仅 有C代码,还有汇编代码!那么汇编代码的对象是如何relocation呢?我们如何得到汇编代码中需要relocation的对象信息呢?答案 在.rel.dyn数据段和.dynsym数据段,这部分将在下一篇文章中分析,同时也将具体分析U-Boot2011.12如何relocation。
阅读(1453) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~