Makefile#

标准格式#

target:dependencies1 dependencies2 ...
   recipe

.PHONY: clean
clean:
   -rm ...

注意事项#

  • 运行make时如果没有指定target,那么make会构建Makefile中的第一个target
  • 通常情况下,目标名即生成的文件名。如果一条规则的目标文件存在并且该文件比它所有的依赖都要新,那么make会跳过recipe;如果目标文件不存在,那么目标文件的 timestamp 为开始的时间;否则timestamp为相应文件的修改时间。
  • 每次运行 make clean,”clean“中的recipe都会被执行,因为clean文件永远都不会被创建。(可以使用 .PHONY 创建伪目标使Makefile可读性更高)
  • recipes必须用tab缩进
  • 可以通过并行的方式运行recipes:make -j 4(指定并行的任务数)
  • 如果没有指定规则,Make会自动化创建规则。例如,本地有一个C文件”program.c“,当运行 make program时,Make会自动编译生成 program

变量#

  • $@ 目标文件
  • $^ 所有的依赖文件
  • $< 第一个依赖

头文件#

环境变量#

  • C_INCLUDE_PATH C语言头文件路径
  • CPLUS_INCLUDE_PATH C++ 头文件路径

搜索路径#

#include<>#

  1. 先搜索 -I 指定的目录
  2. 然后搜索gcc的环境变量 CPLUS_INCLUDE_PATH
  3. 最后搜索gcc的内定目录
    1. /usr/include
    2. /usr/local/include
    3. /usr/lib/gcc/x86_64-redhat-linux/4.1.1/include

#include ""#

  • 搜索当前目录,#include<>方式不会搜索当前目录

动态库#

环境变量#

  • LD_LIBRARY_PATH 动态链接库搜索路径
  • PKG_CONFIG_PATH .pc文件(package config)文件搜索路径

搜索路径#

  1. 首先在环境变量 LD_LIBRARY_PATH 所记录的路径中查找
  2. 在程序链接时指定的 rpath 中查找,可以 readelf binfile | grep RPATH
  3. 然后从缓存文件/etc/ld.so.cache中查找。这个缓存文件由/sbin/ldconfig命令读取配置文件/etc/ld.so.conf 之后生成(也可以在 ld.so.conf.d 目录下增加 .conf 文件,里面写入库路径,在 ld.so.conf 中 include ld.so.conf.d/.conf )
  4. 如果上述步骤都找不到,则到默认的系统路径中查找,先是/usr/lib然后是/lib

编译参数#

-shared#

指定生成动态连接库

-fPIC#

ppc_85xx-gcc -shared -fPIC liberr.c -o liberr.so

-fPIC 作用于编译阶段,告诉编译器产生与位置无关代码(Position-Independent Code),
位置,都可以正确的执行。

gcc -shared -fPIC -o 1.so 1.c

这里有一个-fPIC参数

PIC就是position independent code

PIC使.so文件的代码段变为真正意义上的共享

如果不加-fPIC,则加载.so文件的代码段时,代码段引用的数据对象需要重定位, 重定位会修改代码段的内容,这就造成每个使用这个.so文件代码段的进程在内核里都会生成这个.so文件代码段的copy.每个copy都不一样,取决于 这个.so文件代码段和数据段内存映射的位置.

**不加fPIC编译出来的so,是要再加载时根据加载到的位置再次重定位的.(因为它里面的代码并不是位置无关代码)**如果被多个应用程序共同使用,那么它们必须每个程序维护一份so的代码副本了.(因为so被每个程序加载的位置都不同,显然这些重定位后的代码也不同,当然不能共享)我们总是用fPIC来生成so,也从来不用fPIC来生成a.fPIC与动态链接可以说基本没有关系,libc.so一样可以不用fPIC编译,只是这样的so必须要在加载到用户程序的地址空间时重定向所有表目. 因此,不用fPIC编译so并不总是不好.如果你满足以下4个需求/条件:1.该库可能需要经常更新2.该库需要非常高的效率(尤其是有很多全局量的使用时)3.该库并不很大.4.该库基本不需要被多个应用程序共享 如果用没有加这个参数的编译后的共享库,也可以使用的话,可能是两个原因:1:gcc默认开启-fPIC选项2:loader使你的代码位置无关 从GCC来看,shared应该是包含fPIC选项的,但似乎不是所以系统都支持,所以最好显式加上fPIC选项。参见如下

