程序员的自我修养:链接、装载与库阅读2

简介: 程序员的自我修养:链接、装载与库阅读

程序员的自我修养:链接、装载与库阅读1:https://developer.aliyun.com/article/1597070


五、重定位表

5.1 重定位表结构

 
/* Relocation table entry without addend (in section of type SHT_REL).  */
 
typedef struct
{
  Elf32_Addr    r_offset;               /* Address */
  Elf32_Word    r_info;                 /* Relocation type and symbol index */
} Elf32_Rel;
 
/* I have seen two different definitions of the Elf64_Rel and
   Elf64_Rela structures, so we'll leave them out until Novell (or
   whoever) gets their act together.  */
/* The following, at least, is used on Sparc v9, MIPS, and Alpha.  */
 
typedef struct
{
  Elf64_Addr    r_offset;               /* Address,8bytes */
  Elf64_Xword   r_info;                 /* Relocation type and symbol index,8bytes */
} Elf64_Rel;
 
/* Relocation table entry with addend (in section of type SHT_RELA).  */
 
typedef struct
{
  Elf32_Addr    r_offset;               /* Address */
  Elf32_Word    r_info;                 /* Relocation type and symbol index */
  Elf32_Sword   r_addend;               /* Addend */
} Elf32_Rela;
 
typedef struct
{
  Elf64_Addr    r_offset;               /* Address,8bytes */
  Elf64_Xword   r_info;                 /* Relocation type and symbol index,8bytes */
  Elf64_Sxword  r_addend;               /* Addend,8bytes */
} Elf64_Rela;

5.2 命令行段输出

readelf -r a.o
 
重定位节 '.rela.text' at offset 0x378 contains 5 entries:
  偏移量          信息           类型           符号值        符号名称 + 加数
000000000017  000500000002 R_X86_64_PC32     0000000000000000 .rodata - 4
000000000021  001000000004 R_X86_64_PLT32    0000000000000000 printf - 4
00000000003d  000300000002 R_X86_64_PC32     0000000000000000 .data + 0
000000000043  000400000002 R_X86_64_PC32     0000000000000000 .bss - 4
000000000056  000e00000004 R_X86_64_PLT32    0000000000000000 func1 - 4
 
重定位节 '.rela.eh_frame' at offset 0x3f0 contains 2 entries:
  偏移量          信息           类型           符号值        符号名称 + 加数
000000000020  000200000002 R_X86_64_PC32     0000000000000000 .text + 0
000000000040  000200000002 R_X86_64_PC32     0000000000000000 .text + 28


5.2 命令行段输出

readelf -r a.o
 
重定位节 '.rela.text' at offset 0x378 contains 5 entries:
  偏移量          信息           类型           符号值        符号名称 + 加数
000000000017  000500000002 R_X86_64_PC32     0000000000000000 .rodata - 4
000000000021  001000000004 R_X86_64_PLT32    0000000000000000 printf - 4
00000000003d  000300000002 R_X86_64_PC32     0000000000000000 .data + 0
000000000043  000400000002 R_X86_64_PC32     0000000000000000 .bss - 4
000000000056  000e00000004 R_X86_64_PLT32    0000000000000000 func1 - 4
 
重定位节 '.rela.eh_frame' at offset 0x3f0 contains 2 entries:
  偏移量          信息           类型           符号值        符号名称 + 加数
000000000020  000200000002 R_X86_64_PC32     0000000000000000 .text + 0
000000000040  000200000002 R_X86_64_PC32     0000000000000000 .text + 28

上面列举了32和64位系统的重定位结构,目前我们分析的是64位系统,而64位下有两个结构,一个是Elf64_Rel,另一个是Elf64_Rela。经过实际考察此处应当选择Elf64_Rela结构,每个域大小为24字节,共5个域,总大小为120字节。具体数据可参考下图。

.rela.text段(重定位表),其在文件中起始位置为:0x498+64*2=0x518(段开始位置为0x498,每个section header大小为64)

5.3 二进制段分析

0x378开始的0x78字节如下:

从上面分析可知,重定位主要描述了哪些字段需要重定位。

六、动态链接

6.1 源码

