cpp:usr/bin/ld:找不到-l< nameOfTheLibrary> [英] cpp: usr/bin/ld: cannot find -l<nameOfTheLibrary>

查看:56
本文介绍了cpp:usr/bin/ld:找不到-l< nameOfTheLibrary>的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我创建了一个cpp项目,该项目使用一个名为以下文件的lib文件: libblpapi3_64.so 该文件来自我从Internet下载的库.

I created a cpp project, which used a lib file named: libblpapi3_64.so This file comes from a library which I download it from Internet.

我的项目运行没有任何错误.因此,我将其更新为bitbucket.然后,我的同事下载它并在自己的计算机上运行它.但是他得到了一个错误:

My project runs without any error. So I update it to bitbucket. Then my colleague downloads it and runs it at his own computer. But he gets an error:

usr/bin/ld:找不到-lblpapi3_64 .

实际上,我已将其复制到我的项目存储库中.我的意思是我在项目下创建了一个名为lib的文件,并且我使用的所有lib文件都在其中.

In fact, I have copied it into my project repository. I mean I created a file named lib under my project and all lib files that I used are in it.

还有其他lib文件,例如 liblog4cpp.a ,但它们都很好.只有 libblpapi3_64.so 会收到错误.

There are also other lib files such as liblog4cpp.a, but they are all good. Only the libblpapi3_64.so gets the error.

是因为它是.so文件而不是 .a 文件?还是有其他原因?
顺便说一句, libblpapi3_64.so 的文件名是 green ,其他文件(.a)的文件名是 white .我认为这不是链接文件,而是原始文件.

Is it because it's a .so file not .a file? Or there is other reason?
Btw, the file name of libblpapi3_64.so is green and others files(.a) is white. I think it's not a link file, it's the original file.

推荐答案

简而言之:

ld 不知道项目库位于何处.您必须将其放置在ld的已知目录中,或通过链接器的 -L 参数指定库的完整路径.

ld does not know about where your project libs are located. You have to place it into ld's known directories or specify the full path of your library by -L parameter to the linker.

要构建程序,您需要使库位于/bin/ld 搜索路径中,并且您的同事也必须位于其中.为什么?查看详细答案.

To be able to build your program you need to have your library in /bin/ld search paths and your colleague too. Why? See detailed answer.

详细信息:

首先,我们应该了解哪些工具可以做什么:

