add.c

int data = 1;
int bss;
const int rodata = 1;
int add(int num1, int num2)
{
  int sum = 0;
  sum = num1 + num2;
  return sum;
}

编译add.c成.o文件

gcc -c add.c(-c表示只编译不链接)

file add.o输出结果如下:

此结果表明add.o是个重定位文件。

查看elf header可查看到更详细信息:readelf -h add.o

由于是重定位文件,ELF中并没有program header,entry point为0x0, section header的字符串表在section 中的索引为9(在下面的section header中对应.shstrtable section).ELF header的size为52. 有12个section,每个section header的 size为40。

重定位文件的ELF格式的布局如下。

通过readelf -S add.o 查看section header。

Off这一列是表示section在ELF文件中的偏移量,.text的偏移量是0x34,转换成十进制正好是52(ELF header大小),说明header后面紧接着是.text。

.text内容可以通过objdump来查看:objdump -s -d add.o(-s表示将内容以16进制打印出来,-d表示反汇编)

.text只包含add函数,其大小为0x1d.

.data保存了初始化了的全局变量和静态变量。在add.c中.data只包含data这一个int型变量,所以其size为4。

.data的偏移量为0x34+0x1d=0x51,由于.data是4字节对其的,所以offset为0x54.

.rodata保存的是只读变量(如const修饰)和字符串常量。在add.c中只读变量为int型,大小为4.

.bss保存的是未初始化的全局变量和局部静态变量。按道理int bss是应该存在.bss段中,但是.bss的size是0.这其实与编译器相关,有些编译器并不会将未初始化的全局变量放在.bss中,只是预留一个未定义的全局变量符号,等到最终链接成可执行文件的时候再分配.bss分配空间。这与强符号和弱符号相关。

我们在通过readelf -s add.o看一下ELF的符号表:

其中我们看到符号的value大部分是0;如果符号是函数和变量,那么符号的value就是函数和变量的地址。

符号的value分为以下几种情况:

1.如果ELF文件是目标文件,符号不是“COMMON”类型,函数和变量的地址是不确定的,要等到链接后才知道,所以value值是符号其所在section中的偏移。

2.如果ELF文件是目标文件,符号是“COMMON”类型的,value表示该符号的对其属性。

3.如果ELF文件是可执行文件,value是符号的虚拟地址。

add符号是在代码段,所以Ndx项是1,从前面的section header可一看到.text的下标正好是1.其他符号如data,rodata以此类推。

其中名字没有显示,type是SECTION的符号,表示下标为Ndx段的section名字。比如Num 2的Ndx为1,那么他表示.text section名称,即".text".可以通过objdump -t查看符号表,可以获取所有符号名,即其说在的section名字。

我们在写一个简单的程序,准备调用add.c中的add函数,

#include <stdio.h>
int main()
{
  int sum = add(1,2);
  printf("sum is: %d\n", sum);
  return 0;
}

gcc -c main.c

查看section header:

多了一个section .rel.text,说明.text中有需要重定位的部分。

查看符号表和重定位表:

readelf -s main.o

重定位表:objdump -r main.o

符号add和printf是undefined,需要重定位以确定地址。

add和printff分别在main.o的0x19和0x31的位置需要重新定位。

objdump -d main.o反编译一下:

sum和printf的指令码都为e8 fc ff ff ff.

e8是call的指令码,通过file命令可以看出main.o是小端,所以在重定位前sum和printf的地址均为0xfffffffc,他是常量-4的补码。

我们再将main.c和add.c静态链接起来,

gcc main.c add.c -o test

可以看到形成可执行文件后,main.o中调用add函数的地址被重新修正。

现在的add函数的指令码为e8 1f 00 00 00;

R_386_PC32是相对寻址。对于需要重定位的符号,他修正后的结果为S+A-P.

S是add的实际地址0x08048459

A是修正前的值0xfffffffc,即-4;

P是需要修正的位置,当链接成可执行文件后,这个值是被修正位置的虚拟地址。0x804841d +0x19;

0x8048459-4 -(0x804841d+0x19) = 0x1f 与上述修正后的指令码“e8 0x1f 00 00 00”对应。

在重定位表中,我们还可以看到有个R_386_32类型的重定位项。

R_386_32是绝对寻址方式,其修正后的值是S+A.

S为符号的实际地址,A是被修正的位置。

对于.rodata这个需要重新定位的项,是printf中的常量字符串。S为0x000000

我们查看可执行文件的16进制内容,objdump -x test

那么修正后的值即为0x8048510

