linux gcc链接参数-L -rpath -rpath-link区别

本文深入探讨了Linux环境下动态库的依赖关系、链接过程及运行时库搜索机制,详细解析了-L、-l、rpath与rpath-link参数的作用,通过实验验证了直接依赖与间接依赖的区别。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

       找了一台ubuntu:

root@ubuntu:~/test$  cat /etc/issue
Ubuntu 20.04 LTS \n \l
root@ubuntu:~/test$  gcc -v
……
gcc version 9.3.0 (Ubuntu 9.3.0-10ubuntu2) 

    测试源代码 world.c:

// world.c
void world(){

}

测试源代码 hello.c:

//hello.c

void world();

void hello(){
    world();
}

  测试源代码 main.c:

//main.c

void hello();

int main(){
    hello();
    return 0;
}

 main是主程序,hello和world是动态库,main直接依赖hello, hello依赖world,即main间接依赖world。

 写个简单makefile:

#makefile

main: main.c
        gcc -o $@ $<

hello: hello.c
        gcc -fPIC -shared -o lib$@.so $<

world: world.c
        gcc -fPIC -shared -o lib$@.so $<

先看看目录内容:

root@ubuntu:~/test$ ls -l
总用量 40
drwxrwxr-x  2 root  root   4096 7月  11 12:34 ./
drwxr-xr-x 21 root  root   4096 7月  11 12:32 ../
-rw-rw-r--  1 root  root     56 7月  11 10:26 hello.c
-rw-rw-r--  1 root  root     50 7月  11 10:27 main.c
-rw-rw-r--  1 root  root    129 7月  11 12:31 makefile
-rw-rw-r--  1 root  root     26 7月  11 10:23 world.c

接下来一步一步地尝试,先编译hello试试:

root@ubuntu:~/test$ make hello
gcc -fPIC -shared -o libhello.so hello.c
root@ubuntu:~/test$ ls -l
总用量 40
drwxrwxr-x  2 root  root   4096 7月  11 12:34 ./
drwxr-xr-x 21 root  root   4096 7月  11 12:32 ../
-rw-rw-r--  1 root  root     56 7月  11 10:26 hello.c
-rwxrwxr-x  1 root  root  15872 7月  11 12:32 libhello.so*
-rw-rw-r--  1 root  root     50 7月  11 10:27 main.c
-rw-rw-r--  1 root  root    129 7月  11 12:31 makefile
-rw-rw-r--  1 root  root     26 7月  11 10:23 world.c

居然成功生成动态库libhello.so,都没加-l -L这些参数,说明生成动态库的时候不是一定要去链接它的依赖库,即可以没有链接阶段。

再看看libhello.so的信息:

root@ubuntu:~/test$ ldd libhello.so 
	statically linked                                           #没有依赖,显示是静态链接的
root@ubuntu:~/test$ nm libhello.so 
0000000000004028 b completed.8059
                 w __cxa_finalize
0000000000001060 t deregister_tm_clones
00000000000010d0 t __do_global_dtors_aux
0000000000003e58 d __do_global_dtors_aux_fini_array_entry
0000000000004020 d __dso_handle
0000000000003e60 d _DYNAMIC
0000000000001130 t _fini
0000000000001110 t frame_dummy
0000000000003e50 d __frame_dummy_init_array_entry
00000000000020c0 r __FRAME_END__
0000000000004000 d _GLOBAL_OFFSET_TABLE_
                 w __gmon_start__
0000000000002000 r __GNU_EH_FRAME_HDR
0000000000001119 T hello
0000000000001000 t _init
                 w _ITM_deregisterTMCloneTable
                 w _ITM_registerTMCloneTable
0000000000001090 t register_tm_clones
0000000000004028 d __TMC_END__
                 U world                                      #有一个undefined的符号world

显然,它留了一个未定义的符号world等待去外部链接,跟静态库一样。把没有依赖的so认为是静态库,也行。

再编译world,这个没什么依赖,肯定成功:

root@ubuntu:~/test$ make world
gcc -fPIC -shared -o libworld.so world.c

最后编译主程序main:

root@ubuntu:~/test$ make main
gcc -o main main.c
/usr/bin/ld: /tmp/ccfAC3eh.o: in function `main':
main.c:(.text+0xe): undefined reference to `hello'
collect2: error: ld returned 1 exit status
make: *** [makefile:3:main] 错误 1

看出,编译可执行二进制程序的时候,必然有链接阶段,有符号hello找不到,链接失败。

好了,修改makefile,增加对hello的链接:

#makefile

main: main.c
        gcc -o $@ $< -lhello -L.    # -l指定依赖库名,-L指定依赖库首先搜索的目录

hello: hello.c
        gcc -fPIC -shared -o lib$@.so $<

world: world.c
        gcc -fPIC -shared -o lib$@.so $<

再次编译主程序main:

root@ubuntu:~/test$ make main
gcc -o main main.c -lhello -L.
/usr/bin/ld: ./libhello.so: undefined reference to `world'
collect2: error: ld returned 1 exit status
make: *** [makefile:3:main] 错误 1

这次提示找不到符号world了,意思是还得加-lworld:

#makefile

main: main.c
        gcc -o $@ $< -lhello -lworld -L. 

hello: hello.c
        gcc -fPIC -shared -o lib$@.so $<

world: world.c
        gcc -fPIC -shared -o lib$@.so $<

第3次编译主程序main:

root@ubuntu:~/test$ make main
gcc -o main main.c -lhello -lworld -L.
root@ubuntu:~/test$ ll
drwxrwxr-x  2 root  root   4096 7月  11 13:07 ./
drwxr-xr-x 21 root  root   4096 7月  11 13:07 ../
-rw-rw-r--  1 root  root     41 7月  11 12:47 hello.c
-rwxrwxr-x  1 root  root  15864 7月  11 12:48 libhello.so*
-rwxrwxr-x  1 root  root  15632 7月  11 12:53 libworld.so*
-rwxrwxr-x  1 root  root  16672 7月  11 13:07 main*
-rw-rw-r--  1 root  root     40 7月  11 12:48 main.c
-rw-rw-r--  1 root  root    149 7月  11 13:07 makefile
-rw-rw-r--  1 root  root     21 7月  11 12:47 world.c
root@ubuntu:~/test$ readelf -d main | grep NEEDED             #查看直接依赖库
 0x0000000000000001 (NEEDED)             共享库:[libhello.so]
 0x0000000000000001 (NEEDED)             共享库:[libworld.so]
 0x0000000000000001 (NEEDED)             共享库:[libc.so.6]


上图可以看到,main直接依赖hello和world库。

成功生成main,执行一下:

root@ubuntu:~/test$ ./main
./main: error while loading shared libraries: libhello.so: cannot open shared object file: No such file or directory

报错:找不到动态库libhello.so。libhello.so明明就在当前目录。

用ldd命令查看一下main的依赖,发现同时依赖hello和world,但都not found。

root@ubuntu:~/test$  ldd ./main
	linux-vdso.so.1 (0x00007ffec81da000)
	libhello.so => not found
	libworld.so => not found
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fcfb68ef000)
	/lib64/ld-linux-x86-64.so.2 (0x00007fcfb6af9000)

这里就提一下linux中可执行程序加载动态库时搜索动态库的目录优先顺序,网上总结也很多:

1. 二进制程序被链接时由参数-rpath指定的运行时目录,多个则以冒号分隔。这个是被硬编码进二进制程序的,包括可执行程序和动态库,都可以有这个-rpath.

2.环境变量LD_LIBRARY_PATH指定的目录,多个则以冒号分隔。

3.系统里/etc/ld.so.conf文件里指定的目录。

4.系统里动态库加载器ld-linux.so(ldd命令实际调用的还是这个)默认的搜索目录/lib,usr/lib或者/lib64,/usr/lib64。

于是,可以通过设置环境变量的命令export LD_LIBRARY_PATH=.指定当前目录后再运行main,也可以把hello和world库拷贝到系统的库目录下再运行main。但这些方法不是此次测试的目标。此次要用第1种-rpath的方式。

修改makefile:

#makefile

main: main.c
        gcc -o $@ $< -lhello -lworld -L. -Wl,-rpath=.

hello: hello.c
        gcc -fPIC -shared -o lib$@.so $<

world: world.c
        gcc -fPIC -shared -o lib$@.so $<

删除之前生成的main文件,重新编译生成并运行:

root@ubuntu:~/test$ make main
gcc -o main main.c -lhello -lworld -L. -Wl,-rpath=.
root@ubuntu:~/test$ readelf -d main | grep runpath
 0x000000000000001d (RUNPATH)            Library runpath: [.]  #可见rpath被编码进二进制了
root@ubuntu:~/test$ ldd ./main
	linux-vdso.so.1 (0x00007ffd464dc000)
	libhello.so => ./libhello.so (0x00007f71df4d2000)
	libworld.so => ./libworld.so (0x00007f71df4cd000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f71df2ca000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f71df4de000)
root@ubuntu:~/test$ ./main
root@ubuntu:~/test$

没再报错,运行时能够在当前目录找到那两个库。

以上是在编译链接主程序的时候同时链接所有依赖库,包括从代码层面上说的直接依赖和间接依赖,实际上从链接层面上说两个库都成了直接依赖。

下面继续测试链接时的间接依赖,修改makefile,在编译hello的时候去链接world,编译main的时候不再显式链接world,看行不行:

#makefile

main: main.c
        gcc -o $@ $< -lhello -L. -Wl,-rpath=.

hello: hello.c
        gcc -fPIC -shared -o lib$@.so $< -lworld -L.

world: world.c
        gcc -fPIC -shared -o lib$@.so $<

clean:
        rm -f main libhello.so libworld.so
root@ubuntu:~/test$ make clean
rm -f main libhello.so libworld.so
root@ubuntu:~/test$ make world 
gcc -fPIC -shared -o libworld.so world.c
root@ubuntu:~/test$ make hello
gcc -fPIC -shared -o libhello.so hello.c -lworld -L.
root@ubuntu:~/test$ readelf -d libhello.so | grep NEEDED        #查看直接依赖库
 0x0000000000000001 (NEEDED)             共享库:[libworld.so]
root@ubuntu:~/test$ ldd libhello.so                             #寻找运行时所有依赖库
	linux-vdso.so.1 (0x00007ffe22ddb000)
	libworld.so => not found

以上可以看出,编译链接成功,hello库直接依赖world库,但是用ldd命令看出,运行时hello库找不到world库,这个还是可以指定-rpath解决,但先不管,直接编译主程序main,看main的rpath能不能连带找到world库。

root@ubuntu:~/test$ make main
gcc -o main main.c -lhello -L. -Wl,-rpath=.
root@ubuntu:~/test$ readelf -d main | grep NEEDED    #查看直接依赖库,不再直接依赖world
 0x0000000000000001 (NEEDED)             共享库:[libhello.so]
 0x0000000000000001 (NEEDED)             共享库:[libc.so.6]
root@ubuntu:~/test$ ldd main                         #寻找运行时所有依赖库,包括间接依赖
	linux-vdso.so.1 (0x00007ffd54b89000)
	libhello.so => ./libhello.so (0x00007f04b77c5000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f04b75c2000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f04b77d1000)
	libworld.so => not found
root@ubuntu:~/test$ ./main
./main: error while loading shared libraries: libworld.so: cannot open shared object file: No such file or directory

很遗憾,main运行时找不到world库。

那再试试在链接main的时候加上-lworld:

#makefile

main: main.c
        gcc -o $@ $< -lhello -lworld -L. -Wl,-rpath=.

hello: hello.c
        gcc -fPIC -shared -o lib$@.so $< -lworld -L.

world: world.c
        gcc -fPIC -shared -o lib$@.so $<

clean:
        rm -f main libhello.so libworld.so
root@ubuntu:~/test$ rm main
root@ubuntu:~/test$ make main
gcc -o main main.c -lhello -lworld -L. -Wl,-rpath=.
root@ubuntu:~/test$ readelf -d main | grep NEEDED    #查看直接依赖库,依然不直接依赖world
 0x0000000000000001 (NEEDED)             共享库:[libhello.so]
 0x0000000000000001 (NEEDED)             共享库:[libc.so.6]
root@ubuntu:~/test$ ldd main                        #寻找运行时所有依赖库,包括间接依赖
	linux-vdso.so.1 (0x00007ffd54b89000)
	libhello.so => ./libhello.so (0x00007f04b77c5000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f04b75c2000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f04b77d1000)
	libworld.so => not found
root@ubuntu:~/test$ ./main
./main: error while loading shared libraries: libworld.so: cannot open shared object file: No such file or directory

依然很遗憾,结局一样。说明在链接主程序时通过-l加上间接依赖库并不会将之改变成直接依赖库,除非main本就有亲自调用到world库的代码,有直接依赖关系。

显然,直接依赖world库的是hello库,而main只是间接依赖world库,所以在运行阶段main的rpath对world库不起作用,不能连带找到world库。也就是说,在运行阶段,rpath只对直接依赖库有作用,对间接依赖库没作用。要想通过rpath方式在运行时找到world库,只能hello库自己解决。

继续修改makefile,让hello库去解决world库的运行时目录:

#makefile

main: main.c
        gcc -o $@ $< -lhello -L. -Wl,-rpath=.

hello: hello.c
        gcc -fPIC -shared -o lib$@.so $< -lworld -L. -Wl,-rpath=.

world: world.c
        gcc -fPIC -shared -o lib$@.so $<

clean:
        rm -f main libhello.so libworld.so
root@ubuntu:~/test$ rm main libhello.so
root@ubuntu:~/test$ make hello
gcc -fPIC -shared -o lib$@.so $< -lworld -L. -Wl,-rpath=.
root@ubuntu:~/test$ readelf -d libhello.so | grep NEEDED   #查看hello库的直接依赖库
 0x0000000000000001 (NEEDED)             共享库:[libworld.so]
root@ubuntu:~/test$ readelf -d libhello.so | grep runpath  #查看hello库rapth
 0x000000000000001d (RUNPATH)            Library runpath: [.]
root@ubuntu:~/test$ ldd libhello.so                        #hello库运行时寻找所有依赖库
	linux-vdso.so.1 (0x00007ffc820be000)
	libworld.so => ./libworld.so (0x00007f8dcfe9b000)
root@ubuntu:~/test$
root@ubuntu:~/test$ make main
gcc -o main main.c -lhello -L. -Wl,-rpath=.
root@ubuntu:~/test$ readelf -d main | grep NEEDED    #查看main的直接依赖库,不直接依赖world
 0x0000000000000001 (NEEDED)             共享库:[libhello.so]
 0x0000000000000001 (NEEDED)             共享库:[libc.so.6]
root@ubuntu:~/test$ ldd main                         #寻找运行时所有依赖库,包括间接依赖
	linux-vdso.so.1 (0x00007ffd54b89000)
	libhello.so => ./libhello.so (0x00007f04b77c5000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f04b75c2000)
	libworld.so => ./libworld.so (0x00007f31d1b6d000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f04b77d1000)
root@ubuntu:~/test$ ./main                            #运行成功
root@ubuntu:~/test$

更加证明,rpath只对直接依赖库有作用,主程序并不会在自己的rpath目录里去寻找间接依赖库。

以上说的基本是运行时对直接和间接依赖库的处理。那我又好奇,链接阶段是怎么处理间接依赖库的呢?删除间接依赖库后主程序main还能不能链接成功?下面继续测试:

root@ubuntu:~/test$ rm main libworld.so  #删除main和world库,保留hello库
root@ubuntu:~/test$ make main            #重新编译main
gcc -o main main.c -lhello -L. -Wl,-rpath=.
/usr/bin/ld: warning: libworld.so, needed by ./libhello.so, not found (try using -rpath or -rpath-link)
/usr/bin/ld: ./libhello.so: undefined reference to `world'
collect2: error: ld returned 1 exit status
make: *** [makefile:3:main] 错误 1