At first, we should understand what tools do what:

  1. 编译器会生成带有未解析符号的简单目标文件(它在运行时不太在乎符号).
  2. 链接器将许多 object archive文件组合在一起,重新定位它们的数据并将符号引用绑定到一个文件中:可执行文件或库.
  1. The compiler produces simple object files with unresolved symbols (it does not care about symbols so much at it's running time).
  2. The linker combines a number of object and archive files, relocates their data and ties up symbol references into a single file: an executable or a library.

让我们从一个例子开始.例如,您有一个包含3个文件的项目: main.c func.h func.c .

Let's start with some example. For example, you have a project which consists of 3 files: main.c, func.h and func.c.

main.c

#include "func.h"
int main() {
    func();
    return 0;
}

func.h

void func();

func.c

#include "func.h"
void func() { }

因此,当您将源代码( main.c )编译为目标文件( main.o )时,由于尚未解析,因此它尚无法运行符号.让我们从生成可执行文件工作流程(无详细信息)的开头开始:

So, when you compile your source code (main.c) into an object file (main.o) it can't be run yet because it has unresolved symbols. Let's start from the beginning of producing an executable workflow (without details):

预处理器在工作后会产生以下 main.c.preprocessed :

The preprocessor after its job produces the following main.c.preprocessed:

void func();
int main() {
    func();
    return 0;
}

和以下 func.c.preprocessed :

void func();
void func() { }

正如您在 main.c.preprocessed 中看到的那样,没有连接到 func.c 文件和 void func()code>的实现,编译器根本不了解它,它会分别编译所有源文件.因此,要编译该项目,您必须使用 cc -c main.c -o main.o cc -c func.c -o之类的文件来编译两个源文件.func.o ,这将生成2个目标文件,分别是 main.o func.o . func.o 的所有符号都已解析,因为它只有一个函数,该函数的主体直接写在 func.c 内部,而 main.o 确实可以尚未解析 func 符号,因为它不知道在哪里实现.

As you may see in main.c.preprocessed, there are no connections to your func.c file and to the void func()'s implementation, the compiler simply does not know about it, it compiles all the source files separately. So, to be able to compile this project you have to compile both source files by using something like cc -c main.c -o main.o and cc -c func.c -o func.o, this will produce 2 object files, main.o and func.o. func.o has all it's symbols resolved because it has only one function which body is written right inside the func.c but main.o does not have func symbol resolved yet because it does not know where it is implemented.

让我们看一下 func.o 中的内容:

Let's look what is inside func.o:

$ nm func.o
0000000000000000 T func

简单来说,它包含一个位于文本代码部分的符号,因此这是我们的 func 函数.

Simply, it contains a symbol which is in text code section so this is our func function.

让我们看一下 main.o :

$ nm main.o
                 U func
0000000000000000 T main

我们的 main.o 具有已实现并已解析的静态函数 main ,我们可以在目标文件中看到它.但是我们还会看到 func 符号,它标记为未解析的 U ,因此我们无法看到其地址偏移量.

Our main.o has an implemented and resolved static function main and we are able to see it in the object file. But we also see func symbol which marked as unresolved U, and thus we are unable to see its address offset.

要解决该问题,我们必须使用链接器.它将获取所有目标文件并解析所有这些符号(在我们的示例中为 void func(); ).如果链接器因某种原因无法执行此操作,则会抛出诸如 unresolved external symbol : void func()之类的错误.如果不将 func.o 对象文件提供给链接器,则可能会发生这种情况.因此,让我们将所有需要的目标文件提供给链接器:

For fixing that problem, we have to use the linker. It will take all the object files and resolve all these symbols (void func(); in our example). If the linker somehow is unable to do that it throws a error like unresolved external symbol: void func(). This may happen if you don't give the func.o object file to the linker. So, let's give all the object files we have to the linker:

ld main.o func.o -o test

链接器将通过 main.o ,然后通过 func.o ,尝试解析符号,如果可以的话-将其输出到测试文件.如果我们查看生成的输出,我们将看到所有符号都已解析:

The linker will go through main.o, then through func.o, try to resolve symbols and if it goes okay - put it's output to the test file. If we look at the produced output we will see all symbols are resolved:

$ nm test 
0000000000601000 R __bss_start
0000000000601000 R _edata
0000000000601000 R _end
00000000004000b0 T func
00000000004000b7 T main

我们的工作已经完成.让我们看一下动态(共享)库的情况.让我们从我们的 func.c 源文件创建一个共享库:

Here our job is done. Let's look the situation with dynamic(shared) libraries. Let's make a shared library from our func.c source file:

gcc -c func.c -o func.o
gcc -shared -fPIC -Wl,-soname,libfunc.so.1 -o libfunc.so.1.5.0 func.o

Voila,我们有.现在,将其放入已知的动态链接器库路径/usr/lib/:

Voila, we have it. Now, let's put it into known dynamic linker library path, /usr/lib/:

sudo mv libfunc.so.1.5.0 /usr/lib/ # to make program be able to run
sudo ln -s libfunc.so.1.5.0 /usr/lib/libfunc.so.1  #creating symlink for the program to run
sudo ln -s libfunc.so.1 /usr/lib/libfunc.so # to make compilation possible

通过在编译和静态链接过程后保留未解析的 func()符号,创建可执行文件并将其动态链接到我们的共享库,使我们的项目依赖于该共享库( libfunc ):

And let's make our project depend on that shared library by leaving func() symbol unresolved after compilation and static linkage process, creating an executable and linking it (dynamically) to our shared library (libfunc):

cc main.c -lfunc

现在,如果我们在符号表中查找符号,则符号仍未解析:

Now if we look for the symbol in its symbols table we still have our symbol unresolved:

$ nm a.out | grep fun
             U func

但这不再是问题,因为 func 符号将在每次程序启动之前由动态加载程序解决.好的,现在让我们回到理论上.

But this is not a problem anymore because func symbol will be resolved by dynamic loader before each program start. Okay, now let's back to the theory.

库只是对象文件,它们是通过使用 ar 工具和由 ranlib 工具创建的单个符号表放置在单个存档中的.

Libraries, in fact, are just the object files which are placed into a single archive by using ar tool with a single symbols table which is created by ranlib tool.

编译器在编译目标文件时不会解析符号.这些符号将由链接器替换为地址.因此,可以通过两件事来解析符号:链接器动态加载器:

Compiler, when compiling object files, does not resolve symbols. These symbols will be replaced to addresses by a linker. So resolving symbols can be done by two things: the linker and dynamic loader:

  1. 链接器: ld ,执行2个工作:

a)对于静态库或简单的目标文件,此链接器将目标文件中的外部符号更改为真实实体的地址.例如,如果我们使用C ++名称修改,链接器会将 _ZNK3MapI10StringName3RefI8GDScriptE10ComparatorIS0_E16DefaultAllocatorE3hasERKS0 _ 更改为 0x07f4123f0 .

