【问题标题】:Getting undefined reference error but nm shows symbol present获得未定义的参考错误,但 nm 显示符号存在
【发布时间】:2017-11-14 00:49:24
【问题描述】:

我正在使用 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. 在放置 .o 文件的名称后,在命令末尾使用 -llibrary。因此,当链接器遇到库时,该库中存在未定义的符号。这显然不是我的解决方案,因为我在命令末尾有库。
  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 ""

)

【问题讨论】:

  • 链接顺序很重要...您必须先链接依赖项,然后链接依赖项。如果您有循环引用,则可能需要多次链接到库
  • 另一种可能性是名称拆解以某种方式掩盖了某些东西。试试nm --extern-only --defined-only | egrep 'kmldom.*Serialize.*boost.*intrusive_ptr.*kmldom.*Element',看看你能不能看到精确的损坏符号。也许该符号用于不同的 ABI 版本或其他东西。
  • @Omnifarious 在运行您的命令时,没有产生任何输出。
  • @DaniyalYasin - 嗯,这很有趣。这个怎么样...nm --extern-only --defined-only | fgrep 'kmldom' | fgrep 'SerializePretty' | fgrep 'Element'。这将设置它,因此损坏标识符中嵌入名称的顺序无关紧要,但它可能会给您太多符号。您也可以尝试通过intrusive_ptr 进行过滤。您通常熟悉重命名的名称吗?
  • 只有一个结果。这个:000000000013c9aa T _ZN6kmldom15SerializePrettyB5cxx11ERKN5boost13intrusive_ptrINS_7ElementEEE

标签: c++ makefile cmake kml ld


【解决方案1】:

您似乎遇到了 ABI 不匹配问题。 ABI 是“应用程序二进制接口”,基本上是关于参数如何进入堆栈(或放入寄存器)以及其他各种类似内容的规范。

尝试确保使用-std=c++11(或-std=gnu++11,如果您使用任何GNU 扩展)标志编译您的代码。看起来这就是 libkml 的编译方式。 C++11 有许多新特性需要与 C++11 之前的 ABI 兼容性中断。 C++14 和 C++1z 的变化不大,但它们也可能破坏 ABI 兼容性,我不确定。不过,在这种情况下,去错符号是清晰的,libkml 至少需要 C++11。

【讨论】:

  • 很抱歉这么晚才将此标记为已接受的答案。我项目的 cmake 结构强制执行 c++98.. 我必须找出 add_definitions(-D_GLIBCXX_USE_CXX11_ABI=1) 选项非常感谢您的帮助
  • @DaniyalYasin - 我很高兴你的问题得到了解决。
猜你喜欢
  • 2021-07-22
  • 2011-01-20
  • 1970-01-01
  • 2012-11-04
  • 1970-01-01
  • 2017-08-31
  • 2016-10-03
  • 2020-08-31
  • 1970-01-01
相关资源
最近更新 更多