在 Windows 上构建 C++ 项目时,Tensorflow 2.3 无法解析机器生成的文件中的外部符号

Posted

技术标签:

【中文标题】在 Windows 上构建 C++ 项目时,Tensorflow 2.3 无法解析机器生成的文件中的外部符号【英文标题】:Tensorflow 2.3 unresolved external symbols in machine-generated files when building C++ project on Windows 【发布时间】:2020-07-17 13:07:17 【问题描述】:

我想知道是否有人在 Windows 上成功链接了 Tensorflow 2.3 DLL。 我正在尝试在一个小型 VS2019 测试项目中集成一些 tensorflow 功能,看看我是否可以使用 tensorflow C++ API。

我设法使用 MSVC 14.16.27023 构建了 tensorflow,并按照官方链接中给出的说明进行操作: https://docs.bazel.build/versions/master/windows.html#build-c https://www.tensorflow.org/install/source_windows 我使用以下 Bazel 命令构建了 .dll 和 .lib: bazel build --config=opt //tensorflow:tensorflow.dll bazel build --config=opt //tensorflow:tensorflow.lib VS2019项目属性: 添加相关Additional Include Directories 添加Additional Library Directories,也就是path-to-tensorflow-source\bazel-bin\tensorflow,其中bazel-bin目录是构建系统生成的(即Bazel) 在Linker->Input->Additional Dependencies 添加生成的tensorflow.lib

源代码很小,由我在 Joe Antognini 的 example 中查找的几行代码组成:

#pragma once

#define NOMINMAX

#include "tensorflow/core/public/session.h"
#include "tensorflow/cc/ops/standard_ops.h"

int main()

  tensorflow::Scope root = tensorflow::Scope::NewRootScope();
  auto X = tensorflow::ops::Placeholder(root.WithOpName("x"), tensorflow::DT_FLOAT, 
                            tensorflow::ops::Placeholder::Shape( -1, 2 ));


正如 Ashley Tharp 的 guide 中所述,立即构建会导致链接器错误,并抱怨未解析的外部符号:

LNK2001 无法解析的外部符号“private: class tensorflow::Scope __cdecl tensorflow::Scope::WithOpNameImpl(... LNK2001 未解析的外部符号“public: static class tensorflow::Scope __cdecl tensorflow::Scope::NewRootScope(void)”... LNK2001 未解析的外部符号“public: __cdecl tensorflow::Scope::~Scope(void)”... LNK2001 未解析的外部符号“public: __cdecl tensorflow::ops::Placeholder::Placeholder(...

这是她的导游的解释:

发生这种情况的原因是您只能在 dll 中公开 60,000 个符号。这只是 dll 格式的一些限制。 tensorflow 库代码有超过 60000 个符号,因此作为构建此 dll 的程序员(dll 只是用于在运行时访问库的二进制文件),您将必须手动指示要公开的符号(如果它们尚未公开)。 Google 选择了一些默认设置,但它可能并不适合所有人。

并且,按照指南中的建议,我进入了 tensorflow 标头,并在相关符号前添加了 TF_EXPORT 宏,这是您常用的 DLL 导入-导出模式:

#ifdef TF_COMPILE_LIBRARY
#define TF_EXPORT __declspec(dllexport)
#else
#define TF_EXPORT __declspec(dllimport)
#endif  // TF_COMPILE_LIBRARY

上述解决方法适用于前 3 个错误:

// scope.h
~Scope(); // change to: TF_EXPORT ~Scope();
...
static Scope NewRootScope(); // change to: TF_EXPORT static Scope NewRootScope();
...
Scope WithOpNameImpl(const string& op_name) const; // change to TF_EXPORT Scope...

完成编辑后,我像bazel build --config=opt //tensorflow:tensorflow.lib那样重新运行bazel,错误消失了。

但是,当我尝试执行类似的操作来修复最后一个未解析的符号时,问题就出现了,即Placeholder。该符号位于array_ops.h,这是一个机器生成的文件。因此,一旦我尝试构建 .lib,我在文件中编辑的任何内容都会被覆盖并丢失。

问题:如何公开出现在机器生成文件中的符号?非常感谢您指出正确方向的指针,也许我遗漏了一些明显的东西。

【问题讨论】:

好问题,干得好! +1 【参考方案1】:

包含动态库tensorflow_cc.dll和导入库tensorflow_cc.dll.if.lib的文件夹也有两个文件:

tensorflow_filtered_def_file.def:包含导入符号 tensorflow_cc.dll-2.params :拥有所有内置库

现在,如果您在构建应用程序时有一些未解析的符号,您所要做的就是使用更新的 tensorflow_filtered_def_file.def 文件重建动态库。对于此文件,您必须添加缺少的符号,对于您的示例代码,它是:

EXPORTS
    ??1Scope@tensorflow@@QEAA@XZ
    ?NewRootScope@Scope@tensorflow@@SA?AV12@XZ
    ??0Placeholder@ops@tensorflow@@QEAA@AEBVScope@2@W4DataType@2@AEBUAttrs@012@@Z
    ?WithOpNameImpl@Scope@tensorflow@@AEBA?AV12@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z
    the rest symbols ...

更新.def文件后,调用以下命令重建dll:

link.exe /nologo /DLL /SUBSYSTEM:CONSOLE -defaultlib:advapi32.lib -DEFAULTLIB:advapi32.lib 
   -ignore:4221 /FORCE:MULTIPLE /MACHINE:X64 
    @bazel-out/x64_windows-opt/bin/tensorflow/tensorflow_cc.dll-2.params /OPT:ICF /OPT:REF 
    /DEF:bazel-out/x64_windows-opt/bin/tensorflow/tensorflow_filtered_def_file.def /ignore:4070

在重建 dll/lib 之前删除这些文件(它们只是只读的)。

更多信息here,请参阅 rafix07 评论。

【讨论】:

以上是关于在 Windows 上构建 C++ 项目时,Tensorflow 2.3 无法解析机器生成的文件中的外部符号的主要内容,如果未能解决你的问题,请参考以下文章

使用 CMake、Clang 和 Ninja 在 Windows 上构建 c++ 项目

buildbot C++ 在 Windows 上构建:使用 devenv.com、vcbuild.exe 还是 MSBuild.exe?

使用 Qt 在 OS X/Mac 上构建 C++ 库

如何使用CMake构建c++项目

如何在 Windows 上使用 Visual C++ 强制加载链接库

如何在 Windows 10 上使用 Visual Studio 2015 x64 配置和构建 Tesseract OCR C++