diff --git a/intel_mac.rs b/intel_mac.rs
index d43465c..f3141b0 100644
--- a/intel_mac.rs
+++ b/intel_mac.rs
@@ -77,15 +77,24 @@ fn patch_command(cmd_type: u32, buf: &mut [u8], file_len: usize) {
}
}
-/// Find section data in an Intel Mac Mach-O executable
+/// Find section data in an Intel Mac Mach-O executable.
///
-/// Searches for the sentinel "<~sui-data~>" from the end of the executable,
-/// reads the data length, and returns a reference to the section data.
-///
-/// # Returns
-///
-/// Returns `Some(&[u8])` if section data is found, `None` otherwise
+/// Searches the current executable for the `<~sui-data~>` sentinel and
+/// returns the embedded payload, or `None` if no payload is present.
pub fn find_section() -> std::io::Result> {
+ let exe = std::env::current_exe()?;
+ find_section_in_file(&exe)
+}
+
+/// Find section data in an Intel Mac Mach-O image on disk.
+///
+/// Identical to [`find_section`] but reads from `path` instead of the current
+/// executable, so it can also recover the payload from a dylib loaded into
+/// the process. Used by `find_section_in_current_image` on x86_64 macOS,
+/// where the payload is appended past the Mach-O sections (rather than
+/// written as a real named section) and so cannot be read with
+/// `getsectiondata`.
+pub fn find_section_in_file(path: &std::path::Path) -> std::io::Result > {
use std::io::{Read, Seek, SeekFrom};
// Construct sentinel from reversed string to prevent it from existing as contiguous
@@ -100,8 +109,7 @@ pub fn find_section() -> std::io::Result > {
sentinel.extend_from_slice(&magic);
let sentinel = sentinel.as_slice();
- let exe = std::env::current_exe()?;
- let mut file = std::fs::File::open(exe)?;
+ let mut file = std::fs::File::open(path)?;
// Get file size
let file_size = file.seek(SeekFrom::End(0))?;
diff --git a/lib.rs b/lib.rs
index 9aca935..084eb0a 100644
--- a/lib.rs
+++ b/lib.rs
@@ -992,8 +992,46 @@ mod macho {
) -> std::io::Result > {
#[cfg(target_arch = "x86_64")]
{
- // TODO: Intel Mac dylib support
- Ok(None)
+ // Intel Mach-O embedding does not write a real named section
+ // (see `Macho::build` for x86_64): the payload is appended past
+ // __LINKEDIT and located at runtime by scanning for the
+ // `<~sui-data~>` sentinel. `getsectiondata` therefore cannot
+ // find it. Resolve the file backing the current image with
+ // `dladdr` and run the sentinel scan against that file instead.
+ use std::ffi::CStr;
+ use std::os::raw::c_char;
+ use std::path::PathBuf;
+
+ #[repr(C)]
+ #[allow(non_camel_case_types)]
+ struct Dl_info {
+ dli_fname: *const c_char,
+ dli_fbase: *mut std::ffi::c_void,
+ dli_sname: *const c_char,
+ dli_saddr: *mut std::ffi::c_void,
+ }
+
+ extern "C" {
+ fn dladdr(addr: *const std::ffi::c_void, info: *mut Dl_info) -> std::ffi::c_int;
+ }
+
+ let mut info: Dl_info = unsafe { std::mem::zeroed() };
+ let self_addr = find_section_in_current_image as *const std::ffi::c_void;
+ let ret = unsafe { dladdr(self_addr, &mut info) };
+ if ret == 0 || info.dli_fname.is_null() {
+ return Ok(None);
+ }
+
+ // SAFETY: `dladdr` succeeded with a non-null `dli_fname`, which
+ // points at a NUL-terminated path owned by dyld for the lifetime
+ // of the loaded image. We copy it out immediately.
+ let fname = unsafe { CStr::from_ptr(info.dli_fname) };
+ let Ok(fname) = fname.to_str() else {
+ return Ok(None);
+ };
+ let path = PathBuf::from(fname);
+
+ super::intel_mac::find_section_in_file(&path)
}
#[cfg(not(target_arch = "x86_64"))]