From d67f6981f2945d502441cd469b907c4650c99e87 Mon Sep 17 00:00:00 2001 From: Alexander O'Mara Date: Sun, 19 Jan 2025 01:24:28 -0500 Subject: [PATCH] Universal typeOf --- macho/universal.test.ts | 7 ++++++ macho/universal.ts | 51 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/macho/universal.test.ts b/macho/universal.test.ts index c711e6c..9a5e27a 100644 --- a/macho/universal.test.ts +++ b/macho/universal.test.ts @@ -2,6 +2,7 @@ import { assertEquals } from '@std/assert'; import { fixtureMacho, fixtureMachos } from '../spec/fixture.ts'; import type { Architecture } from './architecture.ts'; import { Universal } from './universal.ts'; +import { MH_DYLIB, MH_EXECUTE } from '../const.ts'; const fixtures = fixtureMachos(); @@ -30,5 +31,11 @@ for (const { kind, arch, file, archs } of fixtures) { assertEquals(architectures.size, archs.size); uni.architectures(architectures); assertEquals(architectures.size, archs.size); + + if (/\.dylib$|\.framework\//i.test(file)) { + assertEquals(await Universal.typeOf(blob), MH_DYLIB); + } else { + assertEquals(await Universal.typeOf(blob), MH_EXECUTE); + } }); } diff --git a/macho/universal.ts b/macho/universal.ts index 2a13658..bebb403 100644 --- a/macho/universal.ts +++ b/macho/universal.ts @@ -300,4 +300,55 @@ export class Universal { public isSuspicious(): boolean { return this.mSuspicious; } + + /** + * Guess type of file. + * + * @param reader Reader object. + * @returns Zero if not a valid Mach-O or Universal. + */ + public static async typeOf(reader: Reader): Promise { + let data = await reader.slice(0, MachHeader.BYTE_LENGTH).arrayBuffer(); + if (data.byteLength !== MachHeader.BYTE_LENGTH) { + return 0; + } + let header = new MachHeader(data); + let arch1; + for (let tries = 3; tries--;) { + switch (header.magic) { + case MH_CIGAM: + case MH_CIGAM_64: + header = new MachHeader(data, 0, !header.littleEndian); + // Falls through. + case MH_MAGIC: + case MH_MAGIC_64: { + return header.filetype; + } + case FAT_CIGAM: + arch1 = new FatArch( + data, + FatHeader.BYTE_LENGTH, + !header.littleEndian, + ); + // Falls through. + case FAT_MAGIC: { + arch1 ??= new FatArch(data, FatHeader.BYTE_LENGTH); + const { offset } = arch1; + // deno-lint-ignore no-await-in-loop + data = await reader + .slice(offset, offset + header.byteLength) + .arrayBuffer(); + if (data.byteLength !== header.byteLength) { + return 0; + } + header = new MachHeader(data, 0, arch1.littleEndian); + continue; + } + default: { + return 0; + } + } + } + return 0; + } }