首页 > 解决方案 > emscripten 链接全局变量命名符号多次定义

问题描述

我有一个 c++ CMAKE (VERSION 3.10.2 -std=c++17) 项目,我可以编译和链接购买的 gcc 和 clang。购买它们会产生按预期工作的目标二进制文件。最近我决定尝试添加另一个目标,即 webassembly。该项目正在按预期编译,但是当正在执行 EMscripten 构建时,即在链接阶段,我收到以下错误:

Elapsed time: 1 s. (time), 0.002241 s. (clock)
[100%] Linking CXX executable wasmExec.js
cd /Projects/time/time.cpp/build/src/wasm && /usr/bin/cmake -E cmake_link_script CMakeFiles/wasmExec.dir/link.txt --verbose=1
/Projects/emscripten/emsdk/emscripten/1.38.12/em++    -s WASM=1  -s NO_EXIT_RUNTIME=1  -s VERBOSE=1  --pre-js /Projects/time/time.cpp/src/wasm/preModule.js -s DEMANGLE_SUPPORT=1 -s DISABLE_EXCEPTION_CATCHING=0 -s ERROR_ON_UNDEFINED_SYMBOLS=0 @CMakeFiles/wasmExec.dir/objects1.rsp  -o wasmExec.js @CMakeFiles/wasmExec.dir/linklibs.rsp
error: Linking globals named '_ZTVN9timeproto3time8defaults20TimeDefaultParametersE': symbol multiply defined!
WARNING:root:Note: Input file "@/tmp/tmpUeJ6zc.response" did not exist.
ERROR:root:Failed to run llvm optimizations: 

当我做

c++filt _ZTVN9timeproto3time8defaults20TimeDefaultParametersE

我明白了

vtable for timeproto::time::defaults::TimeDefaultParameters

来自 Stackoverflow 的另一个答案,即

符号多重定义的可能原因不是“extern”

我确实知道我已经不止一次地定义了这个类,但是我的问题是我无法找到我在第二个定义中犯了错误的那个地方。在上一个答案中,该人有提示,即他犯了那个错误的 cpp 文件,但在我的情况下,emscipten 并不那么慷慨。

此类在许多文件中的整个代码库中都使用过,经过长时间的手动搜索,我找不到任何可以至少指向第二个定义的位置的东西。因此,我希望有人可以帮助我解决以下问题

1)如何进一步排除故障,以便找到该类的第二个定义发生的确切位置,可能是 gcc 或 clang 的标志?
2)为什么只有在我尝试编译/构建 webassmbly 目标时才会显示此错误。常规的 Linux64 构建目标是成功的,并且测试也正常工作。

3)我正在使用以下“add_definitions”运行cmake,即

if(UNIX)
    add_definitions(" -pedantic -pedantic-errors -W ")
    add_definitions(" -Wall -Wextra  -Werror -Wshadow -Wnon-virtual-dtor ")
    add_definitions(" -v ")
#   add_definitions(" -Worl-style-cast -Wcast-align ")
#   add_definitions(" -Wunused -Woverloaded-virtual ")
    add_definitions(" -g  ")
endif(UNIX)

如果 TimeDefaultParameters 已经定义了更多,那么对于使用上述“add_definitions”的 linux 构建,一次也不应该抱怨吗?

这是 TimeDefaultParameters.cpp 下面的代码 这是一个非常简单的文件,它不包含任何对象,而是有 43 个“静态 const uint32_t”变量。

#include "TimeDefaultParameters.h"

namespace timeproto::time::defaults
    {

            TimeDefaultParameters::TimeDefaultParameters() {

            }

            TimeDefaultParameters::~TimeDefaultParameters() {

            }


            const uint32_t TimeDefaultParameters::SIGNED_SHORT_MAX_VALUE = 32767; 

.... (another 42 static const uint32_t)


}

和头文件 TimeDefaultParameters.h:

#ifndef _TIME_DEFAULT_PARAMETERS_
#define _TIME_DEFAULT_PARAMETERS_

#include <stdint.h>

namespace timeproto::time::defaults
    {


    class TimeDefaultParameters final
        {

    public:

        explicit TimeDefaultParameters();
        virtual ~TimeDefaultParameters();

        static const uint32_t SIGNED_SHORT_MAX_VALUE; 
        .....

.... (another 42 static const uint32_t)



        };

    }


#endif  //#ifndef _TIME_DEFAULT_PARAMETERS_

在cmake中,我设置了我的目标属性,例如:

set_target_properties(wasmExec PROPERTIES LINK_FLAGS "-s WASM=1  -s NO_EXIT_RUNTIME=1  -s VERBOSE=1  --pre-js /Projects/time/time.cpp/src/wasm/preModule.js -s DEMANGLE_SUPPORT=1 -s DISABLE_EXCEPTION_CATCHING=0 -s ERROR_ON_UNDEFINED_SYMBOLS=0" )

这就是我如何调用 cmake 从构建目录中进行构建

emconfigure cmake -DCMAKE_BUILD_TYPE=Emscripten -G "Unix Makefiles" -DCMAKE_TOOLCHAIN_FILE=/Projects/emscripten/emsdk/emscripten/1.38.12/cmake/Modules/Platform/Emscripten.cmake ../

make -j8

任何想法都非常感谢。

补充:2020 年 1 月 5 日

我能够找到解决这个问题的方法,但我仍然留下了一些关于错误性质的问题。

有问题的类是动态创建和加载的存档的一部分,即我在该库“set(LIB_TYPE SHARED)”的 CMAKE 部分中使用过。这是 cmake 如何生成该存档的完整示例,即 CMakeLists.txt。

set( TIME_DEFAULTS_SRC

    ...
    TimeDefaultParameters.h TimeDefaultParameters.cpp
    ...

)

set(LIB_TYPE STATIC)
#set(LIB_TYPE SHARED)

add_library(time_defaults ${LIB_TYPE} ${TIME_DEFAULTS_SRC} ) 
target_include_directories(time_defaults PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/")

我已经从动态变为静态,并且我能够创建 wasm 没有显示错误。在编译过程中,我还在编译过程之间的某个地方看到了一些警告,即:

WARNING:root:When Emscripten compiles to a typical native suffix for shared libraries (.so, .dylib, .dll) then it emits an LLVM bitcode file. You should then compile that to an emscripten SIDE_MODULE (using that flag) with suffix .wasm (for wasm) or .js (for asm.js). (You may also want to adapt your build system to emit the more standard suffix for a file with LLVM bitcode, '.bc', which would avoid this warning.)

这个警告现在已经消失了。但是很容易监督这样的事情,特别是如果编译过程需要很长时间。但是我的理解是,第一条错误消息告诉我们,“看看你在代码中对某些符号进行了重复定义,去寻找这个地方并确保该类只定义一次”。这正是我正在做的,即在代码库中搜索那个重复的定义。因此,现在的问题是:为什么 emscripten 在动态链接方面存在问题,即我知道它是官方支持的,即

https://webassembly.org/docs/dynamic-linking/

这是错误的根源还是其他原因?为什么当我更改为静态时此错误消失。我可以通过简单地更改库类型来重现它!

我想我已经在这里找到了答案

https://github.com/emscripten-core/emscripten/wiki/Linking

标签: c++c++17clang++webassemblyemscripten

解决方案


因此,在我的情况下,解决方案是在动态添加库的 CMAKE 文件中找到事件并将其更改为静态链接,即

#set(LIB_TYPE SHARED)
set(LIB_TYPE STATIC) 

推荐阅读