@@ -99,110 +99,112 @@ public enum ExecutableArchitecture: String {
9999 #endif
100100}
101101
102+ #if os(Windows)
102103extension Process {
103- #if os(Windows)
104- /// Resolves the filename from the `Path` environment variable and read its COFF header to determine the architecture
105- /// of the binary.
106- ///
107- /// - Parameters:
108- /// - cwd: The current working directory.
109- /// - env: A dictionary of the environment variables and their values. Usually of the parent shell.
110- /// - filename: The name of the file we are resolving the architecture of.
111- /// - Returns: The architecture of the file which was found in the `Path`.
112- static func readWindowsExecutableArchitecture(
113- cwd: AbsolutePath ? , envBlock: ProcessEnvironmentBlock , filename: String
114- ) -> ExecutableArchitecture {
115- let searchPaths = getEnvSearchPaths (
116- pathString: envBlock [ " Path " ] , currentWorkingDirectory: cwd)
117- guard
118- let filePath = lookupExecutablePath (
119- filename: filename, currentWorkingDirectory: cwd, searchPaths: searchPaths)
120- else {
121- return . unknown
122- }
123- guard let fileHandle = FileHandle ( forReadingAtPath: filePath. pathString) else {
124- return . unknown
125- }
126-
127- defer { fileHandle. closeFile ( ) }
128-
129- // Infering the architecture of a Windows executable from its COFF header involves the following:
130- // 1. Get the COFF header offset from the pointer located at the 0x3C offset (4 bytes long).
131- // 2. Jump to that offset and read the next 6 bytes.
132- // 3. The first 4 are the signature which should be equal to 0x50450000.
133- // 4. The last 2 are the machine architecture which can be infered from the value we get.
134- //
135- // The link below provides a visualization of the COFF header and the process to get to it.
136- // https://upload.wikimedia.org/wikipedia/commons/1/1b/Portable_Executable_32_bit_Structure_in_SVG_fixed.svg
137- guard ( try ? fileHandle. seek ( toOffset: 0x3C ) ) != nil else {
138- return . unknown
139- }
140- guard let offsetPointer = try ? fileHandle. read ( upToCount: 4 ) ,
141- offsetPointer. count == 4
142- else {
143- return . unknown
144- }
104+ /// Resolves the filename from the `Path` environment variable and read its COFF header to determine the architecture
105+ /// of the binary.
106+ ///
107+ /// - Parameters:
108+ /// - cwd: The current working directory.
109+ /// - env: A dictionary of the environment variables and their values. Usually of the parent shell.
110+ /// - filename: The name of the file we are resolving the architecture of.
111+ /// - Returns: The architecture of the file which was found in the `Path`.
112+ static func readWindowsExecutableArchitecture(
113+ cwd: AbsolutePath ? , envBlock: ProcessEnvironmentBlock , filename: String
114+ ) -> ExecutableArchitecture {
115+ let searchPaths = getEnvSearchPaths (
116+ pathString: envBlock [ " Path " ] , currentWorkingDirectory: cwd)
117+ guard
118+ let filePath = lookupExecutablePath (
119+ filename: filename, currentWorkingDirectory: cwd, searchPaths: searchPaths)
120+ else {
121+ return . unknown
122+ }
123+ guard let fileHandle = FileHandle ( forReadingAtPath: filePath. pathString) else {
124+ return . unknown
125+ }
145126
146- let peHeaderOffset = offsetPointer. withUnsafeBytes { $0. load ( as: UInt32 . self) }
127+ defer { fileHandle. closeFile ( ) }
128+
129+ // Infering the architecture of a Windows executable from its COFF header involves the following:
130+ // 1. Get the COFF header offset from the pointer located at the 0x3C offset (4 bytes long).
131+ // 2. Jump to that offset and read the next 6 bytes.
132+ // 3. The first 4 are the signature which should be equal to 0x50450000.
133+ // 4. The last 2 are the machine architecture which can be infered from the value we get.
134+ //
135+ // The link below provides a visualization of the COFF header and the process to get to it.
136+ // https://upload.wikimedia.org/wikipedia/commons/1/1b/Portable_Executable_32_bit_Structure_in_SVG_fixed.svg
137+ guard ( try ? fileHandle. seek ( toOffset: 0x3C ) ) != nil else {
138+ return . unknown
139+ }
140+ guard let offsetPointer = try ? fileHandle. read ( upToCount: 4 ) ,
141+ offsetPointer. count == 4
142+ else {
143+ return . unknown
144+ }
147145
148- guard ( try ? fileHandle. seek ( toOffset: UInt64 ( peHeaderOffset) ) ) != nil else {
149- return . unknown
150- }
151- guard let coffHeader = try ? fileHandle. read ( upToCount: 6 ) , coffHeader. count == 6 else {
152- return . unknown
153- }
146+ let peHeaderOffset = offsetPointer. withUnsafeBytes { $0. load ( as: UInt32 . self) }
154147
155- let signature = coffHeader. prefix ( 4 )
156- let machineBytes = coffHeader. suffix ( 2 )
148+ guard ( try ? fileHandle. seek ( toOffset: UInt64 ( peHeaderOffset) ) ) != nil else {
149+ return . unknown
150+ }
151+ guard let coffHeader = try ? fileHandle. read ( upToCount: 6 ) , coffHeader. count == 6 else {
152+ return . unknown
153+ }
157154
158- guard signature == Data ( [ 0x50 , 0x45 , 0x00 , 0x00 ] ) else {
159- return . unknown
160- }
155+ let signature = coffHeader. prefix ( 4 )
156+ let machineBytes = coffHeader. suffix ( 2 )
161157
162- let machine = machineBytes . withUnsafeBytes { $0 . load ( as : UInt16 . self ) }
163- return . fromPEMachineByte ( machine : Int32 ( machine ) )
158+ guard signature == Data ( [ 0x50 , 0x45 , 0x00 , 0x00 ] ) else {
159+ return . unknown
164160 }
165- #endif
166161
167- #if os(macOS)
168- static func readDarwinExecutableArchitecture(
169- cwd: AbsolutePath ? , envBlock: ProcessEnvironmentBlock , filename: String
170- ) -> ExecutableArchitecture {
171- let magicNumber : UInt32 = 0xcafe_babe
172-
173- let searchPaths = getEnvSearchPaths (
174- pathString: envBlock [ " PATH " ] , currentWorkingDirectory: cwd)
175- guard
176- let filePath = lookupExecutablePath (
177- filename: filename, currentWorkingDirectory: cwd, searchPaths: searchPaths)
178- else {
179- return . unknown
180- }
181- guard let fileHandle = FileHandle ( forReadingAtPath: filePath. pathString) else {
182- return . unknown
183- }
162+ let machine = machineBytes. withUnsafeBytes { $0. load ( as: UInt16 . self) }
163+ return . fromPEMachineByte( machine: Int32 ( machine) )
164+ }
165+ }
166+ #endif
184167
185- defer {
186- try ? fileHandle. close ( )
187- }
168+ #if os(macOS)
169+ extension Process {
170+ static func readDarwinExecutableArchitecture(
171+ cwd: AbsolutePath ? , envBlock: ProcessEnvironmentBlock , filename: String
172+ ) -> ExecutableArchitecture {
173+ let magicNumber : UInt32 = 0xcafe_babe
174+
175+ let searchPaths = getEnvSearchPaths (
176+ pathString: envBlock [ " PATH " ] , currentWorkingDirectory: cwd)
177+ guard
178+ let filePath = lookupExecutablePath (
179+ filename: filename, currentWorkingDirectory: cwd, searchPaths: searchPaths)
180+ else {
181+ return . unknown
182+ }
183+ guard let fileHandle = FileHandle ( forReadingAtPath: filePath. pathString) else {
184+ return . unknown
185+ }
188186
189- // The first 4 bytes of a Mach-O header contain the magic number. We use it to determine if the binary is
190- // universal.
191- // https://github.com/apple/darwin-xnu/blob/main/EXTERNAL_HEADERS/mach-o/loader.h
192- let magicData = fileHandle. readData ( ofLength: 4 )
193- let magic = magicData. withUnsafeBytes { $0. load ( as: UInt32 . self) . bigEndian }
187+ defer {
188+ try ? fileHandle. close ( )
189+ }
194190
195- if magic == magicNumber {
196- return . universal
197- }
191+ // The first 4 bytes of a Mach-O header contain the magic number. We use it to determine if the binary is
192+ // universal.
193+ // https://github.com/apple/darwin-xnu/blob/main/EXTERNAL_HEADERS/mach-o/loader.h
194+ let magicData = fileHandle. readData ( ofLength: 4 )
195+ let magic = magicData. withUnsafeBytes { $0. load ( as: UInt32 . self) . bigEndian }
198196
199- // If the binary is not universal, the next 4 bytes contain the CPU type.
200- guard ( try ? fileHandle. seek ( toOffset: 4 ) ) != nil else {
201- return . unknown
202- }
203- let cpuTypeData = fileHandle. readData ( ofLength: 4 )
204- let cpuType = cpuTypeData. withUnsafeBytes { $0. load ( as: Int32 . self) }
205- return . fromMachoCPUType( cpuType: cpuType)
197+ if magic == magicNumber {
198+ return . universal
206199 }
207- #endif
200+
201+ // If the binary is not universal, the next 4 bytes contain the CPU type.
202+ guard ( try ? fileHandle. seek ( toOffset: 4 ) ) != nil else {
203+ return . unknown
204+ }
205+ let cpuTypeData = fileHandle. readData ( ofLength: 4 )
206+ let cpuType = cpuTypeData. withUnsafeBytes { $0. load ( as: Int32 . self) }
207+ return . fromMachoCPUType( cpuType: cpuType)
208+ }
208209}
210+ #endif
0 commit comments