重定位的相关知识

重定位

在链接器完成符号解析之后,就把代码中的每个符号引用和一个符号定义关联起来,此时链接器已经知道输入目标模块中的代码节和数据节的确切大小,开始重定位。

重定位分为两步:

  1.重定位节和符号定义链接器将所有相同类型的节合并为同一类型的新的聚合节,然后将运行时内存地址赋给新的聚合节,赋给输入模块定义的每个符号。当这一步完成后,程序中的每条指令和全局变量都有唯一的运行时内存地址了。

  2.重定位节中的符号引用。链接器修改代码节和数据节中对每个符号的引用,使他们指向正确的运行时地址。

代码的重定位条目放在.rel.text中,已初始化数据的重定位条目放在.rel.data中。

重定位条目

  ELF重定位条目如下:

          
  其中:offset是需要被修改的引用的节偏移,即重定位的偏移量。
     type告知链接器如何修改新的引用。ELF定义了32种不同的重定位类型,在这里只考虑两种:1.R_X64_PC32:重定位一个使用32位PC相对地址的引用。2.R_X86_64_32:重定位一个使用32位绝对地址的引用。
     symbol符号,与符号表中的符号对应,即重定位后指向哪一个符号。
     addend一个有符号常数,一些类型的重定位要使用它对被修改引用的值做偏移调整。
 
  对于下面两个程序文件中的函数
         
         
  对main.c进行编译得到main.o,反汇编得到如下代码:
        
  main函数引用了两个全局符号:array和sum。汇编器为每个引用产生一个重定位条目,显示在引用的后面一行上。这些重定位条目告诉链接器对sum的引用要使用32位PC相对地址进行重定位,而对array的引用要使用32位绝对地址进行重定位。接下来详细介绍:
重定位PC相对引用:
  在反汇编代码的第六行中,函数main调用sum函数,sum函数是在模块sum.o中定义的。call指令开始于节偏移0xe的地方,包括一字节的操作码0xe8,后面跟着的时对目标sum的32位PC相对引用的占位符。
  重定位条目r由以下4个字段组成:
  r.offset = 0xf
  r.symbol = sum
  r.type = R_X86_64_PC32
  r.addend = -4
  这些字段告诉链接器修改开始于偏移量0xf处的32位PC相对引用,这样在运行时它会指向sum。现假设链接器已经确定
  ADD(s) = ADDR(.text) = 0x4004d0
  ADDR(r.symbol) = ADDR(sum) = 0x4004e8
  使用重定位算法,链接器首先计算出引用的运行时地址:
  refadder = ADDR(s) + r.offset
         =0x4004d0 + 0xf
      =0x4004df
  然后,更新该应用,使得它在运行时指向sum:
  *refptr = (unsigned) (ADDR(r.symbol) + r.addend - refaddr)
     =(unsigned) (0x4004e8 + (-4) - 0x4004df)
     =(unsigned) (0x5)
  因此原来的call指令: e: e8 00 00 00 00 callq 13 <main+ox13> //sum()
  就变成了:4004de: e8 05 00 00 00 callq 4004e8 <sum> //sum()
重定位绝对引用:
  对于array而言,对应的占位符条目r包括以下4个字段:
  r.offset = 0xa
  r.symbol = array
  r.type = R_X64_64_32
  r.addend = 0
  这些字段告诉链接器要修改从偏移量0xa开始的绝对引用,这样在运行时会指向array的第一个字节。现假设链接器已经确定:
  ADDR(r.symbol = ADDR(array) = ox601018
  链接器使用算法修改引用:
  *refptr = (unsigned) (ADDR(r.symbol) + r.addend)
     =(unsigned) (0x601018 + 0)
     =(unsigned) (0x601018)
  因此,原来的汇编指令:9: bf 00 00 00 00 mov $0x0 //edi = &array
  变成了:4004d9: bf 18 10 60 00 mov $ox601018,%edi //edi = &array
       
 
 
 
 

 

posted @ 2020-12-02 11:17  玉可可  阅读(564)  评论(0)    收藏  举报