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

时间:2019-02-04 16:00:29

标签: c++ gcc makefile shared-libraries ld

我目前正在开发一个C ++应用程序,该应用程序将引用多个* .so库,每个库都包含用C编写的不同机器的代码。 我还有一个共享对象,其中包含来自自定义命名空间实用程序的代码,顾名思义,该实用程序包含对应用程序有用的基本实用程序(用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是symlink,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 $^ ##########\n\n"

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 $^ ##########\n\n"

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  #
####################

仅用于git和shigles,我将添加Utility.so和应用程序的构建输出。

utilities.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文件?

我还确保使用$ PATH变量找到.so文件,因此应用程序应该能够找到它们。

1 个答案:

答案 0 :(得分:0)

系统通常在一组固定的目录中搜索共享对象。您可以通过定义环境变量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,则搜索soname /etc/ld.so.cache中找到要加载的最终文件。此缓存是在系统的每次引导时构建的,因此您不必对此进行处理,而仅当您不想重新引导并安装新的库供系统使用时才需要。重要的是要注意,共享对象必须被赋予soname才能起作用,并且文件/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
    

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

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

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

更多...

我从您的汇编中看到:

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

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

必须创建这些链接,而系统通常不会创建。这是共享库安装过程的一部分。 soname链接允许您使用不同版本的库,并检测在运行时将使用哪个版本(所有版本必须兼容,以便在进行不兼容的修改时可以互换,然后必须更改soname,因此系统在加载时不会感到困惑)

知道ld(1)程序仅在被称为lib<name>.so且没有版本信息的情况下选择一个库,这一点非常重要。确实,编译器首先搜索lib<name>.so,然后搜索lib<name>.a,然后抱怨。

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

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

期待这些添加的注释可以使该过程更容易理解。