1.共享库版本共享库,其实从文件结构上来说,共享库和共享对象没什么区别,Linux 下的共享库就是普通的 ELF 共享对象。1.共享库的兼容性共享库的更新分为2类:1.兼容更新所有的更新只是在原有的共享库基础上添加一些内容,所有的接口都保持不变2.不兼容更新共享库更新改变了原有的接口,使用该共享库原有的接口的程序可能不能运行或者运行不正常接口这个词有着很广泛的含义,这里讨论的是二进制接口,即 ABI。共享库的 ABI 跟程序语言有着很大的关系,不同的语言对接口的兼容性要求不同。ABI 对于不同的语言来说,主要包括一些函数调用的堆栈结构,符号命名,参数规则,数据结构的内存分布等方面的规则。导致 C 语言的共享库 ABI 改变的行为主要有4个:1.导出函数的行为发生改变,也就是说调用这个函数以后产生的结果与以前不一样,不再满足旧版本规定的函数行为准则2.导出函数被删除3.导出数据的结构发生变化,比如共享库定义的结构体变量的结构发生变化。4.导出函数的接口发生变化,如函数返回值,参数被修改很多因素会导致 ABI 的不兼容,比如不同版本的编译器,操作系统和硬件平台等。使用不同版本的编译器或系统库可能会导致结构体的成员对齐方式不一样,从而导致了 ABI 的变化。这种 ABI 不兼容导致的问题可能非常微妙,表面上看可能无关紧要,但是一旦故障,相关的 bug 非常难以定位,这也是共享库的一个问题。2.共享库版本的命名Linux 有一套规则来命名系统中的每个共享库,名字如下: libname.so.x.y.z最前面使用前缀 'lib', 中间是库的名字加后缀 '.so',最后面跟着3个数字组成的版本号。'x' 主版本号 : 表示库的重大升级,不同主版本号的库之间是不兼容的,依赖于旧的主版本号的程序需要改动相应的部分,并且重新编译,才可以在新的共享库中运行;或者,系统必须保留旧的共享库,使得那些依赖于旧共享库的程序能够正常运行。'y' 表示次版本号 : 表示库的增量升级,即增加一些新的接口符号,且保持原来的符号不变。在主版本号相同的情况下,高的次版本号的库向后兼容低的次版本号的库。一个依赖于旧的此版本号共享库的程序,可以在新的次版本号共享库中运行,因为新版中保留了原来所有的接口,并且不改变它们的定义和含义。比如系统中有个共享库 libfoo.so.1.2.x,后来在升级过程中,添加了个函数,版本号变成了 1.3.x。因为 1.2.x 的所有的接口都保留到 1.3.x 中了,所以那些依赖 1.1.x 或 1.2.x 的程序都可以在 1.3.x 中正常运行。'z'表示发布版本号 :表示库的一些错误的修正,性能的改进等,并不添加任何新的接口,也不对接口进行修改。相同主版本号,此版本号,不同的发布版本之间完全兼容,依赖于某个发布版本号的程序可以在任何一个其他发布版本号中正常运行,而无需做任何修改。Linux 中也有不遵守这个规则的:比如最基本的 C 语言库 glibc,使用的是 libc-x.y.z.so 这种命名。动态链接库 使用 ld-x.y.z.so 命名.3.SO-NAME可以这么说,共享库的主版本号和次版本号决定了一个共享库的接口。我们假设程序中有一个它所依赖的共享库的列表,其中每一项对应于它所依赖的一个共享库。可以肯定的是,程序中必须包含被依赖的共享库的名字和主版本号。因为我们知道不同的主版本号之间的共享库是不兼容的,所以程序中保持一个诸如 libfoo.so.2 的记录,以防止动态链接器在运行时意外的将程序与 libfoo.so.1 或 libfoo.so.3 链接到一起。通过这个可以发现,如果在程序中运行旧的程序,就需要在程序中保留旧应用程序所需要的旧的主版本号的共享库。对于新的系统来说,包括 SOlaris 和 Linux, 普遍采用一种叫做 SO-NAME 的命名机制来记录共享库的依赖关系。每个共享库都有一个对应的 'SO-NAME', 这个 so-name 即共享库的文件名去掉次版本号和发布版本号,保留主版本号。比如一个共享库叫做 libfoo.so.2.6.1, 那么它的 so-name 即 lobfoo.so.2。很明显,so-name 规定了共享库的接口,so-name 的两个相同的共享库,次版本号大的兼容次版本号小的。在 Linux 系统中,系统会为每个共享库在它的目录创建一个跟 so-name相同的并且指向它的软连接。比如系统中存在一个共享库 /lib/libfoo.so.2.6.1,那么 Linux 中的共享库管理程序就会为它产生一个软连接 /lib/libfoo.so.2 指向它。由于历史原因,动态链接器和 C 语言库的共享库对象文件名规则不按 Linux 标准的共享库命名方法,但是 C 语言的 so-name 还是按照正常的规则。那么以 so-name 为名字建立软连接有什么用处呢?实际上这个软连接会指向目录中主版本号相同,次版本号和发布版本号都是最新的共享库。也就是说,比如目录中有2个共享库分别为 : /lib/libfoo.so.2.6.1 和 /lib/libfoo.2.5.3,那么软连接 /lib/libfoo.so.2 会执行 /lib/libfoo.so.2.6.1。这样保证了所有以 so-name 为名的软连接都指向系统中最新的共享库。建立以 so-name 为名字的软连接的目的是,使得所有依赖某个共享库的模块,在编译,连接和运行时,都是以共享库的 so-name,而不使用详细的版本号。当共享库升级的时候,如果只是进行增量升级,即保持主版本号不变,只改变次版本号或发布版本号,那么我们可以直接将新版的共享库替换旧版,并且修改 so-name的软连接指向新的版本共享库,即可实现升级;当共享库的主版本号升级时,系统就会存在多个 so-name, 由于这些 so-name 并不相同,所以已有的程序不会受到影响。总之,so-name 表示一个库的接口,接口不向后兼容,so-name 就发生变化,这是基本原则。Linux 中提供了一个工具 ldconfig, 当系统中安装或者更新一个共享库时,就需要运行这个工具,它会遍历所有的默认共享库目录,比如 /lib,/usr/lib 等,然后更新所有的软连接,使它们指向最新的共享库;如果安装了新的共享库,那么 ldconfig 会为其创建相应的软连接。链接名:当我们在编译期里面是有共享库的时候(gcc 的 '-l' 参数链接某个共享库),我们使用了更为简洁的方式,比如需要链接一个 libXXX.so.2.6.1的共享库,只需要在编译器命令行里面指定 -lXXX 即可。编译期会根据当前的环境,在系统中的相关路径(往往由 -L 参数指定)查找最新版本的 'XXX 库。这个 XXX 又被称为共享库的连接名。不同类型的共享库可能会有同样的连接名,比如c语言运行库的静态版本 libc.a 和动态版本 libc.so.x.y.z 的区别,如果在链接时使用参数 '-lc', 那么链接器会根据输出文件的情况(动态/静态)来选择合适的版本的库。比如 ld 使用 '-static' 参数时,'-lc' 会查找 libc.a; 如果使用 '-Bdynamic'(这也是默认情况),它会查找最新版本的  libc.so.z.y.x
2.符号版本在一些早期的系统中,应用程序在被构建时,静态链接器会把程序所以来的所有的共享库的名字,主版本号和次版本号都记录到最终的应用程序二进制输出文件中。在运行时,由于动态链接器知道应用程序所以来的确切的版本号,所以兼容性问题比较容易处理。比如,在SunOS 4.x 中,动态链接器会根据程序的共享库依赖列表中的记录,在系统中查询相同库名和主版本号的共享库;如果某个共享库在系统中存在相同的主版本号不同的此版本号的多个副本,那么动态链接器会使用哪个最高此版本号的副本。动态链接器在查找共享库的过程中,如果找到的共享库的次版本号高于或等于依赖列表中的版本,那么链接器就默认共享库满足要求,因为更高次版本号的共享库肯定包含所有需要的符号;如果找到的共享库次版本号低于所需要的版本,SunOS 4.x 系统的策略是向用户发出一个警告,表示系统中仅有低次版本号的共享库,但持续还是继续运行.持续很可能正常运行,比如该程序只用了低次版本号中的接口,而没有用到高次版本号中的接口。如果用到了,就会发送重定位错误。此版本号交会问题并没有因为 so-name 而解决:动态链接器在进行动态链接时,只进行主版本号的判断,即只判断 so-name,如果某个被依赖的共享库 so-name 与系统中存在的实际共享库 so-name 一致,那么系统就认为接口兼容,而不再进行兼容性检查。这样就会出现一个问题,当某个程序依赖于较高的此版本号的共享库,而运行于较低版本号的共享库系统时,就可能产生缺少某些符号的错误。因为此版本号只能保证向后兼容,并不保证向前兼容,新版的此版本号的共享库可能添加了一些没有的符号。这种此版本号交会问题并没有因为 so-name 的存在而得到改善。对于这个问题,现代系统通过一种更加精巧的方式来解决,那就是符号版本机制。基于符号的版本机制:正常情况下,为了表示某个共享库中增加一些接口,我们就把这个共享库的此版本号升高。但是我们需要一种更加巧妙的方法,来解决此版本号交会问题。Linux 下的 glibc 从版本 2.1 之后开始支持一种叫做基于符号的版本机制。这个方案的基本思路是让每个导出和导入的符号都有一个相关的版本号,它的实际做法类似于名称修饰的方法。与以往简单的将某个共享库的版本号重新命名不同,当我们将 libfoo.so.1.2 升至1.3时,仍然保持 libfoo.so.1这个 so-name,但是给在 1.3 这个新版本中添加的全局符号打上一个标记,比如 'VERS_1.3'。那么,如果一个共享库每一次次版本号升级,我们都能给哪些在新的此版本号中添加的全局符号当上相应的标记,就可以清除的看到共享库中的每个符号都着相应的标签,比如 'vers_1.1','vers_1.2','vers_1.3'。Solaris 中的符号版本机制:Solaris 的 ld 链接器为共享库新增了版本机制和范围机制。版本机制的想法很简单,也就是定义一些符号的集合,这些集合本身都用名字,比如叫做 'vers_1.1','vers_1.2' 等,每个集合都包含一些指定的符号,除了可以拥有符号以外,一些集合还可以包含另外一些集合,比如 'vers_1.2'可以包含集合 'vers_1.1'。就概念而言与其说是包含,不如说是'继承',比如 'vers_1.2' 的符号集合包含(继承)了所有 'vers_1.1'的符号,并且包含所有 'vers_1.2'的符号。那么,这些集合的定义以及它们包含哪些符号是怎么指定的呢?在 Solaris 中,程序员可以在链接共享库时编写一种叫做 符号版本脚本的文件,在这个文件中指定这些符号与集合之间以及集合之间的继承依赖关系。链接器在链接时根据符号版本脚本中指定的关系来产生共享库,并且设置符号的集合与它们之间的关系。SUNW_1.1 {global:pop;push;}SUNWprivate {global:__pop;__push;local:*;}在这个脚本中,我们可以看到它定义了两个符号的集合,分别为 'SUNW_1.1' 和 'SUNWprivate'。第一个包含了两个全局符号 pop 和 push;在第二个集合中,包含了两个全局符号 '__pop' 和 '__push'。第二个集合最后的 'local:*'表示:除了上述被标识为全局的 'pop','push','__pop' 和 '__push' 这4个符号以外,共享库中其他的本来是全局符号都将成为共享库局部符号,也就是说链接器会把原先是全局符号全部变成局部符号,这样一来,共享库外部的应用程序或者说其他的共享库将无法访问这些符号。这种方式可以用来保护哪些共享库内部的公用实用函数,但是共享库的作者又不希望共享库的使用者有意或者无意的访问这些函数。这种方法又被称为 范围机制,它实际上是对 C 语言没有很好的符号可见范围的控制机制的补充,或者说是一种补救性措施。现在这个共享库升级了,在原有的基础上添加了一个全局函数 'swap',那么新的符号脚本文件可以在原有的基础上添加如下内容:SUNW_1.2 {global:swap;} SUNW_1.1;上面的脚本就表示了一个典型的向上兼容的接口;1.2版的共享库增加了一个 swap 接口,并继承了1.1的所有接口。那么我们可以按照这种方式,共享库的版本序号 SUNW_1.1,SUNW_1.2, SUNW_1.3... 分别表示每次共享库添加接口以后的更新,它们依次向后继承,向后兼容。这里值得一提的是,跟在 'SUNW_'前缀后面的版本号由主版本号与一个次版本号构成,这里的主版本号对应共享库实际 so-name 中的主版本号。当共享库的符号都有了版本集合之后,一个最明显的效果就是,当我们在构建(编译和链接)应用程序的时候,链接器可以在程序的最终输出文件中记录下它所用到的版本符号集合。值得注意的是,程序里面记录的不是构建时共享库中版本最新的符号集合,而是程序所依赖的集合中版本号最小的那个。比如,一个共享库 libfoo.so.1 中有6个符号版本,从 SUNW_1.1 到 SUNW_1.6,某个应用程序 app_foo 在编译时,系统中的 libfoo.so.1 的符号版本为 SUNW_1.6 ,实际上 app_foo 只用到了最高到 SUNW_1.3 集合的符号,那么应用程序实际上依赖 SUNW_1.3,而不是 SUNW_1.6。链接器会计算出 app_foo 所用到的最高版本的符号,然后把 SUNW_1.3 记录到 app_foo 的可执行文件内。在程序运行时,动态链接器会通过程序内记录的它所依赖的所有共享库的符号集合版本信息,然后判定当前系统共享库中的符号集合是否满足这些被依赖的符号集合。通过这样的机制就可以保证那些在高次版本共享库的系统中编译的程序在低次版本共享库中运行。这种符号版本的方法是对 so-name 机制保证共享库主版本号一致的一种非常好的补充。Linux 中的符号版本:Linux 系统下共享库的符号版本机制并没有广泛应用,主要使用共享库符号版本机制的是 glibc 软件包中所提供的20多个共享库。这些共享库比较有效的利用了符号版本机制来表示符号的版本演化及利用范围机制来屏蔽一些不希望暴露给共享库使用者的符号。gcc 对 Solaris 符号版本机制的扩展:gcc 在 solaris 系统中的符号版本机制的基础上还提供了2个扩展。第一个扩展是,除了可以在符号版本脚本中指定符号的版本之外,gcc还允许使用一个叫做 '.symver'的汇编指令来指定符号的版本,这个汇编宏指令在 GAS 汇编中,也可以在 gcc的 c/c++ 源代码中以嵌入汇编指令的模式使用。Linux 系统中符号版本机制实践:在 Linux 下,当我们使用 ld 连接一个共享库时,可以使用 '--version-script' 参数;如果使用 gcc,则可以使用 '-Xlinker' 参数加 '--version-script',相当于把 '--version-script' 传递给 ld 链接器。如编译源码为 'lib.c',符号版本脚本为 'lib.ver':gcc -shared -fPIC lib.c -Xlinker --version-script lib.ver -o lib.so
3.共享库系统路径目前大多数包括 Linux 在内的开源操作系统都遵守一个叫做 FHS(file hierarchy standard)的标准,这个标准规定了一个系统中的系统文件应该如何存放,包括各个目录的结构,组织和作用,这有利于促进各个开源操作系统之间的兼容性。共享库作为系统中重要的文件,它们的存放方式也被 FHS 列入了规定范围。FHS 规定,一个系统中主要有2个存放共享库的位置,它们分别如下:1./lib,这个位置主要存放系统最关键和基础的共享库,比如动态链接器,c 语言运行库,数学库等。这些库主要是那些 /bin 和 /sbin 下程序所需要用到的库,还有系统启动时需要的库。2./usr/lib,这个目录主要是保存一些非系统运行时所需要的关键性的共享库,主要是一些开发时用到的共享库,这些共享库一般不会被用户的程序或 shell脚本直接用到。这个目录下面还包含了开发时可能会用到的静态库,目标文件等。3./usr/local/lib, 这个目录用来设置一些跟操作系统本身并不十分相关的库,主要是一些第三方的应用程序的库。比如我们在系统中安装了 python语言的解释器,那么与它相关的共享库可能会被放到 /usr/local/lib/python,而它的可执行文件可能被放到 /usr/lib/bin 下面. GNU 的标准推荐第三方的程序应该默认将库安装到 /usr/local/lib 下。总体看来,/lib 和 /usr/lib 是一些很常用的,成熟的,一般是系统本身所需要的库;而 /usr/local/lib 是非系统所需的第三方程序的共享库。
4.共享库查找过程在开源系统中,包括所有的 Linux 系统在内的很多都是基于 glibc 的。我们知道这些系统里面,动态链接的 ELF 可执行文件在启动时同时会启动动态链接器。在 Linux 系统中,动态链接器是 /lib/ld-linux.so.X(X 是版本号),程序所依赖的共享对象全部是由动态链接器负责装载和初始化。我们知道任何一个动态链接的模块所依赖的模块路径保存在 '.dynamic'段里面,又 DT_NEED 类型的项表示。动态链接器对于模块的查找有一定的规则:如果DT_NEED里面保存的是绝对路径,那么动态链接器就按照这个路径去查找;如果 DT_NEED 里面保存的是相对路径,那么动态路径会在 /lib,/usr/lib和 /etc/ld.so.conf 配置文件指定的目录查找共享库。为了程序的可移植性和兼容性,共享库的路径往往是相对的。ld.so.conf 是一个文本配置文件,它可能包含其他的配置文件,这些配置文件存放着目录信息。include ld.so.conf.d/*.conf如果动态链接器在每次查找共享库时都去遍历这些目录,那么将非常耗时。所以 Linux 系统中有一个叫 ldconfig 的程序,这个程序的作用是为了共享库目录下的各个共享库创建,删除和更新相应的 so-name(即相应的符号链接),这样每个共享库的 so-name 就能够指向正确的共享库文件;并且这个程序还会将这些 so-name 收集起来,集中存放到 /etc/ld.so.cache 文件里面,并且建立一个 so-name 的缓存。当动态链接器要查找共享库时,它可以直接从 /etc/ld.so.cache 里面查找。而 /etc/ld.so.cache 的结构是经过特殊设计的,非常适合查找,所以大大加快了共享库的查找过程.如果动态链接器在 /etc/ld.so.cache 里面没有查找到,那么它还会遍历 /lib 和 /usr/lib 这2个目录,如果还没找到,宣告失败。所以理论上讲,如果我们在系统指定的共享库目录下添加,删除或者更新一个共享库或者我们更改了 /etc/ld.so.conf 的配置,都应该运行 ldconfig 这个程序,以便调整 so-name 和 /etc/ld.so.cache。
5.环境变量LD_LIBRARY_PATH :Linux 系统提供了很多方法来改变动态链接器装载共享库路径的方法,通过使用这些方法,我们可以满足一些特殊的需求,比如共享库的调试和测试,应用级别的虚拟等。改变共享库查找路径最简单的方法是使用 LD_LIBRARY_PATH 环境变量,这个方法可以临时改变某个应用程序的共享库查找路径,而不会影响到系统中其他的程序。在 Linux 中,LD_LIBRARY_PATH 是一个由若干路径组成的环境变量,每个路径之间由冒号隔开。默认情况下, LD_LIBRARY_PATH为空。如果我们为某个进程设置了 LD_LIBRARY_PATH,那么进程在启动时,动态链接器在查找共享库时,会首先查找由 LD_LIBRARY_PATH 指定的目录。这个环境变量可以很方便的让我们测试新的共享库或者使用非标准的共享库。比如我们希望使用修改过的 lbc.so.6,可以将这个新版的 libc 放到我们的目录 /home/user 中,然后指定 LD_LIBRARY_PATH:LD_LIBRARY_PATH=/home/user /bin/lsLinux中还有一种方法可以实现与 LD_LIBRARY_PATH 类似的功能,那就是直接运行动态链接器来启动程序,比如:/lib/ld-linux.so.2 -library-path /home/user /bin/ls可以达到一样的效果。有了LD_LIBRARY_PATH之后,在来总结动态链接器的查找顺序。动态链接器会按照下列程序依次装载或查找共享库对象:1.又环境变量 LD_LIBRARY_PATH 指定的路径2.由路径缓存文件 /etc/ld.so.cache 指定的路径3.默认共享库目录,先 /usr/lib,然后 /lib。LD_PRELOAD : 系统中另外还有一个环境变量叫做 LD_PRELOAD,这个文件中我们可以指定预先装载的一些共享库或者目标文件。在 LD_PRELOAD 里面指定的文件会在动态链接器按照固定的规则搜索共享库之前装载,它比 LD_LIBRARY_PATH 里面指定的路面的共享库还有优先。无论是否依赖它们,LD_PRELOAD里面指定的共享库或者目标文件都会被装载。由于全局符号介入这个机制的存在,LD_PRELOAD 里面指定的共享库或者目标文件的全局符号就会覆盖后面加载的同名全局符号,这使得我们可以很方便的做到改写标准C库中的某个或者某个函数而不影响其他函数,对程序的调试或测试非常有用。与 LD_LIBRARY_PATH 一样,正常情况下应该避免使用 LD_PRELOAD。/etc/ld.so.preload 与 /etc/ld.so.cache 作用一样LD_DEBUG:这个变量可以打开动态链接器的调试功能,当我们设置这个变量时,动态链接器会在运行时打印出各种有用的信息。LD_DEBUG=help ./a.outValid options for the LD_DEBUG environment variable are:libs        display library search paths  // 显示共享库的查找过程reloc       display relocation processing     // 显示重定位过程files       display progress for input filesymbols     display symbol table processing        // 显示符号表查找过程bindings    display information about symbol binding   // 显示动态链接器的符号绑定过程versions    display version dependencies        // 显示符号的版本依赖关系scopes      display scope informationall         all previous options combined        // 显示以上所有信息statistics  display relocation statistics        // 显示动态链接过程中的各种统计信息unused      determined unused DSOshelp        display this help message and exit
6.共享库的创建和安装6.1 共享库的创建创建共享库的过程跟创建一般共享对象的过程基本一致,最关键的是使用 gcc 的2个参数,即 '-shared' 和 '-fPIC'。'-shared'表示输出结果是共享库类型的;'-fPIC' 表示使用地址无关代码技术来产生输出文件。还有另外一个参数是  '-Wl' 参数,这个参数可以指定参数传递给链接器,比如我们使用 '-Wl,-soname,my_soname'时,gcc 会将 '-soname my_soname' 传递给链接器,用来指定输出共享库的 so-name,所以我们可以使用如下命令来生成一个共享库:gcc -shared -W1,-soname,my_soname -o library_name source_file library_files注意:如果我们不适用 -soname 来指定共享库的 so-name,那么该共享库默认就没有 so-name,即使用 ldconfig 更新 so-name 的软连接时,对该共享库也没有效果。不要把输出共享库中的符号和调试信息去掉,也不要使用 gcc 的 '-fomit-frame-pointer'选项,这样做虽然不会导致共享库停止运行,但会影响共享库的调试。在开发过程中,你可能需要测试新的共享库,但是你又不希望影响现有的程序正常运行,我们前面提到的 LD_LIBRARY_PATH 是个很好的方法,用它指定共享库的查找路径,还有一种方法是使用链接器的 '-rpath' 选项(或者使用 gcc 的 -Wl,-rpath),这种方法可以指定连接产生的目标程序的共享库查找路径。ld -rpath /home/mylib -o program.out program.o -lsomelib这样产生的输出可执行文件 program.out 在被动态链接器装载时,动态链接器会首先在 '/home/mylib' 查找共享库。6.2 清除符号信息我们可以使用一个叫做 'strip' 的工具清除掉共享库或可执行文件的所有符号和调试信息strip libfoo.so6.3 共享库的安装创建共享库以后我们必须将它按照在系统中,以便于各种程序都可以共享它。最简单的办法就是将共享库复制到某个标准的共享库目录,比 /lib,/usr/lib等,然后运行 ldconfig 即可。不过上述方法往往需要 root 权限。6.4 共享库的构成和析构函数gcc 提供了一种共享库的构造函数,只要在函数声明时加上 '__attribute__((constructor))' 的属性,即指定该函数为共享库的构造函数,拥有这种属性的函数会在共享库加载时被执行,即在程序的 main 函数之前运行。如果我们使用 dlopen() 打开共享库,共享库构造函数会在 dlopen() 返回之前被执行。与共享库构造函数相对应的析构函数,我们可以使用在函数声明之前加上 '__attribute__((destructor))' 的属性,这种函数会在 main() 函数执行完毕之后执行(或者是程序调用 exit()时执行)。如果共享库是运行时加载的,那么我们使用 dlclose() 卸载共享库时,析构函数会在 dlclose()返回之前执行。声明构造和析构函数的格式如下:void __attribute__((constructor)) init_function(void);void __attribute__((destructor)) fini_function(void);指的注意的是,如果我们使用了这种析构或构造函数,那么必须使用系统默认的标准运行库和启动文件,即不可以使用 gcc 的 'nostartfiles' 或 '-nostdlib' 这2个参数。因为这些构造和析构函数是在系统默认的标准运行库或启动文件被运行的,如果没有这些辅助结构,它们可能不会被运行。另外还有一个问题,如果我们又多个构造函数,那么默认情况下,它们被执行的顺序是没有规定的。如果我们希望构造和曦哥函数能够按照一定的顺序执行,gcc 为我们提供了一个参数叫做优先级,我们可以指定某个析构或者构造函数的优先级:void __attribute__((constructor(5))) init_function1(void);void __attribute__((constructor(10))) init_function2(void);对于构造函数来说,属性中优先级数字越小的函数将会在优先级大的函数之前运行;而对于析构函数来讲,刚好相反。这种安排有利于对构造函数和析构函数能够匹配,比如某一对构造函数和析构函数分来用来申请和释放讴歌资源,那么它们可以拥有一样的优先级。这样做的结果往往是先申请的资源后释放,符合资源释放的一般原则。

1.共享库版本

3.共享库系统路径

4.共享库查找过程

5.环境变量

6.共享库的创建和安装

8.程序员的自我修养---动态链接相关推荐

  1. 腾讯朋友力荐书籍:程序员的自我修养:链接、装载与库

    后台开发需要学习底层知识,只有底层知识掌握了,学一些中间件是信手捏来,中间件也是跑在底层的操作系统上.<<程序员的自我修养:链接.装载与库>>对学习底层知识非常有帮助,腾讯的朋 ...

  2. 程序员的自我修养--编译链接资料收集

    1.龙书(Dragon book)  英文名:Compilers: Principles,Techniques,and Tools  作者:Alfred V.Aho,Ravi Sethi,Jeffre ...

  3. 程序员的自我修养—链接、装载与库--书签目录PDF

    <程序员的自我修养> 链接:https://pan.baidu.com/s/14rGjZnE9K99Vz6a7hlLZjw 提取码:wcqp

  4. 好教程推荐系列:《程序员的自我修养》和《程序员修炼之道:通向务实的最高境界(第2版)》

    1.<程序员的自我修养-链接.装载与库> <程序员的自我修养:链接.装载与库>网易云风力荐:莫到用时再读书!主要介绍系统软件的运行机制和原理,涉及在Windows和Linux两 ...

  5. 【读书笔记】【程序员的自我修养 -- 链接、装载与库(二)】进程虚拟地址空间、装载与动态链接、GOT、全局符号表、共享库的组织、DLL、C++与动态链接

    文章目录 前言 介绍 可执行文件的装载与进程 进程虚拟地址空间 装载方式 操作系统对可执行文件的装载 进程虚存空间分布 ELF文件的链接视图和执行视图 堆和栈 Linux 内核装载ELF & ...

  6. 【读书笔记】【程序员的自我修养 -- 链接、装载与库(三)】函数调用与栈(this指针、返回值传递临时对象构建栈、运行库与多线程、_main函数、系统调用与中断向量表、Win32、可变参数、大小端

    文章目录 前言 介绍 内存 内存布局 栈与调用惯例 堆与内存管理 运行库 入口函数和程序初始化 C/C++运行库 运行库与多线程 C++全局构造与析构 fread 实现 系统调用与API 系统调用介绍 ...

  7. 程序员的自我修养—链接、装载与库 笔记

    程序员的自我修养-链接.装载与库 笔记 内存管理 直接使用物理内存地址 虚拟内存-分段 虚拟内存-分页 分页和分段的主要区别 段页式 代码生成过程 预处理 编译 词法分析 语法分析 语义分析 源代码优 ...

  8. 《程序员的自我修养--链接、装载与库》学习笔记(一)

    本系列文章是<程序员的自我修养–链接.装载与库>(电子工业出版社)一书的学习摘录笔记,本文是书中1.1至1.4部分. 文章目录 基础概念 硬件 软件 基础概念 #include <s ...

  9. 【《程序员的自我修养---链接装载于库》读书笔记】可执行文件的装载与进程

    系列文章目录 [<程序员的自我修养-链接装载于库>读书笔记]初探ELF [<程序员的自我修养-链接装载于库>读书笔记]windows PE/COFF [<程序员的自我修养 ...

  10. 程序员的自我修养——链接、装载与库 笔记(一)

    程序员的自我修养   悄咪咪的说一句,这篇文章可能需要对计算机有过系统的学习,不然看着可能一脸懵.如果有疑问的话,当然,很可能是我太菜了,写的不好,欢迎大家评论区留言指教!此笔记只是刚刚开始,后续我会 ...

最新文章

  1. python可视化多个机器学习模型在独立测试集(test data set)上面的AUC值、可视化模型效能
  2. 学用 TStringGrid [7] - ColWidths[0]、RowHeights[0]、GridLineWidth
  3. tomcat报错LifecycleException的解决方案
  4. 写Rap,编菜谱,你画我猜……这些 AI demo 我可以玩一天!
  5. php加本地音乐代码,WordPress添加音乐播放器(纯代码实现)
  6. win7下mysql读写分离_Windows环境下Mysql 5.7读写分离简单记录
  7. block的界面间传值的使用
  8. 正向代理 smtp imap_Nginx代理模式及区别
  9. threadpool —— 基于 pthread 实现的简单线程池(code)
  10. spring cloud config 加密配置
  11. 小米6 twrp_小米手机刷国际版欧版 MIUI 的详细教程
  12. 2020年中国人口出生率为8.52‰,首次跌破10‰,创下1978来新低
  13. 基于STC89C52的测速和超速报警系统设计
  14. win10 电脑没声音 控制面板 realtek高清晰音频管理器没有解决方案
  15. Photoshop平面设计:网页设计之论摹仿和抄袭——xiame.com
  16. 监控摄像头服务器中断是什么原因,监控系统常见问题故障及处理方法
  17. 2017年人工智能十大关键词!
  18. [转载] 高大上的 CSS 效果:Shape Blobbing
  19. Weakly Supervised Semantic Segmentation with Boundary Exploration
  20. MMDetection框架入门教程(一):Anaconda3下的安装教程(mmdet+mmdet3d)

热门文章

  1. centof7无法上网问题
  2. Java课程设计---Eclipse基本环境配置
  3. Shell自动备份部署新项目
  4. itexpdf同一个段落不同文字,如何设置不同的格式
  5. WPF 自定义IconButton
  6. 第二阶段团队站立会议08
  7. 关于Android屏幕适配
  8. 忙了1天的qte-arm环境的搭建
  9. 用.net改写的uploadify多文件上传控件
  10. 最新 Python 爬虫利器!