【问题标题】:"unresolved external symbol"-error when linking a Cython extension against a C library on Windows“未解析的外部符号” - 将 Cython 扩展与 Windows 上的 C 库链接时出错
【发布时间】:2020-12-24 05:58:30
【问题描述】:

我正在学习 Cython,并尝试运行此处找到的一个简单示例:making C library callable

我使用 VS 2019 创建了 mylib.libsetup.py 来构建 Cython 扩展(详细信息和代码见下文),但是链接器失败并出现错误:

错误 LNK2001:无法解析的外部符号 hello

然而,当我运行在其他帖子中发现的 nm mylib.lib 时,我可以看到符号 _hello 存在:

D:\Codes\git_folders\my_repository\Cython_test\lib>nm mylib.lib
...
Debug/examples.obj:
...
00000000 T _hello
...

出了什么问题?


代码:

pyexamples.pyx

cdef extern from "examples.h":
    void hello(const char *name)

def py_hello(name: bytes) -> None:
    hello(name)

setup.py

from distutils.core import setup
from distutils.extension import Extension
from Cython.Build import cythonize

examples_extension = Extension(
    name="pyexamples",
    sources=["pyexamples.pyx"],
    libraries=["mylib"],
    library_dirs=["lib"],
    include_dirs=["lib"]
)
setup(
    name="pyexamples",
    ext_modules=cythonize([examples_extension])
)

examples.c

#include <stdio.h>
#include "examples.h"

void hello(const char *name) {
    printf("hello %s\n", name);
}

examples.h

#ifndef EXAMPLES_H
#define EXAMPLES_H

void hello(const char *name);

#endif

但是,如果我运行此命令, python setup.py build_ext --inplace

我收到了这个错误。

running build_ext
building 'pyexamples' extension
C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.27.29110\bin\HostX86\x64\cl.exe /c /nologo /Ox /W3 /GL /DNDEBUG /MD -Ilib -IC:\Users\swsyo\anaconda3\include -IC:\Users\swsyo\anaconda3\include "-IC:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.27.29110\ATLMFC\include" "-IC:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.27.29110\include" "-IC:\Program Files (x86)\Windows Kits\10\include\10.0.18362.0\ucrt" "-IC:\Program Files (x86)\Windows Kits\10\include\10.0.18362.0\shared" "-IC:\Program Files (x86)\Windows Kits\10\include\10.0.18362.0\um" "-IC:\Program Files (x86)\Windows Kits\10\include\10.0.18362.0\winrt" "-IC:\Program Files (x86)\Windows Kits\10\include\10.0.18362.0\cppwinrt" /Tcpyexamples.c /Fobuild\temp.win-amd64-3.7\Release\pyexamples.obj
pyexamples.c
C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.27.29110\bin\HostX86\x64\link.exe /nologo /INCREMENTAL:NO /LTCG /DLL /MANIFEST:EMBED,ID=2 /MANIFESTUAC:NO /LIBPATH:lib /LIBPATH:C:\Users\swsyo\anaconda3\libs /LIBPATH:C:\Users\swsyo\anaconda3\PCbuild\amd64 "/LIBPATH:C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.27.29110\ATLMFC\lib\x64" "/LIBPATH:C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.27.29110\lib\x64" "/LIBPATH:C:\Program Files (x86)\Windows Kits\10\lib\10.0.18362.0\ucrt\x64" "/LIBPATH:C:\Program Files (x86)\Windows Kits\10\lib\10.0.18362.0\um\x64" mylib.lib /EXPORT:PyInit_pyexamples build\temp.win-amd64-3.7\Release\pyexamples.obj /OUT:D:\git_folders\my_repository\Cython_test\pyexamples.cp37-win_amd64.pyd /IMPLIB:build\temp.win-amd64-3.7\Release\pyexamples.cp37-win_amd64.lib
   Creating library build\temp.win-amd64-3.7\Release\pyexamples.cp37-win_amd64.lib and object build\temp.win-amd64-3.7\Release\pyexamples.cp37-win_amd64.exp
pyexamples.obj : error LNK2001: unresolved external symbol hello
D:\git_folders\my_repository\Cython_test\pyexamples.cp37-win_amd64.pyd : fatal error LNK1120: 1 unresolved externals
error: command 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Community\\VC\\Tools\\MSVC\\14.27.29110\\bin\\HostX86\\x64\\link.exe' failed with exit status 1120

更多详情:

基于下面的 cmets,我运行了 dumpbin mylib.lib 命令来查看库中是否有 hello 函数。这是正确的方法吗?不过,我在此摘要中没有看到名称 hello

D:\Codes\git_folders\my_repository\Cython_test\lib>dumpbin mylib.lib
Microsoft (R) COFF/PE Dumper Version 14.27.29111.0
Copyright (C) Microsoft Corporation.  All rights reserved.


Dump of file mylib.lib

File Type: LIBRARY

  Summary

          E8 .chks64
         C94 .debug$S
          C8 .debug$T
         10A .drectve
           5 .msvcjmc
           A .rdata
           8 .rtc$IMZ
           8 .rtc$TMZ
         1B4 .text$mn

我还运行了在其他帖子中找到的nm mylib.lib。您可以在摘要末尾看到_hello

D:\Codes\git_folders\my_repository\Cython_test\lib>nm mylib.lib

Debug\mylib.obj:
00000000 N .chks64
00000000 N .debug$S
00000000 N .debug$S
00000000 N .debug$S
00000000 N .debug$T
00000000 i .drectve
00000000 d .msvcjmc
00000000 r .rtc$IMZ
00000000 r .rtc$TMZ
00000000 t .text$mn
00000000 t .text$mn
         U @__CheckForDebuggerJustMyCode@4
