再谈 GCC库的搜索路径问题

来源:互联网 发布:苹果刷机 for mac 编辑:程序博客网 时间:2024/04/28 20:03

昨天编译Linux kernel 的代码,刚开始提示 找不到 -lncurses, 安装了 ncurses之后,还是提示找不到。找了半天,才发现Makefile文件里面某个地方给 GCC加了个选项 -m32, 而用的系统是 x86-64的,所以安装的 ncurses肯定能搜到,但是被跳过了。

有下面的文件:

1. ctest1.c

[plain] view plain copy
 print?
  1. void ctest1(int *i)  
  2. {  
  3.    *i=5;  
  4. }  

2. ctest2.c

[plain] view plain copy
 print?
  1. void ctest2(int *i)  
  2. {  
  3.    *i=100;  
  4. }  

3. prog.c

[plain] view plain copy
 print?
  1. #include <stdio.h>  
  2. void ctest1(int *);  
  3. void ctest2(int *);  
  4.   
  5. int main()  
  6. {  
  7.    int x;  
  8.    ctest1(&x);  
  9.    printf("Valx=%d\n",x);  
  10.   
  11.    return 0;  
  12. }  
  13.    

先编译一个静态库:

[plain] view plain copy
 print?
  1. cc -Wall -c ctest1.c ctest2.c   
[plain] view plain copy
 print?
  1. ar -cvq libctest.a ctest1.o ctest2.o  

然后,link库到可执行文件:

[plain] view plain copy
 print?
  1. $ cc  prog.c  -lctest  
  2. /usr/bin/ld: cannot find -lctest  
  3. collect2: ld returned 1 exit status  
那么, gcc 默认会去搜索哪些目录呢?

可以在上面的命令 加选项 -v 来看:

[plain] view plain copy
 print?
  1. #include "..." search starts here:  
  2. #include <...> search starts here:  
  3.  /usr/lib/gcc/i686-linux-gnu/4.6/include  
  4.  /usr/local/include  
  5.  /usr/lib/gcc/i686-linux-gnu/4.6/include-fixed  
  6.  /usr/include/i386-linux-gnu  
  7.  /usr/include  
  8. End of search list.  
  9. GNU C (Ubuntu/Linaro 4.6.3-1ubuntu5) version 4.6.3 (i686-linux-gnu)  
  10.     compiled by GNU C version 4.6.3, GMP version 5.0.2, MPFR version 3.1.0-p3, MPC version 0.9  
  11. GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072  
  12. Compiler executable checksum: 09c248eab598b9e2acb117da4cdbd785  
  13. COLLECT_GCC_OPTIONS='-v' '-mtune=generic' '-march=i686'  
  14.  as --32 -o /tmp/ccZAn7G7.o /tmp/ccNuit3w.s  
  15. COMPILER_PATH=/usr/lib/gcc/i686-linux-gnu/4.6/:/usr/lib/gcc/i686-linux-gnu/4.6/:/usr/lib/gcc/i686-linux-gnu/:/usr/lib/gcc/i686-linux-gnu/4.6/:/usr/lib/gcc/i686-linux-gnu/  
  16. LIBRARY_PATH=/usr/lib/gcc/i686-linux-gnu/4.6/:/usr/lib/gcc/i686-linux-gnu/4.6/../../../i386-linux-gnu/:/usr/lib/gcc/i686-linux-gnu/4.6/../../../../lib/:/lib/i386-linux-gnu/:/lib/../lib/:/usr/lib/i386-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/i686-linux-gnu/4.6/../../../:/lib/:/usr/lib/  
  17. COLLECT_GCC_OPTIONS='-v' '-mtune=generic' '-march=i686'  
  18.  /usr/lib/gcc/i686-linux-gnu/4.6/collect2 --sysroot=/ --build-id --no-add-needed --as-needed --eh-frame-hdr -m elf_i386 --hash-style=gnu -dynamic-linker /lib/ld-linux.so.2 -z relro /usr/lib/gcc/i686-linux-gnu/4.6/../../../i386-linux-gnu/crt1.o /usr/lib/gcc/i686-linux-gnu/4.6/../../../i386-linux-gnu/crti.o /usr/lib/gcc/i686-linux-gnu/4.6/crtbegin.o -L/usr/lib/gcc/i686-linux-gnu/4.6 -L/usr/lib/gcc/i686-linux-gnu/4.6/../../../i386-linux-gnu -L/usr/lib/gcc/i686-linux-gnu/4.6/../../../../lib -L/lib/i386-linux-gnu -L/lib/../lib -L/usr/lib/i386-linux-gnu -L/usr/lib/../lib -L/usr/lib/gcc/i686-linux-gnu/4.6/../../.. /tmp/ccZAn7G7.o -lctest -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/lib/gcc/i686-linux-gnu/4.6/crtend.o /usr/lib/gcc/i686-linux-gnu/4.6/../../../i386-linux-gnu/crtn.o  
  19. /usr/bin/ld: cannot find -lctest  
  20. collect2: ld returned 1 exit status  