失败了,找不到间接依赖的world库了。这里至少可以得出一个结论:链接阶段也必须要找到间接依赖库才行。那到底是怎么找的呢?

 这里也顺便提一下链接器寻找依赖库的目录顺序:

1. 首先是在编译链接时-L等参数显式指定的目录里找,为什么说“-L等”,那说明不止-L,还有其它,上图已经提示了用rpath或者rpath-link,后面再讨论。
2. 系统环境变量 LIBRARY_PATH指定的目录。
3. 编译器安装时自身配置的搜索目录,可用命令gcc --print-search-dir | grep libraries 查看,链接时会以-L参数形式隐式地传递给链接器ld。

4. 链接器ld安装时自身配置的搜索目录,可用命令ld -verbose | grep SEARCH_DIR 可查看。

当然,上面后三种指定目录不在此讨论之列,这里只讨论第一种编译链接时显式指定的链接目录。

回到上面链接时找不到world库的问题,上面已经说过,-l -L并不能改变间接依赖库成为直接依赖库,所以在主程序main链接时找不到间接依赖库world自然就不会是-l -L这两参数的原因。

修改makefile, 将world库生成在./lib目录下:

#makefile

main: main.c
        gcc -o $@ $< -lhello -L. -Wl,-rpath=.

hello: hello.c
        gcc -fPIC -shared -o lib$@.so $< -lworld -L./lib -Wl,-rpath=.

