use crate::format::{PackFooter, FOOTER_SIZE};
use crate::packer::{read_footer_from_sidecar, sidecar_path_for};
use std::path::{Path, PathBuf};
pub enum PackedMode {
#[cfg(target_os = "macos")]
Section {
manifest: Box<crate::format::PackManifest>,
checksum: u32,
assets_ptr: *const u8,
assets_size: usize,
},
Embedded {
exe_path: PathBuf,
footer: PackFooter,
},
Sidecar {
sidecar_path: PathBuf,
footer: PackFooter,
},
}
pub fn detect_packed_mode() -> Option<PackedMode> {
let exe_path = std::env::current_exe().ok()?;
#[cfg(target_os = "macos")]
{
if let Some(mode) = try_section_mode() {
return Some(mode);
}
}
if let Some(mode) = try_sidecar_mode(&exe_path) {
return Some(mode);
}
if let Some(mode) = try_embedded_mode(&exe_path) {
return Some(mode);
}
None
}
fn try_sidecar_mode(exe_path: &Path) -> Option<PackedMode> {
let sidecar = sidecar_path_for(exe_path);
if !sidecar.exists() {
return None;
}
let footer = read_footer_from_sidecar(&sidecar).ok()?;
Some(PackedMode::Sidecar {
sidecar_path: sidecar,
footer,
})
}
fn try_embedded_mode(exe_path: &Path) -> Option<PackedMode> {
let footer = read_footer_direct(exe_path).ok()?;
if footer.assets_offset > 0 {
Some(PackedMode::Embedded {
exe_path: exe_path.to_path_buf(),
footer,
})
} else {
None
}
}
fn read_footer_direct(path: &Path) -> crate::Result<PackFooter> {
use std::fs::File;
use std::io::{Read, Seek, SeekFrom};
let mut file = File::open(path)?;
let file_size = file.metadata()?.len();
if file_size < FOOTER_SIZE as u64 {
return Err(crate::PackError::InvalidMagic);
}
file.seek(SeekFrom::End(-(FOOTER_SIZE as i64)))?;
let mut buf = [0u8; FOOTER_SIZE];
file.read_exact(&mut buf)?;
PackFooter::from_bytes(&buf)
}
#[cfg(target_os = "macos")]
fn try_section_mode() -> Option<PackedMode> {
let embedded = read_embedded_section()?;
Some(PackedMode::Section {
manifest: Box::new(embedded.manifest),
checksum: embedded.header.checksum,
assets_ptr: embedded.assets_ptr,
assets_size: embedded.assets_size,
})
}
#[cfg(target_os = "macos")]
struct EmbeddedData {
header: crate::format::SectionHeader,
manifest: crate::format::PackManifest,
assets_ptr: *const u8,
assets_size: usize,
}
#[cfg(target_os = "macos")]
fn read_embedded_section() -> Option<EmbeddedData> {
use crate::format::{PackManifest, SectionHeader, SECTION_HEADER_SIZE, SECTION_MAGIC};
extern "C" {
fn getsectiondata(
mhp: *const MachHeader64,
segname: *const i8,
sectname: *const i8,
size: *mut usize,
) -> *const u8;
}
#[repr(C)]
struct MachHeader64 {
magic: u32,
cputype: i32,
cpusubtype: i32,
filetype: u32,
ncmds: u32,
sizeofcmds: u32,
flags: u32,
reserved: u32,
}
extern "C" {
fn _dyld_get_image_header(image_index: u32) -> *const MachHeader64;
}
unsafe {
let header = _dyld_get_image_header(0);
if header.is_null() {
return None;
}
let segname = c"__SMOLVM";
let sectname = c"__smolvm";
let mut size: usize = 0;
let data_ptr = getsectiondata(header, segname.as_ptr(), sectname.as_ptr(), &mut size);
if data_ptr.is_null() || size < SECTION_HEADER_SIZE {
return None;
}
let magic_bytes = std::slice::from_raw_parts(data_ptr, 8);
if magic_bytes != SECTION_MAGIC {
return None;
}
let header_bytes = std::slice::from_raw_parts(data_ptr, SECTION_HEADER_SIZE);
let section_header = SectionHeader::from_bytes(header_bytes).ok()?;
let expected_size = SECTION_HEADER_SIZE
+ section_header.manifest_size as usize
+ section_header.assets_size as usize;
if size < expected_size {
return None;
}
let manifest_start = data_ptr.add(SECTION_HEADER_SIZE);
let manifest_bytes =
std::slice::from_raw_parts(manifest_start, section_header.manifest_size as usize);
let manifest = PackManifest::from_json(manifest_bytes).ok()?;
let assets_ptr = manifest_start.add(section_header.manifest_size as usize);
Some(EmbeddedData {
header: section_header,
manifest,
assets_ptr,
assets_size: section_header.assets_size as usize,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_detect_returns_none_for_normal_binary() {
assert!(detect_packed_mode().is_none());
}
#[test]
fn test_read_footer_direct_rejects_short_file() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("short.bin");
std::fs::write(&path, b"too short").unwrap();
assert!(read_footer_direct(&path).is_err());
}
#[test]
fn test_read_footer_direct_rejects_invalid_magic() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("no_magic.bin");
std::fs::write(&path, [0u8; 128]).unwrap();
assert!(read_footer_direct(&path).is_err());
}
}