查看可执行程序(ELF)或动态库所依赖动态库——ldd脚本分析

1.序

搞清可执行程序(动态库)所依赖的动态库信息是非常有帮助的。

2.查看方法

系统信息:

Linux netview 4.4.0-101-generic #124~14.04.1-Ubuntu SMP Fri Nov 10 19:06:11 UTC 2017 i686 i686 i686 GNU/Linux

2.1 使用 ldd

# ldd `which gdb`

linux-gate.so.1 =>  (0xb7737000)
libreadline.so.6 => /usr/local/lib/libreadline.so.6 (0xb76fa000)
libdl.so.2 => /lib/i386-linux-gnu/libdl.so.2 (0xb76da000)
libncurses.so.5 => /lib/i386-linux-gnu/libncurses.so.5 (0xb76b4000)
libtinfo.so.5 => /lib/i386-linux-gnu/libtinfo.so.5 (0xb7692000)
libz.so.1 => /lib/i386-linux-gnu/libz.so.1 (0xb7678000)
libm.so.6 => /lib/i386-linux-gnu/libm.so.6 (0xb7632000)
libpython3.4m.so.1.0 => /usr/lib/i386-linux-gnu/libpython3.4m.so.1.0 (0xb7251000)
libpthread.so.0 => /lib/i386-linux-gnu/libpthread.so.0 (0xb7234000)
libexpat.so.1 => /lib/i386-linux-gnu/libexpat.so.1 (0xb720b000)
liblzma.so.5 => /lib/i386-linux-gnu/liblzma.so.5 (0xb71e5000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7034000)
/lib/ld-linux.so.2 (0xb7739000)
libutil.so.1 => /lib/i386-linux-gnu/libutil.so.1 (0xb7030000)
# ldd `gcc -print-file-name=libreadline.so.6`
linux-gate.so.1 =>  (0xb77cf000)
libtinfo.so.5 => /lib/i386-linux-gnu/libtinfo.so.5 (0xb7750000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb759f000)
/lib/ld-linux.so.2 (0xb77d1000)
ldd `gcc -print-file-name=libpthread.so.0` 
linux-gate.so.1 =>  (0xb772a000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb753e000)
/lib/ld-linux.so.2 (0xb772c000)

2.2 使用 readelf -d

# readelf -d `which gdb`

Dynamic section at offset 0x4d2ec4 contains 34 entries:
  Tag        Type                         Name/Value
 0x00000001 (NEEDED)                     Shared library: [libreadline.so.6]
 0x00000001 (NEEDED)                     Shared library: [libdl.so.2]
 0x00000001 (NEEDED)                     Shared library: [libncurses.so.5]
 0x00000001 (NEEDED)                     Shared library: [libtinfo.so.5]
 0x00000001 (NEEDED)                     Shared library: [libz.so.1]
 0x00000001 (NEEDED)                     Shared library: [libm.so.6]
 0x00000001 (NEEDED)                     Shared library: [libpython3.4m.so.1.0]
 0x00000001 (NEEDED)                     Shared library: [libpthread.so.0]
 0x00000001 (NEEDED)                     Shared library: [libexpat.so.1]
 0x00000001 (NEEDED)                     Shared library: [liblzma.so.5]
 0x00000001 (NEEDED)                     Shared library: [libc.so.6]
 0x0000000c (INIT)                       0x8089c24
 0x0000000d (FINI)                       0x83640bc
 0x00000019 (INIT_ARRAY)                 0x851beb8
 0x0000001b (INIT_ARRAYSZ)               4 (bytes)
 0x0000001a (FINI_ARRAY)                 0x851bebc
 0x0000001c (FINI_ARRAYSZ)               4 (bytes)
 0x6ffffef5 (GNU_HASH)                   0x80481ac
 0x00000005 (STRTAB)                     0x80685a4
 0x00000006 (SYMTAB)                     0x80509d4
 0x0000000a (STRSZ)                      120269 (bytes)
 0x0000000b (SYMENT)                     16 (bytes)
 0x00000015 (DEBUG)                      0x0
 0x00000003 (PLTGOT)                     0x851c000
 0x00000002 (PLTRELSZ)                   3512 (bytes)
 0x00000014 (PLTREL)                     REL
 0x00000017 (JMPREL)                     0x8088e6c
 0x00000011 (REL)                        0x8088c8c
 0x00000012 (RELSZ)                      480 (bytes)
 0x00000013 (RELENT)                     8 (bytes)
 0x6ffffffe (VERNEED)                    0x8088aec
 0x6fffffff (VERNEEDNUM)                 6
 0x6ffffff0 (VERSYM)                     0x8085b72
 0x00000000 (NULL)                       0x0
# readelf -d `gcc -print-file-name=libreadline.so.6`

