--- title: UE5编辑器开启ASAN date: 2026-05-04 22:45:02 excerpt: tags: rating: ⭐ --- UE5的编辑器直接开启ASAN会因为有全局变量初始化依赖问题导致误报阻塞。 虽然网上以及有人探究过底层原因以及解决方式:[https://zhuanlan.zhihu.com/p/14576791261](https://zhuanlan.zhihu.com/p/14576791261) 但是此解法需要修改大量代码而且容易因为UE的升级导致有新代码需要修改,非常不灵活。 后面公司的UE交流群里有同事列出更好的解决方式,就是在引擎启动后主动调用DLL的ASAN初始化函数。 以下是此修改方式是具体落地修改: ## 1. 修改Core.Build.cs Core.Build.cs添加DIA SDK依赖 ```cpp // DIA SDK if (Target.Platform == UnrealTargetPlatform.Win64) { string DiaSdkDir = Target.WindowsPlatform.DiaSdkDir; if (DiaSdkDir == null) { throw new System.Exception("Unable to find DIA SDK directory"); } string ToolChainDir = Target.WindowsPlatform.ToolChainDir; string ToolChainArch = (Target.Architecture == UnrealArch.Arm64) ? "arm64" : "x64"; PrivateIncludePaths.Add(Path.Combine(ToolChainDir, "atlmfc", "include")); PublicAdditionalLibraries.Add(Path.Combine(ToolChainDir, "atlmfc", "lib", ToolChainArch, "atls.lib")); string DiaArch = (Target.Architecture == UnrealArch.Arm64) ? "arm64" : "amd64"; PrivateIncludePaths.Add(Path.Combine(DiaSdkDir, "include")); PublicAdditionalLibraries.Add(Path.Combine(DiaSdkDir, "lib", DiaArch, "diaguids.lib")); RuntimeDependencies.Add("$(TargetOutputDir)/msdia140.dll", Path.Combine(DiaSdkDir, "bin", DiaArch, "msdia140.dll")); } ``` ## 2.新建下面文件到此路径里编译 [Engine/Source/Runtime/Core/Private/Windows/](https://km.netease.com/v4/detail/blog/254722),命名随意,如AsanInitializer.cpp ```cpp #include #include #include #include #include #include #include #include #include // ============================================================================ // Debug Print Utility // ============================================================================ template static void DebugPrint(const TCHAR(&format)[size], TArgs&&... args) { TCHAR msg[1024] = {}; int written = _stprintf_s(msg, format, std::forward(args)...); if (written <= 0) return; OutputDebugString(msg); } namespace fs = std::filesystem; EXTERN_C IMAGE_DOS_HEADER __ImageBase; // ============================================================================ // Symbol Resolution using DIA SDK // ============================================================================ class SimpleDiaCallback : public IDiaLoadCallback2 { LONG m_refCount = 0; public: HRESULT STDMETHODCALLTYPE QueryInterface(REFIID rid, void** ppUnk) override { if (!ppUnk) return E_INVALIDARG; if (rid == __uuidof(IDiaLoadCallback2) || rid == __uuidof(IDiaLoadCallback) || rid == __uuidof(IUnknown)) { *ppUnk = this; AddRef(); return S_OK; } *ppUnk = nullptr; return E_NOINTERFACE; } ULONG STDMETHODCALLTYPE AddRef() override { return ++m_refCount; } ULONG STDMETHODCALLTYPE Release() override { auto count = --m_refCount; if (count == 0) delete this; return count; } // 实现必要的回调方法 HRESULT STDMETHODCALLTYPE NotifyDebugDir(BOOL, DWORD, BYTE*) override { return S_OK; } HRESULT STDMETHODCALLTYPE NotifyOpenDBG(LPCOLESTR, HRESULT) override { return S_OK; } HRESULT STDMETHODCALLTYPE NotifyOpenPDB(LPCOLESTR, HRESULT) override { return S_OK; } HRESULT STDMETHODCALLTYPE RestrictRegistryAccess() override { return S_OK; } HRESULT STDMETHODCALLTYPE RestrictSymbolServerAccess() override { return S_OK; } HRESULT STDMETHODCALLTYPE RestrictOriginalPathAccess() override { return S_OK; } HRESULT STDMETHODCALLTYPE RestrictReferencePathAccess() override { return S_OK; } HRESULT STDMETHODCALLTYPE RestrictDBGAccess() override { return S_OK; } HRESULT STDMETHODCALLTYPE RestrictSystemRootAccess() override { return S_OK; } }; static DWORD GetSymbolOffset(HMODULE hModule, const wchar_t* name) { wchar_t moduleName[MAX_PATH] = {}; GetModuleFileNameW(hModule, moduleName, MAX_PATH); HRESULT hr = CoInitialize(nullptr); bool needUninitialize = SUCCEEDED(hr); IDiaDataSource* diaDataSource = nullptr; hr = CoCreateInstance(__uuidof(DiaSource), nullptr, CLSCTX_INPROC_SERVER, __uuidof(IDiaDataSource), (void**)&diaDataSource); if (FAILED(hr)) { if (needUninitialize) CoUninitialize(); return 0; } SimpleDiaCallback* callback = new SimpleDiaCallback(); callback->AddRef(); hr = diaDataSource->loadDataForExe(moduleName, nullptr, callback); if (FAILED(hr)) { callback->Release(); diaDataSource->Release(); if (needUninitialize) CoUninitialize(); return 0; } IDiaSession* session = nullptr; hr = diaDataSource->openSession(&session); if (FAILED(hr)) { callback->Release(); diaDataSource->Release(); if (needUninitialize) CoUninitialize(); return 0; } IDiaSymbol* globalSymbol = nullptr; hr = session->get_globalScope(&globalSymbol); DWORD result = 0; if (SUCCEEDED(hr)) { IDiaEnumSymbols* enumSymbols = nullptr; if (SUCCEEDED(globalSymbol->findChildren(SymTagFunction, name, nsRegularExpression, &enumSymbols))) { IDiaSymbol* symbol = nullptr; ULONG count = 0; while (SUCCEEDED(enumSymbols->Next(1, &symbol, &count)) && count == 1) { wchar_t* symbolName = nullptr; if (SUCCEEDED(symbol->get_name(&symbolName)) && wcscmp(symbolName, name) == 0) { symbol->get_relativeVirtualAddress(&result); } symbol->Release(); } enumSymbols->Release(); } globalSymbol->Release(); } session->Release(); callback->Release(); diaDataSource->Release(); if (needUninitialize) CoUninitialize(); return result; } static FARPROC MyGetSymbolAddress(HMODULE hModule, const wchar_t* name) { DWORD offset = GetSymbolOffset(hModule, name); if (offset == 0) { return nullptr; } return (FARPROC)((char*)hModule + offset); } // ============================================================================ // ASAN Pre-Initialization // ============================================================================ static int ExecInitializeClonedVariables(HMODULE hModule) { using InitializeClonedVariablesFunc = int(); auto InitializeClonedVariables = (InitializeClonedVariablesFunc*)MyGetSymbolAddress(hModule, L"__asan_initialize_cloned_variables"); if (!InitializeClonedVariables) { return -1; } TCHAR moduleName[MAX_PATH] = {}; if (GetModuleFileName(hModule, moduleName, MAX_PATH) > 0) { DebugPrint(_T("AsanPreInit: Call InitializeClonedVariables on %s\n"), moduleName); } return InitializeClonedVariables(); } static std::vector GetModules(HANDLE hProcess) { size_t count = 2048; std::vector modHandles; DWORD cbNeeded; size_t loadedCount = 0; do { modHandles.resize(count); size_t size = modHandles.size() * sizeof(HMODULE); assert(size < MAXDWORD); if (!EnumProcessModules(hProcess, modHandles.data(), (DWORD)size, &cbNeeded)) { DebugPrint(_T("EnumProcessModules failed: %d\n"), GetLastError()); } loadedCount = cbNeeded / sizeof(HMODULE); if (loadedCount < modHandles.size()) { modHandles.resize(loadedCount); break; } count *= 2; } while (loadedCount <= 10000); return modHandles; } static std::unordered_set GetMainBinaries() { TCHAR executable[1024] = {}; GetModuleFileName(nullptr, executable, sizeof(executable)); fs::path execPath(executable); std::unordered_set binaries; for (auto& entry : fs::directory_iterator(execPath.parent_path())) { auto filename = entry.path().filename(); if (filename.extension() == ".dll") { binaries.emplace(filename.string()); } } return binaries; } __declspec(dllexport) bool AsanPreInit_Setup() { auto modules = GetModules(GetCurrentProcess()); auto binaries = GetMainBinaries(); HANDLE process = GetCurrentProcess(); for (auto& mod : modules) { if (!mod) continue; if (mod == (HMODULE)&__ImageBase) continue; char baseName[MAX_PATH] = {}; GetModuleBaseNameA(process, mod, baseName, sizeof(baseName)); // 简单过滤下除当前执行文件目录外的动态库 if (!binaries.count(baseName)) continue; ExecInitializeClonedVariables(mod); } return true; } class AsanPreInit { public: AsanPreInit() { AsanPreInit_Setup(); } }; static AsanPreInit g_Init; ``` ## 3.正常运行 首次运行可能会有DIA报错,按照下图方式修复 根本原因 系统注册表中找不到 DiaSource 的 CLSID,即msdia140.d11(或对应版本)没有被注册到 Windows COM 注册表中。 解决方案一:注册msdia*.dll (需要管理员权限) 以管理员身份运行 CMD,执行以下命令(根据你的 VS 版本选择): 1. VS2022 1. regsvr32 "C:\Program Files\Microsoft Visual Studio\2022\Professional\DIA SDK\bin\amd64\msdia140.dll" 2. VS2019 1. regsvr32 "C:\Program Files (x86) \Microsoft Visual Studio\2019\Professional\DIA SDK\bin\amd64\msdia140.dll" 3. VS2017 1. regsvr32 "C:\Program Files (x86) \Microsoft Visual Studio\2017\Professional\DIA SDK\bin\amd64\msdia140.dll" 注册成功后再运行代码即可