可见,默认,先搜查的是 COMPILER_PATH, 然后是 LIBRARY_PATH.

然后,加上 当前路径:

[plain] view plain copy
 print?
  1. $ cc  prog.c -L.  -lctest -v  
[plain] view plain copy
 print?
  1. #include "..." search starts here:  
  2. #include <...> search starts here:  
  3.  /usr/lib/gcc/i686-linux-gnu/4.6/include  
  4.  /usr/local/include  
  5.  /usr/lib/gcc/i686-linux-gnu/4.6/include-fixed  
  6.  /usr/include/i386-linux-gnu  
  7.  /usr/include  
  8. End of search list.  
  9. GNU C (Ubuntu/Linaro 4.6.3-1ubuntu5) version 4.6.3 (i686-linux-gnu)  
  10.     compiled by GNU C version 4.6.3, GMP version 5.0.2, MPFR version 3.1.0-p3, MPC version 0.9  
  11. GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072  
  12. Compiler executable checksum: 09c248eab598b9e2acb117da4cdbd785  
  13. COLLECT_GCC_OPTIONS='-L.' '-v' '-mtune=generic' '-march=i686'  
  14.  as --32 -o /tmp/ccWa3nUL.o /tmp/cca6Y5Wi.s  
  15. COMPILER_PATH=/usr/lib/gcc/i686-linux-gnu/4.6/:/usr/lib/gcc/i686-linux-gnu/4.6/:/usr/lib/gcc/i686-linux-gnu/:/usr/lib/gcc/i686-linux-gnu/4.6/:/usr/lib/gcc/i686-linux-gnu/  
  16. LIBRARY_PATH=/usr/lib/gcc/i686-linux-gnu/4.6/:/usr/lib/gcc/i686-linux-gnu/4.6/../../../i386-linux-gnu/:/usr/lib/gcc/i686-linux-gnu/4.6/../../../../lib/  
  17. :/lib/i386-linux-gnu/:/lib/../lib/:/usr/lib/i386-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/i686-linux-gnu/4.6/../../../:/lib/:/usr/lib/  
  18. COLLECT_GCC_OPTIONS='-L.' '-v' '-mtune=generic' '-march=i686'  
  19.  /usr/lib/gcc/i686-linux-gnu/4.6/collect2 --sysroot=/ --build-id --no-add-needed --as-needed --eh-frame-hdr -m elf_i386 --hash-style=gnu   
  20. -dynamic-linker /lib/ld-linux.so.2 -z relro /usr/lib/gcc/i686-linux-gnu/4.6/../../../i386-linux-gnu/crt1.o /usr/lib/gcc/i686-linux-gnu/4.6/../../../i386-linux-gnu/crti.o   
  21. /usr/lib/gcc/i686-linux-gnu/4.6/crtbegin.o -L. -L/usr/lib/gcc/i686-linux-gnu/4.6 -L/usr/lib/gcc/i686-linux-gnu/4.6/../../../i386-linux-gnu -L/usr/lib/gcc/i686-linux-gnu/4.6/../../../../lib   
  22. -L/lib/i386-linux-gnu -L/lib/../lib -L/usr/lib/i386-linux-gnu -L/usr/lib/../lib -L/usr/lib/gcc/i686-linux-gnu/4.6/../../.. /tmp/ccWa3nUL.o -lctest -lgcc --as-needed -lgcc_s  
  23.  --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/lib/gcc/i686-linux-gnu/4.6/crtend.o /usr/lib/gcc/i686-linux-gnu/4.6/../../../i386-linux-gnu/crtn.o  
可见,此时, -L后面的当前目录被放到了所有搜索路径的前面;换句话说,  -L.指定的目录被优先搜索。

然后,编译动态库:

[plain] view plain copy
 print?
  1. $ gcc -Wall -c -fpic ctest*.c  
[plain] view plain copy
 print?
  1. gcc -shared -Wl,-soname,libctest.so.1 -o libctest.so.1.0 ctest*.o  
然后link这个动态库:

[plain] view plain copy
 print?
  1. ~$ gcc -Wall  prog.c -lctest -o prog  
  2. /usr/bin/ld: cannot find -lctest  
  3. collect2: ld returned 1 exit status  
在/etc/ld.so.conf.d 下增加个文件 mylib.conf, 并把刚才那个动态库的目录放进去:

[plain] view plain copy
 print?
  1. $ cat mylib.conf   
  2. /home/charles  