// Lib.c
#include <stdio.h>
 
void foobar(int i) {
    printf("Printing from Lib.so %d\n", i);
    sleep(-1);
}
 
 
// Lib.h
#ifndef LIB_H
#define LIB_H
 
void foobar(int i);
 
#endif
 
 
// Program1.c
#include "Lib.h"
 
int main() {
    foobar(1);
    return 0;
}
 
 
// Program2.c
#include "Lib.h"
#include <stdio.h>
 
//int ab = 10;
 
void ab() {
    int c = 55;
    int b = 66;
    int m = c + b;
    printf("The m 中国 is %d\n", m);
}
 
int main() {
    ab();
    foobar(2);
    return 0;
} 
//e4 b8 ad e5 9b bd  

执行:

# 生成 Lib.so 共享库
gcc -fPIC -shared -o Lib.so Lib.c
 
# 生成 Program1 
gcc -o Program1 Program1.c ./Lib.so
 
# 生成 Program2 
gcc -o Program2 Program2.c ./Lib.so

6.2 .interp 段

.interp段(是interpreter(解释器)的缩写),它保存了可执行文件所需要的动态链接器的路径,linux下一般为 /lib64/ld-linux-x86-64.so.2 ,使用命令:readelf -l Program1 | grep interpreter 可查看。

6.3 .dynamic 段

.dynamic段保存了动态链接器所需要的基本信息,比如依赖于哪些共享对象、动态链接符号表的位置、动态链接重定位表的位置、共享对象初始化代码的位置等。

6.3.1 表结构

/* Dynamic section entry.  */
 
typedef struct
{
  Elf64_Sxword  d_tag;                  /* Dynamic entry type,8bytes */
  union
    {
      Elf64_Xword d_val;                /* Integer value,8bytes */
      Elf64_Addr d_ptr;                 /* Address value,8bytes */
    } d_un;
} Elf64_Dyn;
 
 
/* Legal values for d_tag (dynamic entry type).  */
 
#define DT_NULL         0               /* Marks end of dynamic section */
#define DT_NEEDED       1               /* Name of needed library */
#define DT_PLTRELSZ     2               /* Size in bytes of PLT relocs */
#define DT_PLTGOT       3               /* Processor defined value */
#define DT_HASH         4               /* Address of symbol hash table */
#define DT_STRTAB       5               /* Address of string table */
#define DT_SYMTAB       6               /* Address of symbol table */
#define DT_RELA         7               /* Address of Rela relocs */
#define DT_RELASZ       8               /* Total size of Rela relocs */
#define DT_RELAENT      9               /* Size of one Rela reloc */
#define DT_STRSZ        10              /* Size of string table */
#define DT_SYMENT       11              /* Size of one symbol table entry */
#define DT_INIT         12              /* Address of init function */
#define DT_FINI         13              /* Address of termination function */
#define DT_SONAME       14              /* Name of shared object */
#define DT_RPATH        15              /* Library search path (deprecated) */
#define DT_SYMBOLIC     16              /* Start symbol search here */
#define DT_REL          17              /* Address of Rel relocs */
#define DT_RELSZ        18              /* Total size of Rel relocs */
#define DT_RELENT       19              /* Size of one Rel reloc */
#define DT_PLTREL       20              /* Type of reloc in PLT */
#define DT_DEBUG        21              /* For debugging; unspecified */
#define DT_TEXTREL      22              /* Reloc might modify .text */
#define DT_JMPREL       23              /* Address of PLT relocs */
#define DT_BIND_NOW     24              /* Process relocations of object */
#define DT_INIT_ARRAY   25              /* Array with addresses of init fct */
#define DT_FINI_ARRAY   26              /* Array with addresses of fini fct */
#define DT_INIT_ARRAYSZ 27              /* Size in bytes of DT_INIT_ARRAY */
#define DT_FINI_ARRAYSZ 28              /* Size in bytes of DT_FINI_ARRAY */
#define DT_RUNPATH      29              /* Library search path */
#define DT_FLAGS        30              /* Flags for the object being loaded */
#define DT_ENCODING     32              /* Start of encoded range */
#define DT_PREINIT_ARRAY 32             /* Array with addresses of preinit fct*/
#define DT_PREINIT_ARRAYSZ 33           /* size in bytes of DT_PREINIT_ARRAY */
#define DT_SYMTAB_SHNDX 34              /* Address of SYMTAB_SHNDX section */
#define DT_NUM          35              /* Number used */
#define DT_LOOS         0x6000000d      /* Start of OS-specific */
#define DT_HIOS         0x6ffff000      /* End of OS-specific */
#define DT_LOPROC       0x70000000      /* Start of processor-specific */
#define DT_HIPROC       0x7fffffff      /* End of processor-specific */
#define DT_PROCNUM      DT_MIPS_NUM     /* Most used by any processor */

