zoukankan      html  css  js  c++  java
  • ELF文件格式

    前言

    绝大部分程序都或多或少共享了一些相同的功能,比如读写文件、创建HTTP连接等。这些相同的功能无需在每个程序中包含,只需大家共享一份相同的即可。
    使用这种共享技术的,就是动态链接;相反不使用这种共享技术的,就是静态链接。
    对于一个传统的二进制可执行程序(PE for Windows, ELF for Linux),除却系统特定的一些内容之外,都遵循相同的概念:

    • 每个功能都可以被划分成一个对应函数
    • 函数的二进制名称叫做“符号”
    • 静态链接还是动态链接,将决定这些符号解析至何处
    • 在可执行程序被编译时,实际被划分为编译和链接两个大步骤
    • 编译时只会将程序源代码变成机器码
    • 链接时将解决符号的解析问题
    • 因此动态链接和静态链接,通常是在程序链接时决定的
      运行可执行程序时,操作系统负责查找动态链接的符号,如果都能够找到且互相匹配,那么这个程序很大概率即可正常启动。

    ELF格式

    ELF 格式是一种用于二进制文件、可执行文件、目标代码、共享库和核心转储格式文件。

    一般一个 ELF 文件都会有两个 header ,一个叫 ELF header,作用是描述整个 ELF 文件的一些信息,比如适用的平台是不是 x64,内容是大端序还是小端序等。
    另一个 header 叫 program header,它的作用主要是为了表明自己应该如何被加载和执行。
    通过 readelf 命令,可以在 Linux 系统中查看任意 ELF 文件的信息,包括这两个 header :

    lzb@lzb:~/G-ORBSLAM/ORB_SLAM2-master/Examples/Monocular$ readelf --file-header mono_euroc
    ELF Header:
      Magic:   7f 45 4c 46 02 01 01 03 00 00 00 00 00 00 00 00 
      Class:                             ELF64
      Data:                              2's complement, little endian
      Version:                           1 (current)
      OS/ABI:                            UNIX - GNU
      ABI Version:                       0
      Type:                              DYN (Shared object file)
      Machine:                           Advanced Micro Devices X86-64
      Version:                           0x1
      Entry point address:               0x95b0
      Start of program headers:          64 (bytes into file)
      Start of section headers:          86992 (bytes into file)
      Flags:                             0x0
      Size of this header:               64 (bytes)
      Size of program headers:           56 (bytes)
      Number of program headers:         9
      Size of section headers:           64 (bytes)
      Number of section headers:         31
      Section header string table index: 30
    
    lzb@lzb:~/G-ORBSLAM/ORB_SLAM2-master/Examples/Monocular$ readelf --program-header mono_euroc
    
    Elf file type is DYN (Shared object file)
    Entry point 0x95b0
    There are 9 program headers, starting at offset 64
    
    Program Headers:
      Type           Offset             VirtAddr           PhysAddr
                     FileSiz            MemSiz              Flags  Align
      PHDR           0x0000000000000040 0x0000000000000040 0x0000000000000040
                     0x00000000000001f8 0x00000000000001f8  R      0x8
      INTERP         0x0000000000000238 0x0000000000000238 0x0000000000000238
                     0x000000000000001c 0x000000000000001c  R      0x1
          [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
      LOAD           0x0000000000000000 0x0000000000000000 0x0000000000000000
                     0x000000000000daee 0x000000000000daee  R E    0x200000
      LOAD           0x000000000000e1a8 0x000000000020e1a8 0x000000000020e1a8
                     0x0000000000000ec8 0x00000000000011b8  RW     0x200000
      DYNAMIC        0x000000000000eb58 0x000000000020eb58 0x000000000020eb58
                     0x0000000000000280 0x0000000000000280  RW     0x8
      NOTE           0x0000000000000254 0x0000000000000254 0x0000000000000254
                     0x0000000000000044 0x0000000000000044  R      0x4
      GNU_EH_FRAME   0x000000000000c7a0 0x000000000000c7a0 0x000000000000c7a0
                     0x00000000000003fc 0x00000000000003fc  R      0x4
      GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
                     0x0000000000000000 0x0000000000000000  RW     0x10
      GNU_RELRO      0x000000000000e1a8 0x000000000020e1a8 0x000000000020e1a8
                     0x0000000000000e58 0x0000000000000e58  R      0x1
    
     Section to Segment mapping:
      Segment Sections...
       00     
       01     .interp 
       02     .interp .note.ABI-tag .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt .init .plt .plt.got .text .fini .rodata .eh_frame_hdr .eh_frame .gcc_except_table 
       03     .init_array .fini_array .data.rel.ro .dynamic .got .data .bss 
       04     .dynamic 
       05     .note.ABI-tag .note.gnu.build-id 
       06     .eh_frame_hdr 
       07     
       08     .init_array .fini_array .data.rel.ro .dynamic .got 
    

    在这两个 header 之后,有很多 section。这些 section 会在 ELF 文件被加载时,归纳为若干 segment 。这里面既有代码也有数据,其中我们最关心的一个 section 叫做 .dynamic。
    .dynamic 包含了可执行程序所需的动态链接库,使用 readelf 也可以解析:

    lzb@lzb:~/G-ORBSLAM/ORB_SLAM2-master/Examples/Monocular$ readelf -d mono_euroc
    
    Dynamic section at offset 0xeb58 contains 36 entries:
      Tag        Type                         Name/Value
     0x0000000000000001 (NEEDED)             Shared library: [libORB_SLAM2.so]
     0x0000000000000001 (NEEDED)             Shared library: [libopencv_imgcodecs.so.3.2]
     0x0000000000000001 (NEEDED)             Shared library: [libopencv_core.so.3.2]
     0x0000000000000001 (NEEDED)             Shared library: [libpangolin.so]
     0x0000000000000001 (NEEDED)             Shared library: [libGL.so.1]
     0x0000000000000001 (NEEDED)             Shared library: [libGLEW.so.2.0]
     0x0000000000000001 (NEEDED)             Shared library: [libstdc++.so.6]
     0x0000000000000001 (NEEDED)             Shared library: [libgcc_s.so.1]
     0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
     0x000000000000001d (RUNPATH)            Library runpath: [/home/lzb/G-ORBSLAM/ORB_SLAM2-master/lib:/usr/local/lib:/home/lzb/G-ORBSLAM/ORB_SLAM2-master/Thirdparty/DBoW2/lib:/home/lzb/G-ORBSLAM/ORB_SLAM2-master/Thirdparty/g2o/lib]
     0x000000000000000c (INIT)               0x8158
     0x000000000000000d (FINI)               0xbfd4
     0x0000000000000019 (INIT_ARRAY)         0x20e1a8
     0x000000000000001b (INIT_ARRAYSZ)       16 (bytes)
     0x000000000000001a (FINI_ARRAY)         0x20e1b8
     0x000000000000001c (FINI_ARRAYSZ)       8 (bytes)
     0x000000006ffffef5 (GNU_HASH)           0x298
     0x0000000000000005 (STRTAB)             0x2810
     0x0000000000000006 (SYMTAB)             0xa88
     0x000000000000000a (STRSZ)              15231 (bytes)
     0x000000000000000b (SYMENT)             24 (bytes)
     0x0000000000000015 (DEBUG)              0x0
     0x0000000000000003 (PLTGOT)             0x20edd8
     0x0000000000000002 (PLTRELSZ)           1392 (bytes)
     0x0000000000000014 (PLTREL)             RELA
     0x0000000000000017 (JMPREL)             0x7be8
     0x0000000000000007 (RELA)               0x66e8
     0x0000000000000008 (RELASZ)             5376 (bytes)
     0x0000000000000009 (RELAENT)            24 (bytes)
     0x000000000000001e (FLAGS)              BIND_NOW
     0x000000006ffffffb (FLAGS_1)            Flags: NOW PIE
     0x000000006ffffffe (VERNEED)            0x6608
     0x000000006fffffff (VERNEEDNUM)         3
     0x000000006ffffff0 (VERSYM)             0x6390
     0x000000006ffffff9 (RELACOUNT)          171
     0x0000000000000000 (NULL)               0x0
    

    上面的回显里,标有NEEDED字样的行,即是这个 ELF 文件所需的动态链接库。
    不过要注意,readelf 本质上只解析这一个 ELF 文件。如果一个 ELF 文件依赖的动态链接库,又依赖了其他动态链接库,那么这条命令就不够用了。为此,可以使用 ldd 命令,这个命令会递归地处理依赖:

    lzb@lzb:~/G-ORBSLAM/ORB_SLAM2-master/Examples/Monocular$ ldd mono_euroc
    	linux-vdso.so.1 (0x00007ffc9e7f4000)
    	libORB_SLAM2.so => not found
    	libopencv_imgcodecs.so.3.2 => /usr/local/lib/libopencv_imgcodecs.so.3.2 (0x00007f74563d6000)
    	libopencv_core.so.3.2 => /usr/local/lib/libopencv_core.so.3.2 (0x00007f7455697000)
    	libpangolin.so => /usr/local/lib/libpangolin.so (0x00007f745523d000)
    	libGL.so.1 => /usr/lib/x86_64-linux-gnu/libGL.so.1 (0x00007f7454fb1000)
    	libGLEW.so.2.0 => /usr/lib/x86_64-linux-gnu/libGLEW.so.2.0 (0x00007f7454d1c000)
    	libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f7454993000)
    	libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f745477b000)
    	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f745438a000)
    	libopencv_imgproc.so.3.2 => /usr/local/lib/libopencv_imgproc.so.3.2 (0x00007f7452b72000)
    	libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f7452953000)
    	libjpeg.so.8 => /usr/lib/x86_64-linux-gnu/libjpeg.so.8 (0x00007f74526eb000)
    	libwebp.so.6 => /usr/lib/x86_64-linux-gnu/libwebp.so.6 (0x00007f7452482000)
    	libpng16.so.16 => /usr/lib/x86_64-linux-gnu/libpng16.so.16 (0x00007f7452250000)
    	libtiff.so.5 => /usr/lib/x86_64-linux-gnu/libtiff.so.5 (0x00007f7451fd9000)
    	libIlmImf-2_2.so.22 => /usr/lib/x86_64-linux-gnu/libIlmImf-2_2.so.22 (0x00007f7451b16000)
    	libHalf.so.12 => /usr/lib/x86_64-linux-gnu/libHalf.so.12 (0x00007f74518d3000)
    	libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f7451535000)
    	libz.so.1 => /lib/x86_64-linux-gnu/libz.so.1 (0x00007f7451318000)
    	libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f7451114000)
    	librt.so.1 => /lib/x86_64-linux-gnu/librt.so.1 (0x00007f7450f0c000)
    	libEGL.so.1 => /usr/lib/x86_64-linux-gnu/libEGL.so.1 (0x00007f7450cf8000)
    	libwayland-client.so.0 => /usr/lib/x86_64-linux-gnu/libwayland-client.so.0 (0x00007f7450ae9000)
    	libwayland-egl.so.1 => /usr/lib/x86_64-linux-gnu/libwayland-egl.so.1 (0x00007f74508e7000)
    	libwayland-cursor.so.0 => /usr/lib/x86_64-linux-gnu/libwayland-cursor.so.0 (0x00007f74506df000)
    	libxkbcommon.so.0 => /usr/lib/x86_64-linux-gnu/libxkbcommon.so.0 (0x00007f74504a0000)
    	libX11.so.6 => /usr/lib/x86_64-linux-gnu/libX11.so.6 (0x00007f7450168000)
            后面省略...
    
    

    linux-vdso.so.1

    ldd 命令非常友好地打印出了所有动态链接库的地址,但怎么唯独没有打印出 linux-vdso.so.1 呢?而且这个文件 ldd 并不是没有找到,它甚至给出了入口地址。如果我们自己在系统里搜索,也是无法搜索到这个文件的。
    原来这个 linux-vdso.so.1 文件,并不是一个真实存在的文件,而是 Linux 中的一个虚拟文件,专门用于将内核中一些常用的函数从内核空间映射到用户空间。也就是说,这个文件不用复制。

    LD_LIBRARY_PATH & RPATH & RUNPATH

    我们复制了目标程序所需的动态链接库,但是我们如何确定程序启动时,真的能够顺利找到这些动态链接库呢?
    在 Linux 中,主要有三个因素可以决定特定可执行文件的动态链接库的搜索路径:环境变量 LD_LIBRARY_PATH 、rpath 和 runpath。
    其中RPATH与RUNPATH是编译期间就已经固定的。
    动态库搜索顺序

    1. 先寻找RPATH下的文件,除非RUNPATH被设置
    2. LD_LIBRARY_PATH路径下的文件
    3. RUNPATH路径下的文件
    4. /etc/ld.so.conf
    5. /lib, /usr/lib
    

    参考资料

    zhuanlan.zhihu.com/p/59590848
    stackoverflow.com/questions/7967848/use-rpath-but-not-runpath
    software.intel.com/sites/default/files/m/a/1/e/dsohowto.pdf
    amir.rachum.com/blog/2016/09/17/shared-libraries/

  • 相关阅读:
    JSpider(1):配置与运行
    JSpider(4):Tasks,Events&Visitors
    WebUI Case(1): www.swtdesigner.com 首页 (续)
    java web开发_购物车功能实现
    java web开发_多拿网/淘宝网购物车选择操作
    java开发_快速搜索本地文件_小应用程序
    lucene(全文搜索)_恢复/更新索引操作
    lucene(全文搜索)_根据内容建立索引_源码下载
    程序员,都去写一写前端代码吧
    javascript_时间自动刷新
  • 原文地址:https://www.cnblogs.com/liuzhenbo/p/12263045.html
Copyright © 2011-2022 走看看