码农知识堂 - 1000bd
  •   Python
  •   PHP
  •   JS/TS
  •   JAVA
  •   C/C++
  •   C#
  •   GO
  •   Kotlin
  •   Swift
  • linux-动态库和静态库制作和使用


    【静态连接和动态连接】C/C++编程中的两种有效链接策略_c++ 动态链接 静态链接_SecureCode的博客-CSDN博客

    静、动态库概念和各自优点

    静:

    动:

    动态库:只有一份,运行时具体代码行才加载使用(相对慢);

    静态库:编译时候一块编进去,用几处编几份,执行速度快场景。

    从静态到动态是一个时间换空间的过程。

    静态库制作、使用以及gcc常见报错处理

    将几个内涵若干个函数的.c文件,先各自处理为 .o文件。

    然后,执行静态库制作的命令。生成的 .a 文件即为制作好的静态库文件了。

    注意:库的命名必须以 lib 开头,静态库要以 .a 结尾

    使用静态库(把库和调用文件一块编译即可生成 .out可执行文件即可);

    gcc报错,一般有两种阶段多见,编译和链接。

    报错,有行号,说明是编译阶段报错,一般也是语法检查出错了;

    没行号,说明已经是二进制了,是链接阶段报错。

    根据上面的使用方法,给gcc 再加一个 -Wall 参数,出现告警信息。分析,下面的报错显然是编译阶段:

    原因:显然是调用文件.c中没有声明 使用的库函数;

    解决办法:再另外做一个静态库头文件。

    放在编译文件所在目录下,再执行原来的编译命令,就没问题了。

    加强:静态库制作的时候,注意另外给静态库.a制作一个.h头文件。然后在主函数里面include里面加。完事在头文件所在目录下执行目标文件的编译命令。避免编译器帮你隐式声明而导致的不必要的问题

    万恶之源:C语言中的隐式函数声明_隐式声明-CSDN博客1 什么是C语言的隐式函数声明在C语言中,函数在调用前不一定非要声明。如果没有声明,那么编译器会自动按照一种隐式声明的规则,为调用函数的C代码产生汇编代码。下面是一个例子:int main(int argc, char** argv){ double x = any_name_function(); return 0;}单纯的编译上述源代码,并没有任何报错,只是在链接阶段因为找_隐式声明https://blog.csdn.net/smstong/article/details/50523120?spm=1001.2101.3001.6650.6&utm_medium=distribute.pc_relevant.none-task-blog-2~default~BlogCommendFromBaidu~Rate-6-50523120-blog-124943920.235%5Ev38%5Epc_relevant_anti_vip_base&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2~default~BlogCommendFromBaidu~Rate-6-50523120-blog-124943920.235%5Ev38%5Epc_relevant_anti_vip_base&utm_relevant_index=11

    动态库的制作及使用(与静态库大体相同,特别注意区别):

    动态库的制作和使用

    注意和静态库的关键区别:

    动态库是只有调用到相关方法时,相关库才被加载到内存中被使用。而静态库是在编译时候,静态方法在代码里的位置已经相对main函数确定了。因此,基于这一点区别动态库在制作时,制作库的相关命令参数 稍有不同【要生成与位置无关的代码 借助参数 -fPIC】。

    代码:

    1. //add.c
    2. int add(int a, int b)
    3. {
    4. ​ return a+b;
    5. }
    1. //sub.c
    2. int sub(int a, int b)
    3. {
    4. ​ return a-b;
    5. }
    1. //test.c文件
    2. //不需要包含add.c和sub.c文件,也可以加进行编译工作,但是会提示错误
    3. #include
    4. int main(int argc, char** argv)
    5. {
    6. ​ int a=20,b=10;
    7. ​ printf("a+b=%d\n",add(a,b));
    8. ​ printf("a-b=%d\n",sub(a,b));
    9. ​ return 0;
    10. }
    1. //库头文件
    2. #ifndef _CAL_H
    3. #define _CAL_H
    4. int add(int, int);
    5. int sub(int, int);
    6. #endif

    1.分别将 .c 库函数文件生成 .o文件;

    动态库要求生成与位置无关的代码(函数调用之前需要将其地址固定)

    数据段合并和地址回填 延迟绑定(动态库函数的地址比主函数的其它调用函数分配地址要晚)

    结论:制作动态库的.o文件和静态库有区别,生成位置无关文件,借助 -fPIC选项

    gcc -c add.c -o add.o -fPIC


     

    2.使用gcc 和 -shared选项将所有 .o库文件制作成一个动态库文件

    gcc -shared -o lib库名.so add.o sub.o


     

    3.编译可执行程序时,指定所使用的动态库, -l和-L

    -l:用来指定库名 -L:用来指定库路径(编译时候)

    gcc test.c -o a.out -lcal(cal是库名)  -L./lib

    至此,包含了库的可执行文件就生成了。

    4.运行可执行程序;

     (注意执行过程中会有问题,就是动态库调用不上,需要动态库路径环境变量的添加,这里临时添加了环境变量才执行成功,后面会专门提到)

    ———————————————
    版权声明:本文为CSDN博主「CPPlusQt」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/Blunt_Du/article/details/122329243

    动态库路径环境变量的添加

    制作一个动态文件执行后,直接执行可执行文件会报错:

    原因:

            程序运行阶段找不到自己做的动态文件。准确地说,是程序执行的时候 动态连接器 没有找到程序中使用的动态库。(注意这句话要和 编译可执行文件的时候 “链接器”指定动态库目录 区分开,完全不同阶段的东西)

    概念区分:

    链接器 (编译阶段):工作于链接阶段,工作时需要 -l和-L选项(用于编译静态库)

    动态链接器 (运行阶段):工作于程序运行阶段,工作时需要提供动态库所在目录位置;

    可以使用ldd命令--可以通过下面的命令查看运行程序运行之后,加载动态库的情况(哪些没找到,找到的是在哪找到的):

    ldd是list, dynamic, dependencies的缩写, 意思是, 列出动态库依赖关系。

    Linux中的8个ldd命令示例_ldd -r-CSDN博客

    看上面的依赖显示,c库其实也是一个动态链接库

    解决目标:

    想办法让 a.out执行的时候能找到它使用的so库。即需要适时指定一个路径。

    解决方案来源:

    linux动态链接库的加载顺序:

    linux动态链接库的加载顺序_动态链接库顺序-CSDN博客

    【精选】【linux】程序找不到动态库.so的解决办法|查看.so动态库信息|.so动态库加载顺序_linux-vdso.so.1找不到-CSDN博客

    三、Linux 程序运行时查找SO顺序如下:

    1.  gcc 编译时指定的运行时库路径 -Wl,-rpath

    2.  环境变量 LD_LIBRARY_PATH

    3.  ldconfig 缓存 /etc/ld.so.cache

    4  系统默认库位置 /lib    /usr/lib

    具体解决方案:

    解决方法:

    方案1:临时修改该用户下 bash的环境变量

    程序运行时候,会由一个环境变量(里面存了很多路径)指向动态库在哪。我们在当前shell临时更新下这个环境变量即可。

     
    1. #环境变量生效:
    2. export LD_LIBRARY_PATH=./
    3. #上面填入的是相对路径,如果要一直生效,最好填入绝对路径
    4. #运行程序
    5. ./a.out

    存在不足:环境变量是依赖于终端的,切换终端之后,新的终端的环境变量会失效

    linux 环境变量icon-default.png?t=N7T8https://mp.csdn.net/mp_blog/creation/editor/132511548LD_LIBRARY_PATH用法详解-CSDN博客LD_LIBRARY_PATH_ld_library_pathhttps://blog.csdn.net/m0_58235748/article/details/130557000?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522169699099116800182196576%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=169699099116800182196576&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_click~default-2-130557000-null-null.142%5Ev95%5Einsert_down28v1&utm_term=LD_LIBRARY_PATH&spm=1018.2226.3001.4187

    方案2:修改该用户下 bash的环境变量配置文件:

    1. (1) 通过gedit或者vi修改bash的配置文件;
    2. #脚本的名字为.bashrc
    3. gedit ~/.bashrc
    4. #或者使用vim打开
    5. vi ~/.bashrc
    6. (2)在终端中添加动态库路径
    7. export LD_LIBRARY_PATH=动态库路径
    8. 建议使用绝对路径
    9. (3)使脚本文件生效,通过运行脚本文件实现、
    10. . .bashrc    
    11. #或者
    12. source .bashrc
    13. #或者重启终端,每次重启终端,bashrc都会运行


    方案3:拷贝自定义动态库到标准C库所在库路径(不推荐)

     
    1. 标准C库也是通过动态库进行加载的,而且可以加载成功,这给我们的提示是可以把这个生成的动态库拷贝到C库的文件夹中
    2. sudo cp libmymath.so /lib
    3. 然后把原来设置的环境变量删掉。

    方案4:ld.so.conf 配置文件法

     
    1. #打开配置文件
    2. 1)在配置文件中写入库文件目录所在位置,最好写入绝对目录
    3. sudo vi /etc/ld.so.conf
    4. 2)让配置文件生效
    5. sudo ldconfig -v
    6. //-v选项会在终端显示动态库的加载位置
    7. 或者运行程序生效

    总结四种方法:

    ————————————————
    原文链接:https://blog.csdn.net/Blunt_Du/article/details/122329243

    安装动态库源码包方法:

    configure--prefix_./configure --prefix-CSDN博客文章浏览阅读1.3k次。本文主要说明--prefix参数的作用,其主要用在编译安装源代码应用中的./configure环节。./configure --help 查看详细的说明帮助1、源码安装一般包括几个步骤:配置(configure),编译(make),安装(make install)。2、其中configure是一个可执行脚本,在源码目录中执行可以完成自动的配置工作,即./configure。3、在实..._./configure --prefixhttps://blog.csdn.net/guozuofeng/article/details/98757549?ops_request_misc=&request_id=&biz_id=102&utm_term=./configure%20--prefix&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-3-98757549.nonecase&spm=1018.2226.3001.4187

    Linux源码包安装的详细教程_linux源码包安装教程-CSDN博客文章浏览阅读2.7k次。第一步:./configure --prefix=/usr/local/webserver./xxxx 表示在当前文件夹中运行一个可执行的文件./configure --help |less./configure 一个脚本,指定安装路径,并且生成makefile文件,同时定义软件有哪些功能需要安装哪些不需要安装,同时检查当前系统是否具有安装该软件的环境(当前系统没有c语言编译器,就不会生成),如果一切满足会生成makefile文件,makefil..._linux源码包安装教程https://blog.csdn.net/l578900/article/details/120074222?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522169925298716800197061403%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=169925298716800197061403&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_ecpm_v1~rank_v31_ecpm-6-120074222-null-null.142%5Ev96%5Epc_search_result_base7&utm_term=liunx%E6%BA%90%E7%A0%81%E5%8C%85%E5%AE%89%E8%A3%85%E6%96%B9%E6%B3%95&spm=1018.2226.3001.4187

    动态库没找到案例:

    案例一:libevent库源码包安装后,库调用失败

    libevent库的下载和安装:源码包安装三部曲(参考README):

    • ./configure:检查安装环境,生成makefile
    • make:生成.o文件和可执行文件
    • sudo make install:将必要的资源cp至系统指定目录

    安装完成后,可以发现,库被安装在下面路径:

    /usr/local/lib
    

    测试库的可用性:

    进入sample目录,编译demo验证库安装情况,是否能被正常使用:

    gcc 编译一个demo: hello-world.c

    执行可执行程序 hello;

    报错: ./hello: error while loading shared libraries: libevent-2.1.so.6: cannot  open shared object file: No such file or directory

    初步定位:库没找到

    通过find命令发现库已经安装在 /usr/local/lib 路径下

    定位过程:

    运行下面命令

    LD_DEBUG=libs ./hello -v

    ( LD_DEBUG 是 glibc 中的 loader 为了方便自身调试而设置的一个环境变量。通过设置这个环境变量,可以方便的看到 loader 的加载过程。

    )

    跟踪hello的运行过程中,程序尝试在哪些地方找过动态库,最终没找到(确认下根因)

    解决方案:

    将库源文件路径软连接的到默认动态库路径 /lib:

    ln -s /usr/local/lib/libevent-2.1.so.6  /usr/lib/libevent-2.1.so.6

    根因:

    默认情况下,编译器只会使用/lib和/usr/lib这两个目录下的库文件,通常通过源码包进行安装时,如果不指定--prefix,会将库安装在/usr/local/lib目录下;当运行程序需要链接动态库时,提示找不到相关的.so库,会报错。也就是说,/usr/local/lib目录不在系统默认的库搜索目录中,需要将目录加进去。

    Linux中error while loading shared libraries错误解决办法-CSDN博客

  • 相关阅读:
    【Java】中Maven依赖详解
    MySQL
    ThinkAutomation Crack
    多图详解:不停机分库分表五个步骤
    华为云实验 -- 对云硬盘数据盘进行备份
    SAP TMS系统异常问题分析
    常用的POST请求和GET请求
    告警:线上慎用 BigDecimal ,坑的差点被开了
    数字化安全方案建设
    Gateway Timeout504: 网关超时的完美解决方法
  • 原文地址:https://blog.csdn.net/weixin_46697509/article/details/132430345
  • 最新文章
  • 攻防演习之三天拿下官网站群
    数据安全治理学习——前期安全规划和安全管理体系建设
    企业安全 | 企业内一次钓鱼演练准备过程
    内网渗透测试 | Kerberos协议及其部分攻击手法
    0day的产生 | 不懂代码的"代码审计"
    安装scrcpy-client模块av模块异常,环境问题解决方案
    leetcode hot100【LeetCode 279. 完全平方数】java实现
    OpenWrt下安装Mosquitto
    AnatoMask论文汇总
    【AI日记】24.11.01 LangChain、openai api和github copilot
  • 热门文章
  • 十款代码表白小特效 一个比一个浪漫 赶紧收藏起来吧!!!
    奉劝各位学弟学妹们,该打造你的技术影响力了!
    五年了,我在 CSDN 的两个一百万。
    Java俄罗斯方块,老程序员花了一个周末,连接中学年代!
    面试官都震惊,你这网络基础可以啊!
    你真的会用百度吗?我不信 — 那些不为人知的搜索引擎语法
    心情不好的时候,用 Python 画棵樱花树送给自己吧
    通宵一晚做出来的一款类似CS的第一人称射击游戏Demo!原来做游戏也不是很难,连憨憨学妹都学会了!
    13 万字 C 语言从入门到精通保姆级教程2021 年版
    10行代码集2000张美女图,Python爬虫120例,再上征途
Copyright © 2022 侵权请联系2656653265@qq.com    京ICP备2022015340号-1
正则表达式工具 cron表达式工具 密码生成工具

京公网安备 11010502049817号