6.3.2 命令输出

readelf -d Lib.so
 
Dynamic section at offset 0x2e20 contains 24 entries:
  标记        类型                         名称/值
 0x0000000000000001 (NEEDED)             共享库:[libc.so.6]
 0x000000000000000c (INIT)               0x1000
 0x000000000000000d (FINI)               0x1170
 0x0000000000000019 (INIT_ARRAY)         0x3e10
 0x000000000000001b (INIT_ARRAYSZ)       8 (bytes)
 0x000000000000001a (FINI_ARRAY)         0x3e18
 0x000000000000001c (FINI_ARRAYSZ)       8 (bytes)
 0x000000006ffffef5 (GNU_HASH)           0x2f0
 0x0000000000000005 (STRTAB)             0x3d8
 0x0000000000000006 (SYMTAB)             0x318
 0x000000000000000a (STRSZ)              127 (bytes)
 0x000000000000000b (SYMENT)             24 (bytes)
 0x0000000000000003 (PLTGOT)             0x4000
 0x0000000000000002 (PLTRELSZ)           48 (bytes)
 0x0000000000000014 (PLTREL)             RELA
 0x0000000000000017 (JMPREL)             0x530
 0x0000000000000007 (RELA)               0x488
 0x0000000000000008 (RELASZ)             168 (bytes)
 0x0000000000000009 (RELAENT)            24 (bytes)
 0x000000006ffffffe (VERNEED)            0x468
 0x000000006fffffff (VERNEEDNUM)         1
 0x000000006ffffff0 (VERSYM)             0x458
 0x000000006ffffff9 (RELACOUNT)          3
 0x0000000000000000 (NULL)               0x0

6.3.3 二进制分析

(1).dynstr(dynamic  symbol table),动态符号字符串表,对标(.strtab(字符串表))。

Lib.so的段其实地址为:0x37F8.dynstr段序号为【5】,每个段大小为64字节,故.dynstr在文件中偏移地址为:0x37F8 + 64 * 5 = 0x3938 处。查看可得知其在文件中的偏移地址为:0x3D8,共0x7F字节。

(2)Lib.so的段其实地址为:0x37F8.dynamic 段序号为【21】,每个段大小为64字节,故.dynamic在文件中偏移地址为:0x37F8 + 64 * 21 = 0x3D38 处。

0x2e20开始的0x1c0字节如下:

从上面可知,其共有0x1C0个字节,每个entry大小为16字节,故理论上应当有28个entries,但为什么 readelf 只输出24个entries呢?分析其最后5个entries,发现其值都为0,而0表示动态段结束,故只显示了24个entries。

6.3 .dynsym段

.dynsym(dynamic  symbol table),只保存动态链接相关的符号,对标(.symtab(符号表))。

Lib.so的段其实地址为:0x37F8.dynsym段序号为【4】,每个段大小为64字节,故.dynsym在文件中偏移地址为:0x37F8 + 64 * 4 = 0x38F8 处。

查看可得知其在文件中的偏移地址为:0x318,共0xC0字节,每个entry为0x18字节。

6.3.1 表结构

其表结构和.symtab(符号表)一样

/* Symbol table entry.  */
 
typedef struct
{
  Elf64_Word    st_name;                /* Symbol name (string tbl index),4bytes */
  unsigned char st_info;                /* Symbol type and binding,1bytes */
  unsigned char st_other;               /* Symbol visibility,1bytes */
  Elf64_Section st_shndx;               /* Section index,2bytes */
  Elf64_Addr    st_value;               /* Symbol value,8bytes */
  Elf64_Xword   st_size;                /* Symbol size,8bytes */
} Elf64_Sym;

