C++编译出现Unresolved external symbol错误的原因及解决方法
问题:C++下Direct Syscall实现的LNK2019错误排查与修复
问题场景
尝试实现一个简易的Direct Syscall,包含汇编文件和主源码文件。C语言编译正常,但切换为C++编译时出现LNK2019链接错误。
汇编代码
EXTERN wNtAllocateVirtualMemory: DWORD .CODE NtAllocateVirtualMemory proc mov r10, rcx mov eax, wNtAllocateVirtualMemory syscall ret NtAllocateVirtualMemory endp END
C++主源码文件
#include<Windows.h> #include<stdio.h> DWORD wNtAllocateVirtualMemory; typedef long NTSTATUS; typedef NTSTATUS* PNTSTATUS; extern "C" { NTSTATUS NtAllocateVirtualMemory(HANDLE ProcessHandle, PVOID BaseAddress, ULONG_PTR ZeroBits, PSIZE_T RegionSize, ULONG AllocationType, ULONG Protect); } int main() { PVOID allocBuffer = NULL; SIZE_T buffSize = 0x1000; HANDLE hNtdll = GetModuleHandleA("ntdll"); if (hNtdll == NULL) { perror("Fail to get handle"); return 0; } PVOID pNtAllocateVirtualMemory = GetProcAddress((HMODULE) hNtdll, "NtAllocateVirtualMemory"); wNtAllocateVirtualMemory = *((char*)pNtAllocateVirtualMemory + 4); NtAllocateVirtualMemory((HANDLE)-1, (PVOID*)&allocBuffer, (ULONG_PTR)0, &buffSize, (ULONG)(MEM_COMMIT | MEM_RESERVE), PAGE_EXECUTE_READWRITE); }
链接错误信息
Error LNK2019 unresolved external symbol wNtAllocateVirtualMemory referenced in function NtAllocateVirtualMemory syscall_direct <path>temp.obj 1
错误原因
核心问题是C++的*名字修饰(Name Mangling)*机制:
- C语言不会对全局变量、函数名进行额外修饰,汇编文件中引用的
wNtAllocateVirtualMemory能直接匹配C源码中定义的同名全局变量。 - C++编译器会为全局变量生成带有类型信息的修饰名称(比如MSVC下
wNtAllocateVirtualMemory会被修饰为?wNtAllocateVirtualMemory@@3KA),但汇编文件里仍在引用未修饰的wNtAllocateVirtualMemory,导致链接器无法找到匹配的符号,触发LNK2019错误。
修复方法
方法1:将全局变量声明为extern "C"(推荐)
在C++源码中,把全局变量的声明改为C语言兼容的格式,让编译器不进行名字修饰:
#include<Windows.h> #include<stdio.h> // 修改此处,添加extern "C" extern "C" DWORD wNtAllocateVirtualMemory; typedef long NTSTATUS; typedef NTSTATUS* PNTSTATUS; extern "C" { NTSTATUS NtAllocateVirtualMemory(HANDLE ProcessHandle, PVOID BaseAddress, ULONG_PTR ZeroBits, PSIZE_T RegionSize, ULONG AllocationType, ULONG Protect); } int main() { // 主函数代码不变 PVOID allocBuffer = NULL; SIZE_T buffSize = 0x1000; HANDLE hNtdll = GetModuleHandleA("ntdll"); if (hNtdll == NULL) { perror("Fail to get handle"); return 0; } PVOID pNtAllocateVirtualMemory = GetProcAddress((HMODULE) hNtdll, "NtAllocateVirtualMemory"); wNtAllocateVirtualMemory = *((char*)pNtAllocateVirtualMemory + 4); NtAllocateVirtualMemory((HANDLE)-1, (PVOID*)&allocBuffer, (ULONG_PTR)0, &buffSize, (ULONG)(MEM_COMMIT | MEM_RESERVE), PAGE_EXECUTE_READWRITE); }
这样C++编译器会按照C的规则处理该变量名,生成的符号与汇编文件中引用的wNtAllocateVirtualMemory完全匹配,链接即可正常完成。
方法2:在汇编中使用C++修饰后的符号(不推荐)
直接在汇编文件中替换为C++编译器生成的修饰名(比如MSVC下的?wNtAllocateVirtualMemory@@3KA),但这种方法严重依赖编译器的名字修饰规则,换用其他编译器(如GCC)或不同版本的MSVC时会失效,代码兼容性极差,因此不推荐使用。
内容的提问来源于stack exchange,提问作者Brian MJ
相关产品推荐
相关产品推荐