a) For static libs or simple object files, this linker changes external symbols in the object files to the addresses of the real entities. For example, if we use C++ name mangling linker will change _ZNK3MapI10StringName3RefI8GDScriptE10ComparatorIS0_E16DefaultAllocatorE3hasERKS0_ to 0x07f4123f0.

b)对于动态库,它仅检查符号是否可以解析(您尝试与正确的库链接),但不会用地址替换符号.如果无法解析符号(例如,未在链接到的共享库中实现符号)-它会抛出对错误的 undefined reference 错误,并且由于尝试使用这些符号而破坏了构建过程但是链接器当前在正在处理的目标文件中找不到此类符号.否则,此链接器会将一些信息添加到 ELF 可执行文件中,即:

b) For dynamic libs it only checks if the symbols can be resolved (you try to link with correct library) at all but does not replace the symbols by address. If symbols can't be resolved (for example they are not implemented in the shared library you are linking to) - it throws undefined reference to error and breaks up the building process because you try to use these symbols but linker can't find such symbol in it's object files which it is processing at this time. Otherwise, this linker adds some information to the ELF executable which is:

i. .interp 部分-请求解释器-动态加载器在执行之前被调用,因此此部分仅包含动态加载器的路径.例如,如果您查看依赖于共享库( libfunc )的可执行文件,则会看到interp部分 $ readelf -l a.out :

i. .interp section - request for an interpreter - dynamic loader to be called before executing, so this section just contains a path to the dynamic loader. If you look at your executable which depends on shared library (libfunc) for example you will see the interp section $ readelf -l a.out:

INTERP         0x0000000000000238 0x0000000000400238 0x0000000000400238
               0x000000000000001c 0x000000000000001c  R      1
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]

ii. .dynamic 部分-解释器在执行之前将要查找的共享库的列表.您可能会通过 ldd readelf 看到它们:

ii. .dynamic section - a list of shared libraries which interpreter will be looking for before executing. You may see them by ldd or readelf:

$ ldd a.out
     linux-vdso.so.1 =>  (0x00007ffd577dc000)
     libfunc.so.1 => /usr/lib/libfunc.so.1 (0x00007fc629eca000)
     libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fefe148a000)
     /lib64/ld-linux-x86-64.so.2 (0x000055747925e000)

$ readelf -d a.out

  Dynamic section at offset 0xe18 contains 25 entries:
  Tag        Type                         Name/Value
  0x0000000000000001 (NEEDED)             Shared library: [libfunc.so.1]
  0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]

请注意, ldd 还会找到文件系统中的所有库,而readelf仅显示程序需要的库.因此,所有这些库都将由动态加载程序搜索(下一段).链接器在构建时间工作.

