Chrome V49 SxS 加载问题分析与修复 Chrome V49 SxS 加载问题分析与修复概述本文详细记录了 Chrome V4949.0.2623.112在 ReactOS 上因 Side-by-Side (SxS) 程序集机制不完善导致加载失败的问题从根因分析到代码修复的完整过程。运行环境项目说明操作系统ReactOS (Debug 构建)虚拟机VirtualBox (VCI 镜像:ReactOS-Test.vdi)目标程序Chrome V49 (49.0.2623.112)目录结构C:\Chrome_V49\— Chrome.exe、Chrome.exe.manifestC:\Chrome_V49\49.0.2623.112\— chrome.dll、chrome_elf.dll、其他 DLL问题现象Chrome.exe 启动后立即崩溃调试日志显示LoadLibraryExW(chrome.dll) failing with status c0000135即STATUS_DLL_NOT_FOUND— Chrome 无法加载其核心模块chrome.dll。随后发生 NULL 指针解引用c0000005地址0x00000000进程崩溃。第一阶段SxS 清单分析1.1 提取嵌入清单使用 Windows APILoadLibraryExWFindResourceExW从 Chrome.exe 中提取RT_MANIFEST资源。Chrome.exe 嵌入清单资源 ID 1?xml version1.0 encodingUTF-8 standaloneyes?assemblyxmlnsurn:schemas-microsoft-com:asm.v1manifestVersion1.0!-- 依赖 1: Common Controls 6.0有 publicKeyToken走 WinSxS --dependencydependentAssemblyassemblyIdentitytypeWin32nameMicrosoft.Windows.Common-Controlsversion6.0.0.0processorArchitecture*publicKeyToken6595b64144ccf1dflanguage*//dependentAssembly/dependency!-- 依赖 2: 49.0.2623.112 私有程序集无 publicKeyToken走私有路径 --dependencydependentAssemblyassemblyIdentitytypewin32name49.0.2623.112version49.0.2623.112language*//dependentAssembly/dependencytrustInfo.../trustInfocompatibility.../compatibility/assembly外部Chrome.exe.manifest与嵌入清单类似但缺少49.0.2623.112的依赖声明。49.0.2623.112.manifest位于子目录中assemblymanifestVersion1.0assemblyIdentityname49.0.2623.112version49.0.2623.112typewin32/filenamechrome_elf.dll/filenamekasko.dll//assembly关键发现chrome.dll不在 SxS 清单中。只有chrome_elf.dll和kasko.dll在 SxS 的 DLL 重定向清单中。这意味着 SxS 的 DLL 重定向机制对chrome.dll无效。1.2 其他模块清单模块资源 ID依赖chrome.dllRT_MANIFEST #2Common Controls 6.0chrome_child.dllRT_MANIFEST #2仅有 trustInfo1.3 SxS 安全项验证分析 ReactOS 的 SxS 实现代码sdk/lib/rtl/actctx.c潜在问题验证结果type属性大小写敏感安全— 解析器原样存储不做比较缺少publicKeyToken安全— 所有属性都是可选的lookup_winsxs正确返回STATUS_NO_SUCH_FILE私有程序集路径搜索安全—lookup_assembly正确搜索appdir\name\name.manifestDLL 重定向路径解析安全—find_actctx_dll正确处理非 WinSxS 路径第二阶段调试日志分析2.1 首次运行日志LdrpInitializeProcessCompat: Found guid for winver 0x600 in manifest→ 激活上下文创建成功清单解析正常兼容性垫片设置 Vista 模式。LoadLibraryExW(chrome.dll) failing with status c0000135搜索路径C:\Chrome_V49;C:\ReactOS\System32;C:\ReactOS\system;C:\ReactOS;.; C:\ReactOS\bin;C:\ReactOS\System32;C:\ReactOS;C:\ReactOS\System32\Wbem49.0.2623.112\子目录不在搜索路径中2.2 关键发现SetDllDirectory未被调用LdrpDllDirectory.Length 0, Buffer NULLChrome 在 ReactOS 上没有调用SetDllDirectory导致版本子目录未被加入 DLL 搜索路径。而在 Windows 7 上Chrome 会通过此机制将49.0.2623.112\添加到搜索路径。2.3 Chrome 的两次尝试尝试调用结果1LoadLibraryExW(chrome.dll)❌ Length0找不到2LoadLibraryExW(49.0.2623.112/chrome.dll)✅ Length76找到3LoadLibraryExW(chrome.dll)❌ 再次失败随后崩溃第二次使用的相对路径49.0.2623.112/chrome.dll通过RtlDosSearchPath_U相对于 CWD 解析成功但 Chrome 继续尝试第三次裸名调用并最终崩溃。第三阶段根因分析3.1 搜索路径计算代码BaseComputeProcessDllPathdll/win32/kernel32/client/path.c构建搜索路径当SetDllDirectory已被调用BaseDllDirectory.Buffer ! NULLAppDir → SetDllDirectory → System32 → Windows → PATH当SetDllDirectory未被调用AppDir → System32 → Windows → CWD → PATH (安全模式) AppDir → CWD → System32 → Windows → PATH (非安全模式)Chrome 的情况属于后者搜索路径只有C:\Chrome_V49不包含子目录。3.2 LdrpBuildSearchPath 双重路径LdrpBuildSearchPathdll/ntdll/ldr/ldrutils.c会在 kernel32 传来的搜索路径前额外添加LdrpDllDirectory。但由于SetDllDirectory未被调用LdrpDllDirectory.Length 0不产生任何效果。3.3 核心结论ReactOS 缺少 Windows 7 的行为当进程激活上下文有私有程序集依赖时程序集子目录应被隐式添加到 DLL 搜索路径。在 Windows 7 上LoadLibrary(chrome.dll)的搜索顺序包括 DLL 搜索顺序DLL 重定向API SetsSxS 清单重定向← ReactOS 缺少此步骤的目录扩展已加载模块列表Known DLLs文件系统搜索第四阶段修复实现4.1 修复策略在LdrpResolveDllNamentdll中添加 Windows 7 风格的 fallback当标准 DLL 搜索失败时查询激活上下文中的私有程序集清单路径提取其目录并加入搜索路径重新搜索。4.2 修改代码文件dll/ntdll/ldr/ldrutils.c函数LdrpResolveDllName/* * Windows 7 behavior: if the DLL was not found in the standard search path, * try searching in private assembly subdirectories of the application directory. * The activation context stores assembly manifest paths; we extract their * directories and retry the search with those directories prepended. */if(DllName!wcschr(DllName,L\\)!wcschr(DllName,L/)){NTSTATUS ActCtxStatus;SIZE_T RetLen;ACTIVATION_CONTEXT_DETAILED_INFORMATION DetInfo;DWORD AsmIdx;/* 查询激活上下文详细信息获取程序集数量 */RtlZeroMemory(DetInfo,sizeof(DetInfo));ActCtxStatusRtlQueryInformationActivationContext(0,NULL,NULL,ActivationContextDetailedInformation,DetInfo,sizeof(DetInfo),RetLen);/* 处理缓冲区过小的情况 */if(ActCtxStatusSTATUS_BUFFER_TOO_SMALLRetLensizeof(DetInfo)){PACTIVATION_CONTEXT_DETAILED_INFORMATION pDet;pDetRtlAllocateHeap(LdrpHeap,0,RetLen);if(pDet){RtlZeroMemory(pDet,RetLen);ActCtxStatusRtlQueryInformationActivationContext(0,NULL,NULL,ActivationContextDetailedInformation,pDet,(ULONG)RetLen,RetLen);if(NT_SUCCESS(ActCtxStatus))DetInfo*pDet;RtlFreeHeap(LdrpHeap,0,pDet);}}if(NT_SUCCESS(ActCtxStatus)DetInfo.ulAssemblyCount1){/* 跳过索引 0主程序检查依赖程序集 */for(AsmIdx1;AsmIdxDetInfo.ulAssemblyCount;AsmIdx){BYTE AsmBuf[4096];PACTIVATION_CONTEXT_ASSEMBLY_DETAILED_INFORMATION AsmInfo(PVOID)AsmBuf;WCHAR*LastSlash,NewSearchPath[MAX_PATH];RtlZeroMemory(AsmBuf,sizeof(AsmBuf));ActCtxStatusRtlQueryInformationActivationContext(0,NULL,AsmIdx,AssemblyDetailedInformationInActivationContext,AsmInfo,sizeof(AsmBuf),RetLen);if(!NT_SUCCESS(ActCtxStatus))continue;if(!AsmInfo-lpAssemblyManifestPath)continue;/* 从清单路径中提取目录部分 */LastSlashwcsrchr(AsmInfo-lpAssemblyManifestPath,L\\);if(!LastSlash)continue;/* 构建: manifest_dir;original_search_path */RtlCopyMemory(NewSearchPath,AsmInfo-lpAssemblyManifestPath,(LastSlash-AsmInfo-lpAssemblyManifestPath1)*sizeof(WCHAR));NewSearchPath[LastSlash-AsmInfo-lpAssemblyManifestPath1]L\0;wcscat(NewSearchPath,L;);wcscat(NewSearchPath,DefaultPath);/* 用增强后的搜索路径重试 */LengthRtlDosSearchPath_U(NewSearchPath,DllName,NULL,BufSize,FullDllName-Buffer,BaseDllName-Buffer);if(LengthLengthBufSize){DPRINT1(LDR: Found %ws in assembly dir %ws\n,DllName,NewSearchPath);break;}}}}4.3 关键 APIAPI用途所在文件RtlQueryInformationActivationContext查询激活上下文信息sdk/lib/rtl/actctx.cActivationContextDetailedInformation获取程序集数量和 app 目录sdk/include/xdk/winnt_old.hAssemblyDetailedInformationInActivationContext获取单个程序集的清单路径同上RtlDosSearchPath_U按搜索路径搜索文件sdk/lib/rtl/path.c4.4 数据结构typedefstruct_ACTIVATION_CONTEXT_DETAILED_INFORMATION{DWORD dwFlags;DWORD ulFormatVersion;DWORD ulAssemblyCount;// 程序集总数含主程序DWORD ulRootManifestPathType;DWORD ulRootManifestPathChars;// ... 更多字段PCWSTR lpRootManifestPath;PCWSTR lpAppDirPath;}ACTIVATION_CONTEXT_DETAILED_INFORMATION;typedefstruct_ACTIVATION_CONTEXT_ASSEMBLY_DETAILED_INFORMATION{DWORD ulFlags;DWORD ulEncodedAssemblyIdentityLength;DWORD ulManifestPathType;DWORD ulManifestPathLength;// ...PCWSTR lpAssemblyEncodedAssemblyIdentity;PCWSTR lpAssemblyManifestPath;// 清单文件的完整路径PCWSTR lpAssemblyDirectoryName;// WinSxS 风格目录名DWORD ulFileCount;}ACTIVATION_CONTEXT_ASSEMBLY_DETAILED_INFORMATION;第五阶段测试结果5.1 修复后日志LDR: Found chrome.dll in assembly dir C:\Chrome_V49\49.0.2623.112\;→修复成功chrome.dll通过 SxS 程序集目录回退机制被正确找到。5.2 修复后的问题chrome.dll加载成功后Chrome 继续执行并触发了另一个独立的问题Assertion failed at ../ntoskrnl/fsrtl/filelock.c(1190): Result NT_SUCCESS(IoStatusBlock.Status)调用链Chrome.exe → NtLockFile → FatCommonLockControl → FsRtlProcessFileLock这是ReactOS FAT32 文件系统驱动fastfat.sys的文件锁NtLockFile实现存在缺陷属于独立的内核层问题不影响 SxS 修复本身的有效性。附录A. 涉及的 ReactOS 源代码文件文件修改状态说明dll/ntdll/ldr/ldrutils.c✅ 已修改添加 SxS 程序集目录回退搜索dll/win32/kernel32/client/loader.c✅ 已修改添加调试日志可移除sdk/lib/rtl/actctx.c❌ 未修改SxS 基础设施分析用dll/ntdll/rtl/libsupp.c❌ 未修改现有find_actctx_dll函数分析用dll/win32/kernel32/client/path.c❌ 未修改BaseComputeProcessDllPath分析用B. SxS 清单提取脚本使用 Python ctypes 调用 Windows APIFindResourceExW、LoadResource从 PE 文件中提取RT_MANIFEST资源。C. 参考文档Windows DLL 搜索顺序SxS 程序集搜索顺序私有程序集ReactOS CORE-10843 — SxS 激活上下文相关 bug