6.3.2 命令行输出

 readelf -s Lib.so
 
Symbol table '.dynsym' contains 8 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_deregisterTMCloneTab
     2: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND printf@GLIBC_2.2.5 (2)
     3: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
     4: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_registerTMCloneTable
     5: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND sleep@GLIBC_2.2.5 (2)
     6: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND __cxa_finalize@GLIBC_2.2.5 (2)
     7: 0000000000001139    55 FUNC    GLOBAL DEFAULT   14 foobar
 
Symbol table '.symtab' contains 53 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
 
   ...               ...                ...           ...

6.3.3 二进制分析

6.4 .rela.dyn段

.rela.dyn,动态链接重定位表,对标(.rela.text),实际上是对数据引用的修正,它所修正的位置位于“.got” 以及数据段。

Lib.so的段其实地址为:0x37F8.rela.dyn段序号为【8】,每个段大小为64字节,故.rela.dyn在文件中偏移地址为:0x37F8 + 64 * 8 = 0x39F8 处。

具体分析参加 重定位表 一节

6.5 .rela.plt段

.rela.plt,动态链接重定位表,对标(.rela.data) ,实际上是对函数引用的修正,它所修正的位置位于“.got.plt”。  


Lib.so的段其实地址为:0x37F8,.rela.plt 段序号为【9】,每个段大小为64字节,故.rela.plt 在文件中偏移地址为:0x37F8 + 64 * 9 = 0x3A38 处。

具体分析参加 重定位表 一节

七、Segment

7.1 结构

  
/* Program segment header.  */
 
typedef struct
{
  Elf32_Word    p_type;                 /* Segment type */
  Elf32_Off     p_offset;               /* Segment file offset */
  Elf32_Addr    p_vaddr;                /* Segment virtual address */
  Elf32_Addr    p_paddr;                /* Segment physical address */
  Elf32_Word    p_filesz;               /* Segment size in file */
  Elf32_Word    p_memsz;                /* Segment size in memory */
  Elf32_Word    p_flags;                /* Segment flags */
  Elf32_Word    p_align;                /* Segment alignment */
} Elf32_Phdr;
 
typedef struct
{
  Elf64_Word    p_type;                 /* Segment type,4bytes */
  Elf64_Word    p_flags;                /* Segment flags,4bytes */
  Elf64_Off     p_offset;               /* Segment file offset,8bytes */
  Elf64_Addr    p_vaddr;                /* Segment virtual address,8bytes */
  Elf64_Addr    p_paddr;                /* Segment physical address,8bytes */
  Elf64_Xword   p_filesz;               /* Segment size in file,8bytes */
  Elf64_Xword   p_memsz;                /* Segment size in memory,8bytes */
  Elf64_Xword   p_align;                /* Segment alignment,8bytes */
} Elf64_Phdr;
 
// 共56bytes

7.2 命令输出

 readelf -l Lib.so
 
Elf 文件类型为 DYN (共享目标文件)
Entry point 0x1080
There are 11 program headers, starting at offset 64
 
