出现“未定义的引用错误”,但nm命令显示符号存在

5
我正在使用libkml构建一个大型应用程序。我正在使用这里的libkml cmake端口:https://github.com/rashadkm/libkml
尽管该符号似乎已被引用和定义,但我遇到了奇怪的未定义符号错误。
以下是make命令:
/usr/bin/c++ -fPIC -Werror=return-type -Werror=return-type -Wall 
-Werror=parentheses -Werror=uninitialized -Werror=missing-braces 
-fPIC -O0 -Wall -fPIC -fvisibility=hidden -fno-strict-aliasing 
-Wno-long-long -m64 -g -D_DEBUG --coverage -Wl,-Bsymbolic -Wl,--
no-undefined -shared -o GPS2KML.plb CMakeFiles/GPS2KML.dir
/gps.cpp.o CMakeFiles/GPS2KML.dir/kml.cpp.o CMakeFiles/GPS2KML.dir
/stdafx.cpp.o  /trunk/src/filter/GPS2KML/external/libkml/lib/cmake
/libkml/../../libkmlconvenience.so.1.3.1 /trunk/src/filter/GPS2KML
/external/libkml/lib/cmake/libkml/../../libkmlengine.so.1.3.1 
/trunk/src/filter/GPS2KML/external/libkml/lib/cmake/libkml/../..
/libkmldom.so.1.3.1 /trunk/src/filter/GPS2KML/external/libkml
/lib/cmake/libkml/../../libkmlbase.so.1.3.1 -lminizip -luriparser 
-lexpat

make 输出内容:

CMakeFiles/GPS2KML.dir/kml.cpp.o: In function `cKML::~cKML()':
/trunk/src/filter/GPS2KML/src/kml.cpp:55: undefined reference to `*kmldom::SerializePretty(boost::intrusive_ptr<kmldom::Element> const&)*'
collect2: error: ld returned 1 exit status

现在如果我这样做:

daniyal@daniyal-Inspiron-5521:/$ nm --demangle --extern-only --defined-only ../trunk/src/filter/GPS2KML/external/libkml/lib/libkmldom.so | grep SerializePretty

它明显显示:

000000000013c9aa T kmldom::SerializePretty[abi:cxx11](boost::intrusive_ptr<kmldom::Element> const&)

现在我不明白问题出在哪里。我查看了stackoverflow上现有的关于此问题的问题,发现有以下四个解决方案:
  1. 在某些情况下,解码后的符号名称与导致错误的符号不匹配。这显然不是我的情况。
  2. 在命令的末尾使用-llibrary,并在输入.o文件的名称后放置库的名称。因此,当链接器遇到库时,它具有该库中存在的未定义符号。这显然不是我的解决方案,因为我已经将库放在了命令的末尾。
  3. 在某些情况下,该符号存在于共享库中,但没有外部链接或未定义。可以通过使用nm和--extern-only以及--defined-only进行确认。因此,这对我也不是一个解决方案。

编辑: 附加信息:

这是我正在使用的cmake文件:

find_package(LibKML REQUIRED)

include_directories(${LIBKML_INCLUDE_DIRS})

add_filter(${PROJECT}
        gps.h
        gps.cpp
        kml.h
        kml.cpp
        #can2gps.h
        #can2gps.cpp
        stdafx.h 
        stdafx.cpp )


target_link_libraries (${PROJECT} ${LIBKML_LIBRARIES})

add_filter大致相当于以下宏:

add_library(${NAME} MODULE ${ARGN} ${${NAME}_MOC} ${${NAME}_UI} ${${NAME}_QRC})

target_link_libraries(${NAME} ${BUILD_LIBS} ${QT_LIBRARIES}  ${ADTF_OPENGL_LIBRARY} ${ADTF_ADDITIONAL_UTILS_LIBS})
set_target_properties(${NAME}
PROPERTIES
SUFFIX ".plb"
)
if(UNIX)
set_target_properties(${NAME}
PROPERTIES
PREFIX ""

)


1
链接的顺序很重要...您必须先链接依赖项,然后是依赖关系。如果存在循环引用,则可能需要多次链接库。 - UKMonkey
@DaniyalYasin - 好吧,很有趣。那这样怎么样......nm --extern-only --defined-only | fgrep 'kmldom' | fgrep 'SerializePretty' | fgrep 'Element'。这将设置嵌入在被编码标识符内的名称顺序无关紧要,但可能会给出太多的符号。你可以尝试用 intrusive_ptr 进行筛选。你通常熟悉什么是编码名吗? - Omnifarious
只有一个结果。这个: 000000000013c9aa T _ZN6kmldom15SerializePrettyB5cxx11ERKN5boost13intrusive_ptrINS_7ElementEEE - Daniyal Yasin
现在对 kml.cpp.o 执行以下操作。nm kml.cpp.o | fgrep 'kmldom' | fgrep 'SerializePretty' | fgrep 'Element'。符号匹配吗? - Omnifarious
如果我们进行解缠:kml.cpp.o: U kmldom::SerializePretty(boost::intrusive_ptr<kmldom::Element> const&)libkmldom.so: T kmldom::SerializePretty[abi:cxx11](boost::intrusive_ptr<kmldom::Element> const&) 这个问题是否与c++11有关? - Daniyal Yasin
显示剩余4条评论
1个回答

5
似乎您遇到了ABI不匹配的问题。ABI是“应用程序二进制接口”的缩写,它基本上是指确切地如何将参数放入堆栈(或放入寄存器)以及其他各种类似的规范。
请尝试确保您的代码使用 -std=c++11 (如果使用任何GNU扩展,请使用 -std=gnu++11 )标志进行编译。看起来像libkml是这样编译的。C++11有许多新功能,需要与C++11之前的版本违反ABI兼容性。C++14和C++1z的更改不太激烈,但也可能会破坏ABI兼容性,我不确定。但在这种情况下,解码的符号很清楚,libkml需要至少C++11。

1
非常抱歉这么晚才将此回答标记为已接受。我的项目的cmake结构正在强制使用c++98.. 我不得不找到add_definitions(-D_GLIBCXX_USE_CXX11_ABI=1)选项。非常感谢您的帮助。 - Daniyal Yasin
@DaniyalYasin - 我很高兴你的问题已经解决。 - Omnifarious

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接