程序员的自我修养:链接、装载与库阅读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显示的影响

目录
相关文章
|
4月前
|
程序员
程序员的自我修养:链接、装载与库阅读1
程序员的自我修养:链接、装载与库阅读
24 1
|
5月前
|
设计模式 JavaScript 程序员
探索源码:程序员的自我提升之路
探索源码:程序员的自我提升之路
40 1
|
7月前
奇淫技巧系列第三篇:阅读源码时基于一组快捷键让我们知道身在何方!
奇淫技巧系列第三篇:阅读源码时基于一组快捷键让我们知道身在何方!
|
存储 自然语言处理 IDE
编译与链接------《程序员的自我修养》
链接器同时也会引入标准C函数库中任何被该程序所用到的函数,而且它可以搜索程序员个人 的程序库,将其需要的函数也链接到程序中。
164 0
|
编译器 Linux C语言
【C语言进阶】—— 程序环境和预处理 ( 坚持总会有收获!!!)(下)
【C语言进阶】—— 程序环境和预处理 ( 坚持总会有收获!!!)(下)
121 0
【C语言进阶】—— 程序环境和预处理 ( 坚持总会有收获!!!)(下)
|
自然语言处理 C语言
【C语言进阶】—— 程序环境和预处理 ( 坚持总会有收获!!!)(中)
【C语言进阶】—— 程序环境和预处理 ( 坚持总会有收获!!!)(中)
125 0
【C语言进阶】—— 程序环境和预处理 ( 坚持总会有收获!!!)(中)
|
存储 自然语言处理 Linux
【C语言进阶】—— 程序环境和预处理 ( 坚持总会有收获!!!)(上)
【C语言进阶】—— 程序环境和预处理 ( 坚持总会有收获!!!)(上)
156 0
【C语言进阶】—— 程序环境和预处理 ( 坚持总会有收获!!!)(上)
|
存储 弹性计算 前端开发
服务器对程序员来说意味着什么,此文带你来了解
一、什么是服务器? 维基百科写到服务器指: ①一个管理资源并为用户提供服务的计算机软件,通常分为文件服务器(能使用户在其他计算机访问文件),数据库服务器和应用程序服务器。 ②运行以上软件的计算机,或称为网络主机(Host)。 服务器通常以网络作为介质,既可以通过局域网对内提供服务,也可以通过广域网对外提供服务,服务器的最大特点就是其强大的运算能力。 一般我们说做APP需要两个人,一个前端,一个后台。 前端:画UI页面及UI交互逻辑的人,需要写一些APP无需联网就能做到的内在逻辑,可能还需要写一些读取、写入后台数据逻辑的接口; 后端:写数据处理逻辑,通过服务器对数据进行处理,给前端提供
159 0
服务器对程序员来说意味着什么,此文带你来了解
|
Java Spring 开发工具
Java开发之上帝之眼系列教程前言和章节目录汇总
如果您正在为Java后端庞大的体系所困扰,如果您正在为各种繁出不穷的技术和各种框架所迷茫,那么本系列文章将带您窥探Java庞大的体系。本系列教程希望您能站在上帝的角度去观察(了解)Java体系。使Java的各种后端技术在你心中模块化;让你在工作中能将Java各个技术了然于心;能够即插即用。
2041 0