本文最后更新于:2021年3月3日 下午
概览 :gcc命令与工作流程,静态库的制作与使用,动态库的制作与使用,动态库加载失败原因与解决方案。
gcc简单命令与其工作流程 1 2 3 4 5 6 7 8 9 10 #include <stdio.h> int main () { printf ("10 + 11 = %d \n" ,10 +11 ); return 0 ; }
生成可执行目标文件 对于上述文件,在shell中输入命令:
即可生成可执行的目标文件prog
。
而实际上这一简单的过程有四步主要步骤:预处理,编译,汇编,以及最后的链接,才能生成可执行的目标文件。
1.预处理 gcc -E
gcc - E
表示进行预处理,调用了cpp预处理器
,进行头文件展开,宏替换,以及去掉注释等操作。
进行预处理之后的仍然是一个文本文件,后缀名.i
,大致能看懂一些。
-o
后接要生成的目标文件名,这里的prog.i
即为自己要生成的文件名。
上述命令可以使用cpp prog.c prog.i
来代替。
2.编译 gcc -S
gcc - S
表示进行预处理,调用了cc1
C编译器,将.i
文件编译成为ASCII汇编语言文件.s
.
上述命令可以使用cc1 prog.i -o prog.s
来代替。
生成的汇编代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 .file "prog.c" .text .section .rodata .LC0 : .string "10 + 11 = %d \n" .text .globl main .type main, @function main:.LFB0 : .cfi_startproc endbr64 pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 movl $21, %esi leaq .LC0 (%rip) , %rdi movl $0, %eax call printf@PLT movl $0, %eax popq %rbp .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE0 : .size main, .-main .ident "GCC: (Ubuntu 9.2.1-9ubuntu2) 9.2.1 20191008" .section .note.GNU-stack ,"" ,@progbits .section .note.gnu.property ,"a" .align 8 .long 1f - 0f .long 4f - 1f .long 5 0: .string "GNU" 1: .align 8 .long 0xc0000002 .long 3f - 2f 2: .long 0x3 3: .align 8 4:
3.汇编 gcc -c
gcc -c
表示进行汇编的工作,调用了as
汇编器,将.s
文件编译成为二进制文件 —— 可重定位目标文件(relocatable object file) .o
.
上述命令可以使用as -o prog.o prog.s
来代替。
.o
文件是二进制文件,普通的文本查看工具不能打开,这里可以使用linux中的工具objdump
来查看。
输入命令:
得到了.o
文件的二进制以及对应的汇编语言
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 prog .o: 文件格式 elf64 -x86 -64 Disassembly of section .text:0000000000000000 <main>: 0 : f3 0 f 1 e fa endbr64 4 : 55 push %rbp 5 : 48 89 e5 mov %rsp,%rbp 8 : be 15 00 00 00 mov $0 x15 ,%esi d : 48 8 d 3 d 00 00 00 00 lea 0 x0 (%rip),%rdi # 14 <main+0 x14 > 14 : b8 00 00 00 00 mov $0 x0 ,%eax 19 : e8 00 00 00 00 callq 1 e <main+0 x1 e> 1e : b8 00 00 00 00 mov $0 x0 ,%eax 23 : 5 d pop %rbp 24 : c3 retq
4.链接
这一步gcc将会调用链接器程序ld
,将prog.o
以及某些必要的系统目标文件组合起来,最终创建创建一个可执行的目标文件(executable object file)。
上述命令可以使用ld -o prog [一些系统文件与参数] prog.o
来代替。
同样可以使用objdump
来反编译来进行查看汇编代码:
内容比较多:
点击查看/折叠代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 prog : 文件格式 elf64 -x86 -64 Disassembly of section .init:0000000000001000 <_init>: 1000 : f3 0 f 1 e fa endbr64 1004 : 48 83 ec 08 sub $0 x8 ,%rsp 1008 : 48 8 b 05 d9 2 f 00 00 mov 0 x2 fd9 (%rip),%rax # 3 fe8 <__gmon_start__> 100f : 48 85 c0 test %rax,%rax 1012 : 74 02 je 1016 <_init+0 x16 > 1014 : ff d0 callq *%rax 1016 : 48 83 c4 08 add $0 x8 ,%rsp 101a : c3 retq Disassembly of section .plt:0000000000001020 <.plt>: 1020 : ff 35 9 a 2 f 00 00 pushq 0 x2 f9 a(%rip) # 3 fc0 <_GLOBAL_OFFSET_TABLE_+0 x8 > 1026 : f2 ff 25 9 b 2 f 00 00 bnd jmpq *0 x2 f9 b(%rip) # 3 fc8 <_GLOBAL_OFFSET_TABLE_+0 x10 > 102d : 0 f 1 f 00 nopl (%rax) 1030 : f3 0 f 1 e fa endbr64 1034 : 68 00 00 00 00 pushq $0 x0 1039 : f2 e9 e1 ff ff ff bnd jmpq 1020 <.plt> 103f : 90 nopDisassembly of section .plt.got:0000000000001040 <__cxa_finalize@plt>: 1040 : f3 0 f 1 e fa endbr64 1044 : f2 ff 25 ad 2 f 00 00 bnd jmpq *0 x2 fad(%rip) # 3 ff8 <__cxa_finalize@GLIBC_2 .2 .5 > 104b : 0 f 1 f 44 00 00 nopl 0 x0 (%rax,%rax,1 )Disassembly of section .plt.sec:0000000000001050 <printf@plt>: 1050 : f3 0 f 1 e fa endbr64 1054 : f2 ff 25 75 2 f 00 00 bnd jmpq *0 x2 f75 (%rip) # 3 fd0 <printf@GLIBC_2 .2 .5 > 105b : 0 f 1 f 44 00 00 nopl 0 x0 (%rax,%rax,1 )Disassembly of section .text:0000000000001060 <_start>: 1060 : f3 0 f 1 e fa endbr64 1064 : 31 ed xor %ebp,%ebp 1066 : 49 89 d1 mov %rdx,%r9 1069 : 5 e pop %rsi 106a : 48 89 e2 mov %rsp,%rdx 106d : 48 83 e4 f0 and $0 xfffffffffffffff0 ,%rsp 1071 : 50 push %rax 1072 : 54 push %rsp 1073 : 4 c 8 d 05 66 01 00 00 lea 0 x166 (%rip),%r8 # 11 e0 <__libc_csu_fini> 107a : 48 8 d 0 d ef 00 00 00 lea 0 xef(%rip),%rcx # 1170 <__libc_csu_init> 1081 : 48 8 d 3 d c1 00 00 00 lea 0 xc1 (%rip),%rdi # 1149 <main> 1088 : ff 15 52 2 f 00 00 callq *0 x2 f52 (%rip) # 3 fe0 <__libc_start_main@GLIBC_2 .2 .5 > 108e : f4 hlt 108f : 90 nop0000000000001090 <deregister_tm_clones>: 1090 : 48 8 d 3 d 79 2 f 00 00 lea 0 x2 f79 (%rip),%rdi # 4010 <__TMC_END__> 1097 : 48 8 d 05 72 2 f 00 00 lea 0 x2 f72 (%rip),%rax # 4010 <__TMC_END__> 109e : 48 39 f8 cmp %rdi,%rax 10a1 : 74 15 je 10 b8 <deregister_tm_clones+0 x28 > 10a3 : 48 8 b 05 2 e 2 f 00 00 mov 0 x2 f2 e(%rip),%rax # 3 fd8 <_ITM_deregisterTMCloneTable> 10aa : 48 85 c0 test %rax,%rax 10ad : 74 09 je 10 b8 <deregister_tm_clones+0 x28 > 10af : ff e0 jmpq *%rax 10b1 : 0 f 1 f 80 00 00 00 00 nopl 0 x0 (%rax) 10b8 : c3 retq 10b9 : 0 f 1 f 80 00 00 00 00 nopl 0 x0 (%rax)00000000000010c0 <register_tm_clones>: 10c0 : 48 8 d 3 d 49 2 f 00 00 lea 0 x2 f49 (%rip),%rdi # 4010 <__TMC_END__> 10c7 : 48 8 d 35 42 2 f 00 00 lea 0 x2 f42 (%rip),%rsi # 4010 <__TMC_END__> 10ce : 48 29 fe sub %rdi,%rsi 10d1 : 48 89 f0 mov %rsi,%rax 10d4 : 48 c1 ee 3 f shr $0 x3 f,%rsi 10d8 : 48 c1 f8 03 sar $0 x3 ,%rax 10dc : 48 01 c6 add %rax,%rsi 10df : 48 d1 fe sar %rsi 10e2 : 74 14 je 10 f8 <register_tm_clones+0 x38 > 10e4 : 48 8 b 05 05 2 f 00 00 mov 0 x2 f05 (%rip),%rax # 3 ff0 <_ITM_registerTMCloneTable> 10eb : 48 85 c0 test %rax,%rax 10ee : 74 08 je 10 f8 <register_tm_clones+0 x38 > 10f0 : ff e0 jmpq *%rax 10f2 : 66 0 f 1 f 44 00 00 nopw 0 x0 (%rax,%rax,1 ) 10f8 : c3 retq 10f9 : 0 f 1 f 80 00 00 00 00 nopl 0 x0 (%rax)0000000000001100 <__do_global_dtors_aux>: 1100 : f3 0 f 1 e fa endbr64 1104 : 80 3 d 05 2 f 00 00 00 cmpb $0 x0 ,0 x2 f05 (%rip) # 4010 <__TMC_END__> 110b : 75 2 b jne 1138 <__do_global_dtors_aux+0 x38 > 110d : 55 push %rbp 110e : 48 83 3 d e2 2 e 00 00 cmpq $0 x0 ,0 x2 ee2 (%rip) # 3 ff8 <__cxa_finalize@GLIBC_2 .2 .5 > 1115 : 00 1116 : 48 89 e5 mov %rsp,%rbp 1119 : 74 0 c je 1127 <__do_global_dtors_aux+0 x27 > 111b : 48 8 b 3 d e6 2 e 00 00 mov 0 x2 ee6 (%rip),%rdi # 4008 <__dso_handle> 1122 : e8 19 ff ff ff callq 1040 <__cxa_finalize@plt> 1127 : e8 64 ff ff ff callq 1090 <deregister_tm_clones> 112c : c6 05 dd 2 e 00 00 01 movb $0 x1 ,0 x2 edd(%rip) # 4010 <__TMC_END__> 1133 : 5 d pop %rbp 1134 : c3 retq 1135 : 0 f 1 f 00 nopl (%rax) 1138 : c3 retq 1139 : 0 f 1 f 80 00 00 00 00 nopl 0 x0 (%rax)0000000000001140 <frame_dummy>: 1140 : f3 0 f 1 e fa endbr64 1144 : e9 77 ff ff ff jmpq 10 c0 <register_tm_clones>0000000000001149 <main>: 1149 : f3 0 f 1 e fa endbr64 114d : 55 push %rbp 114e : 48 89 e5 mov %rsp,%rbp 1151 : be 15 00 00 00 mov $0 x15 ,%esi 1156 : 48 8 d 3 d a7 0 e 00 00 lea 0 xea7 (%rip),%rdi # 2004 <_IO_stdin_used+0 x4 > 115d : b8 00 00 00 00 mov $0 x0 ,%eax 1162 : e8 e9 fe ff ff callq 1050 <printf@plt> 1167 : b8 00 00 00 00 mov $0 x0 ,%eax 116c : 5 d pop %rbp 116d : c3 retq 116e : 66 90 xchg %ax,%ax0000000000001170 <__libc_csu_init>: 1170 : f3 0 f 1 e fa endbr64 1174 : 41 57 push %r15 1176 : 4 c 8 d 3 d 3 b 2 c 00 00 lea 0 x2 c3 b(%rip),%r15 # 3 db8 <__frame_dummy_init_array_entry> 117d : 41 56 push %r14 117f : 49 89 d6 mov %rdx,%r14 1182 : 41 55 push %r13 1184 : 49 89 f5 mov %rsi,%r13 1187 : 41 54 push %r12 1189 : 41 89 fc mov %edi,%r12 d 118c : 55 push %rbp 118d : 48 8 d 2 d 2 c 2 c 00 00 lea 0 x2 c2 c(%rip),%rbp # 3 dc0 <__init_array_end> 1194 : 53 push %rbx 1195 : 4 c 29 fd sub %r15 ,%rbp 1198 : 48 83 ec 08 sub $0 x8 ,%rsp 119c : e8 5 f fe ff ff callq 1000 <_init> 11a1 : 48 c1 fd 03 sar $0 x3 ,%rbp 11a5 : 74 1 f je 11 c6 <__libc_csu_init+0 x56 > 11a7 : 31 db xor %ebx,%ebx 11a9 : 0 f 1 f 80 00 00 00 00 nopl 0 x0 (%rax) 11b0 : 4 c 89 f2 mov %r14 ,%rdx 11b3 : 4 c 89 ee mov %r13 ,%rsi 11b6 : 44 89 e7 mov %r12 d,%edi 11b9 : 41 ff 14 df callq *(%r15 ,%rbx,8 ) 11bd : 48 83 c3 01 add $0 x1 ,%rbx 11c1 : 48 39 dd cmp %rbx,%rbp 11c4 : 75 ea jne 11 b0 <__libc_csu_init+0 x40 > 11c6 : 48 83 c4 08 add $0 x8 ,%rsp 11ca : 5 b pop %rbx 11cb : 5 d pop %rbp 11cc : 41 5 c pop %r12 11ce : 41 5 d pop %r13 11d0 : 41 5 e pop %r14 11d2 : 41 5 f pop %r15 11d4 : c3 retq 11d5 : 66 66 2 e 0 f 1 f 84 00 data16 nopw %cs:0 x0 (%rax,%rax,1 ) 11dc : 00 00 00 00 00000000000011e0 <__libc_csu_fini>: 11e0 : f3 0 f 1 e fa endbr64 11e4 : c3 retq Disassembly of section .fini:00000000000011e8 <_fini>: 11e8 : f3 0 f 1 e fa endbr64 11ec : 48 83 ec 08 sub $0 x8 ,%rsp 11f0 : 48 83 c4 08 add $0 x8 ,%rsp 11f4 : c3 retq
然后再输入命令./prog
即可执行编译出来的文件.
1 2 linux > ./prog10 + 11 = 21
其他gcc命令参数 -I dir
: 用于指定include包含文件的搜索目录。
-g
: 在编译时生成调试信息,该程序可以被调试器调试。
-D
: 在程序编译时,指定一个宏。
1 2 3 4 5 6 7 8 9 10 #include <stdio.h> int main () {#ifdef DEBUG printf ("调试信息打印中\n" );#endif printf ("正常输出\n" ); return 0 ; }
若使用gcc时指定了DEBUG
宏,则实际运行时将会打印调试信息,实际上这种方式也是调试的一种好方式。
1 2 3 4 5 6 7 colourso@c:~/桌面/gcctest$ gcc -o prog prog.c -DDEBUG colourso@c:~/桌面/gcctest$ ./prog 调试信息打印中 正常输出 colourso@c:~/桌面/gcctest$ gcc -o prog prog.c colourso@c:~/桌面/gcctest$ ./prog 正常输出
-w
:不生成任何警告信息。
-Wall
:生成所有的警告信息。
-On
:n的取值范围是0-3,表示编译器优化选项的几个级别,-O1
为缺省值,-O3
为优化级别最高。-o0
与-Og
表示没有优化。
-l xx
:在程序编译的时候,指定使用的库。
-L dir
:指定编译的时候,搜索的库的路径。
-fPIC/fpic
:生成与位置无关的代码,一般用于动态库的制作。
-shared
:生成共享目标文件,一般用于动态库的制作。
-std=c99
:指定c方言,gcc默认方言是GUN C
。
静态库 文件:head.h,add.c,sub.c,mult.c,div.c,main.c
1 2 3 4 5 6 7 8 9 10 #ifndef HEAD_H #define HEAD_H int add (int a,int b) ;int sub (int a,int b) ;int mult (int a,int b) ;int div (int a,int b) ;#endif
1 2 3 4 5 6 7 #include <stdio.h> #include "head.h" int add (int a,int b) { return a+b; }
1 2 3 4 5 6 7 #include <stdio.h> #include "head.h" int sub (int a,int b) { return a-b; }
1 2 3 4 5 6 7 #include <stdio.h> #include "head.h" int mult (int a,int b) { return a*b; }
1 2 3 4 5 6 7 #include <stdio.h> #include "head.h" int div (int a,int b) { return a/b; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #include <stdio.h> #include "head.h" int main () { int a = 10 ; int b = 5 ; printf ("%d + %d = %d\n" ,a,b,add(a,b)); printf ("%d - %d = %d\n" ,a,b,sub(a,b)); printf ("%d * %d = %d\n" ,a,b,mult(a,b)); printf ("%d / %d = %d\n" ,a,b,div(a,b)); return 0 ; }
静态库的命名规则 Linux:libxxx.a
lib :前缀,固定
xxx :库的名字,自己起
.a :后缀,固定
Windows:libxxx.lib
制作
使用gcc -c
生成.o
文件;
使用ar工具(archive)打包生成静态库。
1 ar rcs libxxx.a aaa.o bbb.o
r :将文件插入备存文件中
c :建立备存文件
s :索引
对于上述文件,生成静态库:
1 2 3 4 5 6 7 8 colourso@c:~/桌面/ch1linux/05staticlib$ ls add.c div.c head.h main.c mult.c sub.c colourso@c:~/桌面/ch1linux/05staticlib$ gcc -c add.c sub.c mult.c div.c colourso@c:~/桌面/ch1linux/05staticlib$ ls add.c add.o div.c div.o head.h main.c mult.c mult.o sub.c sub.o colourso@c:~/桌面/ch1linux/05staticlib$ ar rcs libmycalc.a add.o sub.o mult.o div.o colourso@c:~/桌面/ch1linux/05staticlib$ ls add.c add.o div.c div.o head.h libmycalc.a main.c mult.c mult.o sub.c sub.o
使用 1 2 3 4 5 6 7 8 colourso@c:~/桌面/ch1linux/05staticlib$ gcc -o prog main.c -L . -l mycalc colourso@c:~/桌面/ch1linux/05staticlib$ ls add.c add.o div.c div.o head.h libmycalc.a main.c mult.c mult.o prog sub.c sub.o colourso@c:~/桌面/ch1linux/05staticlib$ ./prog 10 + 5 = 15 10 - 5 = 5 10 * 5 = 50 10 / 5 = 2
对于命令gcc -o prog main.c -L . -l mycalc
-L .
表示在当前目录下搜索库。-l mycalc
表示使用库libmycalc.a
,这里只需要指定库的名字即可。
动态库 动态库的命名规则 Linux:libxxx.so
lib :前缀,固定
xxx :库的名字,自己起
.sp :后缀,固定
Windows:libxxx.dll
动态库的制作
使用gcc -c -fpic
生成.o
文件,但是要得到位置无关的文件;
使用gcc -shared xx.o -o libxxx.so
生成动态库。
先将上述文件重新整理后放到新的文件夹下:
1 2 3 4 5 6 7 8 9 10 . ├── include │ └── head.h ├── lib ├── main.c └── src ├── add .c ├── div.c ├── mult.c └── sub .c
1 2 3 4 5 6 colourso@c:~/桌面/ch1linux/06dll/src$ gcc -c -fpic *.c -I ../include colourso@c:~/桌面/ch1linux/06dll/src$ ls add.c add.o div.c div.o mult.c mult.o sub.c sub.o colourso@c:~/桌面/ch1linux/06dll/src$ gcc -shared *.o -o libmycalc.so colourso@c:~/桌面/ch1linux/06dll/src$ ls add.c add.o div.c div.o libmycalc.so mult.c mult.o sub.c sub.o
然后将.so
文件移动至lib
目录下。
动态库的使用 文件分布:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 . ├── include │ └── head.h ├── lib │ └── libmycalc.so ├── main.c └── src ├── add.c ├── add.o ├── div .c ├── div .o ├── mult.c ├── mult.o ├── sub.c └── sub.o
使用动态库,生成可执行程序,但是在执行程序的时候却报错了。
1 2 3 4 5 6 colourso@c:~/桌面/ch1linux/06dll$ gcc -o main main.c -I ./include -L lib/ -l mycalc colourso@c:~/桌面/ch1linux/06dll$ ls include lib main main.c src colourso@c:~/桌面/ch1linux/06dll$ ./main ./main: error while loading shared libraries: libmycalc.so: cannot open shared object file: No such file or directory
然后使用ldd命令(list dynamic dependencies)检查动态库依赖关系,可以看到libmycalc.so
未找到。
1 2 3 4 5 colourso@c:~/桌面/ch1linux/06dll$ ldd main linux-vdso.so.1 (0x00007ffc44d16000) libmycalc.so => not found libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fa620ff8000) /lib64/ld-linux-x86-64.so.2 (0x00007fa621200000)
动态库报错原因
静态库:GCC进行链接时,会把静态库中代码打包到可执行程序中;
动态库:GCC进行链接时,动态库的代码不会被打包到可执行程序中,而是在程序启动之后,动态库会被动态加载到内存之中,再被原先程序调用。
当系统加载可执行代码时候,能够知道其所依赖的库的名字,但是还需要知道绝对路径 。此时就需要系统的动态载入器 来获取该绝对路径。
对于elf格式的可执行程序,是由ld-linux.so
来完成的,它会先后搜索elf文件的DT_RPATH段
——> 环境变量LD_LIBRARY_PATH
——> /etc/ld.so.cache
文件列表 ——> /lib/
,/usr/lib
目录找到库文件后将其载入内存。
解决办法 DT_RPATH段
一般不考虑,我们一般改变不了。
配置环境变量LD_LIBRARY_PATH
临时配置,在当前的终端之内有效
当终端关掉或者更换一个终端时就会失效。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 colourso@c:~/桌面/ch1linux/06dll$ echo $LD_LIBRARY_PATH 【空白,什么也没有】 colourso@c:~/桌面/ch1linux/06dll$ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH :/home/colourso/桌面/ch1linux/06dll/lib colourso@c:~/桌面/ch1linux/06dll$ echo $LD_LIBRARY_PATH :/home/colourso/桌面/ch1linux/06dll/lib colourso@c:~/桌面/ch1linux/06dll$ ldd main linux-vdso.so.1 (0x00007ffef9dc2000) libmycalc.so => /home/colourso/桌面/ch1linux/06dll/lib/libmycalc.so (0x00007f82b4f5b000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f82b4d5a000) /lib64/ld-linux-x86-64.so.2 (0x00007f82b4f67000) colourso@c:~/桌面/ch1linux/06dll$ ./main 10 + 5 = 15 10 - 5 = 5 10 * 5 = 50 10 / 5 = 2
用户级别的配置
在用户目录~
下有一个文件.bashrc
,通过修改文件,添加配置即可解决问题。
通过vi编辑,在其文件末尾添加命令export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/colourso/桌面/ch1linux/06dll/lib
,然后要使修改生效,输入命令source .bashrc
即可。
1 2 3 colourso@c:~/桌面/ch1linux/06dll$ cd ~ colourso@c:~$ vi .bashrc colourso@c:~$ source .bashrc
此时再执行原来的文件,也没有任何问题。
系统级别的配置
先将上述用户级别的配置删除。 修改文件/etc/profile
,和上述相同的操作来实现配置。
1 2 3 4 5 6 7 colourso@c:~/桌面/ch1linux/06dll$ sudo vi /etc/profile colourso@c:~/桌面/ch1linux/06dll$ source /etc/profile colourso@c:~/桌面/ch1linux/06dll$ ldd main linux-vdso.so.1 (0x00007fffe52ef000) libmycalc.so => /home/colourso/桌面/ch1linux/06dll/lib/libmycalc.so (0x00007fbfaf973000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fbfaf772000) /lib64/ld-linux-x86-64.so.2 (0x00007fbfaf97f000)
配置/etc/ld.so.cache
文件列表 配置之前,先将上述过程的配置修改为原样。
实际上/etc/ld.so.cache
是一个二进制文件不能修改,我们通常配置的是/etc/ld.so.conf
文件。
打开文件之后,只需要在文件末尾添加路径即可/home/colourso/桌面/ch1linux/06dll/lib
。之后更新配置sudo ldconfig
.
1 2 3 4 5 6 7 colourso@c:~/桌面/ch1linux/06dll$ sudo vi /etc/ld.so.conf colourso@c:~/桌面/ch1linux/06dll$ sudo ldconfig colourso@c:~/桌面/ch1linux/06dll$ ldd main linux-vdso.so.1 (0x00007ffeb4979000) libmycalc.so => /home/colourso/桌面/ch1linux/06dll/lib/libmycalc.so (0x00007f5ca4cab000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f5ca4aba000) /lib64/ld-linux-x86-64.so.2 (0x00007f5ca4cc7000)
/lib/
以及/usr/lib
目录一般不建议将自己的动态库放置到这两个目录下,因为原本它就含有很多的库文件,如果一不小心重名覆盖,将会有很大的隐患。
静态库与动态库对比
链接阶段不同,链接的方式分别称为静态链接方式和动态链接方式。
静态链接方式:GCC进行链接时,会把静态库中代码打包到可执行程序中;
动态链接方式:GCC进行链接时,动态库的代码不会被打包到可执行程序中,而是在程序启动之后,动态库会被动态加载到内存之中,再被原先程序调用。
制作过程
静态库:生成.o
文件,ar工具制作
动态库:使用命令-fpic
生成位置无关的.o
文件,然后再用GCC命令-shared
生成动态库。
动态库使用时需要额外的配置
静态库优点
静态库被打包到应用程序,加载速度快
发布程序不需要提供静态库,移植也比较方便
静态库缺点
消耗系统资源,浪费内存。例如10个程序都需要使用静态库a,那么这个库就会被加载10份在内存之中。
更新,部署、发布麻烦。一旦静态库更新,整个程序都要重新编译。
动态库优点
可以实现进程间的资源共享。多个程序只需要一份动态库被加载到内存即可。
更新、部署、发布简单。
可以控制何时加载动态库。
动态库缺点
加载速度比静态库慢。
发布程序的时候需要提供依赖的动态库。
makefile文件 一个工程中的源文件不计其数,其按类型、功能、模块分别放在若干个目录中, Makefile 文件定义了一系列的规则来指定哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作,因为 Makefile 文件就像一个 Shell 脚本一样,也可以执行操作系统的命令。
Makefile 带来的好处就是自动化编译 ,一旦写好,只需要一个 make 命令,整个工程完全自动编译,极大的提高了软件开发的效率。
make 是一个命令工具,是一个解释 Makefile 文件中指令的命令工具,一般来说,大多数的 IDE 都有这个命令,比如 Delphi 的 make,Visual C++ 的 nmake,Linux 下 GNU 的 make。
文件命名 :makefile
或者 Makefile
。
规则 目标 : 依赖 (tab) 命令
目标:最终要生成的文件(伪目标除外)
依赖:生成目标所需要的文件或是目标
命令:通过执行命令对依赖操作生成目标(命令前必须 Tab 缩进)
一个makefile文件之中可以有多个规则
Makefile中的其它规则一般都是为第一条规则服务的。
工作原理 命令在执行之前,需要先检查规则中的依赖是否存在
如果存在,执行命令
如果不存在,向下检查其它的规则,检查有没有一个规则是用来生成这个依赖的,
如果找到了,则执行该规则中的命令
检测更新,在执行规则中的命令时,会比较目标和依赖文件的时间
如果依赖的时间比目标的时间晚,需要重新生成目标
如果依赖的时间比目标的时间早,目标不需要更新,对应规则中的命令不需要被执行
实例 依然使用静态库所用过的那几个代码。
第一版makefile文件:
1 2 app:add.c sub.c mult.c div.c main.c gcc add.c sub.c mult.c div.c main.c -o app
然后再终端之中执行make.
1 2 3 4 5 6 7 8 9 10 11 colourso@colourso-virtual-machine:~/桌面/ch1linux/07make$ ls add.c div.c head.h main.c makefile mult.c sub.c colourso@colourso-virtual-machine:~/桌面/ch1linux/07make$ make gcc add.c sub.c mult.c div.c main.c -o app colourso@colourso-virtual-machine:~/桌面/ch1linux/07make$ ls add.c app div.c head.h main.c makefile mult.c sub.c colourso@colourso-virtual-machine:~/桌面/ch1linux/07make$ ./app 10 + 5 = 15 10 - 5 = 5 10 * 5 = 50 10 / 5 = 2
第二版本makefile文件:
在这之前,随便增加一个test.c
的命令在makefile文件之中,并且不放在第一条。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 app:add.o sub.o mult.o div.o main.o gcc add.o sub.o mult.o div.o main.o -o appadd.o:add.c gcc -c add.c -o add.osub.o:sub.c gcc -c sub.c -o sub.omult.o:mult.c gcc -c mult.c -o mult.odiv.o:div.c gcc -c div.c -o div.omain.o:main.c gcc -c main.c -o main.otest.o:test.c gcc -c test.c -o test.o
执行:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 colourso@colourso-virtual-machine:~/桌面/ch1linux/07make$ ls add.c div.c head.h main.c makefile mult.c sub.c colourso@colourso-virtual-machine:~/桌面/ch1linux/07make$ make gcc -c add.c -o add.o gcc -c sub.c -o sub.o gcc -c mult.c -o mult.o gcc -c div.c -o div.o gcc -c main.c -o main.o gcc add.o sub.o mult.o div.o main.o -o app colourso@colourso-virtual-machine:~/桌面/ch1linux/07make$ ./app 10 + 5 = 15 10 - 5 = 5 10 * 5 = 50 10 / 5 = 2
通过上述执行效果可以看到test.o没有生成,也没有报错,Makefile中的其它规则一般都是为第一条规则服务的
,规则没有用到的依赖对应的规则,是不会被执行的。
再尝试更改main.c文件,使其和原来略微有所区别,然后重新make查看变化。
1 2 3 4 5 6 7 8 9 10 11 12 colourso@colourso-virtual-machine:~/桌面/ch1linux/07make$ make gcc -c main.c -o main.o gcc add.o sub.o mult.o div.o main.o -o app colourso@colourso-virtual-machine:~/桌面/ch1linux/07make$ ls add.c ap.c div.c head.h main.o mult.c sub.c add.o app div.o main.c makefile mult.o sub.o colourso@colourso-virtual-machine:~/桌面/ch1linux/07make$ ./app Makefile test 10 + 5 = 15 10 - 5 = 5 10 * 5 = 50 10 / 5 = 2
可以看到,仅仅main.o
被重新生成了,这就是make的规则,检查时间来决定需不需要更新。
从上面的例子也可以看到第二版本的makefile比第一版本的makefile要好一些,仅会对更新过的文件进行编译,节省资源和时间。
makefile一些语法
第三版本makefile文件:
1 2 3 4 5 6 7 8 9 10 # src、target为变量名 src=add.o sub.o mult.o div.o main.o target=app $(target):$(src) $(CC) $(src) -o $(target) #采用模式匹配,匹配*.o *.c %.o:%.c $(CC) -c $< -o $@
执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 colourso@colourso-virtual-machine:~/桌面/ch1linux/07make$ ls add.c div.c head.h main.c makefile mult.c sub.c colourso@colourso-virtual-machine:~/桌面/ch1linux/07make$ make cc -c add.c -o add.o cc -c sub.c -o sub.o cc -c mult.c -o mult.o cc -c div.c -o div.o cc -c main.c -o main.o cc add.o sub.o mult.o div.o main.o -o app colourso@colourso-virtual-machine:~/桌面/ch1linux/07make$ ls add.c app div.o main.c makefile mult.o sub.o add.o div.c head.h main.o mult.c sub.c colourso@colourso-virtual-machine:~/桌面/ch1linux/07make$ ./app Makefile test 10 + 5 = 15 10 - 5 = 5 10 * 5 = 50 10 / 5 = 2
makefile常用函数
$(wildcard PATTERN...)
功能:获取指定目录下指定类型的文件列表
参数:PATTERN 指的是某个或多个目录下的对应的某种类型的文件,如果有多个目录,一般使用空格间隔
返回:得到的若干个文件的文件列表,文件名之间使用空格间隔
示例:$(wildcard *.c ./sub/*.c)
返回值格式: a.c b.c c.c d.c e.c f.c
$(patsubst <pattern>,<replacement>,<text>)
功能:查找中的单词(单词以“空格”、“Tab”或“回车”“换行”分隔)是否符合
模式,如果匹配的话,则以替换。可以包括通配符%
,表示任意长度的字串。如果中也包含%
,那么,中的这个%
将是中的那个%所代表的字串。(可以用\
来转义,以\%
来表示真实含义的%
字符)
返回:函数返回被替换过后的字符串
示例:$(patsubst %.c, %.o, x.c bar.c)
返回值格式: x.o bar.o
第四版本makefile文件:
1 2 3 4 5 6 7 8 9 10 11 12 # 获取当前路径下所有.c文件 src=$(wildcard ./*.c) # 将.c文件转为.o文件 objs=$(patsubst %.c,%.o,$(src)) target=app $(target):$(objs) $(CC) $(objs) -o $(target) #采用模式匹配,匹配*.o *.c %.o:%.c $(CC) -c $< -o $@
运行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 colourso@colourso-virtual-machine:~/桌面/ch1linux/07make$ ls add.c div.c head.h main.c makefile mult.c sub.c colourso@colourso-virtual-machine:~/桌面/ch1linux/07make$ make cc -c mult.c -o mult.o cc -c main.c -o main.o cc -c add.c -o add.o cc -c div.c -o div.o cc -c sub.c -o sub.o cc ./mult.o ./main.o ./add.o ./div.o ./sub.o -o app colourso@colourso-virtual-machine:~/桌面/ch1linux/07make$ ls add.c app div.o main.c makefile mult.o sub.o add.o div.c head.h main.o mult.c sub.c colourso@colourso-virtual-machine:~/桌面/ch1linux/07make$ ./app Makefile test 10 + 5 = 15 10 - 5 = 5 10 * 5 = 50 10 / 5 = 2
make clean 上述make执行之后,会生成许多的.o
文件,而我们并用不上。可以添加命令来将其清除。
在makefile文件末尾添加
然后输入命令make clean
即可清除.o
文件。
但若是本地也有一个文件也叫clean
时,将无法清除。会显式make: “clean”已是最新。
。
原因是:make的规则是检查更新,clean没有依赖,则按照依赖的时间总是早于目标的时间,此时就不会再执行这条规则。
解决方法:使用伪目标.PHONY:clean
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 # 获取当前路径下所有.c文件 src=$(wildcard ./*.c) # 将.c文件转为.o文件 objs=$(patsubst %.c,%.o,$(src)) target=app $(target):$(objs) $(CC) $(objs) -o $(target) #采用模式匹配,匹配*.o *.c %.o:%.c $(CC) -c $< -o $@ #伪目标,则其不会生成clean文件,从而解决冲突 .PHONY:clean clean: rm $(objs) -f