zoukankan      html  css  js  c++  java
  • Linking and Install Names

    最近移植一个工程的安装路径,原始的安装路径在/usr/local/lib目录下,现在要移植到/usr/local/watchdata/目录下,具体的环境是这样的:

    一个/Application/app1.app 使用dlopen调用/usr/local/watchdata/dylib1.dylib,而dylib1.dylib静态加载/usr/local/watchdata/ libMKGenarateAlg.dylib.dylib,最后加载的时候发下下面的问题:

    Library not loaded: /usr/local/watchdata/dylib1.dylib

    Referenced from: /Application/app1.app

    Reason: image not found

    经分析,原因是dylib1.dylib找不到libMKGenarateAlg.dylib.dylib,而这两个动态库在同一个目录下了,other link flag中也指定了加载libMKGenarateAlg.dylib.dylib的路径为/usr/local/watchdata/dylib1.dylib,为什么还加载不上呢?最后从网上找错误相关的信息,发现mac上加载动态库是依照install name来加载的。下面是install name 相关的资料,我翻译了下。

    Install name
    The answer to that question varies greatly from one OS to another, but on the Mac, the answer is install names.

    An install name is just a pathname embedded within a dynamic library which tells the linker where that library can be found at runtime. For example, libfoo.dylib might have an install name of/usr/lib/libfoo.dylib. This install name gets copied into the application at link time. When the dynamic linker goes looking for libfoo.dylib at runtime, it will fetch the install name out of the application and know to look for the library in /usr/lib.

    Frameworks are just dynamic libraries with a funny wrapper, so they work the same way. Foo.frameworkmight have an install name of /Library/Frameworks/Foo.framework/Versions/A/Foo, and that's where the dynamic linker will search for it.

    @executable_path

    Absolute paths are annoying. Sometimes you want to embed a framework into an application instead of having to install the framework into /Library or a similar location.

    The Mac's solution to this is @executable_path. This is a magic token that, when placed at the beginning of a library's install name, gets expanded to the path of the executable that's loading it, minus the last component. For example, let's say that Bar.app links against Foo.framework. If Bar.app is installed in /Applications, @executable_path will expand to /Applications/Bar.app/Contents/MacOS. If you intend to embed the framework in Contents/Frameworks, then you can just set Foo.framework's install name to @executable_path/../Frameworks/Foo.framework/Versions/A/Foo. The dynamic linker will expand that to /Applications/Bar.app/Contents/MacOS/../Frameworks/Foo.framework/Versions/A/Foo and will find the framework there.

    @loader_path

    Finding the executable isn't always good enough. Imagine that you ship a plugin or a framework which embeds another framework. Say, Foo.framework embeds Baz.framework. Even though Foo.framework is the one requesting the load, the dynamic linker will still go off of Bar.app's location when figuring out what@executable_path refers to, and this won't work right.

    Starting in 10.4, Apple provided @loader_path which does what you want here. It expands to the full path, minus the last component, of whatever is actually causing the target library to be loaded. If it's an application, then it's the same as @executable_path. If it's a framework or plugin, though, then it's relative to that framework or plugin, which is much more useful.

    @rpath

    While the above is sufficient for anything in theory, it can be troublesome in practice. The problem is that a single copy of a library can only be used in one way. If you want Foo.framework to work when embedded in an application or when installed to/Library/Frameworks, you have to provide two separate copies with two different install names. (Or manually tweak install names later on using install_name_tool.) This is doable, but annoying.

    Starting in 10.5, Apple provides @rpath, which is a solution to this. When placed at the front of an install name, this asks the dynamic linker to search a list of locations for the library. That list is embedded in the application, and can therefore be controlled by the application's build process, not the framework's. A single copy of a framework can thus work for multiple purposes.

    To make this work, Foo.framework's install name would be set to @rpath/Foo.framework/Versions/A/Foo. An application that intends to embed Foo.framework would then pass -rpath @executable_path/../Frameworks to the linker at build time, which tells the dynamic linker to search for @rpath frameworks there. An application that for some reason doesn't want to commit to one or the other at build time can just pass both sets of parameters, which will cause the dynamic linker to try both locations.

    安装名称

    这个问题的答案差别很大从一个操作系统,但在Mac上,答案是安装名称。

    安装的名字是嵌入在一个动态库,它告诉链接器,可以在运行时发现动态库的路径名。例如,libfoo.dylib的可能有一个安装在/ usr/ lib/ libfoo.dylib中的名称。此安装名称被复制到应用程序在链接时。当动态连接器去寻找libfoo.dylib在运行时,它会读取安装名称的应用程序,并知道在/ usr / lib中的库。

    框架是动态库有一个有趣的包装,所以他们的工作方式相同。 /Library/Frameworks/Foo.framework/Versions/A/Foo,这就是动态连接器将寻找它。

    @ executable_path

    绝对路径是烦人。有时你想一个framework 嵌入到应用程序中,而不必将该framework 安装到/Library 或类似的目录。

    Mac的解决方案,这是:@ executable_path。这是一个神奇的符号,当放置在库的install name开始处时,获得加载库的应用程序的扩展路径,并去掉路径中的应用程序的名称。例如,Bar.app动态加载Foo.framework,如果Bar.app安装在/Applications目录下,@executable_path将被扩展为:/Applications/Bar.app/Contents/MacOS。

    如果你打算将framework嵌入到Contents/Frameworks中,那么你只需要将Foo.framework的 install name设置为:@executable_path/../Frameworks/Foo.framework/Versions/A/Foo,动态链接器,将扩展这个路径为:/Applications/Bar.app/Contents/MacOS/../Frameworks/Foo.framework/Versions/A/Foo,并可以将这个在这个路径下加载这个framework。

    @ loader_path

    查找可执行文件一般来说是不够好。试想一下,你运行一个插件或者一个framework,这个插件或framework会嵌入另一个framework。假如说,Foo.framework会嵌入Baz.framework。当​​Foo.framework被加载的时候,动态连接器将将不再指向Bar.app所在的位置,这时使用@ executable_path去加载Baz.framework就不能正确加载。

    从10.4开始,苹果@ loader_path,这个符号可以解决上面你想要解决的问题。他会依据加载它的目标去扩展路径,并去掉目标的名称。如果它是一个应用程序,那么它的一样@ executable_path。但是,如果它是一个framework或插件,它的路径会依赖framework架或插件的路径,这是更为有用。

    @ rpath

    虽然以上在理论上是足够的了,但在在实践中可能还会用麻烦。麻烦的问题是,一个库的单个副本只能用于以一种方式去加载它。如果你想Foo.framework能正常工作当嵌入到应用程序或安装到/Library/Frameworks中,你必须提供两个单独的副本,这个副本过分别具有不同的的install names。 (或手动调整install name或使用install_name_tool。)这是可行的,但恼人的。

    从10.5开始,苹果公司提供了@ rpath参数,这个参数可以有效的解决这个问题。将这个参数放置在install name的前面,当这就要求动态链接连接器在一个列表中查找动态库。这个列表嵌入到应用程序,并且在应用程序构建的时候可以被控制,而不像framework的一个副本过,可以用于多种用途。

    为了使这项工作,Foo.framework的install name将被设置为@rpath/Foo.framework/Versions/A/Foo。一个应用程序想要嵌入Foo.framework的应用程序只需要在构建的时候,向编译器传递一个参数:-rpath @executable_path/../Frameworks。它告诉动态连接器搜索@rpath frameworks这个路径。通过-rpath的打算安装的应用程序的框架/库/框架,告诉动态连接器搜索。应用程序由于某种原因不希望在构建时承诺或其他可以通过这两组参数,这将导致动态链接器尝试这两个位置。

    最后根据上面的理论,使用install_name_tool 工具将libMKGenarateAlg.dylib.dylib 的install name /usr/local/watchdata /Watchdata CCB CSP v3.2/libMKGenarateAlg.dylib

    clip_image002

    修改后使用otool 命令查看的结果:

    clip_image004

    这个问题得以决绝,在网上查找还有下面的方法,大家可以试试,我没有试的原因是我没有libMKGenarateAlg.dylib的源码,如果了源码也可以尝试下面的方法。

    Using @rpath: Why and How

    By Dave on November 15, 2009 8:54 PM | Permalink

    Last week, Mike Ash wrote a post describing the different install name keywords recognized by the Mac OS X dynamic linker: @executable_path, @loader_path, and @rpath. I wanted to chime in with a bit of advice: if at all possible use @rpath.

    This gist is, if you’re targeting 10.5 or later, use @rpath. There’s no reason I can think of to still use@loader_path. If you’re still on 10.4, use @loader_path. And let’s hope that you’re still not targeting 10.3 and earlier, so forget that @executable_path ever existed. There’s really never a good reason to use@executable_path on 10.4 and later.

    As Mike wrote, @rpath is the most flexible of the three options. The big benefit that I see is that the same binary of a framework or dynamic library can be used embedded in a app bundle, by a command line tool, or put in ~/Library/Frameworks/. Basically, it allows you to use the framework wherever you want by putting the onus on the linking app or bundle to define where to find it.

    The only major downside is that @rpath requires 10.5 or later; however, with 10.6 already shipping, there’s increasingly fewer reasons to support anything earlier than 10.5.

    So how do you actually use @rpath? Ideally, I think that targets should be setup to use @rpath out of the box (see rdar://7396127), but unfortunately it takes a bit of legwork.

    As of Xcode 3.x, you set the Installation Directory build setting of the framework (or library) target to just@rpath:

    clip_image002

    This will set the install name to something like this for frameworks:

    @rpath/SpiffyKit.framework/Versions/A/SpiffyKit

    And something like this for dynamic libraries:

    @rpath/libspiffy.dylib

    The linking application now needs to define what the @rpath expands out to. For any bundle, such as an application, framework, or plugin, you’d add @loader_path/../Frameworks to the Runtime Search Pathsbuild setting to find embedded frameworks:

    Note that you’re still using @loader_path here,as it’s still useful to find the framework relative to the actual binary.

    If you want to embed dynamic libraries, it’s probably a good idea to put them in their own directory, sayLibraries along side the Frameworks directory in the bundle. If you do thiclip_image004s, add@loader_path/../Libraries to the Runtime Search Paths build setting, too. Remember, you can have more than one Runtime Search Path.

    I’ve created a sample app project with an embedded framework project, both setup to use @rpath, on BitBucket called rpath-demo

    % hg clone http://bitbucket.org/ddribin/rpath-demo/

    It should compile and run on 10.5 and 10.6 using Xcode 3.0 or newer.

  • 相关阅读:
    23种设计模式之单例模式
    Java基础之IO技术(一)
    spark利用yarn提交任务报:YARN application has exited unexpectedly with state UNDEFINED
    linux配置了dns后导致mysql远程连接慢问题
    发布网站后localhost可以访问ip不行
    electron编译sqlite3
    vscode文件树缩进
    js判断浏览器类型
    vue可复用slide动画
    vscode使用formate格式化less遇到的坑
  • 原文地址:https://www.cnblogs.com/watchdatalearn2012620/p/3012003.html
Copyright © 2011-2022 走看看