foo.c

#include <stdio.h>

void foo(void)
{
printf("foo.\n");
}

main.c

#include <stdio.h>

extern void foo(void);

int main(void)
{
foo();
printf("main.\n");
return ;
}

编译执行


$ gcc -dynamiclib foo.c -o libfoo.dylib
(-dynamiclib 表示将foo.c编译成一个动态库,
-o libfoo.dylib 用于指定生成的动态库的名称) $ gcc main.c -L. -lfoo -o main
(-L. 指定当前目录为链接时动态库的查找目录,
-lfoo 指定要链接的动态库为libfoo.dylib,
-o main 指定生成的可执行文件名称为main) $ ./main
foo.
main.
(在当前目录下执行main,注意要加上 ./)

两种引用


动态库的引用分为链接时引用与运行时引用两种情况。 -L. -lfoo 表示在链接时引用当前目录下的libfoo.dylib,
如果我们把libfoo.dylib移到上一层目录:
$ mv libfoo.dylib ..
这时再次链接:
$ gcc main.c -L. -lfoo -o main
ld: library not found for -lfoo
clang: error: linker command failed with exit code 1 (use -v to see invocation)
会提示出错,再将动态库移回:
$ mv ../libfoo.dylib .
再次链接:
$ gcc main.c -L. -lfoo -o main
执行成功。 在执行上述链接过程时,编译器顺便将动态库libfoo.dylib的install_name记录到了main程序中,
用来在运行时查找并引用该动态库,
查看动态库libfoo.dylib的install_name,运行:
$ otool -D libfoo.dylib
libfoo.dylib:
libfoo.dylib
显示出libfoo.dylib的install_name是libfoo.dylib,
查看一下main程序中记录的install_name是否与此一致:
$ otool -L main
main:
libfoo.dylib (compatibility version 0.0.0, current version 0.0.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1213.0.0)
第一条就是动态库libfoo.dylib对应的install_name,显然两者是一致的,main程序在运行时会根据记录的这些install_name在文件系统中查找所依赖的动态库。

关于install_name


(注意:只有动态库才有install_name,应用程序如果引用了某一个动态库,则会在链接时记录该动态库的install_name。) 在链接时,是可以为动态库指定一个install_name的:
$ gcc -dynamiclib foo.c -install_name aaa -o libfoo.dylib
将该动态库的install_name指定为aaa了,如不放心,可以查看一下:
$ otool -D libfoo.dylib
libfoo.dylib:
aaa
确实为aaa。(注意:如不指定,则会有一个缺省值,如libfoo.dylib) 重新编译main程序:
$ gcc main.c -L. -lfoo -o main
查看一下main程序中记录的install_name:
$ otool -L main
main:
aaa (compatibility version 0.0.0, current version 0.0.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1213.0.0)
果然是aaa。这时再次运行main程序:
$ ./main
dyld: Library not loaded: aaa
Referenced from: /private/tmp/dylib/./main
Reason: image not found
Trace/BPT trap: 5
出错了,提示找不到动态库aaa,这是因为main程序把aaa当成一个路径,而当前路径下确实没有叫aaa的动态库,
我们可以把libfoo.dylib改名为aaa:
$ mv libfoo.dylib aaa
再次运行:
$ ./main
foo.
main.
正常。

install_name_tool


