emscripten 链接全局命名符号乘法定义

emscripten Linking globals named symbol multiply defined

本文关键字:定义 符号 链接 全局 emscripten      更新时间:2023-10-16

我有一个c ++ CMAKE(版本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

来自堆栈溢出的另一个答案,即

符号乘法定义的可能原因不是"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 个"静态常量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 日

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

有问题的类是动态创建和加载的存档的一部分,即我在 CMAKE 部分中用于此库"set(LIB_TYPE SHARED("。 以下是 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

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

#set(LIB_TYPE SHARED)
set(LIB_TYPE STATIC)