ELF学习--重定位文件的更多相关文章

  1. ARM ELF函数重定位

    ARM ELF的函数重定位与x86是一致的,但由于汇编指令不同,再鼓捣一遍. 示例代码: #include <stdio.h> #include <stdlib.h> int ...

  2. 南京大学计算机基础 X64函数调用和链接器原理和可重定位的文件.o

    一. 1.函数调用差别 X64的函数调用,和X86函数调用区别,在于参数的传递不一样了,X64的参数传递不在依靠栈来传,而是寄存器,不过还是具有局限性的 比如只能允许六个寄存器来传,分别是RDI,RS ...

  3. ELF学习--可执行文件

    承接上一篇.我们来看看可执行文件和重定位文件的不同点. readelf -h test 相比较重定位文件,可执行文件的ELF header中入口地址是0x8048320.而且除了section hea ...

  4. 鸿蒙内核源码分析(重定位篇) | 与国际接轨的对外部发言人 | 百篇博客分析OpenHarmony源码 | v55.01

    百篇博客系列篇.本篇为: v55.xx 鸿蒙内核源码分析(重定位篇) | 与国际接轨的对外部发言人 | 51.c.h.o 加载运行相关篇为: v51.xx 鸿蒙内核源码分析(ELF格式篇) | 应用程 ...

  5. 【旧文章搬运】PE重定位表学习手记

    原文发表于百度空间,2008-11-02========================================================================== 先定义一下 ...

  6. ELF Format 笔记(十)—— 重定位(relocation)

    ilocker:关注 Android 安全(新手) QQ: 2597294287 重定位就是把符号引用与符号定义链接起来的过程,这也是 android linker 的主要工作之一. 当程序中调用一个 ...

  7. ELF 动态链接 - so 的 重定位表

    动态链接下,无论时可执行文件还是共享对象,一旦对其他共享对象有依赖,也就是所有导入的符号时,那么代码或数据中就会有对于导入符号的引用.而在编译时期这些导入符号的确切地址时未知的.只有在运行期才能确定真 ...

  8. PE文件 03 重定位表

    0x01  重定位表结构   重定位表是由数据目录表中的第六个成员指出的: typedef struct _IMAGE_DATA_DIRECTORY { DWORD VirtualAddress; D ...

  9. Web 在线文件管理器学习笔记与总结(13)重命名文件夹(14)复制文件夹

    (13)重命名文件夹 ① 重命名文件夹通过 rename($oldname,$newname) 实现 ② 检测文件夹名是否符合规范 ③ 检测当前目录中是否存在同名文件夹名称,如果不存在则重命名成功 i ...

随机推荐

  1. JBPM4入门——9.自动节点单线执行

    JBPM入门系列文章: JBPM4入门——1.jbpm简要介绍 JBPM4入门——2.在eclipse中安装绘制jbpm流程图的插件 JBPM4入门——3.JBPM4开发环境的搭建 JBPM4入门—— ...

  2. mysql创建/删除表的例子

    创建表 简单的方式 CREATE TABLE person ( number INT(11), name VARCHAR(255), birthday DATE ); 或者是 CREATE TABLE ...

  3. Arduino命令行编译 树莓派连接Arduino 电脑上编译Arduino代码后 通过树莓派烧写到Arduino上

    //本教程针对UNO 1.在file->preferences中找到preferences.txt文件 2:用记事本打开preferences.txt,选择hex文件存放的路径,在最后行加入 b ...

  4. uglifyjs使用

    1.安装Nodejs:去这里下载并安装. 2.打开cmd,运行npm,node应该会有输出东西 3.如果在window上运行Node.js的npm报Error: ENOENT, stat 'C:\Us ...

  5. freemaker转word xml注意事项

    java类字符串变量如果含有以下2种字符: &和  <,必须转义否则转化将失败. 其中: &替换成 & <替换成 < 因为一些怪字符数据库存储时已转义了,从数 ...

  6. (win+linux)双系统,删除linux系统的条件下,删除grub引导记录,恢复windows引导

    //(hdx,y) (显示查找到的分区号)第一个数字指第几个硬盘,第二个指第几个分区.   一般我们是(hd0,0) \n Linux的分区已经被你从Windows中删除,系统启动后停在“grub&g ...

  7. PHP生成静态页面

    生成静态页面的本质就是读取缓存中的信息,然后写到一个生成的html页面中. 一.用ob_start和ob_get_contents生成静态页面 //打开缓存 <?phpob_start();// ...

  8. size_type、size_t、differentce_type以及ptrdiff_t

      目录(?)[-] size_type size_t different_type ptrdiff_t size_t是unsigned类型,用于指明数组长度或下标,它必须是一个正数,std::siz ...

  9. java webservice AXIS

    1. eclipse axis 插件下载地址   http://archive.apache.org/dist/ws/axis2/tools/1_4_1/ 一个是代码生成插件   axis2-ecli ...

  10. svn import 向Google code里导入初始代码

    其实很简单的问题,花费了这么多时间,想把初始代码导入到Google code里,用VisaulSVN插件的Switch功能也不可以,Google code上虽然有上传,但是只能单个文件传...... ...