顾名思义,install_name_tool就是用来操作install_name的工具,
它既可以改变动态库自身的install_name,也可以改变应用程序中记录的所引用动态库的install_name。 首先将动态库名称由aaa改为bbb:
$ mv aaa bbb
查看一下动态库的install_name:
$ otool -D bbb
bbb:
aaa
还是之前指定的aaa。
现在我们利用install_name_tool把动态库的install_name也改为bbb:
$ install_name_tool -id bbb bbb
(前一个bbb表示我们指定的install_name,后一个bbb表示动态库文件名)
查看一下修改之后的结果:
$ otool -D bbb
bbb:
bbb
果然改成了bbb。 这时再查看一下main程序中记录的install_name:
$ otool -L main
main:
aaa (compatibility version 0.0.0, current version 0.0.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1213.0.0)
还是之前的aaa,显然如果这时运行main程序还是会失败,因为当前路径下没有叫aaa的动态库文件,
下面我们就将main程序中记录的aaa改为bbb:
$ install_name_tool -change aaa bbb main
(aaa是旧的名称,bbb是新的名称,main表示要操作的文件名称)
查看一下结果:
$ otool -L main
main:
bbb (compatibility version 0.0.0, current version 0.0.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1213.0.0)
修改成功,这时再运行一下main程序:
$ ./main
foo.
main.
运行成功。 现在动态库bbb是和main程序在同一个目录,如果把bbb移到/tmp目录:
$ mv bbb /tmp
我们只需要将main程序中记录的bbb改为/tmp/bbb:
$ install_name_tool -change bbb /tmp/bbb main
查看一下结果:
$ otool -L main
main:
/tmp/bbb (compatibility version 0.0.0, current version 0.0.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1213.0.0)
这样也是可以运行的:
$ ./main
foo.
main. 相对路径也是可以的,现在把tmp目录下的bbb移到当前路径下的temp目录:
$ mkdir temp
$ mv /tmp/bbb temp/
我们只需要将main程序中记录的/tmp/bbb改为./temp/bbb:
$ install_name_tool -change /tmp/bbb ./temp/bbb main
查看一下结果:
$ otool -L main
main:
./temp/bbb (compatibility version 0.0.0, current version 0.0.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1213.0.0)
这样也是可以运行的:
$ ./main
foo.
main.

@executable_path/ @loader_path/ @rpath/


这三个path是用来表示路径的变量,它们可以用于install_name中,如:
@executable_path/../Frameworks/libAAA.dylib
@loader_path/libBBB.dylib
@rpath/libCCC.dylib
(注意:在使用这三个变量时,后面的“/”不能少。)

@executable_path/


.
├── Frameworks
│   ├── bar.c
│   └── foo.c
└── MacOS
└── main.c MacOS/main.c #include <stdio.h> extern void foo(void);
extern void bar(void); int main(void)
{
foo();
bar();
printf("main.\n");
return 0;
} Frameworks/foo.c #include <stdio.h> void foo(void)
{
printf("foo.\n");
} Frameworks/bar.c #include <stdio.h> void bar(void)
{
printf("bar.\n");
} 先把foo.c和bar.c做成动态库:
$ gcc foo.c -dynamiclib -install_name @executable_path/../Frameworks/libfoo.dylib -o libfoo.dylib
$ gcc bar.c -dynamiclib -install_name @executable_path/../Frameworks/libbar.dylib -o libbar.dylib 然后利用两个动态库编译main.c:
$ gcc main.c -L../Frameworks -lfoo -lbar -o main 查看一下main程序引用的动态库:
$ otool -L main
main:
@executable_path/../Frameworks/libfoo.dylib (compatibility version 0.0.0, current version 0.0.0)
@executable_path/../Frameworks/libbar.dylib (compatibility version 0.0.0, current version 0.0.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1213.0.0)
@executable_path/在运行时会替换成当前程序(也就是main程序)所在路径,
我们之前做的动态库文件libfoo.dylib、libbar.dylib正好存在于Frameworks目录下,所以该程序可以运行:
$ ./main
foo.
bar.
main.

@loader_path/


.
├── A
│   ├── B
│   │   └── bar.c
│   └── foo.c
└── main.c main.c #include <stdio.h> extern void foo(void); int main(void)
{
foo();
printf("main.\n");
return 0;
} A/foo.c #include <stdio.h> extern void bar(void); void foo(void)
{
bar();
printf("foo.\n");
} A/B/bar.c #include <stdio.h> void bar(void)
{
printf("bar.\n");
} 先将bar.c编译成动态库:
$ gcc bar.c -dynamiclib -install_name @loader_path/B/libbar.dylib -o libbar.dylib 再将foo.c编译成动态库(用到了libbar.dylib):
$ gcc foo.c -dynamiclib -LB -lbar -install_name @executable_path/A/libfoo.dylib -o libfoo.dylib 最后再编译main程序(用到了libfoo.dylib):
$ gcc main.c -LA -lfoo -o main 执行:
$ ./main
bar.
foo.
main. main程序依赖于libfoo.dylib,@executable_path/就是main程序的执行路径,
所以libfoo.dylib的install_name设置为@executable_path/A/libfoo.dylib正合适。 libfoo.dylib依赖于libbar.dylib,@loader_path/就是libfoo.dylib的加载路径,
所以libbar.dylib的install_name设置为@loader_path/B/libbar.dylib正合适。
(如果用@executable_path/,则可设置为@executable_path/A/B/libbar.dylib)

