问题描述
我正在学习Cython,并尝试运行一个简单的示例,在这里找到:making C library callable。
我使用VS 2019创建mylib.lib
和setup.py
来构建Cython扩展(有关详细信息和代码,请参见下文),但是链接器失败,并出现错误:
错误LNK2001:未解析的外部符号你好
但是当我运行其他帖子中发现的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
更多详细信息:
基于以下注释,我运行了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系统重建项目后(必须在“构建”>“配置管理器”中将“活动解决方案平台”更改为x64),这是dumpbin /symbols mylib.lib
的结果。我可以在摘要中看到hello
函数。
解决方法
为x64重建(静态)库。
您的库中的符号称为_hello
,而不是hello
,正如人们对x64构建所期望的那样(您的扩展名是为64位构建的,在日志中可以看到)。
在x64上,MSVC在编译为C代码时不会更改名称,因此生成的符号很简单-hello
,但在x86上却可以更改C名称(here documentation,{{3 }}(在godbolt.org上):
-
__cdecl
的调用约定(如果不使用特殊的编译标志,则为x86的默认约定)在名称前添加前缀_
,从而导致符号被称为_hello
。 -
__stdcall
的调用约定(如果使用/Gz
编译或明确指定了调用约定,即void __stdcall hello(char *)
),则在名称和后缀{{1 }}加上参数列表中的字节数,即它将导致_
。
因此,很明显,您的库是32位构建的,因此无法链接到64位dll。
如果该库是dll,则符号的名称会稍有不同。打电话
@
将导致(godbolt.org上的here an example):
-
_hello@4
(在__declspec( dllimport ) void hello(char* a);
和__imp__hello
之间有两个下划线),用于x86 / 32位,即由于imp
而引起的前缀hello
和前缀__imp_
由于在x86上declspec(dllimport)
的名称修改。 -
_
(在__cdecl
和__imp_hello
之间有一个下划线),用于x64 / 64位,即由于imp
而只有前缀hello
。
还有一种更直接的方法可以看到该库是32位的,方法是运行:
__imp_
这将为32位版本生成declspec(dllimport)
:
dumpbin /header mylibrary.lib
但{64为machine (x86)
:
...
File Type: LIBRARY
FILE HEADER VALUES
14C machine (x86)
...
此外,当机器不匹配时,MSVC链接器还用于发出警告:
mylibrary.lib:警告LNK4272:库机器类型'X86'冲突 目标计算机类型为“ x64”
不确定为什么它没有出现在您的日志中。