首页 > 系统相关 >[转载] linux下 GCC编译链接静态库&动态库

[转载] linux下 GCC编译链接静态库&动态库

时间:2023-09-27 17:46:35浏览次数:51  
标签:GCC libstack 链接 编译 so linux 共享 main stack

转载自: https://www.cnblogs.com/thechosenone95/p/10605172.html#_label0

静态库

有时候需要把一组代码编译成一个库,这个库在很多项目中都要用到,例如libc就是这样一个库, 我们在不同的程序中都会用到libc中的库函数(例如printf),也会用到libc中的变量(例如以后 要讲到的environ变量)。本文将介绍怎么创建这样一个库。
这些文件的目录结构是:

$ tree 
. 
|-- main.c 
`-- stack    
	|-- is_empty.c    
	|-- pop.c    
	|-- push.c    
	|-- stack.c    
	`-- stack.h 
1 directory, 6 files

我们把stack.c、push.c、pop.c、is_empty.c编译成目标文件:

$ gcc -c stack/stack.c stack/push.c stack/pop.c stack/is_empty.c

然后打包成一个静态库libstack.a:

$ ar rs libstack.a stack.o push.o pop.o is_empty.o 
ar: creating libstack.a 

库文件名都是以lib开头的,静态库以.a作为后缀,表示Archive。ar命令类似于tar命令,起一个打包的作用,但是把目标文件打包成静态库只能用ar命令而不能用tar命令。选项r表示将后面的文件列表添加到文件包,如果文件包不存在就创建它,如果文件包中已有同名文件就替换成新的。s是专用于生成静态库的,表示为静态库创建索引,这个索引被链接器使用。ranlib命令也可以为静态库创建索引,以上命令等价于:

$ ar r libstack.a stack.o push.o pop.o is_empty.o 
$ ranlib libstack.a

然后我们把libstack.a和main.c编译链接在一起:

$ gcc main.c -L. -lstack -Istack -o main

-L选项告诉编译器去哪里找需要的库文件,-L.表示在当前目录找。-lstack告诉编译器要链 接libstack库,-I选项告诉编译器去哪里找头文件。注意,即使库文件就在当前目录,编译器默认 也不会去找的,所以-L.选项不能少。编译器默认会找的目录可以用-print-search-dirs选项查看。编译器会在这些搜索路径以及-L选项指定的路径中查找用-l选项指定的库,比如-lstack,编译器会首先找有没有共享库libstack.so,如果有就链接它,如果没有就找有没有静态库libstack.a,如果有就链接它。所以编译器是优先考虑共享库的,如果希望编译器只链接静态库,可以指定-static选项

动态库(共享库)

组成共享库的目标文件和一般的目标文件有所不同,在编译时要加-fPIC选项,例如:

$ gcc -c -fPIC stack/stack.c stack/push.c stack/pop.c stack/is_empty.c -o libstack.so

-f后面跟一些编译选项,PIC是其中一种,表示生成位置无关代码(Position Independent Code)。
现在把main.c和共享库编译链接在一起,然后运行:

$ gcc main.c -g -L. -lstack -Istack -o main 

或者指定动态库全名:

$ gcc main.c -g -L. -l:libstack.so -Istack -o main 

运行程序:

$ ./main 
./main: error while loading shared libraries: libstack.so: cannot open shared object file: No such file or directory

结果出乎意料,编译的时候没问题,由于指定了-L.选项,编译器可以在当前目录下找到libstack.so,而运行时却说找不到libstack.so。那么运行时在哪些路径下找共享库呢?我们先用ldd命令查看可执行文件依赖于哪些共享库:

$ ldd main        
	linux-gate.so.1 =>  (0xb7f5c000)        
	libstack.so => not found        
	libc.so.6 => /lib/tls/i686/cmov/libc.so.6 (0xb7dcf000)        
	/lib/ld-linux.so.2 (0xb7f42000)

ldd模拟运行一遍main,在运行过程中做动态链接,从而得知这个可执行文件依赖于哪些共享库,每个共享库都在什么路径下,加载到进程地址空间的什么地址。/lib/ld-linux.so.2是动态链接器,它的路径是在编译链接时指定的,gcc在做链接时用dynamic-linker指定动态链接器的路径,它也像其它共享库一样加载到进程的地址空间中。libc.so.6的路径/lib/tls/i686/cmov/libc.so.6是由动态链接器ld-linux.so.2在做动态链接时搜索到的,而libstack.so的路径没有找到。linux-gate.so.1这个共享库其实并不存在于文件系统中,它是由内核虚拟出来的共享库,所以它没有对应的路径,它负责处理系统调用。总之,共享库的搜索路径由动态链接器决定,从ld.so(8)的Man Page可以查到共享库路径的搜索顺序:

  • 首先在环境变量LD_LIBRARY_PATH所记录的路径中查找。
  • 然后从缓存文件/etc/ld.so.cache中查找。这个缓存文件由ldconfig命令读取配置文 件/etc/ld.so.conf之后生成,稍后详细解释。
  • 如果上述步骤都找不到,则到默认的系统路径中查找,先是/usr/lib然后是/lib。

先试试第一种方法,在运行main时通过环境变量LD_LIBRARY_PATH把当前目录添加到共享库的搜索路径:

$ LD_LIBRARY_PATH=. ./main

这种方法只适合在开发中临时用一下,通常LD_LIBRARY_PATH是不推荐使用的,尽量不要设置这个环境变量,理由可以参考Why LD_LIBRARY_PATH is bad。

再试试第二种方法,这是最常用的方法。把libstack.so所在目录的绝对路径(比如/home/akaedu/somedir)添加到/etc/ld.so.conf中(该文件中每个路径占一行),然后运行ldconfig:

$ sudo ldconfig -v 

ldconfig命令除了处理/etc/ld.so.conf中配置的目录之外,还处理一些默认目录,如/lib、/usr/lib等,处理之后生成/etc/ld.so.cache缓存文件,动态链接器就从这个缓存中搜索共享库。现在再用ldd命令查看,libstack.so就能找到了:

$ ldd main        
    linux-gate.so.1 =>  (0xb809c000)
    libstack.so => /home/akaedu/somedir/libstack.so (0xb806a000)
    libc.so.6 => /lib/tls/i686/cmov/libc.so.6 (0xb7f0c000)
    /lib/ld-linux.so.2 (0xb8082000)

第三种方法就是把libstack.so拷到/usr/lib或/lib目录,这样可以确保动态链接器能找到这个共享库。
其实还有第四种方法,在编译可执行文件main的时候就把libstack.so的路径写死在可执行文件中:

$ gcc main.c -g -L. -lstack -Istack -o main -Wl,rpath,/home/akaedu/somedir

-Wl,-rpath,/home/akaedu/somedir表示-rpath /home/akaedu/somedir是由gcc传递给链接器的选项。可以看到readelf的结果多了一条rpath记录:

$ readelf -a main 
... 
Dynamic section at offset 0xf10 contains 23 entries:
  Tag        Type                         Name/Value 
 0x00000001 (NEEDED)                     Shared library: 
[libstack.so]
 0x00000001 (NEEDED)                     Shared library: [libc.so.6]
 0x0000000f (RPATH)                      Library rpath: [/home/akaedu/somedir]
 ...

还可以看出,可执行文件运行时需要哪些共享库也都记录在.dynamic段中。当然rpath这种办法也是不推荐的,把共享库的路径定死了,失去了灵活性。
甚至还可以这样写:

$ gcc -o main main.c -g -L. -lstack -Istack  ./stack/libstack.so

标签:GCC,libstack,链接,编译,so,linux,共享,main,stack
From: https://www.cnblogs.com/sinpo828/p/17733261.html

相关文章

  • linux上如何使用docker部署前后端分离项目(含部署多个前端页面的方法)
    (目录)前言使用前一段时间写的前后端分离开发的简单小项目,确保本地可以启动部署所需镜像汇总mysql镜像2.后端工程自定义镜像3.nginx镜像<fontcolor=red>下面介绍部署过程</font>一、mysql镜像(1)拉取mysql镜像选择使用的mysql版本,我用的是8.0.33dockerpullmysql......
  • 10 分钟学会Linux常用 bash命令
    目录1.基本操作1.1.文件操作1.2.文本操作1.3.目录操作1.4.SSH,系统信息&网络操作2.基本Shell编程2.1.变量2.2.字符串替换2.3.函数2.4.条件2.5.循环3.技巧4.调试1.BasicOperationsa.export显示所有的环境变量,如果你想获取某个变量的详细信息,使用echo......
  • 怎么修改linux的root@后面的名称
    场景.登录服务器,root后面的名称是随机的,想自定义名称建议,直接使用命令执行hostnamectlset-hostnamerdd-test重新连接即可生效,实际也是修改了/etc/hostname名称需要重启机子输入命令:vim/etc/hostname进入文件,修改并保存;只有重启之后,更改的主机名才可以生效。......
  • linux中backport printk和front printk的区别
    在Linux内核中,"backportprintk"和"frontprintk"都是用于记录内核消息和调试信息的机制,但它们的工作方式和使用场景有一些区别。"backportprintk"是一种在内核中记录消息和调试信息的机制,可以将这些信息输出到控制台、串口、网络等目标。它通常用于在内核启动过程中的早期阶段,......
  • Linux0.11代码浅析
    Linux0.11"pulloneselfupbyone'sbootstraps"......
  • Linux Bash 提示符的一些骚操作
    如何设置Bash提示符Bash提示符是通过环境变量PS1(提示符字符串1PromptString1)来设置的,它用于交互式shell提示符。当然如果你需要更多的输入才能完成一个Bash命令时,PS2环境变量就是用来设置多行提示符的:[dneary@dhcp-41-137~]$exportPS1="[LinuxRulez]$"[Lin......
  • Ubuntu18 切换GCC版本
    Ubuntu中存在多个GCC版本,需要将其中一个设置为主要版本目前的版本是7.5.0 存在许多版本 bashsudoupdate-alternatives--install/usr/bin/gccgcc/usr/bin/gcc-4.8100上面的100为优先级,优先级越高越靠前。调整优先级后默认GCC为4.8.5 ......
  • 50、linux修改虚拟机的时间
    1、查看当前时区与电脑端实际时间不一致 2、修改时区 再次查看发现时间已同步时区 ......
  • Linux2.1.13网络源代码学习(https://qiankunli.github.io/2022/07/04/linux_2_1_13_ne
    简介简介源码目录网络分层数据结构套接字套接字与vfssk_buff结构网络协议栈实现——数据struct和协议structlinux1.2.13接收数据收到数据包的几种情况Socket读取发送数据面向过程/对象/ioc以下来自linux1.2.13源码,算是参见Linux1.0的学习笔记。源码目......
  • clickhouse linux 客户端安装和使用
    clickhouselinux客户端安装步骤1:上传安装文件到服务器目录(可以使用正常用户上传)2:使用root用户安装,否则会提示权限不够报错,此外非X86架构服务器也可能会报错(如linuxone服务器报错:packageclickhouse-common-static-0:23.3.6.7-1.x86_64isintendedforadifferentarchitect......