• ldd 以及 ld-linux.so.2


    最近跟编译工具干上了,可能是问题积累集中爆发的结果。

    今天对 ld-linux.so.x 有很大兴趣,想对它多些了解,遂百度之。发现了指令 ldd。

    关于 ldd

    其实 ldd 是一个脚本,并不是一个二进制文件。

    它的原理很简单:当环境变量 LD_TRACE_LOADED_OBJECTS 不为空时,只显示依赖关系,不执行程序。我们可以手动试试。

    ldd 开启了一个独立的运行空间,设置了环境变量,然后执行程序,把执行的结果返回。

    其他的变量(和值)分别对应一些选项:
      -d, --data-relocs -》 LD_WARN=yes
      -r, --function-relocs -》LD_WARN和LD_BIND_NOW=yes
      -u, --unused -》 LD_DEBUG=“unused”
      -v, --verbose -》 LD_VERBOSE=yes
    LD_TRACE_LOADED_OBJECTS为必要环境变量,其他视具体情况。

    编译 dummy.c

    继续使用 dummy.c 对工具链进行实验。

    $ echo 'main(){}' > dummy.c
    $ gcc dummy.c -o demo
    $ ldd demo 
        linux-gate.so.1 =>  (0x00a6c000)
        libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0x00ad5000)
        /lib/ld-linux.so.2 (0x00a71000)

    所以,最简单没有任何任务的 dummy.c ,需要这 3 个动态库才能执行:

    1. linux-gate.so.1 这个库无法在文件下系统中找到。它只是一个虚拟的动态链接库(VDSO, Virtual Dynamically Shared Object),是一个完整的 elf shared object,是内核镜像中的某个特定页;在使用 exec() 执行新的镜像时,linux-gate.so 被页面映射到程序的进程空间中(process's memmory)。想对它进一步了解,可以阅读参考 1 。

    2. libc.so.6 标准 C 库。就是我们在制作工具链时编译出来的那一个。

    3. ld-linux.so.2 动态连接的实际执行者。

    在手上的 armbox 中进行上面实验,并没有 linux-gate.so.1:

    $ ldd demo 
        libc.so.6 => /lib/arm-linux-gnueabihf/libc.so.6 (0x76e13000)
        /lib/ld-linux-armhf.so.3 (0x76f27000)

    今天的主角是 ld-linux.so.x ,这里就不对 linux-gate.so.1 进行深究了。

    ELF 中的 PT_INTERP

    如果只是说 ld-linux.so.x 是动态链接库的解释器,那么,未免有些单调了。我先从追踪 gcc 编译出来程序开始。

    $ strace ./demo
    execve("./demo", ["./demo"], [/* 41 vars */]) = 0
    brk(0)                                  = 0x9712000
    access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
    mmap2(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7795000
    ...

    这里可以看到,执行文件是交给 execve() 来解释执行的。那么,这里的问题就变成了 execve() 怎么解释这 demo 的问题。没有特殊设置,那么,这里的 demo 肯定是 ELF 格式的文件。。。所以,这里就最后变成了,execve() 怎么解释及执行 ELF 文件的问题。

    不想去一点点抠 ELF 的具体结构,所以 man execv() 看了一下。有如下描述:

     If the executable is a dynamically linked ELF executable, the interpreter named the PT_INTERP segment is  used  to load  the  needed  shared  libraries. This interpreter is typically /lib/ld-linux.so.1 for binaries linked with the Linux libc 5, or /lib/ld-linux.so.2 for binaries linked with the glibc 2 

     execve() 是 <unistd.h> 中的函数。

    使用 readelf 查看我们编译出的二进制文件:

    Elf file type is EXEC (Executable file)
    Entry point 0x8048300
    There are 9 program headers, starting at offset 52
    
    Program Headers:
      Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
      PHDR           0x000034 0x08048034 0x08048034 0x00120 0x00120 R E 0x4
      INTERP         0x000154 0x08048154 0x08048154 0x00013 0x00013 R   0x1
          [Requesting program interpreter: /lib/ld-linux.so.2]
    ...

    这里看到的内容和我们上面了解到的是一致的。

    所以,我们在这里得出结论:1) 使用 ld-linux.so.* 作为解释器,是写在二进制文件中的,比如上面编译好的 demo 中。另外的,2) 其它库的查找和加载,则是 ld-linux.so.* 完成的。

    ld-linux.so.* 如何加载库?

    目前已知的方法是,使用环境变量 LD_LIBRARY_PATH=/lib/ 来定位库的位置。待探索。

    参考:

    1. http://www.trilithium.com/johan/2005/08/linux-gate/

    2. man execve

    3. man elf

  • 相关阅读:
    穷举
    菱形
    docker安装cloudera manager,切换cloudera-scm用户报错can not open session
    修改cloudera manager的端口号
    postgresql拓展if、ifnull、group_concat函数
    clion调试postgresql
    Java面向切面原理与实践
    Spring-boot非Mock测试MVC,调试启动tomcat容器
    spring-cloud-feign 使用@RequetParam报错QueryMap parameter must be a Map: class java.lang.String
    linux虚拟机拓展大小
  • 原文地址:https://www.cnblogs.com/pied/p/6609988.html
Copyright © 2020-2023  润新知