world: world.c
        mkdir -p ./lib
        gcc -fPIC -shared -o ./lib/lib$@.so $^

clean:
        rm -f main libhello.so libworld.so ./lib/libworld.so

先删除之前生成的主程序和所有库文件:

root@ubuntu:~/test$ make clean
rm -f main libhello.so libworld.so ./lib/libworld.so
root@ubuntu:~/test$ ll
总用量 24
drwxrwxr-x  2 root  root  4096 7月  11 16:07 ./
drwxr-xr-x 21 root  root  4096 7月  11 16:07 ../
-rw-rw-r--  1 root  root    41 7月  11 12:47 hello.c
-rw-rw-r--  1 root  root    40 7月  11 12:48 main.c
-rw-rw-r--  1 root  root   265 7月  11 16:07 makefile
-rw-rw-r--  1 root  root    21 7月  11 12:47 world.c

再重新编译:

root@ubuntu:~/test$ make world 
mkdir -p ./lib
gcc -fPIC -shared -o ./lib/libworld.so world.c
root@ubuntu:~/test$
root@ubuntu:~/test$ make hello
gcc -fPIC -shared -o libhello.so hello.c -lworld -L./lib -Wl,-rpath=.
root@ubuntu:~/test$
root@ubuntu:~/test$ make main
gcc -o main main.c -lhello -L. -Wl,-rpath=.
/usr/bin/ld: warning: libworld.so, needed by ./libhello.so, not found (try using -rpath or -rpath-link)
/usr/bin/ld: ./libhello.so: undefined reference to `world'
collect2: error: ld returned 1 exit status
make: *** [makefile:3:main] 错误 1

world库生成在./lib目录,hello库通过-L./lib找到world库链接成功并生成在当前目录,而主程序main通过-L.在当前目录里找到hello

库进行链接却报找不到world库。提示很清楚,尝试用-rpath或者-rpath-link。

先尝试在main的链接时rpath增加一个目录./lib:

#makefile

main: main.c
        gcc -o $@ $< -lhello -L. -Wl,-rpath=.:./lib

hello: hello.c
        gcc -fPIC -shared -o lib$@.so $< -lworld -L./lib -Wl,-rpath=.

world: world.c
        mkdir -p ./lib
        gcc -fPIC -shared -o ./lib/lib$@.so $^

clean:
        rm -f main libhello.so libworld.so ./lib/libworld.so

再重新编译main:

root@ubuntu:~/test$ rm main
root@ubuntu:~/test$ make main
gcc -o main main.c -lhello -L. -Wl,-rpath=.:./lib
root@ubuntu:~/test$ readelf -d main | grep runpath
 0x000000000000001d (RUNPATH)            Library runpath: [.:./lib]
root@ubuntu:~/test$ ./main
./main: error while loading shared libraries: libworld.so: cannot open shared object file: No such file or directory
root@ubuntu:~/test$

上图看出,链接成功了,说明在链接时,链接器会到rapth指定的目录里寻找间接依赖库。

但是运行失败,找不到world库,之前说过,运行时只在rpath里搜索直接依赖库而不搜索间接依赖库。world库的运行时搜索目录由hello库的rpath指定。

再尝试在main的链接时rpath-link指定为./lib:

#makefile

main: main.c
        gcc -o $@ $< -lhello -L. -Wl,-rpath=.,-rpath-link=./lib

hello: hello.c
        gcc -fPIC -shared -o lib$@.so $< -lworld -L./lib -Wl,-rpath=.

world: world.c
        mkdir -p ./lib
        gcc -fPIC -shared -o ./lib/lib$@.so $^

clean:
        rm -f main libhello.so libworld.so ./lib/libworld.so

重新编译main:

root@ubuntu:~/test$ make main
gcc -o main main.c -lhello -L. -Wl,-rpath=.,-rpath-link=./lib
root@ubuntu:~/test$ readelf -d main | grep runpath
 0x000000000000001d (RUNPATH)            Library runpath: [.]   #这里没有rpath-link指定的目录
root@ubuntu:~/test$ ./main
./main: error while loading shared libraries: libworld.so: cannot open shared object file: No such file or directory
root@ubuntu:~/test$

然后是链接成功,运行时失败,主程序的runpath里也没有./lib。

可见,rpath-link和rpath都可以用于指定间接依赖库的搜索目录,但rpath还指定了运行时直接依赖库的搜索目录。显然rpath的作用包含了rpath-link的作用,rpath目录在链接阶段间接依赖库的搜索目录,在运行阶段直接依赖库的搜索目录。

那么,rpath-link和rpath在链接阶段还能指定直接依赖库的搜索目录吗?下面把main链接参数改一下,去掉-L.,在rpath和rpath-link里都指定当前目录:

#makefile

main: main.c
        gcc -o $@ $< -lhello -Wl,-rpath=.,-rpath-link=.:./lib

hello: hello.c
        gcc -fPIC -shared -o lib$@.so $< -lworld -L./lib -Wl,-rpath=.

world: world.c
        mkdir -p ./lib
        gcc -fPIC -shared -o ./lib/lib$@.so $^

clean:
        rm -f main libhello.so libworld.so ./lib/libworld.so

重新编译main:

root@ubuntu:~/test$ rm main
root@ubuntu:~/test$ make main
gcc -o main main.c -lhello -Wl,-rpath=.,-rpath-link=.:./lib
/usr/bin/ld: 找不到 -lhello
collect2: error: ld returned 1 exit status
make: *** [makefile:3:main] 错误 1

遗憾,虽然hello库在当前目录,链接器还是找不到hello库,说明rpath-link和rpath在链接阶段确实不能用来搜索直接依赖库。

验证最后一个问题,直接依赖库的rapth的另一个作用。

修改makefile,main的rpath改为当前目录,把hello库的rpath改成./lib,再重新编译hello和main,最后运行main:

#makefile

main: main.c
        gcc -o $@ $< -lhello -L. -Wl,-rpath=.

hello: hello.c
        gcc -fPIC -shared -o lib$@.so $< -lworld -L./lib -Wl,-rpath=./lib

world: world.c
        mkdir -p ./lib
        gcc -fPIC -shared -o ./lib/lib$@.so $^

clean:
        rm -f main libhello.so libworld.so ./lib/libworld.so
root@ubuntu:~/test$ rm main libhello.so
root@ubuntu:~/test$ make hello
gcc -fPIC -shared -o libhello.so hello.c -lworld -L./lib -Wl,-rpath=./lib
root@ubuntu:~/test$ make main 
root@ubuntu:~/test$ ./main
root@ubuntu:~/test$

上图可见main的链接和运行都成功了,显然,由于world库是在./lib目录下,在主程序main链接阶段,通过主程序的rpath是找不到world库,唯一的可能性就是通过直接依赖库hello的rpath指定的目录找到了间接依赖库world。也就是说,直接依赖库的rpath目录也被用来搜索间接依赖库了。

综上,进行总结:

1.  编译生成动态库A时,并不是一定要去链接它的代码层面上的依赖库B,它们可以一起成其它使用者比如main的直接依赖库,这个时候A、B库在依赖关系树上是同层次的;当然也可以在在编译动态库A先链接好B,形成A依赖B的关系,而其它使用者比如main使用它们时,只须链接动态库A,形成main直接依赖A,A直接依赖B,main间接依赖B。这里就有了直接依赖和间接依赖的概念。注:main不一定是一个有main函数的可执行程序,也可以是一个动态库。

2. 链接器ld(gcc\g++在链接阶段调用ld)的-L指定的是链接阶段时的直接依赖库的搜索目录。

3. rpath-link指定了链接阶段所有间接依赖库的搜索目录,由于它没有被硬编码写进生成目标,所以只临时用于当前编译链接目标。比如main->A->B->C,其中->表示依赖关系,在编译链接A时,指定了rpath-link目录,而后编译链接main时,编译链接A时指定的rpath-link不复存在。

4. rpath指定了链接阶段间接依赖库的搜索目录,也指定了运行阶段直接依赖库的搜索目录。rpath被硬编码进了当前生成目标文件中,在依赖关系树上一直存在。当前生成目标main的直接依赖库A里的rapth也是A的直接依赖库B的链接阶段和运行阶段时的索目录之一。

5. 在依赖关系树上,如果各个库的rpath设置得不一样,或者没有设置,在链接阶段还好,通过rpath或者rpath-link统一指定就行,但运行阶段就麻烦了,要想最后整体打包发布,最好就是靠环境变量LD_LIBRARY_PATH或者/etc/ld.so.conf配置的方式了。

最后画个图再说一下rpath:

图中箭头表示依赖关系。

在编译链接A1时,直接依赖库B1只能放在A1的-L指定的./lib1目录中;在编译链接A2时,直接依赖库B2只能放在A2的-L指定的./lib2目录中。

在编译链接main时,直接依赖库A1和A2只能放在main的-L指定的./lib目录中;而间接依赖库B1既可以放在A1的rpath指定的./lib1中,也可以放在main的rpath指定的./lib中;同理,B2既可以在./lib2中,也可以在./lib中。当然了,B1放./lib2或者B2放./lib1中是找不到的。

在main运行时,直接依赖库A1和A2只能放在main的rpath指定的./lib目录中,间接依赖库B1只能放在A1的rpath指定的./lib1目录中,同理B2只能放在./lib2中。

<think>好的,用户问的是关于链接器选项 -Wl,-rpath-link 的介绍。我需要先回忆一下这个选项的作用和用途。记得在编译和链接程序时,动态库的查找路径是一个重要的问题。-rpath-link 是用于指定运行时库的搜索路径,但具体和 -rpath 有什么区别呢? 首先,应该区分链接时和运行时。链接器在链接阶段需要找到依赖的共享库,而运行时加载器(如ld.so)在程序运行时也需要找到这些库。-rpath 通常用于设置运行时库的路径,而 -rpath-link 可能是在链接阶段解决间接依赖的问题。比如,当程序A依赖库B,而库B又依赖库C,这时候链接A的时候,链接器需要知道库C的位置,但可能默认搜索路径中没有,这时候就需要用 -rpath-link 来指定库C的路径,以便链接器能够解析所有符号。 接下来需要确认这个选项的使用方式。因为用户提到了 -Wl,-rpath-link,这说明这是在通过gcc传递选项给链接器ld。所以,正确的用法应该是将选项传递给链接器,比如在gcc命令行中加入 -Wl,-rpath-link,/path/to/libs。需要注意逗号的使用是否正确,或者是否需要空格分隔。例如,-Wl,-rpath-link,/path 或者 -Wl,-rpath-link,/path 是否正确? 另外,需要说明这个选项和 -rpath区别-rpath 会被写入到可执行文件的运行时路径中,影响程序的运行时的库查找,而 -rpath-link 仅在链接阶段起作用,帮助链接器找到间接依赖的库,但不会影响运行时。所以,当链接时存在间接依赖,而运行时这些库的位置可能不同,这时候应该用 -rpath-link 来确保链接成功,同时用其他方式(如LD_LIBRARY_PATH或-rpath)来指定运行时的路径。 还需要注意常见的错误情况。如果链接时缺少必要的库路径,会导致链接失败,提示未定义的引用,这时候可能需要添加 -rpath-link 来指定路径。例如,当链接一个依赖于libfoo.so的可执行文件,而libfoo.so又依赖于libbar.so,但libbar.so不在默认的链接路径中,这时候就需要使用 -rpath-link 来告诉链接器libbar.so的位置。 可能还需要举一个例子,说明如何在实际中使用这个选项。例如,gcc -o myprog main.o -L/path/to/libs -lfoo -Wl,-rpath-link,/path/to/libs。这里的-L指定了链接时查找libfoo.so的路径,而-rpath-link确保链接器能找到libbar.so,如果libfoo依赖它的话。 最后,需要提醒用户,虽然-rpath-link解决了链接时的依赖问题,但运行时可能还需要设置其他选项或环境变量,比如-rpath或者LD_LIBRARY_PATH。此外,需要注意相对路径和绝对路径的问题,确保路径的正确性。 总结一下,回答的结构应该是先解释选项的作用,然后说明使用场景,比较与其他选项的区别,给出使用示例,最后注意事项。这样用户能够逐步理解这个选项的用途和用法。</think>在编译和链接动态库时,`-Wl,-rpath-link` 是一个关键选项,用于解决**间接依赖库的路径查找问题**。以下是逐步说明: --- ### **1. 核心作用** - **链接阶段的路径扩展** 当目标文件依赖库A,而库A又依赖库B时,链接器需要同时找到库A和库B。 `-rpath-link` 会额外指定一个路径,帮助链接器在**链接阶段**找到间接依赖的库(如示例中的库B)。 - **与 `-rpath` 的区别** - `-rpath`:写入可执行文件,直接影响**运行时**的库搜索路径- `-rpath-link`:仅在**链接阶段**生效,不影响运行时行为。 --- ### **2. 使用场景** 当遇到以下链接错误时,需使用 `-rpath-link`: ```bash /usr/bin/ld: warning: libxxx.so, needed by libyyy.so, not found ``` **原因**: 链接器默认搜索路径(如 `/usr/lib`)中缺少间接依赖库(如 `libxxx.so`)。 --- ### **3. 语法格式** 通过 `-Wl` 将参数传递给链接器(`ld`): ```bash gcc ... -Wl,-rpath-link,/path/to/libs ... ``` - `-Wl`:告知编译器将后续参数传给链接- `,`:分隔多个参数(例如同时传递 `-rpath-link` 和路径) --- ### **4. 示例** 假设项目结构: ``` /path/to/libs/ ├── libfoo.so # 直接依赖 └── libbar.so # libfoo.so 依赖的间接库 ``` 编译命令: ```bash gcc -o myprog main.c \ -L/path/to/libs -lfoo \ # 指定直接依赖库路径 -Wl,-rpath-link,/path/to/libs # 指定间接依赖库路径 ``` --- ### **5. 注意事项** - **路径优先级** `-rpath-link` 路径优先级高于默认链接路径,但低于 `-L` 显式指定的路径。 - **运行时路径需单独处理** 若间接依赖库在运行时不在系统默认路径中,仍需通过以下方式指定: - `-Wl,-rpath,/path/to/libs` - 环境变量 `LD_LIBRARY_PATH` - **绝对路径 vs 相对路径** 建议使用**绝对路径**,避免因工作目录变化导致路径失效。 --- ### **总结** `-Wl,-rpath-link` 是解决动态库**间接依赖**问题的关键工具,它扩展了链接器的搜索路径,确保所有依赖关系在编译阶段被正确解析。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值