1. 程式人生 > >GCC 中 -L、-rpath和-rpath-link的區別

GCC 中 -L、-rpath和-rpath-link的區別

目錄 恢復 固定 成功 ref exist fyi man手冊 錯誤

GCC 中 -L、-rpath和-rpath-link的區別

來源 http://blog.csdn.net/q1302182594/article/details/42102961

關於這3個參數的說明,有不少資料,但是看完了還是覺得模糊,分不清它們的區別。本文將用實驗的方法去探討這3個參數的區別。

1、三個.c文件

1.1 world.c

  1. #include<stdio.h>
  2. void world(void) {
  3. printf("world.\n");
  4. }

1.2 hello.c

  1. #include <stdio.h>
  2. void world(void);
  3. void hello(void) {
  4. printf("hello\n");
  5. world();
  6. }

1.3 test.c

  1. void main(void) {
  2. hello();
  3. }

2、生成動態庫

參照《Linux靜態庫與動態庫制作》,將hello.c和world.c分別生成動態庫

  1. ubuntu $ gcc -c hello.c world.c
  2. ubuntu $ gcc -shared -o libhello.so hello.o
  3. ubuntu $ gcc -shared -o libworld.so world.o

這時,生成的文件及其依賴如下圖:

技術分享圖片

由上圖可見,libhello.so和libworld都依賴於linux-gate.so.1、libc.so.6以及/lib/ld-linux.so.2,並且這3個庫的路徑都以及硬編碼進libhello.so和libworld.so中了(=>右邊的部分)。

然而,雖然libhello.so中調用了libworld.so的函數,但是在上圖中並沒有顯示出此關系。為了達到使libhello.so依賴於libworld.so的目的,在生成libhello.so時要鏈接到libworld.so:

  1. ubuntu $ gcc -shared -o libworld.so world.o -lhello -L .

此時,再使用ldd查看libhello.so的依賴:

技術分享圖片
由上圖可見,此時libhello.so已經依賴於libworld.so。

3、編譯test.c

3.1 -L

由於test.c直接依賴於libhello.so,因此使用-lhello -L

  1. ubuntu $ gcc test.c -lhello -L .

結果如下圖:
技術分享圖片

由上圖可見已經在-L指定的路徑找打了libhello.so,只是libhello.so還需要libworld.so。雖然它都在同一目錄下,但是還是沒有辦法自動找到libworld.so。

那麽,能不能使用-lworld將libworld.so也一並鏈接到test.c中呢?下面做一個嘗試:

  1. ubuntu $ gcc test.c -lhello -lworld -L .

沒有報錯,成功生成a.out。

執行a.out並且使用ldd查看a.out的依賴:

技術分享圖片

由上圖可見,雖然使用-lworld參數將libworld.so鏈接到了a.out中,但是上面只顯示a.out依賴於libhello.so。由於找不到libhello.so(=> not found)的路徑,因此需要設置環境變量LD_LIBRARY_PATH

  1. ubuntu export LD_LIBRARY_PATH=/home/liyihai/documents

再次執行a.out並使用ldd命令查看a.out的依賴庫:

技術分享圖片

由上圖可見,libhello.so已經通過LD_LIBRARY_PATH環境變量找到,並且libworld.so也出現在a.out的依賴中!

結論:-L指定的是鏈接時的庫路徑,生成的可執行文件在運行時庫的路徑由LD_LIBRARY_PATH環境變量指定。

3.2 -rpath

根據3.1第1張圖的提示,由於libhello.so依賴於libworld.so,可以只用-rpath或者-rpath-link來指定。這裏先使用-rpath。

先清空LD_LIBRARY_PATH環境變量,然後重新編譯test.c並且帶上-rpath參數:

  1. ubuntu $ export LD_LIBRARY_PATH=
  2. ubuntu $ gcc test.c -lhello -L . -Wl,-rpath .

執行a.out,並且使用ldd命令查看a.out的依賴:

技術分享圖片

由上圖可見,雖然沒有明確指出鏈接libworld.so,但是libworld.so還是出現在a.out的依賴中。

另外,雖然LD_LIBRARY_PATH已經清空,但是a.out還是可以執行,這說明庫的路徑已經被編譯進a.out中了。需要註意的是,libhello.so和libworld.so的路徑都是通過-rpath指定的路徑找到的。

3.2.1 實驗1

這時候,如果libhello.so和libworld.so的路徑改變了,將會發生什麽情況呢?下面做一個實驗。

創建一個lib_tmp目錄,然後將libhello.so和libworld.so移動進這個目錄。

  1. ubuntu $ mdir lib_tmp
  2. ubuntu $ mv libhello.so lib_tmp/
  3. ubuntu $ mv libworld.so lib_tmp/

這時再執行a.out時,提示找不動態庫,使用ldd命令查看a.out的庫路徑:

技術分享圖片

由上圖紅色圈部分可見,libhello.so的路徑是not found的,並且libworld.so沒有出現在其中。這和3.1的情況是相同的。

究其原因,就是要先找到libhello.so再去找libworl.so,因為是libhello.so依賴於libworld.so,而不是a.out依賴於libworld.so。

由此可見,使用了-rpath參數指定庫的路徑後,生成的可執行文件的依賴庫路徑並非就固定不變了。而是執行時先從-rpath指定的路徑去找依賴庫,如果找不到,還是會報not fund。

那麽,這時候,LD_LIBRARY_PATH對a.out是否還有影響呢?下面將LD_LIBRARY_PATH設為當前libhello.so和libworld.so所在的路徑

  1. ubuntu $ export LD_LIBRARY_PATH=./lib_tmp

再次執行a.out,並且使用ldd查看此時a.out的依賴庫路徑:

技術分享圖片

由上圖可見LD_LIBRARY_PATH還是起作用的!由上圖可見,和使用-rpath指定路徑的效果是一樣的。

3.2.2 實驗2

將LD_LIBRARY_PATH清空,然後將libhello.so移動到lib_tmp中,而libworld.so則留在documents目錄中。

執行a.out,並且使用ldd查看此時a.out的依賴庫:

技術分享圖片

由上圖可見,找不到libhello.so。這時,再指定LD_LIBRARY_PATH的路徑為libhello.so所在的路徑:

  1. ubuntu $ export LD_LIBRARY_PATH=./lib_tmp/

再次執行a.out,並且使用ldd查看其依賴庫:
技術分享圖片

由上圖可見,一切又恢復了正常。此時,libhello.so是通過LD_LIBRARY_PATH找到的,而libworld.so則是通過-rpath指定的路徑找到的。

3.2.3 回顧

其實,經過測試,在3.1小節中,如果先指定LD_LIBRARY_PATH的值為libhello.so和libworld.so所在的路徑,然後再編譯test.c(執行3.1節的第1條編譯命令),是可以成功編譯的,並不會報3.1小節第1張圖的那種錯誤。也就是說,LD_LIBRARY_PATH不僅指定可執行文件的庫路徑,還指定了庫所依賴於其它庫的路徑。

3.2.4 結論

並非指定-rpath參數後,就拋棄LD_LIBRARY_PATH環境變量,只是多了個可選的依賴庫路徑而已。

3.3 -rpath-link

先將LD_LIBRARY_PATH的值清空,然後將libworld.so移動到lib_tmp目錄中,而libhello.so則留在documents目錄中,使用以下命令對test.c進行編譯:

  1. ubuntu $ gcc test.c -lhello -L . -Wl,-rpath-link ./lib_tmp

執行a.out並且使用ldd查看a.out的依賴庫:

技術分享圖片

找不到 libhello.so,這在預料之中。下面指定LD_LIBRARY_PATH的值為libhello.so的路徑,然後在執行a.out,並且查看a.out的依賴:

技術分享圖片

由上圖可見,libhello.so已經通過LD_LIBRARY_PATH找到,但是libworld.so由於沒有在LD_LIBRARY_PATH指定的路徑中,而且編譯時a.out又沒有包含庫的路徑,因此找不到。這

對比3.2.2可以得出結論:-rpath和-rpath-link都可以在鏈接時指定庫的路徑;但是運行可執行文件時,-rpath-link指定的路徑就不再有效(鏈接器沒有將庫的路徑包含進可執行文件中),而-rpath指定的路徑還有效(因為鏈接器已經將庫的路徑包含在可執行文件中了。)

最後,不管使用了-rpath還是-rpath-link,LD_LIBRARY_PATH還是有效的。

4、ld命令的man手冊

4.1 -rpath=dir

  1. Add a directory to the runtime library search path. This is used when linking an ELF executable with shared objects.
  2. All -rpath arguments are concatenated and passed to the runtime linker, which uses them to locate shared objects at
  3. runtime. The -rpath option is also used when locating shared objects which are needed by shared objects explicitly
  4. included in the link; see the description of the -rpath-link option. If -rpath is not used when linking an ELF
  5. executable, the contents of the environment variable "LD_RUN_PATH" will be used if it is defined.
  6. The -rpath option may also be used on SunOS. By default, on SunOS, the linker will form a runtime search path out of
  7. all the -L options it is given. If a -rpath option is used, the runtime search path will be formed exclusively using
  8. the -rpath options, ignoring the -L options. This can be useful when using gcc, which adds many -L options which may
  9. be on NFS mounted file systems.
  10. For compatibility with other ELF linkers, if the -R option is followed by a directory name, rather than a file name, it
  11. is treated as the -rpath option.

4.2 -rpath-link=dir

  1. When using ELF or SunOS, one shared library may require another. This happens when an "ld -shared" link includes a
  2. shared library as one of the input files.
  3. When the linker encounters such a dependency when doing a non-shared, non-relocatable link, it will automatically try
  4. to locate the required shared library and include it in the link, if it is not included explicitly. In such a case,
  5. the -rpath-link option specifies the first set of directories to search. The -rpath-link option may specify a sequence
  6. of directory names either by specifying a list of names separated by colons, or by appearing multiple times.
  7. This option should be used with caution as it overrides the search path that may have been hard compiled into a shared
  8. library. In such a case it is possible to use unintentionally a different search path than the runtime linker would do.

4.3 search paths

  1. The linker uses the following search paths to locate required shared libraries:
  2. 1. Any directories specified by -rpath-link options.
  3. 2. Any directories specified by -rpath options. The difference between -rpath and -rpath-link is that directories
  4. specified by -rpath options are included in the executable and used at runtime, whereas the -rpath-link option is
  5. only effective at link time. Searching -rpath in this way is only supported by native linkers and cross linkers
  6. which have been configured with the --with-sysroot option.
  7. 3. On an ELF system, for native linkers, if the -rpath and -rpath-link options were not used, search the contents of
  8. the environment variable "LD_RUN_PATH".
  9. 4. On SunOS, if the -rpath option was not used, search any directories specified using -L options.
  10. 5. For a native linker, search the contents of the environment variable "LD_LIBRARY_PATH".
  11. 6. For a native ELF linker, the directories in "DT_RUNPATH" or "DT_RPATH" of a shared library are searched for shared
  12. libraries needed by it. The "DT_RPATH" entries are ignored if "DT_RUNPATH" entries exist.
  13. 7. The default directories, normally /lib and /usr/lib.
  14. 8. For a native linker on an ELF system, if the file /etc/ld.so.conf exists, the list of directories found in that
  15. file.

參考資料

[1]動態庫的鏈接和鏈接選項-L,-rpath-link,-rpath

[2]ld的-rpath與-rpath-link選項

GCC 中 -L、-rpath和-rpath-link的區別