详解readelf命令

Last Updated: 2023-07-27 08:30:56 Thursday

-- TOC --

readelf命令,是专门用来读取ELF格式文件的工具,Linux系统下的可执行二进制文件,.so文件,编译过程的object文件等,都是ELF格式文件。

-h,打印ELF文件的头。

$ readelf -h test_time
ELF Header:
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
  Class:                             ELF64
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           Advanced Micro Devices X86-64
  Version:                           0x1
  Entry point address:               0x4010e0
  Start of program headers:          64 (bytes into file)
  Start of section headers:          22256 (bytes into file)
  Flags:                             0x0
  Size of this header:               64 (bytes)
  Size of program headers:           56 (bytes)
  Number of program headers:         13
  Size of section headers:           64 (bytes)
  Number of section headers:         31
  Section header string table index: 30

-S|--sections,大S,打印ELF文件的sections。

各个section的name和编号可以一目了然。

-s[C],小s,打印ELF文件的symbol table,如果加上-C,可以将C++的符号做demangle,即反向修饰,还原成原来的名字。

object文件中的符号value,一般为符号所在section的offset或size。可执行文件中符号的value,为虚拟地址。用readelf查看符号表,表的第一项永远是UND,不用care。另一个查看符号表的命名是nm

用readelf命令看符号表,如果符号名比较长,在显示的时候,会被省略掉,不如nm命令。

-p<N|sectionname>,将N号section按照字符串的方式打印出来,或者使用section name。

查看各种string table,用这个参数很方便:

$ readelf -p13 test_3.o

String dump of section '.shstrtab':
  [     1]  .symtab
  [     9]  .strtab
  [    11]  .shstrtab
  [    1b]  .rela.text
  [    26]  .data
  [    2c]  .bss
  [    31]  .rodata
  [    39]  .comment
  [    42]  .note.GNU-stack
  [    52]  .note.gnu.property
  [    65]  .rela.eh_frame

查看ELF中保存的动态链接器的位置:

$ readelf -p .interp test

String dump of section '.interp':
  [     0]  /lib64/ld-linux-x86-64.so.2

-x<N>,将N号section按照十六进制的方式打印出来;

比如查看.data section内的全部变量的初始值。

$ readelf -x3 test_3.o

Hex dump of section '.data':
  0x00000000 01000000 00000000 02000000          ............

-r,将文件中的relocation信息打印出来:

$ readelf -r test.o

Relocation section '.rela.text' at offset 0x268 contains 9 entries:
  Offset          Info           Type           Sym. Value    Sym. Name + Addend
00000000001a  000300000002 R_X86_64_PC32     0000000000000000 .rodata + 18
000000000027  000300000002 R_X86_64_PC32     0000000000000000 .rodata + 1c
00000000006a  00030000000a R_X86_64_32       0000000000000000 .rodata + 0
000000000074  000500000004 R_X86_64_PLT32    0000000000000000 printf - 4
00000000007c  000300000002 R_X86_64_PC32     0000000000000000 .rodata + 1c
00000000008e  00030000000a R_X86_64_32       0000000000000000 .rodata + 18
000000000098  000500000004 R_X86_64_PLT32    0000000000000000 printf - 4
0000000000a9  00030000000a R_X86_64_32       0000000000000000 .rodata + 18
0000000000b3  000500000004 R_X86_64_PLT32    0000000000000000 printf - 4

Relocation section '.rela.eh_frame' at offset 0x340 contains 1 entry:
  Offset          Info           Type           Sym. Value    Sym. Name + Addend
000000000020  000200000002 R_X86_64_PC32     0000000000000000 .text + 0

-l|--segments,查看segment信息,即program headers。segment是OS的加载视角,相同属性的section在加载的时候,会被合并成segment,以segment为单位加载,因为内存以page为单位,合并后加载可以有效节省内存。(查看加载属性)

有的物理page还会被多个虚拟地址段映射.....进一步节约内存....

$ readelf -l test_time

Elf file type is EXEC (Executable file)
Entry point 0x4010e0
There are 13 program headers, starting at offset 64