然后执行   sudo ldconfig.之后,再确认:
[plain] view plain copy
 print?
  1. $ sudo ldconfig -p | grep "libctest"  
  2.     libctest.so.1 (libc6) => /home/charles/libctest.so.1  

这个时候已经自动生了一个文件 libctest.so.1

[plain] view plain copy
 print?
  1. $ ls ~/libctest* -l  
  2. -rw-rw-r-- 1 charles charles 1942 Nov  9 01:37 /home/charles/libctest.a  
  3. lrwxrwxrwx 1 root    root      15 Nov  9 08:17 /home/charles/libctest.so.1 -> libctest.so.1.0  
  4. -rwxrwxr-x 1 charles charles 6704 Nov  9 08:12 /home/charles/libctest.so.1.0  
再执行:

[plain] view plain copy
 print?
  1. $ gcc -Wall  prog.c -lctest -o prog  
  2. /usr/bin/ld: cannot find -lctest  
  3. collect2: ld returned 1 exit status  
还是找不到库。

原因是, gcc会去查找 名字为 libctest.so 和 libctest.a的库。  lddconfig 指定的/home/charles下面没有 libctest.so; 因为 lddconfig指定的库只对动态库有效,

所以 libctest.a也找不到。

[plain] view plain copy
 print?
  1. ln -sf libctest.so.1 libctest.so  
[plain] view plain copy
 print?
  1. $ ls ~/libctest* -l  
  2. -rw-rw-r-- 1 charles charles 1942 Nov  9 01:37 /home/charles/libctest.a  
  3. lrwxrwxrwx 1 charles charles   13 Nov  9 08:44 /home/charles/libctest.so -> libctest.so.1  
  4. lrwxrwxrwx 1 root    root      15 Nov  9 08:17 /home/charles/libctest.so.1 -> libctest.so.1.0  
  5. -rwxrwxr-x 1 charles charles 6704 Nov  9 08:12 /home/charles/libctest.so.1.0  

需要重新执行  ldconfig:

[plain] view plain copy
 print?
  1. ~$ sudo ldconfig  
[plain] view plain copy
 print?
  1. ~$ sudo ldconfig -p | grep libctest  
  2.     libctest.so.1 (libc6) => /home/charles/libctest.so.1  
  3.     libctest.so (libc6) => /home/charles/libctest.so  
再执行:

[plain] view plain copy
 print?
  1. $ gcc -Wall  prog.c -lctest -o prog  
  2. /usr/bin/ld: cannot find -lctest  
  3. collect2: ld returned 1 exit status  
还是找不到。

原因是,ldconfig配置的路径是用来在运行时搜索的。

[plain] view plain copy
 print?
  1. $ gcc -Wall  prog.c -L. -lctest -o prog  
  2. charles@taotao:~$ ./prog   
  3. Valx=5  
此时运行也没有问题,原因是已经在  ldconfig里设置好了动态库的位置。


动态库运行时的搜索路径:

1)LD_LIBRARY_PATH里列出的路径

2) /etc/ld.so.cache 里保存的路径

3) /lib

4)/usr/lib


------------------------------------------------------------------------

1. 动态库不同版本的链接,有两种方法:

    ln -sf /opt/lib/libctest.so.1.0 /opt/lib/libctest.so.1    ln -sf /opt/lib/libctest.so.1.0 /opt/lib/libctest.so
或者级联的形式:

    ln -sf /opt/lib/libctest.so.1.0 /opt/lib/libctest.so.1    ln -sf /opt/lib/libctest.so.1   /opt/lib/libctest.so


2. 可以用 gcc -print-file-name=libfilename 确认一个库是不是在link时能被搜索到(不用额外设置):

[plain] view plain copy
 print?
  1. $ gcc -print-file-name=libncurses.a  
  2. /usr/lib/gcc/i686-linux-gnu/4.6/../../../i386-linux-gnu/libncurses.a  
  3. charles@taotao:~$ gcc -print-file-name=libncurses.so  
  4. /usr/lib/gcc/i686-linux-gnu/4.6/../../../i386-linux-gnu/libncurses.so  

如果能输出完整的路径,那说明这个库已经包含在gcc的链接和运行时的搜索路径里面了。

3. 下面的命令把动态库直接写到了二进制文件里面:

[plain] view plain copy
 print?
  1. gcc -Wl,-R/home/charles -L.  prog.c -o prog -lctest  
[plain] view plain copy
 print?
  1. $ readelf  -d prog  | grep charles  
  2.  0x0000000f (RPATH)                      Library rpath: [/home/charles]  
因此,不需要设置运行时的路径,直接就可以运行。

2.参考:

http://www.yolinux.com/TUTORIALS/LibraryArchives-StaticAndDynamic.html