Skip to content

Commit 47bc64b

Browse files
use COFF header to infer python.exe architecture
1 parent 958e8eb commit 47bc64b

File tree

2 files changed

+74
-14
lines changed

2 files changed

+74
-14
lines changed

Package.resolved

Lines changed: 4 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Sources/SwiftDriver/Driver/Driver.swift

Lines changed: 70 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import class TSCBasic.DiagnosticsEngine
1717
import class TSCBasic.UnknownLocation
1818
import enum TSCBasic.ProcessEnv
1919
import func TSCBasic.withTemporaryDirectory
20+
import func TSCBasic.lookupExecutablePath
2021
import protocol TSCBasic.DiagnosticData
2122
import protocol TSCBasic.FileSystem
2223
import protocol TSCBasic.OutputByteStream
@@ -869,7 +870,7 @@ public struct Driver {
869870
#if os(Windows)
870871
if args[1] == "-repl" { // a `-` gets automatically added to `swift repl`.
871872
try Driver.checkIfMatchingPythonArch(
872-
executor: executor, env: env, diagnosticsEngine: diagnosticsEngine)
873+
cwd: ProcessEnv.cwd, env: env, diagnosticsEngine: diagnosticsEngine)
873874
}
874875
#endif
875876

@@ -1452,24 +1453,20 @@ extension Driver {
14521453
/// when running lldb (`0xC000007B`). Calling this function before invoking
14531454
/// lldb gives them a warning to help troublshoot the issue.
14541455
public static func checkIfMatchingPythonArch(
1455-
executor: DriverExecutor, env: [String: String], diagnosticsEngine: DiagnosticsEngine
1456+
cwd: AbsolutePath?, env: [String: String], diagnosticsEngine: DiagnosticsEngine
14561457
) throws {
14571458
#if arch(arm64)
1458-
let toolchainArchitecture = "arm64"
1459+
let toolchainArchitecture = ExecutableArchitecture.arm64
14591460
#elseif arch(x86_64)
1460-
let toolchainArchitecture = "amd64"
1461+
let toolchainArchitecture = ExecutableArchitecture.x64
14611462
#elseif arch(x86)
1462-
let toolchainArchitecture = "x86"
1463+
let toolchainArchitecture = ExecutableArchitecture.x86
14631464
#else
14641465
return;
14651466
#endif
1466-
let pythonArchitecture = try executor.checkNonZeroExit(
1467-
args: "python.exe", "-c", "import platform; print(platform.machine())",
1468-
environment: env
1469-
).trimmingCharacters(in: .whitespacesAndNewlines)
1470-
.lowercased()
1467+
let pythonArchitecture = readWindowsExecutableArchitecture(cwd: cwd, env: env)
14711468

1472-
if !pythonArchitecture.contains(toolchainArchitecture) {
1469+
if toolchainArchitecture != pythonArchitecture {
14731470
diagnosticsEngine.emit(
14741471
.warning(
14751472
"""
@@ -1479,6 +1476,68 @@ extension Driver {
14791476
"""))
14801477
}
14811478
}
1479+
1480+
enum ExecutableArchitecture: String {
1481+
case x86 = "X86"
1482+
case x64 = "X64"
1483+
case arm64 = "ARM64"
1484+
case unknown = "Unknown"
1485+
1486+
static func fromPEMachineByte(machine: UInt16) -> Self {
1487+
// https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#machine-types
1488+
switch machine {
1489+
case 0x014c: return .x86
1490+
case 0x8664: return .x64
1491+
case 0xAA64: return .arm64
1492+
default: return .unknown
1493+
}
1494+
}
1495+
}
1496+
1497+
static func readWindowsExecutableArchitecture(cwd: AbsolutePath?, env: [String: String])
1498+
-> ExecutableArchitecture
1499+
{
1500+
guard let pathEnv = env["Path"]?.split(separator: ";") else {
1501+
return .unknown
1502+
}
1503+
let searchPaths = pathEnv.compactMap { path in
1504+
try? AbsolutePath(validating: String(path))
1505+
}
1506+
guard let filePath = lookupExecutablePath(
1507+
filename: "python.exe", currentWorkingDirectory: cwd, searchPaths: searchPaths)
1508+
else {
1509+
return .unknown
1510+
}
1511+
guard let fileHandle = FileHandle(forReadingAtPath: filePath.pathString) else {
1512+
return .unknown
1513+
}
1514+
1515+
defer { fileHandle.closeFile() }
1516+
1517+
// https://upload.wikimedia.org/wikipedia/commons/1/1b/Portable_Executable_32_bit_Structure_in_SVG_fixed.svg
1518+
fileHandle.seek(toFileOffset: 0x3C)
1519+
guard let offsetPointer = try? fileHandle.read(upToCount: 4),
1520+
offsetPointer.count == 4
1521+
else {
1522+
return .unknown
1523+
}
1524+
1525+
let peHeaderOffset = offsetPointer.withUnsafeBytes { $0.load(as: UInt32.self) }
1526+
1527+
fileHandle.seek(toFileOffset: UInt64(peHeaderOffset))
1528+
guard let coffHeader = try? fileHandle.read(upToCount: 6), coffHeader.count == 6 else {
1529+
return .unknown
1530+
}
1531+
1532+
let signature = coffHeader.prefix(4)
1533+
let machineBytes = coffHeader.suffix(2)
1534+
1535+
guard signature == Data([0x50, 0x45, 0x00, 0x00]) else {
1536+
return .unknown
1537+
}
1538+
1539+
return .fromPEMachineByte(machine: machineBytes.withUnsafeBytes { $0.load(as: UInt16.self) })
1540+
}
14821541
}
14831542

14841543
extension Driver {

0 commit comments

Comments
 (0)