GCC 中 -L、-rpath和-rpath-link的區別
GCC 中 -L、-rpath和-rpath-link的區別
來源 http://blog.csdn.net/q1302182594/article/details/42102961
關於這3個參數的說明,有不少資料,但是看完了還是覺得模糊,分不清它們的區別。本文將用實驗的方法去探討這3個參數的區別。
1、三個.c文件
1.1 world.c
- #include<stdio.h>
- void world(void) {
- printf("world.\n");
- }
1.2 hello.c
- #include <stdio.h>
- void world(void);
- void hello(void) {
- printf("hello\n");
- world();
- }
1.3 test.c
- void main(void) {
- hello();
- }
2、生成動態庫
參照《Linux靜態庫與動態庫制作》,將hello.c和world.c分別生成動態庫
- ubuntu $ gcc -c hello.c world.c
- ubuntu $ gcc -shared -o libhello.so hello.o
- 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:
- 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
- ubuntu $ gcc test.c -lhello -L .
結果如下圖:
由上圖可見已經在-L指定的路徑找打了libhello.so,只是libhello.so還需要libworld.so。雖然它都在同一目錄下,但是還是沒有辦法自動找到libworld.so。
那麽,能不能使用-lworld將libworld.so也一並鏈接到test.c中呢?下面做一個嘗試:
- 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
- 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參數:
- ubuntu $ export LD_LIBRARY_PATH=
- 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移動進這個目錄。
- ubuntu $ mdir lib_tmp
- ubuntu $ mv libhello.so lib_tmp/
- 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所在的路徑
- 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所在的路徑:
- 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進行編譯:
- 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
- Add a directory to the runtime library search path. This is used when linking an ELF executable with shared objects.
- All -rpath arguments are concatenated and passed to the runtime linker, which uses them to locate shared objects at
- runtime. The -rpath option is also used when locating shared objects which are needed by shared objects explicitly
- included in the link; see the description of the -rpath-link option. If -rpath is not used when linking an ELF
- executable, the contents of the environment variable "LD_RUN_PATH" will be used if it is defined.
- The -rpath option may also be used on SunOS. By default, on SunOS, the linker will form a runtime search path out of
- all the -L options it is given. If a -rpath option is used, the runtime search path will be formed exclusively using
- the -rpath options, ignoring the -L options. This can be useful when using gcc, which adds many -L options which may
- be on NFS mounted file systems.
- For compatibility with other ELF linkers, if the -R option is followed by a directory name, rather than a file name, it
- is treated as the -rpath option.
4.2 -rpath-link=dir
- When using ELF or SunOS, one shared library may require another. This happens when an "ld -shared" link includes a
- shared library as one of the input files.
- When the linker encounters such a dependency when doing a non-shared, non-relocatable link, it will automatically try
- to locate the required shared library and include it in the link, if it is not included explicitly. In such a case,
- the -rpath-link option specifies the first set of directories to search. The -rpath-link option may specify a sequence
- of directory names either by specifying a list of names separated by colons, or by appearing multiple times.
- This option should be used with caution as it overrides the search path that may have been hard compiled into a shared
- 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
- The linker uses the following search paths to locate required shared libraries:
- 1. Any directories specified by -rpath-link options.
- 2. Any directories specified by -rpath options. The difference between -rpath and -rpath-link is that directories
- specified by -rpath options are included in the executable and used at runtime, whereas the -rpath-link option is
- only effective at link time. Searching -rpath in this way is only supported by native linkers and cross linkers
- which have been configured with the --with-sysroot option.
- 3. On an ELF system, for native linkers, if the -rpath and -rpath-link options were not used, search the contents of
- the environment variable "LD_RUN_PATH".
- 4. On SunOS, if the -rpath option was not used, search any directories specified using -L options.
- 5. For a native linker, search the contents of the environment variable "LD_LIBRARY_PATH".
- 6. For a native ELF linker, the directories in "DT_RUNPATH" or "DT_RPATH" of a shared library are searched for shared
- libraries needed by it. The "DT_RPATH" entries are ignored if "DT_RUNPATH" entries exist.
- 7. The default directories, normally /lib and /usr/lib.
- 8. For a native linker on an ELF system, if the file /etc/ld.so.conf exists, the list of directories found in that
- file.
參考資料
[1]動態庫的鏈接和鏈接選項-L,-rpath-link,-rpath
[2]ld的-rpath與-rpath-link選項
GCC 中 -L、-rpath和-rpath-link的區別