程序头:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  LOAD           0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000560 0x0000000000000560  R      0x1000
  LOAD           0x0000000000001000 0x0000000000001000 0x0000000000001000
                 0x000000000000017d 0x000000000000017d  R E    0x1000
  LOAD           0x0000000000002000 0x0000000000002000 0x0000000000002000
                 0x00000000000000dc 0x00000000000000dc  R      0x1000
  LOAD           0x0000000000002e10 0x0000000000003e10 0x0000000000003e10
                 0x0000000000000220 0x0000000000000228  RW     0x1000
  DYNAMIC        0x0000000000002e20 0x0000000000003e20 0x0000000000003e20
                 0x00000000000001c0 0x00000000000001c0  RW     0x8
  NOTE           0x00000000000002a8 0x00000000000002a8 0x00000000000002a8
                 0x0000000000000020 0x0000000000000020  R      0x8
  NOTE           0x00000000000002c8 0x00000000000002c8 0x00000000000002c8
                 0x0000000000000024 0x0000000000000024  R      0x4
  GNU_PROPERTY   0x00000000000002a8 0x00000000000002a8 0x00000000000002a8
                 0x0000000000000020 0x0000000000000020  R      0x8
  GNU_EH_FRAME   0x000000000000201c 0x000000000000201c 0x000000000000201c
                 0x000000000000002c 0x000000000000002c  R      0x4
  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000000 0x0000000000000000  RW     0x10
  GNU_RELRO      0x0000000000002e10 0x0000000000003e10 0x0000000000003e10
                 0x00000000000001f0 0x00000000000001f0  R      0x1
 
 Section to Segment mapping:
  段节...
   00     .note.gnu.property .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt 
   01     .init .plt .plt.got .plt.sec .text .fini 
   02     .rodata .eh_frame_hdr .eh_frame 
   03     .init_array .fini_array .dynamic .got .got.plt .data .bss 
   04     .dynamic 
   05     .note.gnu.property 
   06     .note.gnu.build-id 
   07     .note.gnu.property 
   08     .eh_frame_hdr 
   09     
   10     .init_array .fini_array .dynamic .got 


7.3 二进制分析

 

八、参考

字符编码方式对ELF显示的影响

目录
相关文章
|
11月前
|
程序员 开发工具 Docker
13个程序员常用开发工具用途推荐整理
13个程序员常用开发工具用途推荐整理
132 0
|
程序员
程序员的自我修养—链接、装载与库--书签目录PDF
程序员的自我修养—链接、装载与库--书签目录PDF
1156 0
|
24天前
|
程序员
程序员的自我修养:链接、装载与库阅读1
程序员的自我修养:链接、装载与库阅读
14 1
|
2月前
|
设计模式 JavaScript 程序员
探索源码:程序员的自我提升之路
探索源码:程序员的自我提升之路
23 1
|
4月前
|
算法 芯片
嵌入式工程师如何快速的阅读datasheet的方法
嵌入式工程师如何快速的阅读datasheet的方法
116 0
|
编译器 C语言
【程序环境和程序预处理】万字详文,忘记了,看这篇就对了(1)
1.程序翻译环境和运行环境 假设一个test.c文件经过编译器编译运行后生成可执行文件test.exe,这中间存在两个过程: 一个是翻译,在这个环境中源代码被转换为可执行的机器指令。 一个是运行,它用于实际执行代码。 在翻译环境阶段,会进行编译和链接操作。 在汇编阶段,是将汇编指令转换成二进制指令。 1.1程序翻译中的的编译和链接
【程序环境和程序预处理】万字详文,忘记了,看这篇就对了(2)
1.程序翻译环境和运行环境 假设一个test.c文件经过编译器编译运行后生成可执行文件test.exe,这中间存在两个过程: 一个是翻译,在这个环境中源代码被转换为可执行的机器指令。 一个是运行,它用于实际执行代码。 在翻译环境阶段,会进行编译和链接操作。 在汇编阶段,是将汇编指令转换成二进制指令。
|
程序员 测试技术 开发工具
程序员成长第十篇:从阅读代码开始
程序员成长第十篇:从阅读代码开始
184 0
|
存储 自然语言处理 IDE
编译与链接------《程序员的自我修养》
链接器同时也会引入标准C函数库中任何被该程序所用到的函数,而且它可以搜索程序员个人 的程序库,将其需要的函数也链接到程序中。
141 0
|
程序员 Android开发
补充|程序猿技术干货资源【附源码】
上次,发布了篇 程序猿充电优质编程技术电子书 ,得到了很多同行的认可和点赞,我感觉我付出时间和精力收集的资源对大家有帮助,还是很值得的,下次会继续推送编程干货。但是,有个认真的圈友,在公众号后台私聊我说,第一本书《第一行代码》不是第2版,我认真看了发现确实不是,亏了那名圈友,感谢他的分享我拿到了郭霖的《第一行代码》第2版电子书了。下面把这本书和书里的源代码给大家送过去,希望大家在Android 开发中,可以快速入门和取得技术上的进步。
91 0
补充|程序猿技术干货资源【附源码】