c++ - 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 的另一个答案,即
我确实知道我已经不止一次地定义了这个类,但是我的问题是我无法找到我在第二个定义中犯了错误的那个地方。在上一个答案中,该人有提示,即他犯了那个错误的 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/
这是错误的根源还是其他原因?为什么当我更改为静态时此错误消失。我可以通过简单地更改库类型来重现它!
我想我已经在这里找到了答案
解决方案
因此,在我的情况下,解决方案是在动态添加库的 CMAKE 文件中找到事件并将其更改为静态链接,即
#set(LIB_TYPE SHARED)
set(LIB_TYPE STATIC)
推荐阅读
- cloudflare - HTTP/2 服务器两次推送资产下载
- xslt-1.0 - 在 XSLT 模板匹配中使用谓词或层次结构 XPath 获得更好的性能
- ruby-on-rails - 如何在 Ruby 或 ROR 中使用 SHA256 散列编码 RSA-OAEP
- jquery - 复选框验证
- node.js - 在 Windows 上安装环回时出现问题
- wordpress - 删除特殊词 woocommerce 产品标题
- python - 使用 re.finditer 不会返回所有匹配项
- r - 如何用R从字符执行代码?
- excel - 如何使用变量单元格引用在不同的工作簿之间链接它们(变量:工作表名称)
- c# - Unity - 完成关卡时的意外行为