dyld:库未加载:.....如何正确告诉GCC编译器在哪里可以找到另一个静态库? [英] dyld: Library not loaded: ..... How to correctly tell GCC Compiler where to find another static library?

查看:114
本文介绍了dyld:库未加载:.....如何正确告诉GCC编译器在哪里可以找到另一个静态库?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我编写了C程序,该程序计算波函数的时间步迭代,以求解与时间有关的Schrödinger方程.在某些步骤中,我需要使用库 kissfft 进行正向和反向快速傅立叶变换(FFT).( https://github.com/mborgerding/kissfft )

I wrote C program that calculates the time step iterations of wavefunctions to solve the time depended Schrödinger equation. At some step I need to do forward and backward Fast Fourier Transformations (FFT) for which I used the library kissfft. (https://github.com/mborgerding/kissfft)

我的程序结构如下:

  • TDSE(工作目录)
    • 模块
    • 包括
    • 脚本
    • 测试
      • inttest_analytical.c
      • libkissfft-double.dylib

      现在,当我编译 inttest_analytical.c 时,它可以工作了.但是当以后尝试运行可执行文件时,出现以下错误:

      Now when I compile my inttest_analytical.c it works. But when trying to run the executable afterwards I get the following error:

      (base) user TDSE % ./inttest_analytical
      dyld: Library not loaded: libkissfft-double.dylib
        Referenced from: /Users/user/Documents/Uni/HU Berlin/Computational Physics 2/Project 3 - Time-dependet Schroedinger Equation/TDSE/./inttest_analytical
        Reason: image not found
      zsh: abort      ./inttest_analytical
      

      运行 otool -L ./inttest_analytical 后,我得到

      /inttest_analytical:
              libkissfft-double.dylib (compatibility version 0.0.0, current version 0.0.0)
              /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1281.100.1)
      

      据我在阅读有关Google搜索的其他问题时所了解的, libkissfft-double.dylib 是一个静态库,但我无法告诉gcc在哪里可以找到该库.并且它给出的路径(由编译器还是链接器?)是工作目录TDSE而不是TDSE/kissfft为了进行编译,我运行了:

      As far as I understand from reading about this in other questions on google searches, libkissfft-double.dylib is a static library but I don't manage to tell gcc where to find the library. And the path it was given (by the compiler or linker?) is the working directory TDSE instead of TDSE/kissfft For compilation I ran:

      gcc -g -Wall -fPIC -I include -I kissff ./modules/wavefunction.c ./modules/integrator.c ./modules/geometry.c ./modules/linearalgebra.c ./modules/assert.c ./modules/hamiltonian.c ./modules/conjugategradient.c ./test/inttest_analytical.c -Lkissfft -lkissfft-double -o inttest_analytical
      

      所以我想我在错误使用 -L -l <​​/code>标志?

      So I guess I am using the flags -L and -l wrong??

      感谢您的帮助.

      推荐答案

      我想我将使用这个问题为所有找不到图片"问题写一个规范的答案.问题.

      I guess I'll use this question to write a canonical answer for all "image not found" issues.

      让我们从包含主二进制文件和库的最小设置开始,像这样:

      Let's start with a minimal setup consisting of a main binary and a library, like so:

      main.c :

      #include <stdio.h>
      
      extern int f(void);
      
      int main(void)
      {
          printf("%u\n", f());
          return 0;
      }
      

      xyz.c :

      int f(void)
      {
          return 42;
      }
      

      命令行:

      % cc -Wall -O3 -shared -o libxyz.dylib xyz.c
      % cc -Wall -O3 -o main main.c -L. -lxyz
      

      这有效.您可以运行 ./main ,它将显示 42 .
      但是,如果您现在创建一个文件夹 lib ,则将 libxyz.dylib 移至此处,然后像这样重新编译 main :

      This works. You can run ./main and it will print 42.
      However, if you now create a folder lib, move libxyz.dylib there and recompile main like so:

      % cc -Wall -O3 -o main main.c -Llib -lxyz
      

      然后编译仍然会成功,但是启动它不会:

      Then the compilation will still succeed, however launching it will not:

      % ./main
      dyld: Library not loaded: libxyz.dylib
        Referenced from: /private/tmp/./main
        Reason: image not found
      

      但是,如果您返回并直接将 libxyz.dylib 重新编译到 lib 文件夹,然后重建 main ,如下所示:

      But if you go back and recompile libxyz.dylib to the lib folder directly and then rebuild main, like so:

      % cc -Wall -O3 -shared -o lib/libxyz.dylib xyz.c
      % cc -Wall -O3 -o main main.c -Llib -lxyz
      

      然后它将再次起作用.只是为了说明,这是如果再次移动 libxyz.dylib 会遇到的错误:

      Then it will once again work. But just to illustrate, this is the error you get if you move libxyz.dylib once more:

      % ./main
      dyld: Library not loaded: lib/libxyz.dylib
        Referenced from: /private/tmp/./main
        Reason: image not found
      

      但是,只是使情况更糟,即使不移动库也可能产生相同的错误:只需 cd lib 并调用 ../main .

      But just to make things worse, you can produce the same error without even moving the library: simply cd lib and invoke ../main.

      还请注意与 libxyz.dylib 与以前的区别.这将我们带到了问题的核心.

      Also note the difference to before, libxyz.dylib vs lib/libxyz.dylib. This brings us to the core of the issue.

      在macOS上,每个共享库都有一个安装名称",即在运行时期望在其上找到的路径.此路径可以采用三种形式:

      On macOS, each shared library has an "install name", i.e. the path at which it is expected to be found at runtime. This path can take three forms:

      • 绝对值,例如/usr/lib/libxyz.dylib .
      • 相对,例如 lib/libxyz.dylib .
      • 魔术,例如 @ rpath/libxyz.dylib .

      此路径通过 LC_ID_DYLIB 加载命令嵌入在库的Mach-O标头中.可以使用 otool 进行查看,如下所示:

      This path is embedded in the Mach-O header of the library, via the LC_ID_DYLIB load command. It can be viewed with otool like so:

      % otool -l /tmp/lib/libxyz.dylib | fgrep -B1 -A5 LC_ID_DYLIB
      Load command 2
                cmd LC_ID_DYLIB
            cmdsize 48
               name lib/libxyz.dylib (offset 24)
         time stamp 1 Thu Jan  1 01:00:01 1970
            current version 0.0.0
      compatibility version 0.0.0
      

      此加载命令由链接器创建,链接器的手册页( man ld )告诉我们以下内容:

      This load command is created by the linker, whose man page (man ld) tells us the following:

      -install_name name
              Sets an internal "install path" (LC_ID_DYLIB) in a dynamic library.
              Any clients linked against the library will record that path as the
              way dyld should locate this library. If this option is not specified,
              then the -o path will be used. This option is also called
              -dylib_install_name for compatibility.
      

      这告诉我们安装名称如何工作的三个步骤:

      This tells us the three steps of how install names work:

      1. 链接器在构建库时嵌入名称.
      2. 链接程序将名称复制到二进制文件中,然后在链接到该库时将其复制.
      3. Dyld使用该名称尝试加载该库.

      如果移动了库,或者甚至没有使用与它们最终指向的路径匹配的安装名称来编译库,这显然会引起问题.

      This will obviously cause issues if libraries are moved, or aren't even being compiled with the install name matching the path at which they will end up.

      解决方案是更改安装名称路径.在哪里取决于您的设置.您可以通过两种方式对其进行更改:

      The solution is to change the install name path. Where are how depends on your setup. You can change it by two means:

      1. 使用正确的安装名称( -Wl,-install_name,... 或完全 -o ... )重新编译库,然后重新编译主二进制文件对此进行链接.
      2. 使用 install_name_tool .这涉及更多.
      1. Recompile the library with the correct install name (either -Wl,-install_name,... or outright -o ...), then recompile the main binary to link against that.
      2. Use install_name_tool. This is a bit more involved.

      无论哪种情况,您都需要确定要使用的安装名称形式:

      In either case, you need to decide what form of install name you want to use:

      • 绝对.
        对于所有用户共享的全局路径中的库,建议使用此方法.您还可以使用 指向您的用户目录,但这有点丑陋,因为您无法移动二进制文件或将其分发给其他人.

      • Absolute.
        This is recommended for libraries in global paths, shared by all users. You can also use this to point to your user directory, but it's a bit ugly since you can't move the binaries around or distribute them to someone else.

      相对.
      相对于您的工作目录意味着完全不可靠.永远不要使用这个.只是不要.

      Relative.
      Being relative to your working directory means this is entirely unreliable. Never use this. Just don't.

      魔术.
      存在三个特殊"标记.超出绝对和相对路径的标记:

      Magic.
      There are three "special" tokens that go beyond absolute and relative paths:

      • @executable_path 是进程主二进制文件的运行时目录.这是最简单的形式,但是仅当您的库仅在单个主二进制文件中使用时才有效.
      • @loader_path 是二进制文件的运行时目录,具体取决于库.我建议不要使用此选项,因为如果您要在两个文件夹中链接两个二进制文件并链接到同一个库,则会导致中断.
      • @rpath 是从 LC_RPATH 加载命令汇编的运行时目录的列表.这有点复杂,但这是最灵活的解决方案,因为它本身可以包含 @executable_path @loader_path .
      • @executable_path is the runtime directory of the main binary of the process. This is the simplest form, but only works if your libraries are only used in a single main binary.
      • @loader_path is the runtime directory of the binary depending on the library. I recommend not using this, as it breaks if you have two binaries in different folders that want to link to the same library.
      • @rpath is a list of runtime directories assembled from LC_RPATH load commands. This is a bit more complex, but it's the most flexible solution, since it can itself contain @executable_path and @loader_path.

      使用这些二进制代码可以构建可以自由移动的二进制文件,只要它们都保持相对位置即可.
      有关它们的完整说明,请参见 man dyld .

      Use of those allows you to build binaries that can be moved around freely, so long as they all retain their relative position.
      For a full description of them, see man dyld.

      就此而言,让我们来看一下实现可能的解决方案的方法.我们有:

      With that out of the way, let's look at implementing the possible solutions. We have:

      • cc -Wl,-install_name,... 在编译时指定安装名称.
      • install_name_tool -id ... 更改嵌入在库中的路径.
      • install_name_tool -change old new 来更改嵌入到库的二进制链接中的路径.
      • cc -Wl,-install_name,... to specify an install name at compile time.
      • install_name_tool -id ... to change the path embedded in a library.
      • install_name_tool -change old new to change the path embedded in a binary linking against a library.

      如果您可以重新编译库和主二进制文件:

      If you can recompile both the library and the main binary:

      % cc -Wall -O3 -shared -o /tmp/lib/libxyz.dylib xyz.c
      % cc -Wall -O3 -o main main.c -L/tmp/lib -lxyz
      

      如果您只能重新编译主二进制文件:

      If you can only recompile the main binary:

      % install_name_tool -id '/tmp/lib/libxyz.dylib' /tmp/lib/libxyz.dylib
      % cc -Wall -O3 -o main main.c -L/tmp/lib -lxyz
      

      如果您不能重新编译:

      % install_name_tool -id '/tmp/lib/libxyz.dylib' /tmp/lib/libxyz.dylib
      % install_name_tool -change 'libxyz.dylib' '/tmp/lib/libxyz.dylib' main
      

      3.2 @executable_path

      如果您可以重新编译库和主二进制文件:

      3.2 @executable_path

      If you can recompile both the library and the main binary:

      % cc -Wall -O3 -shared -o lib/libxyz.dylib xyz.c -Wl,-install_name,'@executable_path/lib/libxyz.dylib'
      % cc -Wall -O3 -o main main.c -Llib -lxyz
      

      如果您只能重新编译主二进制文件:

      If you can only recompile the main binary:

      % install_name_tool -id '@executable_path/lib/libxyz.dylib' lib/libxyz.dylib
      % cc -Wall -O3 -o main main.c -Llib -lxyz
      

      如果您不能重新编译:

      % install_name_tool -id '@executable_path/lib/libxyz.dylib' lib/libxyz.dylib
      % install_name_tool -change 'libxyz.dylib' '@executable_path/lib/libxyz.dylib' main
      

      3.3 @rpath

      Rpath需要手动添加运行时路径,这需要进行一些计划.假设您具有以下文件层次结构:

      3.3 @rpath

      Rpath requires manual addition of runtime paths, which requires some planning. Suppose you have the follwing file hierarchy:

      • a
      • bin/
        • b
        • liby.dylib
        • libz.dylib

        a b 是二进制文件,它们都链接到 libx liby ,后者又链接到 libz .对于 libz 的安装名称,您不能使用 @executable_path (因为 a b 位于不同的目录中))或 @loader_path (因为 libx liby 位于不同的目录中).但是您可以 @rpath 中使用它们中的任何一个,这是您必须做出的决定:

        a and b are binaries that both link against libx and liby, which in turn both link against libz. For the install name of libz, you can use neither @executable_path (because a and b are in different directories) nor @loader_path (because libx and liby are in different directories). But you can use either of them inside @rpath, and here is the decision you have to make:

        • 您可以在 a 中嵌入 @executable_path 的rpath,并在 b 中嵌入 @executable_path/...然后,您可以使用 @rpath 从所有二进制文件引用项目根目录. libz 的安装名称为 @ rpath/lib/libz.dylib .
        • 或者您可以在 libx 中嵌入 @ loader_path/lib 的rpath,并在 liby 中嵌入 @loader_path .然后,您可以使用 @rpath 引用包含每个二进制文件的目录. libz 的安装名称为 @ rpath/libz.dylib .
        • You can either embed an rpath of @executable_path in a and @executable_path/.. in b. Then you can use @rpath to refer to the project root from all binaries. libz would have an install name of @rpath/lib/libz.dylib.
        • Or you can embed an rpath of @loader_path/lib in libx and @loader_path in liby. Then you can use @rpath to refer to the directory containing each binary. libz would have an install name of @rpath/libz.dylib.

        我通常发现前者更容易处理,但是如果您有大量的二进制文件散布在许多目录中并且只有几个库,则后者可能是更可取的.

        I generally find the former to be easier to deal with, but the latter may be preferable if you have a large number of binaries scattered over many directories and only a few libraries.

        要将rpath实际添加到二进制文件中,可以使用:

        To actually add an rpath to a binary, you can use:

        • cc -Wl,-rpath,... 在编译时.
        • 然后
        • install_name_tool -add_rpath ... .
        • cc -Wl,-rpath,... at compile time.
        • install_name_tool -add_rpath ... afterwards.

        因此,如果您可以重新编译库和主二进制文件:

        So if you can recompile both the library and the main binary:

        % cc -Wall -O3 -shared -o lib/libxyz.dylib xyz.c -Wl,-install_name,'@rpath/lib/libxyz.dylib'
        % cc -Wall -O3 -o main main.c -Llib -lxyz -Wl,-rpath,'@executable_path'
        

        如果您只能重新编译主二进制文件:

        If you can only recompile the main binary:

        % install_name_tool -id '@rpath/lib/libxyz.dylib' lib/libxyz.dylib
        % cc -Wall -O3 -o main main.c -Llib -lxyz -Wl,-rpath,'@executable_path'
        

        如果您不能重新编译:

        % install_name_tool -id '@rpath/lib/libxyz.dylib' lib/libxyz.dylib
        % install_name_tool -change 'libxyz.dylib' '@rpath/lib/libxyz.dylib' main
        % install_name_tool -add_rpath '@executable_path' main
        

        请注意,如果您的任何二进制文件已签名,则这当然会使该签名无效.使用 codesign -f ... 替换现有签名.

        Note that if any of your binaries are signed, this will of course invalidate that signature. Use codesign -f ... to replace the existing signature(s).

        这篇关于dyld:库未加载:.....如何正确告诉GCC编译器在哪里可以找到另一个静态库?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