加载共享库时出错:无法在外部硬件上打开共享对象文件:

Error loading shared libraries: cannot open shared object file :: on external hardware

本文关键字:共享 硬件 文件 对象 外部 出错 加载      更新时间:2023-10-16

我目前正在开发一个C++应用程序,该应用程序将引用多个*.so库,每个库都包含用C编写的不同机器的代码。我还有一个共享对象,其中包含来自自定义命名空间Utilities的代码,它(顾名思义)包含对应用程序有用的基本实用程序(用C++编写,与应用程序的其他部分一样)。

目前,utilities.so是应用程序引用的唯一(自定义)库。应用程序编译和链接很好,但在目标硬件上执行时会显示以下错误:bin/updater_v4test: error while loading shared libraries: ../../../bin/device_modules/utilities.so.1.0.0: cannot open shared object file: No such file or directory

在所述应用程序上调用LDD时,显示以下输出:../../../bin/device_modules/utilities.so.1.0.0 => not found

我写了一个脚本,它(在无错误退出时)收集所有SO,并将它们推送到目标硬件上的/lib/目录中;这意味着在每次成功构建应用程序(无论是部分还是全部)时,更新的文件都会被推送到目标硬件上的正确目录。

为了模仿我使用过的其他库(例如zlib),我尝试创建一个指向库文件的符号链接,但没有成功(utilities.so.1.0.0是符号链接,utilities.so是实际的so)。utilities.so.1.0.0 --> utilities.so

现在我不知所措,我不能再浪费更多的时间试图自己弄清楚这一点了。


以下是实用程序Makefile:的摘录

include ../../common/user.mk
LIB_DIR=../../../libs/lib/powerpc-linux-gnu
CFLAGS=-Wall -ggdb -I../../libs/include -I../../common -lpthread -lddc -std=c++0x -I../../libs/include/zlib
LDFLAGS=../../libs/lib/powerpc-linux-gnu/libcurl.so.4 ../../libs/include/minizip/.libs/libminizip.so.1.0.0 
-L ../../libs/lib 
-L ../../libs/lib/powerpc-linux-gnu 
-Wl,-rpath-link,../../libs/lib/powerpc-linux-gnu 
-lrt -lddc -lpthread -shared
BIN_DIR=../bin
CANONICAL_BIN_DIR := $(shell readlink -f $(BIN_DIR))
CANONICAL_CUR_DIR := $(shell readlink -f "./")
COMP_OBJECTS := $(wildcard *.cpp)
OBJECTS := $(patsubst %.cpp,%.o,$(wildcard *.cpp))
all: objects utilities.so
-cp --parents utilities.so $(GLOBAL_BIN_LIB)
-ln -s utilities.so utilities.so.1.0.0
-mv utilities.so.1.0.0 $(GLOBAL_BIN_LIB)
#   -cp --parents *.h $(GLOBAL_HEADER_DIR)
-rm -f utilities.so
-rm -f *.o
for header in $(wildcard *.h); do 
echo $$header; 
ln -s $(CANONICAL_CUR_DIR)/$$header $(GLOBAL_HEADER_DIR)/$$header; 
done;
@printf "########## BUILT $^ ##########nn"
utilities.so: $(OBJECTS)
${CXX} $^ -o $@ ${LDFLAGS}
objects: $(COMP_OBJECTS)
${CXX} -c $^ ${CFLAGS}

现在是应用程序Makefile的摘录。在这里,我在链接器标志中添加了对库的引用,头文件都包含在一个目录中。