Program Headers:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  PHDR           0x0000000000000040 0x0000000000400040 0x0000000000400040
                 0x00000000000002d8 0x00000000000002d8  R      0x8
  INTERP         0x0000000000000318 0x0000000000400318 0x0000000000400318
                 0x000000000000001c 0x000000000000001c  R      0x1
      [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
  LOAD           0x0000000000000000 0x0000000000400000 0x0000000000400000
                 0x0000000000000740 0x0000000000000740  R      0x1000
  LOAD           0x0000000000001000 0x0000000000401000 0x0000000000401000
                 0x00000000000003b1 0x00000000000003b1  R E    0x1000
  LOAD           0x0000000000002000 0x0000000000402000 0x0000000000402000
                 0x0000000000000158 0x0000000000000158  R      0x1000
  LOAD           0x0000000000002e10 0x0000000000403e10 0x0000000000403e10
                 0x0000000000000264 0x0000000000000268  RW     0x1000
  DYNAMIC        0x0000000000002e20 0x0000000000403e20 0x0000000000403e20
                 0x00000000000001d0 0x00000000000001d0  RW     0x8
  NOTE           0x0000000000000338 0x0000000000400338 0x0000000000400338
                 0x0000000000000040 0x0000000000000040  R      0x8
  NOTE           0x0000000000000378 0x0000000000400378 0x0000000000400378
                 0x0000000000000044 0x0000000000000044  R      0x4
  GNU_PROPERTY   0x0000000000000338 0x0000000000400338 0x0000000000400338
                 0x0000000000000040 0x0000000000000040  R      0x8
  GNU_EH_FRAME   0x000000000000209c 0x000000000040209c 0x000000000040209c
                 0x000000000000002c 0x000000000000002c  R      0x4
  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000000 0x0000000000000000  RW     0x10
  GNU_RELRO      0x0000000000002e10 0x0000000000403e10 0x0000000000403e10
                 0x00000000000001f0 0x00000000000001f0  R      0x1

 Section to Segment mapping:
  Segment Sections...
   00
   01     .interp
   02     .interp .note.gnu.property .note.gnu.build-id .note.ABI-tag .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt
   03     .init .plt .text .fini
   04     .rodata .eh_frame_hdr .eh_frame
   05     .init_array .fini_array .dynamic .got .got.plt .data .bss
   06     .dynamic
   07     .note.gnu.property
   08     .note.gnu.build-id .note.ABI-tag
   09     .note.gnu.property
   10     .eh_frame_hdr
   11
   12     .init_array .fini_array .dynamic .got

.init .plt .text .fini被合并成03 segment!

readelf -h查看elf文件头,里面有program header信息!配合-W|--wide参数,让显示的列数超过80,在较宽的终端上看起来更舒服!

.o文件没有program header,因为.o文件不能被加载执行。而可执行的二进制文件和.so文件因为需要被加载运行,所以存在program header。

可执行文件的加载属性,能看到已经确定的虚拟地址(如上的0x0000000000400000),而动态链接库文件的加载属性,虚拟地址是0,因为动态链接库.so文件在编译的时候,并不能确定自己会被加载到进程虚拟地址空间的什么位置,也就具有了可以被加载到任意位置的属性。这种能力是PIC(Position Independent Code)带来的,而非装载时重定位(Load Time Relocation),后者虽能实现任意位置加载,但不能实现同时被多个进程共享。

-d,打印文件的dynamic section。

$ readelf -d /usr/lib64/libc.so.6

Dynamic section at offset 0x1f6ba0 contains 26 entries:
  Tag        Type                         Name/Value
 0x0000000000000001 (NEEDED)             Shared library: [ld-linux-x86-64.so.2]
 0x000000000000000e (SONAME)             Library soname: [libc.so.6]
 0x0000000000000019 (INIT_ARRAY)         0x1f4908
 0x000000000000001b (INIT_ARRAYSZ)       16 (bytes)
 0x0000000000000004 (HASH)               0x1ef390
 0x000000006ffffef5 (GNU_HASH)           0x3e8
 0x0000000000000005 (STRTAB)             0x16638
 0x0000000000000006 (SYMTAB)             0x4ae8
 0x000000000000000a (STRSZ)              32498 (bytes)
 0x000000000000000b (SYMENT)             24 (bytes)
 0x0000000000000003 (PLTGOT)             0x1f8000
 0x0000000000000002 (PLTRELSZ)           1320 (bytes)
 0x0000000000000014 (PLTREL)             RELA
 0x0000000000000017 (JMPREL)             0x27a00
 0x0000000000000007 (RELA)               0x20230
 0x0000000000000008 (RELASZ)             30672 (bytes)
 0x0000000000000009 (RELAENT)            24 (bytes)
 0x000000006ffffffc (VERDEF)             0x1fcc8
 0x000000006ffffffd (VERDEFNUM)          37
 0x000000000000001e (FLAGS)              BIND_NOW STATIC_TLS
 0x000000006ffffffb (FLAGS_1)            Flags: NOW
 0x000000006ffffffe (VERNEED)            0x201f0
 0x000000006fffffff (VERNEEDNUM)         1
 0x000000006ffffff0 (VERSYM)             0x1e52a
 0x000000006ffffff9 (RELACOUNT)          1191
 0x0000000000000000 (NULL)               0x0

判断一个动态链接库是否是通过-fPIC出来的:

$ readelf -d /usr/lib64/libc.so.6 | grep TEXTREL

没有输出,表示这个so库中没有需要动态重定位的指令,是PIC代码。

本文链接:https://cs.pynote.net/sf/c/cdm/202207281/

-- EOF --

-- MORE --