`-shared' Produce a shared object which can then be linked with other objects to form an executable. Not all systems support this option. For predictable results, you must also specify the same set of options that were used to generate code (`-fpic’, `-fPIC’, or model suboptions) when you specify this option.(1)

-fPIC 的使用,会生成 PIC 代码,.so 要求为 PIC,以达到动态链接的目的,否则,无法实现动态链接。 non-PIC 与 PIC 代码的区别主要在于 access global data, jump label 的不同。比如一条 access global data 的指令,non-PIC 的形势是:ld r3, var1PIC 的形式则是:ld r3,var1-offset@GOT 指示的地址处装载一个值,即处的4个 byte 其实就是 var1 的地址。这个地址只有在运行的时候才知道,是由 dynamic-loader(ld-linux.so) 填进去的。 再比如 jump label 指令non-PIC 的形势是:jump printf ,意思是调用 printf。PIC 的形式则是:jumpprintf-offset@GOT 同时执行这个函数。这样,第2次呼叫 printf 的时候,就会直接跳到 printf 的地址,而不必再查找了。 **GOT 是 data section, 是一个 table, 除专用的几个 entry,每个 entry 的内容可以再执行的时候修改;****PLT 是 text section, 是一段一段的 code,执行中不需要修改。**每个 target 实现 PIC 的机制不同,但大同小异。比如 MIPS 没有 .plt, 而是叫 .stub,功能和 .plt 一样。 可见,动态链接执行很复杂,比静态链接执行时间长;但是,极大的节省了 size,PIC 和动态链接技术是计算机发展史上非常重要的一个

gcc mauall上面有说-fpic

If the GOT size for the linked executable exceeds a machine-specific maximum size, you get an error message from the linker indicating that -fpic does not work; in that case, recompile with -fPIC instead. (These maximums are 8k on the SPARC and 32k on the m68k and RS/6000. The 386 has no such limit.) -fPIC If supported for the target machine, emit position-independent code, suitable for dynamic linking and avoiding any limit on the size of the global offset table. This option makes a difference on the m68k, PowerPC and SPARC. Position-independent code requires special support, and therefore works only on certain machines. 关键在于GOT全局偏移量表里面的跳转项大小。intel处理器应该是统一4字节,没有问题。powerpc上由于汇编码或者机器码的特殊要求,所以跳转项分为短、长两种。 -fpic为了节约内存,在GOT里面预留了“短”长度。而-fPIC则采用了更大的跳转项。

命令#

ldd:查看依赖的动态库#

┌─[root@develop] - [/usr/local/nginx] - [Fri Jul 19, 10:10]
└─[$] <> ldd nginx
        linux-vdso.so.1 =>  (0x00007fffcf8fc000)
        libdl.so.2 => /lib64/libdl.so.2 (0x00007f4d1ea02000)
        libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f4d1e7e5000)
        libcrypt.so.1 => /lib64/libcrypt.so.1 (0x00007f4d1e5ae000)
        libssl.so.10 => /lib64/libssl.so.10 (0x00007f4d1e33c000)
        libcrypto.so.10 => /lib64/libcrypto.so.10 (0x00007f4d1deda000)
        libc.so.6 => /lib64/libc.so.6 (0x00007f4d1db19000)
        /lib64/ld-linux-x86-64.so.2 (0x000055743612e000)
        libfreebl3.so => /lib64/libfreebl3.so (0x00007f4d1d916000)
        libgssapi_krb5.so.2 => /lib64/libgssapi_krb5.so.2 (0x00007f4d1d6c8000)
        libkrb5.so.3 => /lib64/libkrb5.so.3 (0x00007f4d1d3e0000)
        libcom_err.so.2 => /lib64/libcom_err.so.2 (0x00007f4d1d1dc000)
        libk5crypto.so.3 => /lib64/libk5crypto.so.3 (0x00007f4d1cfa8000)
        libz.so.1 => /lib64/libz.so.1 (0x00007f4d1cd92000)
        libkrb5support.so.0 => /lib64/libkrb5support.so.0 (0x00007f4d1cb84000)
        libkeyutils.so.1 => /lib64/libkeyutils.so.1 (0x00007f4d1c97f000)
        libresolv.so.2 => /lib64/libresolv.so.2 (0x00007f4d1c765000)
        libselinux.so.1 => /lib64/libselinux.so.1 (0x00007f4d1c53d000)
        libpcre.so.1 => /lib64/libpcre.so.1 (0x00007f4d1c2dc000)

ldconfig:配置动态库路径#

读取配置文件/etc/ld.so.conf 之后生成 /etc/ld.so.cache 文件

┌─[root@develop] - [/usr/local/nginx] - [Fri Jul 19, 10:13]
└─[$] <> strings /etc/ld.so.cache |head
ld.so-1.7.0
glibc-ld.so.cache1.1
p11-kit-trust.so
/lib64/p11-kit-trust.so
libzmq.so.3
/lib64/libzmq.so.3
libz.so.1
/lib64/libz.so.1
libz.so
/lib64/libz.so

pkg-config:软件包信息查看#

[root@localhost ~]# pkg-config --cflags librtmp
-I/usr/local/include
[root@localhost ~]# pkg-config --libs librtmp
-L/usr/local/lib -lrtmp -lz -lssl -lcrypto

strings:从可执行文件中提取文本信息#

┌─[root@develop] - [/usr/local/nginx] - [Fri Jul 19, 10:13]
└─[$] <> strings /usr/local/lib64/libstdc++.so.6 |grep GLIBCXX
GLIBCXX_3.4
GLIBCXX_3.4.1
GLIBCXX_3.4.2
GLIBCXX_3.4.3
GLIBCXX_3.4.4
GLIBCXX_3.4.5
GLIBCXX_3.4.6
GLIBCXX_3.4.7
GLIBCXX_3.4.8
GLIBCXX_3.4.9
GLIBCXX_3.4.10
GLIBCXX_3.4.11
GLIBCXX_3.4.12
GLIBCXX_3.4.13
GLIBCXX_3.4.14
GLIBCXX_3.4.15
GLIBCXX_3.4.16
GLIBCXX_3.4.17
GLIBCXX_3.4.18
GLIBCXX_3.4.19
GLIBCXX_3.4.20
GLIBCXX_3.4.21
GLIBCXX_3.4.22
GLIBCXX_3.4.23
GLIBCXX_3.4.24
GLIBCXX_3.4.25

nm:查看可执行文件中的符号#

┌─[root@develop] - [/usr/local/nginx] - [Fri Jul 19, 10:14]
└─[$] <> nm nginx
                 U abort@@GLIBC_2.2.5
                 U accept4@@GLIBC_2.10
                 U accept@@GLIBC_2.2.5
00000000004114b7 t add_list_to_class.isra.9.constprop.12
0000000000411430 t add_not_list_to_class.isra.10
00000000004af2c0 t add_to_class.isra.8
00000000004af120 t adjust_recurse.isra.5
00000000004c6580 T adler32
00000000004c6590 T adler32_combine
00000000004c65b0 T adler32_combine64
00000000004c5fe0 t adler32_combine_.part.0
00000000004c60d0 T adler32_z
00000000006fc160 d args.26303
00000000004c9820 r argument_number
                 U ASN1_d2i_bio@@libcrypto.so.10
                 U ASN1_GENERALIZEDTIME_print@@libcrypto.so.10
                 U ASN1_TIME_print@@libcrypto.so.10
00000000004dbe80 r autoposstab
00000000004e18c0 r base_dist
00000000004e1940 r base_length
00000000006fc460 d basis64.26215
00000000006fc400 d basis64.26220
00000000006fc300 d basis64.26237
00000000006fc200 d basis64.26242
00000000004c54c0 t bi_flush
                 U bind@@GLIBC_2.2.5
                 U BIO_ctrl@@libcrypto.so.10
                 U BIO_free@@libcrypto.so.10
                 U BIO_int_ctrl@@libcrypto.so.10
                 U BIO_new_file@@libcrypto.so.10

静态库#

环境变量#

  • LIBRARY_PATH 静态链接库搜索路径

命令#

ar:创建或操作静态库#

-r 将objfile文件插入静态库尾或者替换静态库中同名文件

-x 从静态库文件中抽取文件objfile

-t 打印静态库的成员文件列表

-d 从静态库中删除文件objfile

-s 重置静态库文件索引

-v 创建文件冗余信息

-c 创建静态库文件

Q & A#

  1. 程序运行时出现libxxx.so.y => not found

    a

  2. /usr/bin/ld: /usr/local/openssl-1.0.2s/lib//libcrypto.a(cryptlib.o): relocation R_X86_64_32 against `OPENSSL_ia32cap_P’ can not be used when making a shared object; recompile with -fPIC

Reference#