• C/C++编译问题,摆脱编译时的动态库依赖



    前言

    在编译程序时,考虑如下场景:
    代码中使用到了A库的函数,此外再无其它依赖库。但是,A库中使用了B库的函数,B库依赖C库,C库依赖D库。因此在编译可执行文件时,需要找到ABCD,并指定库的路径,在链接时才不会爆出找不到某个定义的错误。
    如果在交叉编译时A库的依赖项高达几十甚至更多个,会让编译变得非常麻烦,需要指定每个库的位置和库名,有些情况可能需要给库添加软链。有没有什么方法在编译时不需要指定依赖?


    一、命令行gcc编译或shell脚本编译

    // libfunA.so
    void funA() {printf("libFunA.so\n");funB();}
    // libfunB.so
    void funB() {printf("libFunB.so\n");funC();}
    // libfunC.so
    void funC() {printf("libFunC.so\n");}
    
    int main()
    {printf("main\n");funA();}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    编译动态库

    gcc -fPIC -shared xxx.c -o libfunA.so
    
    • 1
    • 此时生成的动态库中只含有一个libfunA.so字符串

    • 通过ldd命令查看libfunA.so的依赖,其中没有libfunB.so,即依赖项没有添加入动态库的二进制文件中

    二、cmake编译

    Cmake简单使用
    cmake 编译时会默认指定一些参数,因此和上面简单的指定参数相比cmake会加入更多选项。

    生成动态库时:

    • 此时生成的动态库中含有两个libfunA.so字符串
    • 当未指定动态库的依赖项时,ldd命令没有显示依赖项libfunB.so; 当指定依赖项时,ldd显示依赖libfunB.so

    三、取消编译时的依赖

    #include   // link with -ldl
    dlopen
    dlerror
    dlsym
    dlclose
    
    • 1
    • 2
    • 3
    • 4
    • 5
    // loads the dynamic shared object
    void *handle(const char*filename, int flags);
    - filename: filename, if is null, the return handle is for the main program.
    			          if contain slash("/"), relative or absolute pathname
    
    • 1
    • 2
    • 3
    • 4

    filename: filename, if is null, the return handle is for the main program.if contain slash(“/”), relative or absolute pathname
    动态库搜索:

    1. 如果过调用对象中包含DT_RPATH,不包含 DT_RUNPATH,则搜索DT_RPATH中的路径
    2. 环境变量LD_LIBRARY_PATH中的路径顺序
    3. DT_RUNPATH的目录
    4. 缓存文件/etc/ld.so.cache(由ldconfig(8)维护)
    5. 目录/lib然后/usr/lib

    flags: 以下二者之一

    1. RTLD_LAZY: 使用时加载
    2. RTLD_NOW: 立即加载
    #include 
    #include 
    #include 
    #include   // GNU libc so name macros
    int main()
    {
      void* handle; double(*cosine)(double); char* error;
      handle = dlopen(LIBM_SO, RTLD_LAZY);
      if (!handle) {exit(1);}
      dlerror(); // clear error
      cosine = (double(*)(double))dlsym(handle, "cos");
      error = dlerror();
      if (error != NULL) {printf("%s\n", error); exit(1);}
      printf("%f\n", cosine(3.1415926));
      dlclose(handle);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    四、运行时指定依赖库路径

    1. 将动态库路径放入LD_LIBRARY_PATH

    总结

    在编译可执行文件时,需要找到所有已使用符号的定义,如果该符号在动态库中就需要指定动态库; 如果动态库依赖了另外的动态库,也需要指明该库。
    通过dl库可在编译时避免指定动态库,方便编译。

  • 相关阅读:
    程序员如何过中秋 | 如何画月饼
    【实战】SpringBoot对接外部API并一步一步性能调优
    Redis 访问控制列表(ACL)
    09.2. 长短期记忆网络(LSTM)
    知识管理系统有效推动中小企业信息化发展
    安装Ubuntu和Windows双系统
    判断 Mac显卡 mps 是否可以正常运行
    14.8 Socket 一收一发通信
    【gazebo要素5】ROS是如何接入gazebo的
    学习Python的经历和一些经验分享
  • 原文地址:https://blog.csdn.net/surfaceyan/article/details/136729715