@rpath/


一个应用程序只有一个@executable_path/,可能有多个@loader_path/。
@executable_path/表示应用程序执行时路径,@loader_path/表示动态库(包括应用程序)加载时路径,
程序执行时,这两个路径由系统赋值。然而@rpath/则是由用户随意赋值,比前者更为灵活。 .
├── A
├── B
├── C
├── foo.c
└── main.c main.c #include <stdio.h> extern void foo(void); int main(void)
{
foo();
printf("main.\n");
return 0;
} foo.c #include <stdio.h> void foo(void)
{
printf("foo.\n");
} 首先编译动态库,把@rpath/加入到install_name中:
$ gcc foo.c -dynamiclib -install_name @rpath/libfoo.dylib -o libfoo.dylib 然后编译应用程序,并添加自定义rpath
$ gcc main.c -L. -lfoo -Wl,-rpath,@loader_path/A/ -Wl,-rpath,@loader_path/B/ -Wl,-rpath,@loader_path/C/ -o main 我们可以用命令查看一下这三个rpath:
$ otool -l main | grep path
name @rpath/libfoo.dylib (offset 24)
path @loader_path/A/ (offset 12)
path @loader_path/B/ (offset 12)
path @loader_path/C/ (offset 12)
第一行name表示main程序记录的动态库的install_name,后面三个path组成了rpath列表,
运行时@rpath/就被依次替换成了@loader_path/A/ 、@loader_path/B/ 、 @loader_path/C/,
@loader_path/又最终被替换成了动态库(或应用程序)的加载路径。 将libfoo.dylib分别移动到A、B、C三个目录,测试main程序是否可以正常运行:
$ mv libfoo.dylib A/
$ ./main
foo.
main.
$ mv A/libfoo.dylib B/
$ ./main
foo.
main.
$ mv B/libfoo.dylib C/
$ ./main
foo.
main.

Edit By MaHua

