Skip to content

Commit 32767f4

Browse files
Fix runtime architecture detection logic in ANCM. (#63652)
1 parent 30cde1e commit 32767f4

File tree

5 files changed

+88
-36
lines changed

5 files changed

+88
-36
lines changed

src/Servers/IIS/AspNetCoreModuleV2/CommonLib/Environment.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,23 @@ std::wstring Environment::GetDllDirectoryValue()
133133
return expandedStr;
134134
}
135135

136+
ProcessorArchitecture Environment::GetCurrentProcessArchitecture()
137+
{
138+
// Use compile-time detection - we know which architectures we support
139+
// and this is the most reliable and efficient approach. IsWow64Process2
140+
// doesn't show the correct architecture when running under x64 emulation
141+
// on ARM64.
142+
#if defined(_M_ARM64)
143+
return ProcessorArchitecture::ARM64;
144+
#elif defined(_M_AMD64)
145+
return ProcessorArchitecture::AMD64;
146+
#elif defined(_M_IX86)
147+
return ProcessorArchitecture::x86;
148+
#else
149+
static_assert(false, "Unknown target architecture");
150+
#endif
151+
}
152+
136153
bool Environment::IsRunning64BitProcess()
137154
{
138155
// Check the bitness of the currently running process

src/Servers/IIS/AspNetCoreModuleV2/CommonLib/Environment.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
#include <string>
77
#include <optional>
8+
#include "ProcessorArchitecture.h"
89

910
class Environment
1011
{
@@ -23,6 +24,8 @@ class Environment
2324
static
2425
bool IsRunning64BitProcess();
2526
static
27+
ProcessorArchitecture GetCurrentProcessArchitecture();
28+
static
2629
HRESULT CopyToDirectory(const std::wstring& source, const std::filesystem::path& destination, bool cleanDest, const std::filesystem::path& directoryToIgnore, int& copiedFileCount);
2730
static
2831
bool CheckUpToDate(const std::wstring& source, const std::filesystem::path& destination, const std::wstring& extension, const std::filesystem::path& directoryToIgnore);

src/Servers/IIS/AspNetCoreModuleV2/CommonLib/HostFxrResolver.cpp

Lines changed: 38 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -411,7 +411,6 @@ HostFxrResolver::InvokeWhereToFindDotnet()
411411
HandleWrapper<InvalidHandleTraits> hThread;
412412
CComBSTR pwzDotnetName = nullptr;
413413
DWORD dwFilePointer = 0;
414-
BOOL fIsCurrentProcess64Bit = FALSE;
415414
DWORD dwExitCode = 0;
416415
STRU struDotnetSubstring;
417416
STRU struDotnetLocationsString;
@@ -426,6 +425,7 @@ HostFxrResolver::InvokeWhereToFindDotnet()
426425
securityAttributes.bInheritHandle = TRUE;
427426

428427
LOG_INFO(L"Invoking where.exe to find dotnet.exe");
428+
auto currentProcessArch = Environment::GetCurrentProcessArchitecture();
429429

430430
// Create a read/write pipe that will be used for reading the result of where.exe
431431
FINISHED_LAST_ERROR_IF(!CreatePipe(&hStdOutReadPipe, &hStdOutWritePipe, &securityAttributes, 0));
@@ -499,13 +499,9 @@ HostFxrResolver::InvokeWhereToFindDotnet()
499499
}
500500

501501
FINISHED_IF_FAILED(struDotnetLocationsString.CopyA(pzFileContents, dwNumBytesRead));
502-
503502
LOG_INFOF(L"where.exe invocation returned: '%ls'", struDotnetLocationsString.QueryStr());
504503

505-
fIsCurrentProcess64Bit = Environment::IsRunning64BitProcess();
506-
507-
LOG_INFOF(L"Current process bitness type detected as isX64=%d", fIsCurrentProcess64Bit);
508-
504+
// Look for a dotnet.exe that matches the current process architecture
509505
while (TRUE)
510506
{
511507
index = struDotnetLocationsString.IndexOf(L"\r\n", prevIndex);
@@ -518,37 +514,47 @@ HostFxrResolver::InvokeWhereToFindDotnet()
518514
// \r\n is two wchars, so add 2 here.
519515
prevIndex = index + 2;
520516

521-
LOG_INFOF(L"Processing entry '%ls'", struDotnetSubstring.QueryStr());
522-
523-
if (fIsCurrentProcess64Bit == IsX64(struDotnetSubstring.QueryStr()))
517+
ProcessorArchitecture dotnetArch = GetFileProcessorArchitecture(struDotnetSubstring.QueryStr());
518+
if (dotnetArch == currentProcessArch)
524519
{
525-
// The bitness of dotnet matched with the current worker process bitness.
520+
LOG_INFOF(L"Found dotnet.exe matching current process architecture (%ls) '%ls'",
521+
ProcessorArchitectureToString(dotnetArch),
522+
struDotnetSubstring.QueryStr());
523+
526524
return std::make_optional(struDotnetSubstring.QueryStr());
527525
}
526+
else
527+
{
528+
LOG_INFOF(L"Skipping dotnet.exe with non-matching architecture %ls (need %ls). '%ls'",
529+
ProcessorArchitectureToString(dotnetArch),
530+
ProcessorArchitectureToString(currentProcessArch),
531+
struDotnetSubstring.QueryStr());
532+
}
528533
}
529534

530535
Finished:
531536
return result;
532537
}
533538

