Bug 1858509 add thread-safety annotations around MediaSourceDemuxer::mMonitor r=alwu
[gecko.git] / mozglue / tests / TestNativeNt.cpp
blob1ff98a0e2ef9efbf127ad860adc167a132e99e0f
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
7 #include "nscore.h"
8 #include "mozilla/NativeNt.h"
9 #include "mozilla/ThreadLocal.h"
10 #include "mozilla/UniquePtr.h"
11 #include "mozilla/WindowsEnumProcessModules.h"
13 #include <limits>
14 #include <stdio.h>
15 #include <windows.h>
16 #include <strsafe.h>
18 const wchar_t kNormal[] = L"Foo.dll";
19 const wchar_t kHex12[] = L"Foo.ABCDEF012345.dll";
20 const wchar_t kHex15[] = L"ABCDEF012345678.dll";
21 const wchar_t kHex16[] = L"ABCDEF0123456789.dll";
22 const wchar_t kHex17[] = L"ABCDEF0123456789a.dll";
23 const wchar_t kHex24[] = L"ABCDEF0123456789cdabef98.dll";
24 const wchar_t kHex8[] = L"01234567.dll";
25 const wchar_t kNonHex12[] = L"Foo.ABCDEFG12345.dll";
26 const wchar_t kHex13[] = L"Foo.ABCDEF0123456.dll";
27 const wchar_t kHex11[] = L"Foo.ABCDEF01234.dll";
28 const wchar_t kPrefixedHex16[] = L"Pabcdef0123456789.dll";
29 const uint32_t kTlsDataValue = 1234;
30 static MOZ_THREAD_LOCAL(uint32_t) sTlsData;
32 // Need non-inline functions to bypass compiler optimization that the thread
33 // local storage pointer is cached in a register before accessing a thread-local
34 // variable. See bug 1803322 for a motivating example.
35 MOZ_NEVER_INLINE uint32_t getTlsData() { return sTlsData.get(); }
36 MOZ_NEVER_INLINE void setTlsData(uint32_t x) { sTlsData.set(x); }
38 const char kFailFmt[] =
39 "TEST-FAILED | NativeNt | %s(%s) should have returned %s but did not\n";
41 #define RUN_TEST(fn, varName, expected) \
42 if (fn(varName) == !expected) { \
43 printf(kFailFmt, #fn, #varName, #expected); \
44 return 1; \
47 #define EXPECT_FAIL(fn, varName) RUN_TEST(fn, varName, false)
49 #define EXPECT_SUCCESS(fn, varName) RUN_TEST(fn, varName, true)
51 using namespace mozilla;
52 using namespace mozilla::nt;
54 bool TestVirtualQuery(HANDLE aProcess, LPCVOID aAddress) {
55 MEMORY_BASIC_INFORMATION info1 = {}, info2 = {};
56 SIZE_T result1 = ::VirtualQueryEx(aProcess, aAddress, &info1, sizeof(info1)),
57 result2 = mozilla::nt::VirtualQueryEx(aProcess, aAddress, &info2,
58 sizeof(info2));
59 if (result1 != result2) {
60 printf("TEST-FAILED | NativeNt | The returned values mismatch\n");
61 return false;
64 if (!result1) {
65 // Both APIs failed.
66 return true;
69 if (memcmp(&info1, &info2, result1) != 0) {
70 printf("TEST-FAILED | NativeNt | The returned structures mismatch\n");
71 return false;
74 return true;
77 // This class copies the self executable file to the %temp%\<outer>\<inner>
78 // folder. The length of its path is longer than MAX_PATH.
79 class LongNameModule {
80 wchar_t mOuterDirBuffer[MAX_PATH];
81 wchar_t mInnerDirBuffer[MAX_PATH * 2];
82 wchar_t mTargetFileBuffer[MAX_PATH * 2];
84 const wchar_t* mOuterDir;
85 const wchar_t* mInnerDir;
86 const wchar_t* mTargetFile;
88 public:
89 explicit LongNameModule(const wchar_t* aNewLeafNameAfterCopy)
90 : mOuterDir(nullptr), mInnerDir(nullptr), mTargetFile(nullptr) {
91 const wchar_t kFolderName160Chars[] =
92 L"0123456789ABCDEF0123456789ABCDEF"
93 L"0123456789ABCDEF0123456789ABCDEF"
94 L"0123456789ABCDEF0123456789ABCDEF"
95 L"0123456789ABCDEF0123456789ABCDEF"
96 L"0123456789ABCDEF0123456789ABCDEF";
97 UniquePtr<wchar_t[]> thisExe = GetFullBinaryPath();
98 if (!thisExe) {
99 return;
102 // If the buffer is too small, GetTempPathW returns the required
103 // length including a null character, while on a successful case
104 // it returns the number of copied characters which does not include
105 // a null character. This means len == MAX_PATH should never happen
106 // and len > MAX_PATH means GetTempPathW failed.
107 wchar_t tempDir[MAX_PATH];
108 DWORD len = ::GetTempPathW(MAX_PATH, tempDir);
109 if (!len || len >= MAX_PATH) {
110 return;
113 if (FAILED(::StringCbPrintfW(mOuterDirBuffer, sizeof(mOuterDirBuffer),
114 L"\\\\?\\%s%s", tempDir,
115 kFolderName160Chars)) ||
116 !::CreateDirectoryW(mOuterDirBuffer, nullptr)) {
117 return;
119 mOuterDir = mOuterDirBuffer;
121 if (FAILED(::StringCbPrintfW(mInnerDirBuffer, sizeof(mInnerDirBuffer),
122 L"\\\\?\\%s%s\\%s", tempDir,
123 kFolderName160Chars, kFolderName160Chars)) ||
124 !::CreateDirectoryW(mInnerDirBuffer, nullptr)) {
125 return;
127 mInnerDir = mInnerDirBuffer;
129 if (FAILED(::StringCbPrintfW(mTargetFileBuffer, sizeof(mTargetFileBuffer),
130 L"\\\\?\\%s%s\\%s\\%s", tempDir,
131 kFolderName160Chars, kFolderName160Chars,
132 aNewLeafNameAfterCopy)) ||
133 !::CopyFileW(thisExe.get(), mTargetFileBuffer,
134 /*bFailIfExists*/ TRUE)) {
135 return;
137 mTargetFile = mTargetFileBuffer;
140 ~LongNameModule() {
141 if (mTargetFile) {
142 ::DeleteFileW(mTargetFile);
144 if (mInnerDir) {
145 ::RemoveDirectoryW(mInnerDir);
147 if (mOuterDir) {
148 ::RemoveDirectoryW(mOuterDir);
152 operator const wchar_t*() const { return mTargetFile; }
155 // Make sure module info retrieved from nt::PEHeaders is the same as one
156 // retrieved from GetModuleInformation API.
157 bool CompareModuleInfo(HMODULE aModuleForApi, HMODULE aModuleForPEHeader) {
158 MODULEINFO moduleInfo;
159 if (!::GetModuleInformation(::GetCurrentProcess(), aModuleForApi, &moduleInfo,
160 sizeof(moduleInfo))) {
161 printf("TEST-FAILED | NativeNt | GetModuleInformation failed - %08lx\n",
162 ::GetLastError());
163 return false;
166 PEHeaders headers(aModuleForPEHeader);
167 if (!headers) {
168 printf("TEST-FAILED | NativeNt | Failed to instantiate PEHeaders\n");
169 return false;
172 Maybe<Range<const uint8_t>> bounds = headers.GetBounds();
173 if (!bounds) {
174 printf("TEST-FAILED | NativeNt | PEHeaders::GetBounds failed\n");
175 return false;
178 if (bounds->length() != moduleInfo.SizeOfImage) {
179 printf("TEST-FAILED | NativeNt | SizeOfImage does not match\n");
180 return false;
183 // GetModuleInformation sets EntryPoint to 0 for executables
184 // except the running self.
185 static const HMODULE sSelf = ::GetModuleHandleW(nullptr);
186 if (aModuleForApi != sSelf &&
187 !(headers.GetFileCharacteristics() & IMAGE_FILE_DLL)) {
188 if (moduleInfo.EntryPoint) {
189 printf(
190 "TEST-FAIL | NativeNt | "
191 "GetModuleInformation returned a non-zero entrypoint "
192 "for an executable\n");
193 return false;
196 // Cannot verify PEHeaders::GetEntryPoint.
197 return true;
200 // For a module whose entrypoint is 0 (e.g. ntdll.dll or win32u.dll),
201 // MODULEINFO::EntryPoint is set to 0, while PEHeaders::GetEntryPoint
202 // returns the imagebase (RVA=0).
203 intptr_t rvaEntryPoint =
204 moduleInfo.EntryPoint
205 ? reinterpret_cast<uintptr_t>(moduleInfo.EntryPoint) -
206 reinterpret_cast<uintptr_t>(moduleInfo.lpBaseOfDll)
207 : 0;
208 if (rvaEntryPoint < 0) {
209 printf("TEST-FAILED | NativeNt | MODULEINFO is invalid\n");
210 return false;
213 if (headers.RVAToPtr<FARPROC>(rvaEntryPoint) != headers.GetEntryPoint()) {
214 printf("TEST-FAILED | NativeNt | Entrypoint does not match\n");
215 return false;
218 return true;
221 bool TestModuleInfo() {
222 UNICODE_STRING newLeafName;
223 ::RtlInitUnicodeString(&newLeafName,
224 L"\u672D\u5E4C\u5473\u564C.\u30E9\u30FC\u30E1\u30F3");
226 LongNameModule longNameModule(newLeafName.Buffer);
227 if (!longNameModule) {
228 printf(
229 "TEST-FAILED | NativeNt | "
230 "Failed to copy the executable to a long directory path\n");
231 return 1;
235 nsModuleHandle module(::LoadLibraryW(longNameModule));
237 bool detectedTarget = false;
238 bool passedAllModules = true;
239 auto moduleCallback = [&](const wchar_t* aModulePath, HMODULE aModule) {
240 UNICODE_STRING modulePath, moduleName;
241 ::RtlInitUnicodeString(&modulePath, aModulePath);
242 GetLeafName(&moduleName, &modulePath);
243 if (::RtlEqualUnicodeString(&moduleName, &newLeafName,
244 /*aCaseInsensitive*/ TRUE)) {
245 detectedTarget = true;
248 if (!CompareModuleInfo(aModule, aModule)) {
249 passedAllModules = false;
253 if (!mozilla::EnumerateProcessModules(moduleCallback)) {
254 printf("TEST-FAILED | NativeNt | EnumerateProcessModules failed\n");
255 return false;
258 if (!detectedTarget) {
259 printf(
260 "TEST-FAILED | NativeNt | "
261 "EnumerateProcessModules missed the target file\n");
262 return false;
265 if (!passedAllModules) {
266 return false;
270 return true;
273 // Make sure PEHeaders works for a module loaded with LOAD_LIBRARY_AS_DATAFILE
274 // as well as a module loaded normally.
275 bool TestModuleLoadedAsData() {
276 const wchar_t kNewLeafName[] = L"\u03BC\u0061\u9EBA.txt";
278 LongNameModule longNameModule(kNewLeafName);
279 if (!longNameModule) {
280 printf(
281 "TEST-FAILED | NativeNt | "
282 "Failed to copy the executable to a long directory path\n");
283 return 1;
286 const wchar_t* kManualLoadModules[] = {
287 L"mshtml.dll",
288 L"shell32.dll",
289 longNameModule,
292 for (const auto moduleName : kManualLoadModules) {
293 // Must load a module as data first,
294 nsModuleHandle moduleAsData(::LoadLibraryExW(
295 moduleName, nullptr,
296 LOAD_LIBRARY_AS_DATAFILE | LOAD_LIBRARY_AS_IMAGE_RESOURCE));
298 // then load a module normally to map it on a different address.
299 nsModuleHandle module(::LoadLibraryW(moduleName));
301 if (!CompareModuleInfo(module.get(), moduleAsData.get())) {
302 return false;
305 PEHeaders peAsData(moduleAsData.get());
306 PEHeaders pe(module.get());
307 if (!peAsData || !pe) {
308 printf("TEST-FAIL | NativeNt | Failed to load the module\n");
309 return false;
312 if (peAsData.RVAToPtr<HMODULE>(0) == pe.RVAToPtr<HMODULE>(0)) {
313 printf(
314 "TEST-FAIL | NativeNt | "
315 "The module should have been mapped onto two different places\n");
316 return false;
319 const auto* pdb1 = peAsData.GetPdbInfo();
320 const auto* pdb2 = pe.GetPdbInfo();
321 if (pdb1 && pdb2) {
322 if (pdb1->pdbSignature != pdb2->pdbSignature ||
323 pdb1->pdbAge != pdb2->pdbAge ||
324 strcmp(pdb1->pdbFileName, pdb2->pdbFileName)) {
325 printf(
326 "TEST-FAIL | NativeNt | "
327 "PDB info from the same module did not match.\n");
328 return false;
330 } else if (pdb1 || pdb2) {
331 printf(
332 "TEST-FAIL | NativeNt | Failed to get PDB info from the module.\n");
333 return false;
336 uint64_t version1, version2;
337 bool result1 = peAsData.GetVersionInfo(version1);
338 bool result2 = pe.GetVersionInfo(version2);
339 if (result1 && result2) {
340 if (version1 != version2) {
341 printf("TEST-FAIL | NativeNt | Version mismatch\n");
342 return false;
344 } else if (result1 || result2) {
345 printf(
346 "TEST-FAIL | NativeNt | Failed to get PDB info from the module.\n");
347 return false;
351 return true;
354 LauncherResult<HMODULE> GetModuleHandleFromLeafName(const wchar_t* aName) {
355 UNICODE_STRING name;
356 ::RtlInitUnicodeString(&name, aName);
357 return nt::GetModuleHandleFromLeafName(name);
360 // Need a non-inline function to bypass compiler optimization that the thread
361 // local storage pointer is cached in a register before accessing a thread-local
362 // variable.
363 MOZ_NEVER_INLINE PVOID SwapThreadLocalStoragePointer(PVOID aNewValue) {
364 auto oldValue = RtlGetThreadLocalStoragePointer();
365 RtlSetThreadLocalStoragePointerForTestingOnly(aNewValue);
366 return oldValue;
369 #if defined(_M_X64)
370 bool TestCheckStack() {
371 auto stackBase = reinterpret_cast<uint8_t*>(RtlGetThreadStackBase());
372 auto stackLimit = reinterpret_cast<uint8_t*>(RtlGetThreadStackLimit());
373 uint8_t* stackPointer = nullptr;
374 asm volatile("mov %%rsp, %0;" : "=r"(stackPointer));
375 if (!(stackLimit < stackBase && stackLimit <= stackPointer &&
376 stackPointer < stackBase)) {
377 printf("TEST-FAIL | NativeNt | Stack addresses are not coherent.\n");
378 return false;
380 uintptr_t committedBytes = stackPointer - stackLimit;
381 const uint32_t maxExtraCommittedBytes = 0x10000;
382 if ((committedBytes + maxExtraCommittedBytes) >
383 std::numeric_limits<uint32_t>::max()) {
384 printf(
385 "TEST-FAIL | NativeNt | The stack limit is too high to perform the "
386 "test.\n");
387 return false;
389 for (uint32_t extraSize = 0; extraSize < maxExtraCommittedBytes;
390 ++extraSize) {
391 CheckStack(static_cast<uint32_t>(committedBytes) + extraSize);
392 auto expectedNewLimit = stackLimit - ((extraSize + 0xFFF) & ~0xFFF);
393 if (expectedNewLimit != RtlGetThreadStackLimit()) {
394 printf(
395 "TEST-FAIL | NativeNt | CheckStack did not grow the stack "
396 "correctly (expected: %p, got: %p).\n",
397 expectedNewLimit, RtlGetThreadStackLimit());
398 return false;
401 return true;
403 #endif // _M_X64
405 int wmain(int argc, wchar_t* argv[]) {
406 UNICODE_STRING normal;
407 ::RtlInitUnicodeString(&normal, kNormal);
409 UNICODE_STRING hex12;
410 ::RtlInitUnicodeString(&hex12, kHex12);
412 UNICODE_STRING hex16;
413 ::RtlInitUnicodeString(&hex16, kHex16);
415 UNICODE_STRING hex24;
416 ::RtlInitUnicodeString(&hex24, kHex24);
418 UNICODE_STRING hex8;
419 ::RtlInitUnicodeString(&hex8, kHex8);
421 UNICODE_STRING nonHex12;
422 ::RtlInitUnicodeString(&nonHex12, kNonHex12);
424 UNICODE_STRING hex13;
425 ::RtlInitUnicodeString(&hex13, kHex13);
427 UNICODE_STRING hex11;
428 ::RtlInitUnicodeString(&hex11, kHex11);
430 UNICODE_STRING hex15;
431 ::RtlInitUnicodeString(&hex15, kHex15);
433 UNICODE_STRING hex17;
434 ::RtlInitUnicodeString(&hex17, kHex17);
436 UNICODE_STRING prefixedHex16;
437 ::RtlInitUnicodeString(&prefixedHex16, kPrefixedHex16);
439 EXPECT_FAIL(Contains12DigitHexString, normal);
440 EXPECT_SUCCESS(Contains12DigitHexString, hex12);
441 EXPECT_FAIL(Contains12DigitHexString, hex13);
442 EXPECT_FAIL(Contains12DigitHexString, hex11);
443 EXPECT_FAIL(Contains12DigitHexString, hex16);
444 EXPECT_FAIL(Contains12DigitHexString, nonHex12);
446 EXPECT_FAIL(IsFileNameAtLeast16HexDigits, normal);
447 EXPECT_FAIL(IsFileNameAtLeast16HexDigits, hex12);
448 EXPECT_SUCCESS(IsFileNameAtLeast16HexDigits, hex24);
449 EXPECT_SUCCESS(IsFileNameAtLeast16HexDigits, hex16);
450 EXPECT_SUCCESS(IsFileNameAtLeast16HexDigits, hex17);
451 EXPECT_FAIL(IsFileNameAtLeast16HexDigits, hex8);
452 EXPECT_FAIL(IsFileNameAtLeast16HexDigits, hex15);
453 EXPECT_FAIL(IsFileNameAtLeast16HexDigits, prefixedHex16);
455 if (RtlGetProcessHeap() != ::GetProcessHeap()) {
456 printf("TEST-FAILED | NativeNt | RtlGetProcessHeap() is broken\n");
457 return 1;
460 #ifdef HAVE_SEH_EXCEPTIONS
461 PVOID origTlsHead = nullptr;
462 bool isExceptionThrown = false;
463 // Touch sTlsData.get() several times to prevent the call to sTlsData.set()
464 // from being optimized out in PGO build.
465 printf("sTlsData#1 = %08x\n", getTlsData());
466 MOZ_SEH_TRY {
467 // Need to call SwapThreadLocalStoragePointer inside __try to make sure
468 // accessing sTlsData is caught by SEH. This is due to clang's design.
469 // https://bugs.llvm.org/show_bug.cgi?id=44174.
470 origTlsHead = SwapThreadLocalStoragePointer(nullptr);
471 setTlsData(~kTlsDataValue);
473 MOZ_SEH_EXCEPT(GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION
474 ? EXCEPTION_EXECUTE_HANDLER
475 : EXCEPTION_CONTINUE_SEARCH) {
476 isExceptionThrown = true;
478 SwapThreadLocalStoragePointer(origTlsHead);
479 printf("sTlsData#2 = %08x\n", getTlsData());
480 setTlsData(kTlsDataValue);
481 printf("sTlsData#3 = %08x\n", getTlsData());
482 if (!isExceptionThrown || getTlsData() != kTlsDataValue) {
483 printf(
484 "TEST-FAILED | NativeNt | RtlGetThreadLocalStoragePointer() is "
485 "broken\n");
486 return 1;
488 #endif
490 if (RtlGetCurrentThreadId() != ::GetCurrentThreadId()) {
491 printf("TEST-FAILED | NativeNt | RtlGetCurrentThreadId() is broken\n");
492 return 1;
495 const wchar_t kKernel32[] = L"kernel32.dll";
496 DWORD verInfoSize = ::GetFileVersionInfoSizeW(kKernel32, nullptr);
497 if (!verInfoSize) {
498 printf(
499 "TEST-FAILED | NativeNt | Call to GetFileVersionInfoSizeW failed with "
500 "code %lu\n",
501 ::GetLastError());
502 return 1;
505 auto verInfoBuf = MakeUnique<char[]>(verInfoSize);
507 if (!::GetFileVersionInfoW(kKernel32, 0, verInfoSize, verInfoBuf.get())) {
508 printf(
509 "TEST-FAILED | NativeNt | Call to GetFileVersionInfoW failed with code "
510 "%lu\n",
511 ::GetLastError());
512 return 1;
515 UINT len;
516 VS_FIXEDFILEINFO* fixedFileInfo = nullptr;
517 if (!::VerQueryValueW(verInfoBuf.get(), L"\\", (LPVOID*)&fixedFileInfo,
518 &len)) {
519 printf(
520 "TEST-FAILED | NativeNt | Call to VerQueryValueW failed with code "
521 "%lu\n",
522 ::GetLastError());
523 return 1;
526 const uint64_t expectedVersion =
527 (static_cast<uint64_t>(fixedFileInfo->dwFileVersionMS) << 32) |
528 static_cast<uint64_t>(fixedFileInfo->dwFileVersionLS);
530 PEHeaders k32headers(::GetModuleHandleW(kKernel32));
531 if (!k32headers) {
532 printf(
533 "TEST-FAILED | NativeNt | Failed parsing kernel32.dll's PE headers\n");
534 return 1;
537 uint64_t version;
538 if (!k32headers.GetVersionInfo(version)) {
539 printf(
540 "TEST-FAILED | NativeNt | Unable to obtain version information from "
541 "kernel32.dll\n");
542 return 1;
545 if (version != expectedVersion) {
546 printf(
547 "TEST-FAILED | NativeNt | kernel32.dll's detected version "
548 "(0x%016llX) does not match expected version (0x%016llX)\n",
549 version, expectedVersion);
550 return 1;
553 Maybe<Span<IMAGE_THUNK_DATA>> iatThunks =
554 k32headers.GetIATThunksForModule("kernel32.dll");
555 if (iatThunks) {
556 printf(
557 "TEST-FAILED | NativeNt | Detected the IAT thunk for kernel32 "
558 "in kernel32.dll\n");
559 return 1;
562 const mozilla::nt::CodeViewRecord70* debugInfo = k32headers.GetPdbInfo();
563 if (!debugInfo) {
564 printf(
565 "TEST-FAILED | NativeNt | Unable to obtain debug information from "
566 "kernel32.dll\n");
567 return 1;
570 #ifndef WIN32 // failure on windows10x32
571 if (stricmp(debugInfo->pdbFileName, "kernel32.pdb")) {
572 printf(
573 "TEST-FAILED | NativeNt | Unexpected PDB filename "
574 "in kernel32.dll: %s\n",
575 debugInfo->pdbFileName);
576 return 1;
578 #endif
580 PEHeaders ntdllheaders(::GetModuleHandleW(L"ntdll.dll"));
582 auto ntdllBoundaries = ntdllheaders.GetBounds();
583 if (!ntdllBoundaries) {
584 printf(
585 "TEST-FAILED | NativeNt | "
586 "Unable to obtain the boundaries of ntdll.dll\n");
587 return 1;
590 iatThunks =
591 k32headers.GetIATThunksForModule("ntdll.dll", ntdllBoundaries.ptr());
592 if (!iatThunks) {
593 printf(
594 "TEST-FAILED | NativeNt | Unable to find the IAT thunk for "
595 "ntdll.dll in kernel32.dll\n");
596 return 1;
599 // To test the Ex version of API, we purposely get a real handle
600 // instead of a pseudo handle.
601 nsAutoHandle process(
602 ::OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, GetCurrentProcessId()));
603 if (!process) {
604 printf("TEST-FAILED | NativeNt | OpenProcess() failed - %08lx\n",
605 ::GetLastError());
606 return 1;
609 // Test Null page, Heap, Mapped image, and Invalid handle
610 if (!TestVirtualQuery(process, nullptr) || !TestVirtualQuery(process, argv) ||
611 !TestVirtualQuery(process, kNormal) ||
612 !TestVirtualQuery(nullptr, kNormal)) {
613 return 1;
616 auto moduleResult = GetModuleHandleFromLeafName(kKernel32);
617 if (moduleResult.isErr() ||
618 moduleResult.inspect() != k32headers.template RVAToPtr<HMODULE>(0)) {
619 printf(
620 "TEST-FAILED | NativeNt | "
621 "GetModuleHandleFromLeafName returns a wrong value.\n");
622 return 1;
625 moduleResult = GetModuleHandleFromLeafName(L"invalid");
626 if (moduleResult.isOk()) {
627 printf(
628 "TEST-FAILED | NativeNt | "
629 "GetModuleHandleFromLeafName unexpectedly returns a value.\n");
630 return 1;
633 if (!TestModuleInfo()) {
634 return 1;
637 if (!TestModuleLoadedAsData()) {
638 return 1;
641 #if defined(_M_X64)
642 if (!TestCheckStack()) {
643 return 1;
645 #endif // _M_X64
647 printf("TEST-PASS | NativeNt | All tests ran successfully\n");
648 return 0;