OS X 下动态库的引用的更多相关文章

  1. c++动态库封装及调用(2、windows下动态库创建)

    DLL即动态链接库(Dynamic-Link Libaray)的缩写,相当于Linux下的共享对象.Windows系统中大量采用了DLL机制,甚至内核的结构很大程度依赖与DLL机制.Windows下的 ...

  2. Linux下动态库查找路径的问题

    说到和动态库查找路径相关的问题,总体上可以分为两类:    第一类: 通过源代码编译程序时出现的找不到某个依赖包的问题,而如果此时你恰好已经按照它的要求确确实实.千真万确.天地良心地把依赖库给装好了, ...

  3. 谈谈Linux下动态库查找路径的问题 ldconfig LD_LIBRARY_PATH PKG_CONFIG_PATH

    谈谈Linux下动态库查找路径的问题 ldconfig LD_LIBRARY_PATH  PKG_CONFIG_PATH 转载自:http://blog.chinaunix.net/xmlrpc.ph ...

  4. 谈谈Linux下动态库查找路径的问题

    学习到了一个阶段之后,就需要不断的总结.沉淀.清零,然后才能继续"上路".回想起自己当年刚接触Linux时,不管是用源码包编译程序,还是程序运行时出现的和动态库的各种恩恩怨怨,心里 ...

  5. Linux下动态库生成和使用

    Linux下动态库生成和使用 一.动态库的基本概念 1.动态链接库是程序运行时加载的库,当动态链接库正确安装后,所有的程序都可以使用动态库来运行程序.动态链接库是目标文件的集合,目标文件在动态链接库中 ...

  6. Windows下动态库的编译以及调用

    1.MFC下生成动态库 1>显式调用 在.cpp文件里添加接口函数 int sum(int a,int b) { return a + b; } int sub(int a,int b) { r ...

  7. [转]谈谈Linux下动态库查找路径的问题

    http://blog.chinaunix.net/uid-23069658-id-4028681.html 学习到了一个阶段之后,就需要不断的总结.沉淀.清零,然后才能继续“上路”.回想起自己当年刚 ...

  8. 深入理解LINUX下动态库链接器/加载器ld-linux.so.2

    [ld-linux-x86-64.so.2] 最近在Linux 环境下开发,搞了好几天 Compiler 和 linker,觉得有必要来写一篇关于Linux环境下 ld.so的文章了,google上搜 ...

  9. 转:谈谈Linux下动态库查找路径的问题

    http://blog.chinaunix.net/uid-23069658-id-4028681.html 学习到了一个阶段之后,就需要不断的总结.沉淀.清零,然后才能继续“上路”.回想起自己当年刚 ...

随机推荐

  1. 【CF913G】Power Substring 数论+原根

    [CF913G]Power Substring 题意:T组询问,每次给定一个数a,让你求一个k,满足$2^k$的10进制的后$min(100,length(k))$位包含a作为它的子串.你只需要输出一 ...

  2. Edge Animate初篇教程(二)

    Edge Animate 是Adobe最新出品的制作HTML5动画的可视化工具,简单的可以理解为HTML5版本的Flash Pro.在之后的文章中,我会逐一的介绍这款新的HTML5动画神器. 一.创建 ...

  3. thinkphp---部署在IIS8.0服务器上

    最近做了一个项目,使用的是我自己基于thinkphp开发的一套CMS,由于我本地使用的都是apche的环境,即使是线上环境用的也是宝塔面板,但是现在要将thinkphp的系统部署在IIS8.0的环境下 ...

  4. Web前端编码规范[转]

    先插入一条广告,博主新开了一家淘宝店,经营自己纯手工做的发饰,新店开业,只为信誉!需要的亲们可以光顾一下!谢谢大家的支持!店名: 小鱼尼莫手工饰品店经营: 发饰.头花.发夹.耳环等(手工制作)网店: ...

  5. pandas numpy处理缺失值,none与nan比较

    原文链接:https://junjiecai.github.io/posts/2016/Oct/20/none_vs_nan/ 建议从这里下载这篇文章对应的.ipynb文件和相关资源.这样你就能在Ju ...

  6. Java工程师之SpringBoot系列教程前言&目录

    前言 与时俱进是每一个程序员都应该有的意识,当一个Java程序员在当代步遍布的时候,你就行该想到我能多学点什么.可观的是后端的框架是稳定的,它们能够维持更久的时间在应用中,而不用担心技术的更新换代.但 ...

  7. SSH教程从零打造在线网盘系统前言&目录

    本系列教程内容提要 本系列教程是一个学习教程,是关于Java工程师的SSH(Struts2+Spring+Hibernate)系列教程,本教程将会分为四个部分和大家一同打造一个在线网盘系统,由于教程是 ...

  8. Ubuntu下更改Vim配置文件打造C/C++风格

    转载:Ubuntu下更改Vim配置文件打造C/C++风格 Vim默认的配置使用起来还不能让人满意,还需要自己配置 默认配置文件是:/etc/vim/vimrc我们可以在家目录下建立自己的配置文件切换到 ...

  9. SQL中的四种语言DDL,DML,DCL,TCL

    1.DDL(Data Definition Language)数据库定义语言statements are used to define the database structure or schema ...

  10. MTD 移动目标防御技术

    移动目标防御技术,主要包括系统随机化,生物启发MTD,网络随机化,云MTD,动态编译等等.研讨会还就威胁建模和量化移动目标防御技术的效能评估进行了推进.理论和定量的模型对于该技术的颠覆性影响至关重要. ...