zoukankan      html  css  js  c++  java
  • linux 关于动态库的知识

    问题起缘于编译一个程序时,使用glib2-2.28.8的动态库,而系统自带的是glib2-2.22.5

    不想升级系统的glib2库,而使用程序自带库文件的方式加载(类似windows系统,优先加载当前目录的DLL文件)。

    1.首先编译时,使用-L指定lib路径,-lglib-2.0指定库文件名

    2.运行时通过,LD_LIBRARY_PATH=./ 指定搜索当前目录为库目录

    3.测试发现使用的是系统的库

    ldd ./test
    linux-vdso.so.1 => (0x00007fff86fff000)
    libglib-2.0.so.0 => /lib64/libglib-2.0.so.0 (0x00007f386e16c000)
    libgmodule-2.0.so.0 => /lib64/libgmodule-2.0.so.0 (0x00007f386df68000)
    libz.so.1 => /lib64/libz.so.1 (0x00007f386dd52000)
    libc.so.6 => /lib64/libc.so.6 (0x00007f386d9bf000)
    libdl.so.2 => /lib64/libdl.so.2 (0x00007f386d7bb000)
    /lib64/ld-linux-x86-64.so.2 (0x00007f386e45f000)

    失败,怀疑是LD_LIBRARY_PATH指定库路径,处于搜索路径队列尾部,造成优先找到系统库,那么可不可以通过修改“链接的库文件名”,达到目的了

    1.修改libglib-2.0.so文件为libmyglib.so,链接时使用-lmyglib指定链接库

    2.运行时通过,LD_LIBRARY_PATH=./ 指定搜索当前目录为库目录

    3.测试发现我定义的库居然没有出现,而且还使用来系统的GLIB2库

    ldd ./test 
    linux-vdso.so.1 => (0x00007fff86fff000)
    libglib-2.0.so.0 => /lib64/libglib-2.0.so.0 (0x00007f386e16c000)
    libgmodule-2.0.so.0 => /lib64/libgmodule-2.0.so.0 (0x00007f386df68000)
    libz.so.1 => /lib64/libz.so.1 (0x00007f386dd52000)
    libc.so.6 => /lib64/libc.so.6 (0x00007f386d9bf000)
    libdl.so.2 => /lib64/libdl.so.2 (0x00007f386d7bb000)
    /lib64/ld-linux-x86-64.so.2 (0x00007f386e45f000)

    查询资料后发现,“对于动态库,实际链接发生在运行时而不是编译和链接时”,好吧,我理解。

    那么编译的时候,难道没有把程序要使用到的动态库文件名称,记录到程序内部??

    就像WINDOWS下,缺少某个DLL,程序会提示缺少DLL文件的文件名。

    于是又查询资料发现  “生成可执行文件时,如果链接的是静态库,那么链接器会按照静态链接规则,将对应的符号引用进行重定位。而如果是动态库,链接器会将这个符号标记为动态链接的符号,不进行重定位,而是在装载时再进行。所以,尽管是动态链接,如果是已经进入到了链接阶段,那么也需要能在相应的.so中找到某符号的定义,否则也会引发Undefined reference to的链接错误。因为链接器只有通过.so文件,才能判断某符号是个动态链接符号,所以也需要读取这些.so文件,找到相应符号的定义”

    意思就是,连接器只是做确认函数符号可用,就编译通过来,更本没记录动态库的文件名,这点和WINDOWS不一样

    而实际运行装载的时候,只通过函数符号,装载需要的.SO就可以来,索引我定义的libmyglib.so又被没有机会装载进来,被系统的替代了。

    知道问题原因,解决方法有2个

    1.解决加载顺序的问题,LD_PRELOAD可以实现

    LD_LIBRARY_PATH 将其他目录加入库搜索路径。它的内容应该是由冒号
    分隔的目录列表,与可执行文件的 PATH 变量具有相同的格式。
    如果调用设置用户 ID 或者进程 ID 的程序,该变量被忽略。

    LD_PRELOAD 首先装入用户定义的库,使得它们有机会覆盖或者重新定义标准库。

    export LD_PRELOAD=./libglib.so:./libgmodule.so

    ldd ./test
    linux-vdso.so.1 => (0x00007fffc31ff000)
    ./libglib.so (0x00007ffa5d757000)
    ./libgmodule.so (0x00007ffa5d553000)
    libz.so.1 => /lib64/libz.so.1 (0x00007ffa5d330000)
    libc.so.6 => /lib64/libc.so.6 (0x00007ffa5cf9d000)
    librt.so.1 => /lib64/librt.so.1 (0x00007ffa5cd95000)
    libdl.so.2 => /lib64/libdl.so.2 (0x00007ffa5cb91000)
    /lib64/ld-linux-x86-64.so.2 (0x00007ffa5da6e000)
    libpthread.so.0 => /lib64/libpthread.so.0 (0x00007ffa5c974000)

    2.替换系统的glib2库,有三种方式,最好使用第一种,其他方式会遗留垃圾

    2.1升级安装覆盖

    2.2直接拷贝.so覆盖,或更改libglib-2.0.so.0链接的指向

    2.3共享当前版本的库系统 sudo ldconfig  /opt/glib-2.28.8/lib

    ldd ../bin/test
    linux-vdso.so.1 => (0x00007fff897ff000)
    libglib-2.0.so.0 => /opt/glib-2.28.8/lib/libglib-2.0.so.0 (0x00007f59f5f02000)
    libgmodule-2.0.so.0 => /opt/glib-2.28.8/lib/libgmodule-2.0.so.0 (0x00007f59f5cfe000)
    libz.so.1 => /lib64/libz.so.1 (0x00007f59f5ae8000)
    libc.so.6 => /lib64/libc.so.6 (0x00007f59f5755000)
    librt.so.1 => /lib64/librt.so.1 (0x00007f59f554d000)
    libdl.so.2 => /lib64/libdl.so.2 (0x00007f59f5349000)
    /lib64/ld-linux-x86-64.so.2 (0x00007f59f6226000)
    libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f59f512c000)

    LD_DEBUG通过设置这个环境变量,可以方便的看到 loader 的加载过程(包括库的加载,符号解析等过程),使用【LD_DEBUG=help 可执行文件路径】可查看使用帮助。
     
    LD_PRELOAD环境变量指定的共享库会被预先加载,如果出现重名的函数,预先加载的函数将会被调用,如在预先加载的库中包含自定义的puts函数,则在执行程序时将使用自定义版本的puts函数,而不是libc库中的puts函数。
     

    列出链接符号

     nm -Du /lib64/libglib-2.0.so.0 

  • 相关阅读:
    雅虎军规34条 (一)
    jetty和tomcat的区别
    Jsp--9大内置对象
    java 重定向和转发的区别
    layer弹出层
    html 锚点
    css绘制三角形
    原生js下拉菜单联动
    layui省市区下拉菜单三级联动
    tp5时间格式转换
  • 原文地址:https://www.cnblogs.com/xuybin/p/3164201.html
Copyright © 2011-2022 走看看