Note that ldd also finds all the libraries in your filesystem while readelf only shows what libraries does your program need. So, all of these libraries will be searched by dynamic loader (next paragraph). The linker works at build time.

动态加载程序: ld.so ld-linux .它查找并加载程序所需的所有共享库(如果之前未加载),通过在程序开始之前将其替换为实际地址来解析符号,准备运行该程序,然后运行它.在构建后且在运行程序之前即可工作.简而言之,动态链接意味着在每个程序启动之前在可执行文件中解析符号.

Dynamic loader: ld.so or ld-linux. It finds and loads all the shared libraries needed by a program (if they were not loaded before), resolves the symbols by replacing them to real addresses right before the start of the program, prepares the program to run, and then runs it. It works after the build and before running the program. Less speaking, dynamic linking means resolving symbols in your executable before each program start.

实际上,当您运行带有 .interp 部分的 ELF 可执行文件(它需要加载一些共享库)时,操作系统(Linux)首先运行解释器,但不运行解释器您的程序.否则,您将出现不确定的行为-您的程序中有符号,但是它们不是由地址定义的,这通常意味着该程序将无法正常工作.

Actually, when you run an ELF executable with .interp section (it needs to load some shared libraries) the OS (Linux) runs an interpreter at first but not your program. Otherwise you have an undefined behavior - you have symbols in your program but they are not defined by addresses which usually means that the program will be unable to work properly.

