Only scan bundle execuable macho file

This commit is contained in:
Sam Carlton 2021-02-06 22:03:30 -06:00
parent 148f1c41b1
commit 879d27dcb4

View file

@ -279,8 +279,26 @@ export default class AppFilesScanner {
return await parseMacho( machOFile ) return await parseMacho( machOFile )
} }
classifyArchitecture () { getBundleExecutablePath ( info ) {
if ( info.CFBundleExecutable.includes('/') ) return `/Contents/${ info.CFBundleExecutable }`
return `/Contents/MacOS/${ info.CFBundleExecutable }`
}
classifyBinaryEntryArchitecture ( binaryEntry ) {
// Find an ARM Architecture
const armArchitecture = binaryEntry.architectures.find( architecture => {
// if ( architecture.processorType === 0 ) return false
// If processorType not a string
// then return false
if ( !isString(architecture.processorType) ) return false
return architecture.processorType.toLowerCase().includes('arm')
})
// Was an ARM Architecture found
return (armArchitecture !== undefined)
} }
async submitScanInfo ({ async submitScanInfo ({
@ -413,8 +431,6 @@ export default class AppFilesScanner {
// Parse the Info.plist data // Parse the Info.plist data
const info = plist.parse( infoXml ) const info = plist.parse( infoXml )
// console.log('info', info)
file.appVersion = info.CFBundleShortVersionString file.appVersion = info.CFBundleShortVersionString
file.displayName = info.CFBundleDisplayName file.displayName = info.CFBundleDisplayName
@ -444,86 +460,65 @@ export default class AppFilesScanner {
console.log(`Parsing Macho ${ file.machOEntries.length } files`) console.log(`Parsing Macho ${ file.machOEntries.length } files`)
const parsedMachoEntries = await Promise.all( file.machOEntries.map( async ( machOEntry, machEntryIndex ) => { // console.log('info.CFBundleExecutable', info.CFBundleExecutable)
console.log('Parsing ', machOEntry.filename, machOEntry.uncompressedSize / 1000 ) // console.log('info', info)
// console.log('file.machOEntries', file.machOEntries)
if ( machEntryIndex === 0 ) { const bundelExecutablePath = this.getBundleExecutablePath( info )
file.displayBinarySize = prettyBytes( machOEntry.uncompressedSize )
file.binarySize = machOEntry.uncompressedSize const bundleExecutables = file.machOEntries.filter( machoEntry => {
return machoEntry.filename.includes(bundelExecutablePath)
})
// Warn if Bundle Executable doesn't look right
if ( bundleExecutables.length > 1) {
console.warn('More than one root bundleExecutable found', bundleExecutables)
} else if ( bundleExecutables.length === 0 ) {
console.warn('No root bundleExecutable found', bundleExecutables)
}
const [ bundleExecutable ] = bundleExecutables
console.log('Parsing ', bundleExecutable.filename, bundleExecutable.uncompressedSize / 1000 )
file.displayBinarySize = prettyBytes( bundleExecutable.uncompressedSize )
file.binarySize = bundleExecutable.uncompressedSize
// Get blob data from zip
// https://gildas-lormeau.github.io/zip.js/core-api.html#zip-entry
const bundleExecutableBlob = await bundleExecutable.getData(
// writer
// https://gildas-lormeau.github.io/zip.js/core-api.html#zip-writing
new zip.BlobWriter(),
// options
{
useWebWorkers: true
} }
)
// Get blob data from zip const mainExecutableMeta = await this.parseMachOBlob( bundleExecutableBlob, file.name )
// https://gildas-lormeau.github.io/zip.js/core-api.html#zip-entry console.log( 'mainExecutableMeta', mainExecutableMeta )
const machOBlob = await machOEntry.getData(
// writer
// https://gildas-lormeau.github.io/zip.js/core-api.html#zip-writing
// new zip.TextWriter(),
new zip.BlobWriter(),
// options
{
useWebWorkers: true,
// onprogress: (index, max) => {
// const percentageNumber = (index / max * 100)
// // onprogress callback
// console.log(`Writer progress ${percentageNumber}`)
// }
}
)
return await this.parseMachOBlob( machOBlob, file.name ) const binarySupportsNative = this.classifyBinaryEntryArchitecture( mainExecutableMeta )
} ) )
// console.log('parsedMachoEntries', parsedMachoEntries)
// file.statusMessage = `🏁 Scan Finished. ${file.machOEntries.length} Mach-o files`
// file.statusMessage = `🏁 Scan Finished. `
console.log(`Searching ${ parsedMachoEntries.length } binaries for architecture info`)
let supportedBinaries = 0 if ( binarySupportsNative ) {
let unsupportedBinaries = 0
// Count supported and unsupported binaries
parsedMachoEntries.forEach( binaryEntry => {
const armBinary = binaryEntry.architectures.find( architecture => {
if ( architecture.processorType === 0 ) return false
return architecture.processorType.toLowerCase().includes('arm')
})
if ( armBinary !== undefined ) {
supportedBinaries++
} else {
unsupportedBinaries++
}
} )
console.log(`Found ${ supportedBinaries } supportedBinaries and ${unsupportedBinaries} unsupportedBinaries`)
// console.log('supportedBinaries', supportedBinaries)
// console.log('unsupportedBinaries', unsupportedBinaries)
if (supportedBinaries !== 0 && unsupportedBinaries !== 0) {
file.statusMessage = `🔶 App has some support. `
} else if ( unsupportedBinaries !== 0 ) {
file.statusMessage = `🔶 This app file is not natively compatible with Apple Silicon and may only run via Rosetta 2 translation, however, software vendors will sometimes will ship separate install files for Intel and ARM instead of a single one. `
} else if ( supportedBinaries !== 0 ) {
file.statusMessage = '✅ This app is natively compatible with Apple Silicon!' file.statusMessage = '✅ This app is natively compatible with Apple Silicon!'
// Shift this scan to the top // Shift this scan to the top
this.files.unshift( this.files.splice( scanIndex, 1 )[0] ) this.files.unshift( this.files.splice( scanIndex, 1 )[0] )
} else {
file.statusMessage = `🔶 This app file is not natively compatible with Apple Silicon and may only run via Rosetta 2 translation, however, software vendors will sometimes will ship separate install files for Intel and ARM instead of a single one. `
} }
// console.log('parsedMachoEntries', JSON.parse( JSON.stringify(parsedMachoEntries) ))
console.log( 'parsedMachoEntries', parsedMachoEntries )
this.submitScanInfo ({ this.submitScanInfo ({
filename: file.name, filename: file.name,
appVersion: file.appVersion, appVersion: file.appVersion,
result: file.statusMessage, result: file.statusMessage,
machoMeta: parsedMachoEntries.map( machoMeta => { machoMeta: {
...mainExecutableMeta,
const architectures = machoMeta.architectures.map( architecture => { file: undefined,
architectures: mainExecutableMeta.architectures.map( architecture => {
return { return {
bits: architecture.bits, bits: architecture.bits,
fileType: architecture.fileType, fileType: architecture.fileType,
@ -535,13 +530,7 @@ export default class AppFilesScanner {
processorType: architecture.processorType, processorType: architecture.processorType,
} }
}) })
},
return {
...machoMeta,
file: undefined, // Remove file
architectures
}
}),
infoPlist: info infoPlist: info
}) })
@ -593,12 +582,13 @@ export default class AppFilesScanner {
}) })
})) }))
// Go through and set all files to finished to clean up any straglers // Go through and set all files to finished to clean up any straglers
this.files.forEach( file => { this.files.forEach( file => {
file.status = 'finished' file.status = 'finished'
}) })
console.log('All Scans Finished')
return return
} }