534-
BOOL HostFxrResolver::IsX64(const WCHAR* dotnetPath)
539+
// Reads the PE header of the binary to determine its architecture.
540+
ProcessorArchitecture HostFxrResolver::GetFileProcessorArchitecture(const WCHAR* binaryPath)
535541
{
536542
// Errors while reading from the file shouldn't throw unless
537543
// file.exception(bits) is set
538-
std::ifstream file(dotnetPath, std::ios::binary);
544+
std::ifstream file(binaryPath, std::ios::binary);
539545
if (!file.is_open())
540546
{
541-
LOG_TRACEF(L"Failed to open file %ls", dotnetPath);
542-
return false;
547+
LOG_TRACEF(L"Failed to open file %ls", binaryPath);
548+
return ProcessorArchitecture::Unknown;
543549
}
544550

545551
// Read the DOS header
546552
IMAGE_DOS_HEADER dosHeader{};
547553
file.read(reinterpret_cast<char*>(&dosHeader), sizeof(dosHeader));
548554
if (dosHeader.e_magic != IMAGE_DOS_SIGNATURE) // 'MZ'
549555
{
550-
LOG_TRACEF(L"%ls is not a valid executable file (missing MZ header).", dotnetPath);
551-
return false;
556+
LOG_TRACEF(L"%ls is not a valid executable file (missing MZ header).", binaryPath);
557+
return ProcessorArchitecture::Unknown;
552558
}
553559

554560
// Seek to the PE header
@@ -559,32 +565,30 @@ BOOL HostFxrResolver::IsX64(const WCHAR* dotnetPath)
559565
file.read(reinterpret_cast<char*>(&peSignature), sizeof(peSignature));
560566
if (peSignature != IMAGE_NT_SIGNATURE) // 'PE\0\0'
561567
{
562-
LOG_TRACEF(L"%ls is not a valid PE file (missing PE header).", dotnetPath);
563-
return false;
568+
LOG_TRACEF(L"%ls is not a valid PE file (missing PE header).", binaryPath);
569+
return ProcessorArchitecture::Unknown;
564570
}
565571

566572
// Read the file header
567573
IMAGE_FILE_HEADER fileHeader{};
568574
file.read(reinterpret_cast<char*>(&fileHeader), sizeof(fileHeader));
569575

570-
// Read the optional header magic field
571-
WORD magic{};
572-
file.read(reinterpret_cast<char*>(&magic), sizeof(magic));
573-
574-
// Determine the architecture based on the magic value
575-
if (magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC)
576+
// Determine the architecture based on the machine type
577+
switch (fileHeader.Machine)
576578
{
577-
LOG_INFOF(L"%ls is 32-bit", dotnetPath);
578-
return false;
579-
}
580-
else if (magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC)
581-
{
582-
LOG_INFOF(L"%ls is 64-bit", dotnetPath);
583-
return true;
579+
case IMAGE_FILE_MACHINE_I386:
580+
LOG_INFOF(L"%ls is x86 (32-bit)", binaryPath);
581+
return ProcessorArchitecture::x86;
582+
case IMAGE_FILE_MACHINE_AMD64:
583+
LOG_INFOF(L"%ls is AMD64 (x64)", binaryPath);
584+
return ProcessorArchitecture::AMD64;
585+
case IMAGE_FILE_MACHINE_ARM64:
586+
LOG_INFOF(L"%ls is ARM64", binaryPath);
587+
return ProcessorArchitecture::ARM64;
588+
default:
589+
LOG_INFOF(L"%ls has unknown architecture (machine type: 0x%X)", binaryPath, fileHeader.Machine);
590+
return ProcessorArchitecture::Unknown;
584591
}
585-
586-
LOG_INFOF(L"%ls is unknown architecture %i", dotnetPath, fileHeader.Machine);
587-
return false;
588592
}
589593

590594
std::optional<fs::path>

src/Servers/IIS/AspNetCoreModuleV2/CommonLib/HostFxrResolver.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88
#include <filesystem>
99
#include <optional>
1010
#include <string>
11-
1211
#include "ErrorContext.h"
12+
#include "ProcessorArchitecture.h"
1313

1414
#define READ_BUFFER_SIZE 4096
1515

@@ -74,7 +74,7 @@ class HostFxrResolver
7474
const std::filesystem::path & requestedPath
7575
);
7676

77-
static BOOL IsX64(const WCHAR* dotnetPath);
77+
static ProcessorArchitecture GetFileProcessorArchitecture(const WCHAR* binaryPath);
7878

7979
struct LocalFreeDeleter
8080
{
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the MIT License. See License.txt in the project root for license information.
3+
4+
#pragma once
5+
6+
enum class ProcessorArchitecture
7+
{
8+
Unknown,
9+
x86,
10+
AMD64,
11+
ARM64
12+
};
13+
14+
inline const wchar_t* ProcessorArchitectureToString(ProcessorArchitecture arch)
15+
{
16+
switch (arch)
17+
{
18+
case ProcessorArchitecture::x86:
19+
return L"x86";
20+
case ProcessorArchitecture::AMD64:
21+
return L"AMD64";
22+
case ProcessorArchitecture::ARM64:
23+
return L"ARM64";
24+
case ProcessorArchitecture::Unknown:
25+
default:
26+
return L"Unknown";
27+
}
28+
}

0 commit comments

Comments
 (0)