您也可以自己运行动态加载程序,但这是不必要的(对于32位体系结构elf和/lib64/ld-,二进制文件为/lib/ld-linux.so.2 linux-x86-64.so.2 (用于64位体系结构小精灵).

You may also run dynamic loader by yourself but it is unnecessary (binary is /lib/ld-linux.so.2 for 32-bit architecture elf and /lib64/ld-linux-x86-64.so.2 for 64-bit architecture elf).

为什么链接程序声称/usr/bin/ld:在您的情况下找不到-lblpapi3_64 ?因为它尝试查找其已知路径中的所有库.为什么要在运行时加载该库,为什么要搜索该库?因为它需要检查该库是否可以解析所有需要的符号,并将其名称放入动态加载程序的 .dynamic 部分中.实际上, .interp 部分几乎存在于每个c/c ++精灵中,因为 libc libstdc ++ 库都是共享的,并且默认情况下是编译器链接任何动态地对他们的项目.您也可以静态链接它们,但这将扩大可执行文件的总大小.因此,如果找不到共享库,您的符号将仍然无法解析,并且您将无法启用来运行应用程序,因此无法生成可执行文件.您可能会获得通常通过以下方式搜索库的目录列表:

Why does the linker claim that /usr/bin/ld: cannot find -lblpapi3_64 in your case? Because it tries to find all the libraries in it's known paths. Why does it search the library if it will be loaded during runtime? Because it needs to check if all the needed symbols can be resolved by this library and to put it's name into the .dynamic section for dynamic loader. Actually, the .interp section exists in almost every c/c++ elf because the libc and libstdc++ libraries are both shared, and compiler by default links any project dynamically to them. You may link them statically as well but this will enlarge the total executable size. So, if the shared library can't be found your symbols will remain unresolved and you will be UNABLE to run your application, thus it can't produce an executable. You may get the list of directories where libraries are usually searched by:

  1. 在编译器参数中将命令传递给链接器.
  2. 通过解析 ld --verbose 的输出.
  3. 通过解析 ldconfig 的输出.
  1. Passing a command to the linker in compiler arguments.
  2. By parsing ld --verbose's output.
  3. By parsing ldconfig's output.

其中一些方法在此处进行了解释.

Some of these methods are explained here.

动态加载程序尝试使用以下命令查找所有库:

Dynamic loader tries to find all the libraries by using:

    ELF文件的
  1. DT_RPATH 动态部分.
  2. 可执行文件的
  3. DT_RUNPATH 部分.
  4. LD_LIBRARY_PATH 环境变量.
  5. /etc/ld.so.cache -自己的缓存文件,其中包含以前在增强库路径中找到的候选库的已编译列表.
  6. 默认路径:在默认路径/lib中,然后在/usr/lib中.如果二进制文件已使用 -z nodeflib 链接器选项链接,则将跳过此步骤.
  1. DT_RPATH dynamic section of an ELF file.
  2. DT_RUNPATH section of the executable.
  3. LD_LIBRARY_PATH environment variable.
  4. /etc/ld.so.cache - own cache file which contains a compiled list of candidate libraries previously found in the augmented library path.
  5. Default paths: In the default path /lib, and then /usr/lib. If the binary was linked with -z nodeflib linker option, this step is skipped.

ld-linux搜索算法

另外,请注意,如果我们在谈论共享库,它们的名称不是 .so ,而是 .so.version 格式.当您构建应用程序时,链接器将查找 .so 文件(通常是指向 .so.version 的符号链接),但是在您运行应用程序时,动态加载程序会寻找改为 .so.version 文件.例如,假设我们有一个库 test ,根据

Also, note please, that if we are talking about shared libraries, they are not named .so but in .so.version format instead. When you build your application the linker will look for .so file (which is usually a symlink to .so.version) but when you run your application the dynamic loader looks for .so.version file instead. For example, let's say we have a library test which version is 1.1.1 according to semver. In the filesystem it will look like:

/usr/lib/libtest.so -> /usr/lib/libtest.so.1.1.1
/usr/lib/libtest.so.1 -> /usr/lib/libtest.so.1.1.1
/usr/lib/libtest.so.1.1 -> /usr/lib/libtest.so.1.1.1
/usr/lib/libtest.so.1.1.1

因此,要进行编译,您必须具有所有版本控制的文件( libtest.so.1 libtest.so.1.1 libtest.so.1.1.1 )和一个 libtest.so 文件,但要运行您的应用,您必须首先列出3个版本化的库文件.这也解释了为什么Debian或rpm软件包分别具有 devel -软件包:普通软件包(仅由已编译的应用程序运行它们所需的文件组成)具有3个版本库文件和一个devel软件包其中只有一个symlink文件,可用于编译项目.

So, to be able to compile you must have all of versioned files (libtest.so.1, libtest.so.1.1 and libtest.so.1.1.1) and a libtest.so file but for running your app you must have only 3 versioned library files listed first. This also explains why do Debian or rpm packages have devel-packages separately: normal one (which consists only of the files needed by already compiled applications for running them) which has 3 versioned library files and a devel package which has only symlink file for making it possible to compile the project.

恢复

所有这些:

    您,您的同事和应用程序代码的 EACH 用户必须在其系统链接器路径中拥有所有库,才能进行编译(构建应用程序).否则,他们必须更改Makefile(或编译命令)以通过添加 -L< somePathToTheSharedLibrary> 作为参数来添加共享库位置目录.
  1. 成功构建后,您还需要再次使用库才能运行该程序.您的库将由动态加载程序( ld-linux )搜索,因此它必须位于它的路径(见上文)或系统链接器路径中.在大多数Linux程序发行版中,例如Steam发行的游戏,都有一个shell脚本来设置 LD_LIBRARY_PATH 变量,该变量指向游戏所需的所有共享库.
  1. You, your colleague and EACH user of your application code must have all the libraries in their system linker paths to be able to compile (build your application). Otherwise, they have to change Makefile (or compile command) to add the shared library location directory by adding -L<somePathToTheSharedLibrary> as argument.
  2. After successful build you also need your library again to be able to run the program. Your library will be searched by dynamic loader (ld-linux) so it needs to be in it's paths (see above) or in system linker paths. In most of linux program distributions, for example, games from steam, there is a shell-script which sets the LD_LIBRARY_PATH variable which points to all shared libraries needed by the game.

这篇关于cpp:usr/bin/ld:找不到-l&lt; nameOfTheLibrary&gt;的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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