010471b7 a @comp.id
80000391 a @feat.00
00000000 d __87B4E6C0_mylib@c
00000000 T __JustMyCode_Default
         U __RTC_CheckEsp
         U __RTC_InitBase
00000000 r __RTC_InitBase.rtc$IMZ
         U __RTC_Shutdown
00000000 r __RTC_Shutdown.rtc$TMZ
00000000 T _fnmylib

Debug/examples.obj:
00000000 N .chks64
00000000 N .debug$S
00000000 N .debug$S
00000000 N .debug$S
00000000 N .debug$S
00000000 N .debug$S
00000000 N .debug$S
00000000 N .debug$T
00000000 i .drectve
00000000 d .msvcjmc
00000000 r .rdata
00000000 r .rtc$IMZ
00000000 r .rtc$TMZ
00000000 t .text$mn
00000000 t .text$mn
00000000 t .text$mn
00000000 t .text$mn
00000000 t .text$mn
00000000 R ??_C@_09DEHHIH@hello?5?$CFs?6@
00000008 C ?_OptionsStorage@?1??__local_stdio_printf_options@@9@9
         U @__CheckForDebuggerJustMyCode@4
010471b7 a @comp.id
80000391 a @feat.00
00000000 T ___local_stdio_printf_options
00000001 d __101834BA_corecrt_wstdio@h
00000003 d __2F33A99E_examples@c
00000002 d __AD6A91B7_stdio@h
00000000 d __F66CEB67_corecrt_stdio_config@h
         U __imp____acrt_iob_func
         U __imp____stdio_common_vfprintf
00000000 T __JustMyCode_Default
         U __RTC_CheckEsp
         U __RTC_InitBase
00000000 r __RTC_InitBase.rtc$IMZ
         U __RTC_Shutdown
00000000 r __RTC_Shutdown.rtc$TMZ
00000000 T __vfprintf_l
00000000 T _hello
00000000 T _printf

在我为 x64 系统重建项目后(我必须在 Build > Configuration Manager 中将“Active solution platform”更改为 x64),这是dumpbin /symbols mylib.lib 的结果。我可以在摘要中看到hello 函数。

【问题讨论】:

  • 目前看起来还不错。问题可能是图书馆。你究竟是如何建造它的?它是一个dll吗?您应该检查符号 hello 是否确实存在于库中。
  • @ead 感谢您的评论。我使用 VS2019 制作了一个静态库并按照指令 (docs.microsoft.com/en-us/cpp/build/…) 进行操作。我还制作了 dll 文件以防万一(不同的名称)。但是,无论您使用 dll 还是 lib,错误都是相同的。我将库文件放在文件夹名称“lib”中。 “检查库中真正存在的符号 hello”到底是什么意思?
  • 参见例如stackoverflow.com/q/305287/5769463可能是编译成c++的
  • @ead,我在上面编辑了我的问题,添加了检查库的结果。如何知道源文件被编译为 c++ ?当我们使用 *.c 和 *.h 在 VS 中编译为 c++ 时是否可以?
  • @ead,非常感谢。我成功地为 x64 构建了库。不知道VS默认系统架构是x86,不是x64。最后按预期编译 cython 代码。

标签: python c windows visual-studio cython


【解决方案1】:

为 x64 重建(静态)库。

您库中的符号称为 _hello 而不是 hello 就像人们对 x64 构建所期望的那样(您的扩展程序是为 64 位构建的,如日志中所示)。

在 x64 上,MSVC 在编译为 C 代码时不会破坏名称,因此生成的符号很简单 - hello,但它确实会在 x86 上破坏 C 名称(here documentationhere an example on godbolt.org ):

  • __cdecl-calling 约定(如果没有使用特殊的编译标志,则默认为 x86)在名称中添加前缀 _,这将导致符号被称为 _hello
  • __stdcall-调用约定(如果编译时使用/Gz或显式指定调用约定,即void __stdcall hello(char *))在名称中添加前缀_,后缀@与参数列表中的字节数,即这将导致_hello@4

因此,很明显,您的库是 32 位构建的,因此无法链接到 64 位 dll。


如果库是 dll,符号的名称会略有不同。调用

__declspec( dllimport ) void hello(char* a);

会导致(see live on godbolt.org):

  • __imp__helloimphello 之间的两个下划线)对于 x86/32 位,即前缀 __imp_ 由于 declspec(dllimport) 和前缀 _ 由于 x86 上 __cdecl 的名称修改。
  • 对于 x64/64 位,__imp_helloimphello 之间有一个下划线),即由于 declspec(dllimport) 而只有前缀 __imp_

还有一种更直接的方法可以看到,库是 32 位的,通过运行:

dumpbin /headers mylibrary.lib

这将为 32 位构建生成 machine (x86)

...
File Type: LIBRARY

FILE HEADER VALUES
             14C machine (x86)
...

machine (x64) 用于 64 位构建:

...
File Type: LIBRARY

FILE HEADER VALUES
            8664 machine (x64)

此外,MSVC 链接器用于在机器不匹配时产生警告:

mylibrary.lib:警告 LNK4272:库机器类型“X86”冲突 目标机器类型为“x64”

不知道为什么它没有出现在您的日志中。

【讨论】:

  • 非常感谢您的帮助。按照您的建议,我在 VS 2019 中成功编译了 cython 代码。
猜你喜欢
  • 2015-10-11
  • 2012-07-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-07-17
  • 2021-04-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多