ldd 脚本分析

ldd 脚本分析

1. 背景

在开发某嵌入式平台时,发现没有动态库依赖查看工具ldd,后来几经搜索与源码分析,将相关记录一下。

2. 查看ldd源码

  • ldd 指令本质

    • ldd 本质不是一个可执行程序,而是 shell脚本,可以使用 file ldd进行确认

      # 查看ldd指令的路径
      which ldd
      # 显示如下:
      /usr/bin/ldd
      
      # 查看ldd文件类型
      file /usr/bin/ldd 
      # 显示如下:
      /usr/bin/ldd: Bourne-Again shell script, ASCII text executable
      
  • ldd 源码

    • 可以在 usr/bin目录下查看源码 cat /usr/bin/ldd

    • 也可以下载 glibc 源码,编译后,在安装目录的 bin 目录下有对应的源码

3. ldd 源码分析

这里我们使用 cat /usr/bin/ldd指令查看源码

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=

其中第4行指定了 ld动态库的列表,最终是通过这个列表中的某一个来显示可执行程序的动态库依赖的。

 while test $# -gt 0; do
  case "$1" in
  --vers | --versi | --versio | --version)
    echo 'ldd (Ubuntu GLIBC 2.31-0ubuntu9.9) 2.31'
    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.
" "2020"
    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/glibc/+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

上面就是对 ldd命令行参数进行分析,其中 $#表示所有命令行的参数,匹配到对应选项后,会把对应变量设置为yes下面是翻译后的提示

wangzhonglai@shell:~/learn/c_test$ ldd --help
用法:ldd [选项]… 文件…
      --help              印出这份说明然后离开
      --version           印出版本信息然后离开
  -d, --data-relocs       进程数据重寻址
  -r, --function-relocs   进程数据和函数重寻址
  -u, --unused            印出未使用的直接依赖关系
  -v, --verbose           印出所有信息
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

上面代码设置了环境变量,用于ld.so解析可执行文件时使用

try_trace() (
   output=$(eval $add_env '"$@"' 2>&1; rc=$?; printf 'x'; exit $rc)
   rc=$?
   printf '%s' "${output%x}"
   return $rc
)

eval指令是一个bash内置命令,用于将参数作为命令执行。在这段代码中,eval被用于执行ldd命令,并将add_env和"$@"作为参数传递给它。这样做的好处是可以动态地构建命令行参数,从而实现更灵活的命令执行。

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

上述指令用于判断 ldd 后面跟的文件个数,是单个还是多个,并设置对应的标记。

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
    1)
      # This can be a non-ELF binary or no binary at all.
      nonelf "$file" || {
        echo $" not a dynamic executable" >&2
        result=1
      }
      ;;
    0|2)
      try_trace "$RTLD" "$file" || 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

上述代码 其中 for file do 这个语句表示遍历所有的 $file,等价于 for file in "$@",相当于对终端输入的参数进行遍历。

第5-9行判断是否是绝对路径,不是绝对路径的话,以当前路径寻找文件。

第11-17行,先判断文件是否存在,是否是一个普通文件,是否可读,满足条件之后,进行后续测试。

第20-30行对 $RTLDLIST ld.so 列表进行查询,确认可以使用的 ld.so,保存在 "$RTLD"中。

verify_out=${rtld} --verify "$file"会使用ld.so对二进制文件进行校验,例如使用/lib/ld-linux.so.2 --verify /usr/bin/cat会对 cat 指令进行分析,检测当前系统中加载的所有动态库是否存在缺失的符号、函数或变量,可以帮助我们解决动态库加载出错的问题。

第31-52行会对上面校验的结果进行确认,只有正确的情况下(也就是 $ret 为 0|2 的时候),会调用 try_trace函数进行解析,try_trace函数使用了一个环境变量LD_TRACE_LOADED_OBJECTS,并且将环境变量设置为 true

在 Linux 系统上,动态链接器(ld.so)用于在程序运行时加载和解析库文件,以及为这些库提供符号链接。当您运行一个程序时,系统会自动查找它所依赖的共享库,以及这些库所依赖的其他库。使用 LD_TRACE_LOADED_OBJECTS=1 环境变量可以显示某个程序运行时所加载的所有共享库(也就是动态链接库)的路径。

4. ldd 指令移植

经过上述分析,ldd指令最终是设置了一个LD_TRACE_LOADED_OBJECTS=1,然后使用 xx_ld.so进行显示可执行程序的动态依赖。因此,我们可以在RTLDLIST链表中添加我们实际依赖的交叉编译环境下的ld.so,比如ld-linux-armhf.so.3

5. 总结

  1. 本文介绍了 ldd指令的源码位置以及查看方法;
  2. 本文分析了ldd脚本文件的实际执行流程;
  3. 本文介绍了在交叉编译环境下移植ldd指令的方法。
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

来鸟 鸣间

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值