序言

前几天排查LD_LIBRARY_PATH问题的时候(详细排查过程见 ldconfig问题排查 ),在排查过程中,使用到了readelf命令,在当时的排查过程中,主要使用的参数是 -d ,今日,笔者就大概讲一下readelf其他的一些参数和得到数据的一些含义和用途。

今日主要讲的是readelf的使用和参数含义,及输出数据的用途,而readelf命令本身就是对ELF文件的解析和使用,了解ELF文件格式,相对于readelf的数据会有很大的帮助,对ELF文件格式有稍微涉猎,并不会太深,后面,笔者会就ELF文件格式本身做一个详细的描述。

由于篇幅的问题,本文会分多篇进行梳理,本文为第一篇。

使用readelf命令,可以很方便查看动态库的一些信息,但只能对文件进行分析,不能把依赖之间的关系进行梳理,后续,笔者将尝试着利用elf文件格式对某动态库进行依赖的梳理,并展示给大家,敬请期待。

第一个参数 -h

readelf -h     
Usage: readelf <option(s)> elf-file(s)
 Display information about the contents of ELF format files
 Options are:
  -a --all               Equivalent to: -h -l -S -s -r -d -V -A -I
  -h --file-header       Display the ELF file header
  -l --program-headers   Display the program headers
     --segments          An alias for --program-headers
  -S --section-headers   Display the sections' header
     --sections          An alias for --section-headers
  -g --section-groups    Display the section groups
  -t --section-details   Display the section details
  -e --headers           Equivalent to: -h -l -S
  -s --syms              Display the symbol table
     --symbols           An alias for --syms
     --dyn-syms          Display the dynamic symbol table
     --lto-syms          Display LTO symbol tables
     --sym-base=[0|8|10|16]
                         Force base for symbol sizes.  The options are
                         mixed (the default), octal, decimal, hexadecimal.
  -C --demangle[=STYLE]  Decode low-level symbol names into user-level names
                          The STYLE, if specified, can be `auto' (the default),
                          `gnu', `lucid', `arm', `hp', `edg', `gnu-v3', `java'
                          or `gnat'
     --no-demangle       Do not demangle low-level symbol names.  (default)
     --recurse-limit     Enable a demangling recursion limit.  (default)
     --no-recurse-limit  Disable a demangling recursion limit
  -n --notes             Display the core notes (if present)
  -r --relocs            Display the relocations (if present)
  -u --unwind            Display the unwind info (if present)
  -d --dynamic           Display the dynamic section (if present)
  -V --version-info      Display the version sections (if present)
  -A --arch-specific     Display architecture specific information (if any)
  -c --archive-index     Display the symbol/file index in an archive
  -D --use-dynamic       Use the dynamic section info when displaying symbols
  -L --lint|--enable-checks
                         Display warning messages for possible problems
  -x --hex-dump=<number|name>
                         Dump the contents of section <number|name> as bytes
  -p --string-dump=<number|name>
                         Dump the contents of section <number|name> as strings
  -R --relocated-dump=<number|name>
                         Dump the relocated contents of section <number|name>
  -z --decompress        Decompress section before dumping it
  -w --debug-dump[a/=abbrev, A/=addr, r/=aranges, c/=cu_index, L/=decodedline,
                  f/=frames, F/=frames-interp, g/=gdb_index, i/=info, o/=loc,
                  m/=macro, p/=pubnames, t/=pubtypes, R/=Ranges, l/=rawline,
                  s/=str, O/=str-offsets, u/=trace_abbrev, T/=trace_aranges,
                  U/=trace_info]
                         Display the contents of DWARF debug sections
  -wk --debug-dump=links Display the contents of sections that link to separate
                          debuginfo files
  -P --process-links     Display the contents of non-debug sections in separate
                          debuginfo files.  (Implies -wK)
  -wK --debug-dump=follow-links
                         Follow links to separate debug info files (default)
  -wN --debug-dump=no-follow-links
                         Do not follow links to separate debug info files
  --dwarf-depth=N        Do not display DIEs at depth N or greater
  --dwarf-start=N        Display DIEs starting at offset N
  --ctf=<number|name>    Display CTF info from section <number|name>
  --ctf-parent=<number|name>
                         Use section <number|name> as the CTF parent
  --ctf-symbols=<number|name>
                         Use section <number|name> as the CTF external symtab
  --ctf-strings=<number|name>
                         Use section <number|name> as the CTF external strtab
  -I --histogram         Display histogram of bucket list lengths
  -W --wide              Allow output width to exceed 80 characters
  -T --silent-truncation If a symbol name is truncated, do not add [...] suffix
  @<file>                Read options from <file>
  -H --help              Display this information
  -v --version           Display the version number of readelf

使用一个命令的第一反应,就应该是-h 或者 –help输出当前命令的usage,即使用说明。 使用说明中,列举出来了所有的可以使用的参数,以及必须输入的参数。 如 readelf <option(s)> elf-file(s) 输入中,必须有一个elf-file或者多个elf文件,有一个或多个options的参数,即下列列出来的参数名称。请注意,下列的参数,是区分大小写的,输入大写或者小写,虽然是同一个字符,但输出的内容是完全不同的。当然,经常使用命令行的同学请原谅笔者的啰嗦。

-d参数

从help的列表中,我们可以看到-d参数的描述, -d --dynamic Display the dynamic section (if present)使用-d 或者–dynamic 参数,可以显示ELF文件的动态段。

这里,允许笔者稍微跑一下题,来大概介绍下ELF的格式。

ELF(Executable and Linkable Format)是一种可执行和链接文件格式,广泛应用于各种操作系统,如Linux、Unix、Windows等。ELF格式是一种二进制文件格式,用于表示可执行文件、动态链接库(Dynamic Link Library,简称DLL)和共享对象(Shared Object,简称SO)。

ELF格式的主要组成部分包括:ELF头部(ELF Header)、程序头部表(Program Header Table)、节(Section)、动态链接表(Dynamic Linker)等。

-d参数,展示的主要就是 dynamic linker 部分的信息

输出如下:

Dynamic section at offset 0x3b2a68 contains 33 entries:
  标记        类型                         名称/值
 0x0000000000000001 (NEEDED)             共享库:[libOpenThreads.so.21]
 0x0000000000000001 (NEEDED)             共享库:[libpthread.so.0]
 0x0000000000000001 (NEEDED)             共享库:[librt.so.1]
 0x0000000000000001 (NEEDED)             共享库:[libdl.so.2]
 0x0000000000000001 (NEEDED)             共享库:[libGL.so.1]
 0x0000000000000001 (NEEDED)             共享库:[libstdc++.so.6]
 0x0000000000000001 (NEEDED)             共享库:[libm.so.6]
 0x0000000000000001 (NEEDED)             共享库:[libgcc_s.so.1]
 0x0000000000000001 (NEEDED)             共享库:[libc.so.6]
 0x000000000000000e (SONAME)             Library soname: [libosg.so.160]
 0x000000000000000c (INIT)               0x1109d0
 0x000000000000000d (FINI)               0x31136c
 0x0000000000000019 (INIT_ARRAY)         0x5868f8
 0x000000000000001b (INIT_ARRAYSZ)       1240 (bytes)
 0x000000000000001a (FINI_ARRAY)         0x586dd0
 0x000000000000001c (FINI_ARRAYSZ)       8 (bytes)
 0x000000006ffffef5 (GNU_HASH)           0x1f0
 0x0000000000000005 (STRTAB)             0x3c8a8
 0x0000000000000006 (SYMTAB)             0xda18
 0x000000000000000a (STRSZ)              357460 (bytes)
 0x000000000000000b (SYMENT)             24 (bytes)
 0x0000000000000003 (PLTGOT)             0x5b4000
 0x0000000000000002 (PLTRELSZ)           52656 (bytes)
 0x0000000000000014 (PLTREL)             RELA
 0x0000000000000017 (JMPREL)             0x103c20
 0x0000000000000007 (RELA)               0x97cc8
 0x0000000000000008 (RELASZ)             442200 (bytes)
 0x0000000000000009 (RELAENT)            24 (bytes)
 0x000000006ffffffe (VERNEED)            0x97b88
 0x000000006fffffff (VERNEEDNUM)         7
 0x000000006ffffff0 (VERSYM)             0x93cfc
 0x000000006ffffff9 (RELACOUNT)          343
 0x0000000000000000 (NULL)               0x0

大概解释下:

前面的NEEDED列是当前动态库依赖的库名称,列在后面的共享库[***]一般是动态库的名称,通过LD_LIBRARY_PATH设置的ld路径的,一般情况下就是去找相应目录下的文件名。当然,也有特殊的情况,就比如我们前面排查的问题的遇到的现象,使用ldconfig 命令加载的动态库,由于当前NEEDED中列的是SD_DBIO.tx的名称,如果是ldconfig直接加载的话,是会把当前文件给过滤掉的,而如果修改成符合ldconfig格式的文件,那么,ld路径就难以找到当前文件名的文件,所以,在ldconfig命令中,会创建一个软连接,使用 SONAME 相应的library name作为文件名,文件指向到原文件。那么,在ld的时候就可以通过当前文件名加载到相应的文件。 后面的参数,使用的比较少,一般是在运行过程中对文件进行加载,后续再讲。

本文链接readelf常用命令-I –>http://b.zf520.net.cn/2023/12/readelf.html