Dynamic section at offset 0x38b88 contains 26 entries:
  Tag        Type                         Name/Value
 0x00000001 (NEEDED)                     Shared library: [libtinfo.so.5]
 0x00000001 (NEEDED)                     Shared library: [libc.so.6]
 0x0000000e (SONAME)                     Library soname: [libreadline.so.6]
 0x0000000c (INIT)                       0xb0fc
 0x0000000d (FINI)                       0x2e484
 0x00000019 (INIT_ARRAY)                 0x39278
 0x0000001b (INIT_ARRAYSZ)               4 (bytes)
 0x0000001a (FINI_ARRAY)                 0x3927c
 0x0000001c (FINI_ARRAYSZ)               4 (bytes)
 0x6ffffef5 (GNU_HASH)                   0x138
 0x00000005 (STRTAB)                     0x45ec
 0x00000006 (SYMTAB)                     0x15ac
 0x0000000a (STRSZ)                      13381 (bytes)
 0x0000000b (SYMENT)                     16 (bytes)
 0x00000003 (PLTGOT)                     0x3a000
 0x00000002 (PLTRELSZ)                   848 (bytes)
 0x00000014 (PLTREL)                     REL
 0x00000017 (JMPREL)                     0xadac
 0x00000011 (REL)                        0x80dc
 0x00000012 (RELSZ)                      11472 (bytes)
 0x00000013 (RELENT)                     8 (bytes)
 0x6ffffffe (VERNEED)                    0x803c
 0x6fffffff (VERNEEDNUM)                 1
 0x6ffffff0 (VERSYM)                     0x7a32
 0x6ffffffa (RELCOUNT)                   1172
 0x00000000 (NULL)                       0x0
# readelf -d `gcc -print-file-name=libpthread.so.0` 

Dynamic section at offset 0x18eb8 contains 31 entries:
  Tag        Type                         Name/Value
 0x00000001 (NEEDED)                     Shared library: [libc.so.6]
 0x00000001 (NEEDED)                     Shared library: [ld-linux.so.2]
 0x0000000e (SONAME)                     Library soname: [libpthread.so.0]
 0x0000000c (INIT)                       0x433c
 0x0000000d (FINI)                       0x114d0
 0x00000019 (INIT_ARRAY)                 0x18dc8
 0x0000001b (INIT_ARRAYSZ)               8 (bytes)
 0x0000001a (FINI_ARRAY)                 0x18dd0
 0x0000001c (FINI_ARRAYSZ)               4 (bytes)
 0x00000004 (HASH)                       0x16e34
 0x6ffffef5 (GNU_HASH)                   0x198
 0x00000005 (STRTAB)                     0x24b4
 0x00000006 (SYMTAB)                     0xed4
 0x0000000a (STRSZ)                      5163 (bytes)
 0x0000000b (SYMENT)                     16 (bytes)
 0x00000003 (PLTGOT)                     0x19000
 0x00000002 (PLTRELSZ)                   616 (bytes)
 0x00000014 (PLTREL)                     REL
 0x00000017 (JMPREL)                     0x40d4
 0x00000011 (REL)                        0x3e7c
 0x00000012 (RELSZ)                      600 (bytes)
 0x00000013 (RELENT)                     8 (bytes)
 0x6ffffffc (VERDEF)                     0x3b9c
 0x6ffffffd (VERDEFNUM)                  16
 0x0000001e (FLAGS)                      STATIC_TLS
 0x6ffffffb (FLAGS_1)                    Flags: NODELETE INITFIRST
 0x6ffffffe (VERNEED)                    0x3dcc
 0x6fffffff (VERNEEDNUM)                 2
 0x6ffffff0 (VERSYM)                     0x38e0
 0x6ffffffa (RELCOUNT)                   63
 0x00000000 (NULL)                       0x0

NOTE: 有个比较奇怪的库 linux-gate.so.1 , 其实它不是一个真正的动态库,它是不存在flash上的。

leon@netview:~$ ldd  linux-gate.so.1
ldd: ./linux-gate.so.1: No such file or directory

leon@netview:~$ readelf -d `gcc -print-file-name=linux-gate.so.1`
readelf: Error: 'linux-gate.so.1': No such file

既然是不存在的库,那么为什么会出现在ldd命令显示的动态库列表中呢? 请参考 Linux Virtual Dynamic Shared Objects

