You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 01:05:04