include ../../../common/user.mk
LIB_DIR=../../../libs/lib/powerpc-linux-gnu
CFLAGS=-Wall -ggdb -I../../../libs/include -I${COMMON_DIR} -lpthread -lddc -std=c++0x -I../../../libs/include/zlib -I${HEADER_DIR}
LDFLAGS=../../../libs/lib/powerpc-linux-gnu/libcurl.so.4 ../../../libs/include/minizip/.libs/libminizip.so.1.0.0 ../../../bin/device_modules/utilities.so.1.0.0 
-L ../../../libs/lib 
-L ../../../libs/lib/powerpc-linux-gnu 
-Wl,-rpath-link,../../../libs/lib/powerpc-linux-gnu 
-lrt -lddc -lpthread -L ${SO_DIR}
BIN_DIR=../bin
CANONICAL_BIN_DIR := $(shell readlink -f $(BIN_DIR))
CANONICAL_CUR_DIR := $(shell readlink -f "./")
all: test_update.bin
-cp --parents test_update.bin $(BIN_DIR)
-ln -s $(CANONICAL_BIN_DIR)/test_update.bin $(GLOBAL_BIN_APP)/test_update.bin
-rm -f *.bin
@printf "########## BUILT $^ ##########nn"
test_update.bin: main.o updaterdelegate.o commonfunctions.o tinyxml.o
${CXX} $^ -o $@ ${LDFLAGS}
####################
#  Required Files  #
####################
main.o: Main.cpp
${CXX} -c $^ -o $@ ${CFLAGS}
updaterdelegate.o: UpdaterDelegate.cpp
${CXX} -c $^ -o $@ ${CFLAGS}
commonfunctions.o: $(shell python -c "import os.path; print os.path.relpath('${IMPL_CMN_FUNC}'.replace('"', ''), '${CANONICAL_CUR_DIR}'.replace('"', ''))")
${CXX} -c $^ -o $@ ${CFLAGS}
tinyxml.o: $(shell python -c "import os.path; print os.path.relpath('${IMPL_TXML}'.replace('"', ''), '${CANONICAL_CUR_DIR}'.replace('"', ''))")
${CXX} -c $^ -o $@ ${CFLAGS}
####################
# /Required Files  #
####################

仅针对gits和shiggles,我将添加实用程序.so和应用程序的构建输出。

实用程序。so:

(清洁委托)

=============== CLEAN COMPLETE... BUILDING... ===============

make: Entering directory `~/_workspace/upv4/common/utils'
powerpc-linux-gnu-g++  -c ArgumentHandling.cpp Extensions.cpp Logging.cpp -Wall -ggdb -I../../libs/include -I../../common -lpthread -lddc -std=c++0x -I../../libs/include/zlib
ArgumentHandling.cpp: In member function ‘void Utilities::ArgumentHandler::freeMemory()’:
ArgumentHandling.cpp:136: warning: deleting ‘void*’ is undefined
powerpc-linux-gnu-g++  ArgumentHandling.o Extensions.o Logging.o -o utilities.so ../../libs/lib/powerpc-linux-gnu/libcurl.so.4 ../../libs/include/minizip/.libs/libminizip.so.1.0.0 -L ../../libs/lib -L ../../libs/lib/powerpc-linux-gnu -Wl,-rpath-link,../../libs/lib/powerpc-linux-gnu -lrt -lddc -lpthread -shared
cp --parents utilities.so """~/_workspace/upv4""/bin/device_modules"
ln -s utilities.so utilities.so.1.0.0
mv utilities.so.1.0.0 """~/_workspace/upv4""/bin/device_modules"
rm -f utilities.so
rm -f *.o
for header in ArgumentHandling.h Enumerations.h Extensions.h Logging.h; do 
echo $header; 
ln -s ~/_workspace/upv4/common/utils/$header """"~/_workspace/upv4""/bin/device_modules"/headers"/$header; 
done;
ArgumentHandling.h
Enumerations.h
Extensions.h
Logging.h
########## BUILT objects utilities.so ##########
make: Leaving directory `~/_workspace/upv4/common/utils'

=============== BUILD COMPLETE... PARSING... ===============

========== Warnings ==========
Total: 1
ArgumentHandling.cpp:136: warning: deleting ‘void*’ is undefined

========== Errors ==========
Total: 0

test_update.bin:

(清洁委托)

=============== CLEAN COMPLETE... BUILDING... ===============

