mirror of
https://github.com/morluto/rea.git
synced 2026-10-26 17:35:11 +00:00
* feat(native): inspect a universal Mach-O slice in Go and Rust metadata readers inspect_go_binary and inspect_rust_binary refused universal Mach-O files and offered no slice selection, so hosts without lipo could not inspect them. Both now accept architecture (x86, x86_64, arm, arm64). One selector reads REA's own FAT table, validates the slice range, alignment and header CPU, and returns the thin image with its macho-selected-image-v1 identity. The universal file stays the artifact; results add selected_slice and keep every source range a file offset. Missing, absent, duplicate or misplaced selections fail on architecture and list the slices present. Subject format mach-o-universal now has one owner shared by the services and the retained Go/Rust views. Fixes #4102 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(native): reject damaged universal files and self-contradicting slice reports A FAT table whose slice could not be read was treated as a non-universal file, so callers were told to omit architecture. Its reason now fails on path. Go and Rust reports with a selected slice must be Mach-O and name the slice's CPU family, through one shared agreement check. The CLI architecture flag is described in help. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
63 lines
2.2 KiB
TypeScript
63 lines
2.2 KiB
TypeScript
/** One thin Mach-O slice and the CPU identity its header and FAT entry declare. */
|
|
export interface UniversalSlice {
|
|
readonly bytes: Buffer;
|
|
readonly cpuType: number;
|
|
readonly cpuSubtype: number;
|
|
/** Declare a different CPU in the FAT table than in the slice header. */
|
|
readonly tableCpuType?: number;
|
|
}
|
|
|
|
/** The FAT32 container and where each slice landed. */
|
|
export interface UniversalMachoFixture {
|
|
readonly bytes: Buffer;
|
|
readonly slices: readonly {
|
|
readonly offset: number;
|
|
readonly size: number;
|
|
}[];
|
|
readonly tableBytes: number;
|
|
}
|
|
|
|
const ALIGNMENT_EXPONENT = 12;
|
|
|
|
/** Build a big-endian FAT32 universal Mach-O whose slices are 4 KiB aligned. */
|
|
export const universalMacho = (
|
|
slices: readonly UniversalSlice[],
|
|
): UniversalMachoFixture => {
|
|
const tableBytes = 8 + slices.length * 20;
|
|
const align = 2 ** ALIGNMENT_EXPONENT;
|
|
const placed: { offset: number; size: number }[] = [];
|
|
let offset = Math.ceil(tableBytes / align) * align;
|
|
for (const slice of slices) {
|
|
placed.push({ offset, size: slice.bytes.length });
|
|
offset = Math.ceil((offset + slice.bytes.length) / align) * align;
|
|
}
|
|
const bytes = Buffer.alloc(offset);
|
|
bytes.writeUInt32BE(0xcafebabe, 0);
|
|
bytes.writeUInt32BE(slices.length, 4);
|
|
for (const [index, slice] of slices.entries()) {
|
|
const { offset: sliceOffset, size } = placed[index]!;
|
|
const entry = 8 + index * 20;
|
|
bytes.writeUInt32BE(slice.tableCpuType ?? slice.cpuType, entry);
|
|
bytes.writeUInt32BE(slice.cpuSubtype, entry + 4);
|
|
bytes.writeUInt32BE(sliceOffset, entry + 8);
|
|
bytes.writeUInt32BE(size, entry + 12);
|
|
bytes.writeUInt32BE(ALIGNMENT_EXPONENT, entry + 16);
|
|
const image = Buffer.from(slice.bytes);
|
|
const little =
|
|
image.readUInt32BE(0) === 0xcffaedfe ||
|
|
image.readUInt32BE(0) === 0xcefaedfe;
|
|
if (little) {
|
|
image.writeUInt32LE(slice.cpuType, 4);
|
|
image.writeUInt32LE(slice.cpuSubtype, 8);
|
|
} else {
|
|
image.writeUInt32BE(slice.cpuType, 4);
|
|
image.writeUInt32BE(slice.cpuSubtype, 8);
|
|
}
|
|
image.copy(bytes, sliceOffset);
|
|
}
|
|
return { bytes, slices: placed, tableBytes };
|
|
};
|
|
|
|
export const CPU_X86_64 = 0x01000007;
|
|
export const CPU_ARM64 = 0x0100000c;
|