Linux Virtual Dynamic Shared Objects
在早期的 x86 处理器中,用户程序与管理服务之间的通信通过软中断实现。 随着处理器速度的提高,这已成为一个严重的瓶颈。 自 Pentium® II 处理器开始,Intel® 引入了 Fast System Call 装置来提高系统调用速度, 即采用 SYSENTER 和 SYSEXIT 指令,而不是中断。
您所看到的 linux-vdso.so.1 是个虚拟库,或者说是 Virtual Dynamic Shared Object,它只存在于程序的地址空间当中。 在旧版本系统中该库为 linux-gate.so.1。 该虚拟库为用户程序以处理器可支持的最快的方式 (对于特定处理器,采用中断方式;对于大多数最新的处理器,采用快速系统调用方式) 访问系统函数提供了必要的逻辑 。

3. ldd脚本

脚本信息:

leon@netview:~$ 
leon@netview:~$ ls -al `which ldd`
-rwxr-xr-x 1 root root 5425 Jun 17 04:41 /usr/bin/ldd
leon@netview:~$ 
leon@netview:~$ 
leon@netview:~$ file `which ldd`
/usr/bin/ldd: Bourne-Again shell script, ASCII text executable
leon@netview:~$ 

脚本内容:

#! /bin/bash
# Copyright (C) 1996-2014 Free Software Foundation, Inc.
# This file is part of the GNU C Library.

# The GNU C Library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.

# The GNU C Library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.

# You should have received a copy of the GNU Lesser General Public
# License along with the GNU C Library; if not, see
# <http://www.gnu.org/licenses/>.


# This is the `ldd' command, which lists what shared libraries are
# used by given dynamically-linked executables.  It works by invoking the
# run-time dynamic linker as a command and setting the environment
# variable LD_TRACE_LOADED_OBJECTS to a non-empty value.

# We should be able to find the translation right at the beginning.
TEXTDOMAIN=libc
TEXTDOMAINDIR=/usr/share/locale

RTLDLIST="/lib/ld-linux.so.2 /lib64/ld-linux-x86-64.so.2 /libx32/ld-linux-x32.so.2"
warn=
bind_now=
verbose=

while test $# -gt 0; do
  case "$1" in
  --vers | --versi | --versio | --version)
    echo 'ldd (Ubuntu EGLIBC 2.19-0ubuntu6.13) 2.19'
    printf $"Copyright (C) %s Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
" "2014"
    printf $"Written by %s and %s.
" "Roland McGrath" "Ulrich Drepper"
    exit 0
    ;;
  --h | --he | --hel | --help)
    echo $"Usage: ldd [OPTION]... FILE...
      --help              print this help and exit
      --version           print version information and exit
  -d, --data-relocs       process data relocations
  -r, --function-relocs   process data and function relocations
  -u, --unused            print unused direct dependencies
  -v, --verbose           print all information
"
    printf $"For bug reporting instructions, please see:\\n%s.\\n" \
      "<https://bugs.launchpad.net/ubuntu/+source/eglibc/+bugs>"
    exit 0
    ;;
  -d | --d | --da | --dat | --data | --data- | --data-r | --data-re | \
  --data-rel | --data-relo | --data-reloc | --data-relocs)
    warn=yes
    shift
    ;;
  -r | --f | --fu | --fun | --func | --funct | --functi | --functio | \
  --function | --function- | --function-r | --function-re | --function-rel | \
  --function-relo | --function-reloc | --function-relocs)
    warn=yes
    bind_now=yes
    shift
    ;;
  -v | --verb | --verbo | --verbos | --verbose)
    verbose=yes
    shift
    ;;
  -u | --u | --un | --unu | --unus | --unuse | --unused)
    unused=yes
    shift
    ;;
  --v | --ve | --ver)
    echo >&2 $"ldd: option \`$1' is ambiguous"
    exit 1
    ;;
  --)       # Stop option processing.
    shift; break
    ;;
  -*)
    echo >&2 'ldd:' $"unrecognized option" "\`$1'"
    echo >&2 $"Try \`ldd --help' for more information."
    exit 1
    ;;
  *)
    break
    ;;
  esac
done

nonelf ()
{
  # Maybe extra code for non-ELF binaries.
  return 1;
}

add_env="LD_TRACE_LOADED_OBJECTS=1 LD_WARN=$warn LD_BIND_NOW=$bind_now"
add_env="$add_env LD_LIBRARY_VERSION=\$verify_out"
add_env="$add_env LD_VERBOSE=$verbose"
if test "$unused" = yes; then
  add_env="$add_env LD_DEBUG=\"$LD_DEBUG${LD_DEBUG:+,}unused\""
fi

# The following command substitution is needed to make ldd work in SELinux
# environments where the RTLD might not have permission to write to the
# terminal.  The extra "x" character prevents the shell from trimming trailing
# newlines from command substitution results.  This function is defined as a
# subshell compound list (using "(...)") to prevent parameter assignments from
# affecting the calling shell execution environment.
try_trace() (
  output=$(eval $add_env '"$@"' 2>&1; rc=$?; printf 'x'; exit $rc)
  rc=$?
  printf '%s' "${output%x}"
  return $rc
)