make: Entering directory `~/_workspace/_workspace/upv4/test/app/src'
powerpc-linux-gnu-g++  -c Main.cpp -o main.o -Wall -ggdb -I../../../libs/include -I"""~/_workspace/_workspace/upv4""/common" -lpthread -lddc -std=c++0x -I../../../libs/include/zlib -I"""""~/_workspace/_workspace/upv4""/bin/device_modules"/headers""
powerpc-linux-gnu-g++  -c UpdaterDelegate.cpp -o updaterdelegate.o -Wall -ggdb -I../../../libs/include -I"""~/_workspace/_workspace/upv4""/common" -lpthread -lddc -std=c++0x -I../../../libs/include/zlib -I"""""~/_workspace/_workspace/upv4""/bin/device_modules"/headers""
powerpc-linux-gnu-g++  -c ../../../common/commonFunctions.cpp -o commonfunctions.o -Wall -ggdb -I../../../libs/include -I"""~/_workspace/_workspace/upv4""/common" -lpthread -lddc -std=c++0x -I../../../libs/include/zlib -I"""""~/_workspace/_workspace/upv4""/bin/device_modules"/headers""
powerpc-linux-gnu-g++  -c ../../../common/xmlreader/tinyxml2.cpp -o tinyxml.o -Wall -ggdb -I../../../libs/include -I"""~/_workspace/_workspace/upv4""/common" -lpthread -lddc -std=c++0x -I../../../libs/include/zlib -I"""""~/_workspace/_workspace/upv4""/bin/device_modules"/headers""
powerpc-linux-gnu-g++  main.o updaterdelegate.o commonfunctions.o tinyxml.o -o test_update.bin ../../../libs/lib/powerpc-linux-gnu/libcurl.so.4 ../../../libs/include/minizip/.libs/libminizip.so.1.0.0 ../../../bin/device_modules/utilities.so.1.0.0 -L ../../../libs/lib -L ../../../libs/lib/powerpc-linux-gnu -Wl,-rpath-link,../../../libs/lib/powerpc-linux-gnu -lrt -lddc -lpthread -L """"~/_workspace/_workspace/upv4""/bin/device_modules""
cp --parents test_update.bin ../bin
ln -s ~/_workspace/_workspace/upv4/test/app/bin/test_update.bin """~/_workspace/_workspace/upv4""/bin"/test_update.bin
ln: failed to create symbolic link `~/_workspace/_workspace/upv4/bin/test_update.bin': File exists
make: [all] Error 1 (ignored)
rm -f *.bin
########## BUILT test_update.bin ##########
make: Leaving directory `~/_workspace/_workspace/upv4/test/app/src'

=============== BUILD COMPLETE... PARSING... ===============

========== Warnings ==========
Total: 0


========== Errors ==========
Total: 0

构建脚本是自定义的,我自己构建它是为了在不需要的时候摆脱整个make输出,它会解析出所有的错误和警告。由于这个问题,我重新打开了make输出

我是在编译/链接库的过程中遗漏了什么,还是在链接应用程序的过程中出现了问题?我倾向于说这是在库的编译/链接过程中——尽管我不知道到底出了什么问题,因为一切都在编译和链接中。为什么应用程序在不可能拥有的路径中查找.so文件?

我还确保.so文件是用$PATH变量找到的,所以应用程序应该能够找到它们。

系统通常在一组固定的目录中搜索共享对象。您可以通过定义环境变量LD_LIBRARY_PATH并添加将共享库安装到的目录来解决此问题。

或者,您可以将这些库添加到一些标准库目录中,并执行ldconfig -a来更新共享库的缓存。

有关更多信息,请参见ldconfig(8)

编辑

加载共享对象有两种机制。

  • 第一种是普通的库加载机制,它指定在启动可执行文件时必须加载的内容,并暗示将可执行文件与共享对象链接。这就是你在Makefile中所做的。指定共享的可执行文件,ld.so.xxx共享对象(在动态链接应用程序时链接到应用程序)加载并遵循所有未解析的标识符,以便在虚拟地址空间中为它们找到位置。ld.so.xxx对象使用/etc/ld.so.cache文件,该文件只是一个哈希表,其中包含可通过这种方式加载的目录和共享可执行文件。该文件由所谓的soname索引(这对允许同一库的不同版本在同一系统中共存很有用),通常映射到文件/etc/ld.so.conf中列出的目录列表中的最后一个版本共享(这是加速加载库过程的静态信息,在系统每次启动时生成)。如果在链接时发现共享(ld不使用此机制,但仅用于在程序启动时加载库)具有soname,则在/etc/ld.so.cache中搜索该soname以找到必须加载的最终文件。这个缓存是在系统每次启动时构建的,所以你不必处理这个问题,但前提是你不想重新启动,并且你安装了一个新的库供系统使用。需要注意的是,共享对象必须被赋予sonames才能工作,并且文件/etc/ld.so.conf中的目录列表是唯一用于搜索文件的目录。这意味着,要使标准库可用,您需要将其放在其中一个目录中(或将该目录添加到/etc/ld.so.conf中的列表中),然后执行ldconfig -a以重建缓存,并使其包含该soname下文件的引用。

    另一种方法是将其添加到搜索列表中,以PATH变量的形式放置列表。变量是LD_LIBRARY_PATH,因此,如果您在${HOME}/libs中有共享对象,您可以将此行添加到.profile:

    export LD_LIBRARY_PATH=${HOME}/libs
    

    setenv LD_LIBRARY_PATH ${HOME}/libs
    

    这允许您的库位于标准目录之外,但要三思而后行,因为这是一种加载文件的效率低得多的方法(因为它涉及到处理目录列表以查找最终共享对象,而以前的方法是直接的,您需要与ld.so.xxx要求的soname匹配的文件,只需一次搜索,仅需一步)

  • 第二种方法是使用库函数dlopen(3),该函数允许您在调用内部任何内容之前加载共享对象并进行一些内务处理。dl库允许您在共享可执行文件中搜索符号,然后决定将其解释为数据还是跳转目标。CCD_ 28只是打开并将共享对象加载到虚拟地址空间中。它解析依赖关系(如果请求),并且是加载未知代码以供执行的更灵活的方式(但它也是不透明的)。这是插件正常工作的方式。你可以在配置文件中决定,或者动态地决定加载什么,然后再加载它。程序不必事先知道你正在处理的内容的符号表,你可以在加载的模块中自由实现你想要的任何内容。

所有这些方法都适用于ELF二进制文件,因此您有很大的自由度,但也有很大的复杂性。

更多

正如我从你的汇编中看到的:

  • 您将-l包含在仅编译命令中,只有在链接可执行文件时才需要库,不要将库置于编译阶段
  • 对于要由链接器搜索和选择的库,它必须命名为lib<name>.so(末尾没有版本信息),因此这意味着通常可以为标准库找到三个名称(让我以数学库-lm为例):

    /usr/lib/libm.so.3.2.8#这是包含库内容的ELF文件。/usr/lib/libm.so.3->libm.so.3.28#这是用于创建库的soname。/usr/lib/libm.so->libm.so.3#这是ld(1)程序在使用-lm时搜索的实际文件。

必须创建这些链接,因为系统通常不会创建这些链接。这是共享库安装过程的一部分。soname链接允许您拥有不同版本的库,并检测它们中的哪一个将在运行时使用(所有版本都必须兼容,这样您才能进行交换,当您进行不兼容的修改时,您必须更改soname,这样系统在加载时就不会混淆)

非常重要的是要知道,ld(1)程序只选择一个名为lib<name>.so的库,而没有版本信息。事实上,编译器首先搜索lib<name>.so,然后搜索lib<name>.a,然后它会抱怨。

在任何将在链接参数中使用这些目录的-l选项之前,将-L放在搜索库的位置是非常重要的。

如果不打算使用LD_LIBRARY_PATH机制,则只需要运行ldconfig -a并将库安装在系统目录中。(这个机制不适用于root帐户,原因很明显:)

期望这些添加的评论能为这个过程提供一些启示。