case $# in
0)
  echo >&2 'ldd:' $"missing file arguments"
  echo >&2 $"Try \`ldd --help' for more information."
  exit 1
  ;;
1)
  single_file=t
  ;;
*)
  single_file=f
  ;;
esac

result=0
for file do
  # We don't list the file name when there is only one.
  test $single_file = t || echo "${file}:"
  case $file in
  */*) :
       ;;
  *) file=./$file
     ;;
  esac
  if test ! -e "$file"; then
    echo "ldd: ${file}:" $"No such file or directory" >&2
    result=1
  elif test ! -f "$file"; then
    echo "ldd: ${file}:" $"not regular file" >&2
    result=1
  elif test -r "$file"; then
    RTLD=
    ret=1
    for rtld in ${RTLDLIST}; do
      if test -x $rtld; then
    dummy=`$rtld 2>&1` 
    if test $? = 127; then
      verify_out=`${rtld} --verify "$file"`
      ret=$?
      case $ret in
      [02]) RTLD=${rtld}; break;;
      esac
    fi
      fi
    done
    case $ret in
    0|2)
      try_trace "$RTLD" "$file" || result=1
      ;;
    1)
      # This can be a non-ELF binary or no binary at all.
      nonelf "$file" || {
    echo $"    not a dynamic executable"
    result=1
      }
      ;;
    *)
      echo 'ldd:' ${RTLD} $"exited with unknown exit code" "($ret)" >&2
      exit 1
      ;;
    esac
  else
    echo 'ldd:' $"error: you do not have read permission for" "\`$file'" >&2
    result=1
  fi
done

exit $result
# Local Variables:
#  mode:ksh
# End:
### 解决方案概述 在 Linux 系统中遇到无法找到外部依赖模块和的问题,通常可以通过以下几个方面来排查并解决问题: 1. **确认环境变量配置** 需要确保 `LD_LIBRARY_PATH` 和其他相关路径已正确设置。如果未设置这些环境变量,则动态链接器可能无法定位所需的共享文件。 2. **检查依赖关系** 使用工具如 `ldd` 来分析可执行程序共享对象的依赖项,并验证所有必需的是否存在以及是否可以被加载。 3. **安装缺失的包** 如果某些确实不存在于当前系统上,可通过包管理器(例如 `apt-get`, `yum` 者手动编译源码的方式)进行安装。 4. **调整权限与访问控制列表 (ACL)** 对目标二进制文件及其所需资源实施适当的安全策略,防止因权限不足而导致失败。 以下是具体操作方法及相关说明: --- ### 设置环境变量 为了使应用程序能够识别到自定义位置下的.so文件(即动态链接),需将该目录加入至`LD_LIBRARY_PATH`环境中去: ```bash export LD_LIBRARY_PATH=/path/to/your/libraries:$LD_LIBRARY_PATH ``` 此命令临时修改会话期间的有效路径;若希望永久生效,则应将其添加到用户的shell profile脚本当中比如`.bashrc`. 上述做法适用于当标准系统级存储区域之外还有额外放置第三方组件的情况.[^1] --- ### 利用 ldd 工具检测依赖状况 通过运行下面这条指令可以获得关于某个特定ELF格式的目标文件所关联的所有共享的信息: ```bash ldd /path/to/executable_or_shared_object ``` 它将以清晰易懂的形式展示每一个必要组成部分的状态——无论是已经成功解析还是仍然处于悬而未决之中. 对于那些标记为"not found"的结果来说,就表明存在缺少对应实体的现象发生;此时就需要按照前面提到的办法予以补充完善.[^1] --- ### 安装必要的软件包 假如经过前述步骤之后发现仍有若干关键要素缺席的话,那么很可能是由于尚未部署好基础架构所致.针对基于Debian系列发行版的操作平台而言,我们可以借助APT机制快速完成这项工作: ```bash sudo apt update && sudo apt install libexample-dev ``` 这里假设需求指向名为libexample的一个典型例子而已实际情形下应当替换成为确切名称才行. 另外值得注意的是有时候即使完成了表面上看似完整的流程却依旧遭遇挫折这往往是因为版本兼容性方面的缘故所以务必仔细核验彼此之间相互匹配程度如何. --- ### 调整安全设定 SELinux/AppArmor之类强制存取管制框架可能会阻碍正常运作因此有必要视具体情况做出相应处置譬如说放松约束条件者干脆关闭它们以便测试效果究竟怎样变化. --- #### 注意事项 尽管托管解决方案提供了极大的便利性和灵活性[^2],但在处理本地开发环境中的这类技术难题时并不适用.我们需要专注于操作系统层面的基础建设和维护活动才能有效应对挑战. ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值