From e3121bd5a110fdba0e88b869e3e43d645cb2458c Mon Sep 17 00:00:00 2001 From: bjorn3 Date: Sun, 14 Apr 2019 18:04:25 +0200 Subject: [PATCH 1/6] Rustfmt --- examples/find-debug.rs | 6 +-- src/lib.rs | 90 ++++++++++++++++++++---------------------- 2 files changed, 46 insertions(+), 50 deletions(-) diff --git a/examples/find-debug.rs b/examples/find-debug.rs index 3d0f734..54c356a 100644 --- a/examples/find-debug.rs +++ b/examples/find-debug.rs @@ -7,13 +7,13 @@ use std::fs::File; use std::io::Read; fn work() -> Result<(), failure::Error> { - let path = env::args_os().nth(1) + let path = env::args_os() + .nth(1) .ok_or(failure::err_msg("Usage: find-debug "))?; let mut f = File::open(&path)?; let mut buf = vec![]; f.read_to_end(&mut buf)?; - let obj = object::File::parse(&*buf) - .or(Err(failure::err_msg("Couldn't parse binary")))?; + let obj = object::File::parse(&*buf).or(Err(failure::err_msg("Couldn't parse binary")))?; let debug_path = moria::locate_debug_symbols(&obj, &path)?; println!("{}", debug_path.to_string_lossy()); Ok(()) diff --git a/src/lib.rs b/src/lib.rs index 207eab9..d4c8980 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,10 +5,10 @@ extern crate libc; extern crate object; extern crate uuid; -#[cfg(target_os="macos")] +#[cfg(target_os = "macos")] #[macro_use] extern crate core_foundation; -#[cfg(target_os="macos")] +#[cfg(target_os = "macos")] extern crate core_foundation_sys; use failure::Error; @@ -35,13 +35,14 @@ cfg_if! { } } -#[cfg(target_os="macos")] +#[cfg(target_os = "macos")] mod dsym { use core_foundation::array::{CFArray, CFArrayRef}; use core_foundation::base::{CFType, CFTypeRef, TCFType}; use core_foundation::string::CFString; - use core_foundation_sys::base::{CFAllocatorRef, CFIndex, CFOptionFlags, CFRelease, CFTypeID, - kCFAllocatorDefault}; + use core_foundation_sys::base::{ + kCFAllocatorDefault, CFAllocatorRef, CFIndex, CFOptionFlags, CFRelease, CFTypeID, + }; use core_foundation_sys::string::CFStringRef; use failure::{self, Error}; use libc::c_void; @@ -63,32 +64,30 @@ mod dsym { const kMDQuerySynchronous: CFOptionFlags = 1; #[link(name = "CoreServices", kind = "framework")] extern "C" { - #[link_name="\u{1}_MDQueryCreate"] - fn MDQueryCreate(allocator: CFAllocatorRef, - queryString: CFStringRef, - valueListAttrs: CFArrayRef, - sortingAttrs: CFArrayRef) - -> MDQueryRef; + #[link_name = "\u{1}_MDQueryCreate"] + fn MDQueryCreate( + allocator: CFAllocatorRef, + queryString: CFStringRef, + valueListAttrs: CFArrayRef, + sortingAttrs: CFArrayRef, + ) -> MDQueryRef; #[link_name = "\u{1}_MDQueryGetTypeID"] fn MDQueryGetTypeID() -> CFTypeID; #[link_name = "\u{1}_MDQueryExecute"] - fn MDQueryExecute(query: MDQueryRef, - optionFlags: CFOptionFlags) - -> Boolean; + fn MDQueryExecute(query: MDQueryRef, optionFlags: CFOptionFlags) -> Boolean; #[link_name = "\u{1}_MDQueryGetResultCount"] fn MDQueryGetResultCount(query: MDQueryRef) -> CFIndex; #[link_name = "\u{1}_MDQueryGetResultAtIndex"] - fn MDQueryGetResultAtIndex(query: MDQueryRef, - idx: CFIndex) - -> *const ::std::os::raw::c_void; + fn MDQueryGetResultAtIndex( + query: MDQueryRef, + idx: CFIndex, + ) -> *const ::std::os::raw::c_void; #[link_name = "\u{1}_MDItemCreate"] fn MDItemCreate(allocator: CFAllocatorRef, path: CFStringRef) -> MDItemRef; #[link_name = "\u{1}_MDItemGetTypeID"] pub fn MDItemGetTypeID() -> CFTypeID; #[link_name = "\u{1}_MDItemCopyAttribute"] - fn MDItemCopyAttribute(item: MDItemRef, - name: CFStringRef) - -> CFTypeRef; + fn MDItemCopyAttribute(item: MDItemRef, name: CFStringRef) -> CFTypeRef; #[link_name = "\u{1}_kMDItemPath"] static mut kMDItemPath: CFStringRef; } @@ -99,10 +98,12 @@ mod dsym { pub fn create(query_string: &str) -> Result { let cf_query_string = CFString::new(&query_string); let query = unsafe { - MDQueryCreate(kCFAllocatorDefault, - ctref(&cf_query_string), - ptr::null(), - ptr::null()) + MDQueryCreate( + kCFAllocatorDefault, + ctref(&cf_query_string), + ptr::null(), + ptr::null(), + ) }; if query == ptr::null_mut() { return Err(failure::err_msg("MDQueryCreate failed")); @@ -118,9 +119,7 @@ mod dsym { } impl Drop for MDQuery { fn drop(&mut self) { - unsafe { - CFRelease(self.as_CFTypeRef()) - } + unsafe { CFRelease(self.as_CFTypeRef()) } } } impl_TCFType!(MDQuery, MDQueryRef, MDQueryGetTypeID); @@ -128,16 +127,15 @@ mod dsym { struct MDItem(MDItemRef); impl Drop for MDItem { fn drop(&mut self) { - unsafe { - CFRelease(self.as_CFTypeRef()) - } + unsafe { CFRelease(self.as_CFTypeRef()) } } } impl_TCFType!(MDItem, MDItemRef, MDItemGetTypeID); #[inline] fn ctref(t: &T) -> C - where T: TCFType + where + T: TCFType, { t.as_concrete_TypeRef() } @@ -146,9 +144,7 @@ mod dsym { if !cft.instance_of::<_, CFString>() { return Err(failure::err_msg("Not a string")); } - let cf_string = unsafe { - CFString::wrap_under_get_rule(ctref(&cft) as CFStringRef) - }; + let cf_string = unsafe { CFString::wrap_under_get_rule(ctref(&cft) as CFStringRef) }; Ok(cf_string.to_string()) } @@ -176,23 +172,19 @@ mod dsym { /// Get the path to the Mach-O file containing DWARF debug info inside `bundle`. fn spotlight_get_dsym_path(bundle: &str) -> Result { let cf_bundle_string = CFString::new(bundle); - let bundle_item = unsafe { MDItemCreate(kCFAllocatorDefault, - ctref(&cf_bundle_string)) }; + let bundle_item = unsafe { MDItemCreate(kCFAllocatorDefault, ctref(&cf_bundle_string)) }; if bundle_item == ptr::null_mut() { return Err(failure::err_msg("MDItemCreate failed")); } let bundle_item = unsafe { MDItem::wrap_under_create_rule(bundle_item) }; let attr = CFString::from_static_string("com_apple_xcode_dsym_paths"); let cf_attr = unsafe { - CFType::wrap_under_get_rule(MDItemCopyAttribute(ctref(&bundle_item), - ctref(&attr))) + CFType::wrap_under_get_rule(MDItemCopyAttribute(ctref(&bundle_item), ctref(&attr))) }; if !cf_attr.instance_of::<_, CFArray>() { return Err(failure::err_msg("dsym_paths attribute not an array")); } - let cf_array = unsafe { - CFArray::wrap_under_get_rule(ctref(&cf_attr) as CFArrayRef) - }; + let cf_array = unsafe { CFArray::wrap_under_get_rule(ctref(&cf_attr) as CFArrayRef) }; if let Some(cf_item) = cf_array.iter().nth(0) { let cf_item = unsafe { CFType::wrap_under_get_rule(cf_item) }; return cftype_to_string(cf_item); @@ -206,7 +198,7 @@ mod dsym { } } -#[cfg(not(target_os="macos"))] +#[cfg(not(target_os = "macos"))] mod dsym { use failure::{self, Error}; use std::path::{Path, PathBuf}; @@ -215,11 +207,13 @@ mod dsym { /// Attempt to find the DWARF-containing file inside a dSYM bundle for the Mach-O binary /// at `path` using simple path manipulation. pub fn locate(path: &Path, _uuid: Uuid) -> Result { - let filename = path.file_name() - .ok_or(failure::err_msg("Bad path"))?; + let filename = path.file_name().ok_or(failure::err_msg("Bad path"))?; let mut dsym = filename.to_owned(); dsym.push(".dSYM"); - let f = path.with_file_name(&dsym).join("Contents/Resources/DWARF").join(filename); + let f = path + .with_file_name(&dsym) + .join("Contents/Resources/DWARF") + .join(filename); if f.exists() { Ok(f) } else { @@ -235,7 +229,8 @@ mod dsym { /// /// Currently only locating Mach-O dSYM bundles is supported. pub fn locate_debug_symbols(object: &File, path: T) -> Result - where T: AsRef, +where + T: AsRef, { if let Some(uuid) = object.mach_uuid() { return locate_dsym(path.as_ref(), uuid); @@ -257,7 +252,8 @@ pub fn locate_debug_symbols(object: &File, path: T) -> Result /// Attempt to locate the Mach-O file contained within a dSYM bundle containing the debug /// symbols for the Mach-O file at `path` with UUID `uuid`. pub fn locate_dsym(path: T, uuid: Uuid) -> Result - where T: AsRef, +where + T: AsRef, { dsym::locate(path.as_ref(), uuid) } From 010a3b11dd13c0b49f7ad4f2bacae82dd1248c2d Mon Sep 17 00:00:00 2001 From: bjorn3 Date: Sun, 14 Apr 2019 18:09:14 +0200 Subject: [PATCH 2/6] Use 2018 edition --- Cargo.toml | 1 + examples/find-debug.rs | 4 ---- src/lib.rs | 18 +++--------------- 3 files changed, 4 insertions(+), 19 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e560117..7a0f696 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,7 @@ name = "moria" description = "Locate debug symbols for stripped binaries." version = "0.1.0" +edition = "2018" license = "Apache-2.0/MIT" authors = ["Ted Mielczarek "] repository = "https://github.com/gimli-rs/moria" diff --git a/examples/find-debug.rs b/examples/find-debug.rs index 54c356a..b084db7 100644 --- a/examples/find-debug.rs +++ b/examples/find-debug.rs @@ -1,7 +1,3 @@ -extern crate failure; -extern crate moria; -extern crate object; - use std::env; use std::fs::File; use std::io::Read; diff --git a/src/lib.rs b/src/lib.rs index d4c8980..070e495 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,16 +1,3 @@ -#[macro_use] -extern crate cfg_if; -extern crate failure; -extern crate libc; -extern crate object; -extern crate uuid; - -#[cfg(target_os = "macos")] -#[macro_use] -extern crate core_foundation; -#[cfg(target_os = "macos")] -extern crate core_foundation_sys; - use failure::Error; use object::{File, Object}; use std::fmt::Write; @@ -18,7 +5,7 @@ use std::fs; use std::path::{Path, PathBuf}; use uuid::Uuid; -cfg_if! { +cfg_if::cfg_if! { if #[cfg(unix)] { use std::ffi::OsStr; use std::os::unix::ffi::OsStrExt; @@ -39,6 +26,7 @@ cfg_if! { mod dsym { use core_foundation::array::{CFArray, CFArrayRef}; use core_foundation::base::{CFType, CFTypeRef, TCFType}; + use core_foundation::impl_TCFType; use core_foundation::string::CFString; use core_foundation_sys::base::{ kCFAllocatorDefault, CFAllocatorRef, CFIndex, CFOptionFlags, CFRelease, CFTypeID, @@ -228,7 +216,7 @@ mod dsym { /// or if the debug symbol file is not present on disk, return an error. /// /// Currently only locating Mach-O dSYM bundles is supported. -pub fn locate_debug_symbols(object: &File, path: T) -> Result +pub fn locate_debug_symbols(object: &File<'_>, path: T) -> Result where T: AsRef, { From 1a9e9e3cd90ec481fe90039d4c4068d320e4fd86 Mon Sep 17 00:00:00 2001 From: bjorn3 Date: Sat, 20 Apr 2019 14:46:11 +0200 Subject: [PATCH 3/6] Clippy fixes --- src/lib.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 070e495..23a6c4f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,5 @@ +#![warn(clippy::all)] + use failure::Error; use object::{File, Object}; use std::fmt::Write; @@ -93,7 +95,7 @@ mod dsym { ptr::null(), ) }; - if query == ptr::null_mut() { + if query.is_null() { return Err(failure::err_msg("MDQueryCreate failed")); } unsafe { Ok(MDQuery::wrap_under_create_rule(query)) } @@ -146,7 +148,7 @@ mod dsym { let item = unsafe { MDQueryGetResultAtIndex(ctref(&query), i) as MDItemRef }; let attr = unsafe { CFString::wrap_under_get_rule(kMDItemPath) }; let cf_attr = unsafe { MDItemCopyAttribute(item, ctref(&attr)) }; - if cf_attr == ptr::null_mut() { + if cf_attr.is_null() { return Err(failure::err_msg("MDItemCopyAttribute failed")); } let cf_attr = unsafe { CFType::wrap_under_get_rule(cf_attr) }; @@ -154,14 +156,14 @@ mod dsym { return Ok(path); } } - return Err(failure::err_msg("dSYM not found")); + Err(failure::err_msg("dSYM not found")) } /// Get the path to the Mach-O file containing DWARF debug info inside `bundle`. fn spotlight_get_dsym_path(bundle: &str) -> Result { let cf_bundle_string = CFString::new(bundle); let bundle_item = unsafe { MDItemCreate(kCFAllocatorDefault, ctref(&cf_bundle_string)) }; - if bundle_item == ptr::null_mut() { + if bundle_item.is_null() { return Err(failure::err_msg("MDItemCreate failed")); } let bundle_item = unsafe { MDItem::wrap_under_create_rule(bundle_item) }; @@ -177,7 +179,7 @@ mod dsym { let cf_item = unsafe { CFType::wrap_under_get_rule(cf_item) }; return cftype_to_string(cf_item); } - return Err(failure::err_msg("dsym_paths array is empty")); + Err(failure::err_msg("dsym_paths array is empty")) } pub fn locate(_path: &Path, uuid: Uuid) -> Result { @@ -275,7 +277,7 @@ where U: AsRef, { let path = fs::canonicalize(path)?; - let parent = path.parent().ok_or(failure::err_msg("Bad path"))?; + let parent = path.parent().ok_or_else(|| failure::err_msg("Bad path"))?; let filename = filename.as_ref(); // TODO: check CRC From 483c0e41a721f1c47a907f5979c6c7278733ac94 Mon Sep 17 00:00:00 2001 From: bjorn3 Date: Sun, 14 Apr 2019 17:51:04 +0200 Subject: [PATCH 4/6] Move macOS dSYM search code to separate file --- src/lib.rs | 191 ++------------------------------------------------- src/macos.rs | 157 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 164 insertions(+), 184 deletions(-) create mode 100644 src/macos.rs diff --git a/src/lib.rs b/src/lib.rs index 23a6c4f..9b3d741 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -24,189 +24,12 @@ cfg_if::cfg_if! { } } -#[cfg(target_os = "macos")] -mod dsym { - use core_foundation::array::{CFArray, CFArrayRef}; - use core_foundation::base::{CFType, CFTypeRef, TCFType}; - use core_foundation::impl_TCFType; - use core_foundation::string::CFString; - use core_foundation_sys::base::{ - kCFAllocatorDefault, CFAllocatorRef, CFIndex, CFOptionFlags, CFRelease, CFTypeID, - }; - use core_foundation_sys::string::CFStringRef; - use failure::{self, Error}; - use libc::c_void; - use std::path::{Path, PathBuf}; - use std::ptr; - use uuid::Uuid; - - type Boolean = ::std::os::raw::c_uchar; - //const TRUE: Boolean = 1; - const FALSE: Boolean = 0; - #[repr(C)] - struct __MDQuery(c_void); - type MDQueryRef = *mut __MDQuery; - #[repr(C)] - struct __MDItem(c_void); - type MDItemRef = *mut __MDItem; - - #[allow(non_upper_case_globals)] - const kMDQuerySynchronous: CFOptionFlags = 1; - #[link(name = "CoreServices", kind = "framework")] - extern "C" { - #[link_name = "\u{1}_MDQueryCreate"] - fn MDQueryCreate( - allocator: CFAllocatorRef, - queryString: CFStringRef, - valueListAttrs: CFArrayRef, - sortingAttrs: CFArrayRef, - ) -> MDQueryRef; - #[link_name = "\u{1}_MDQueryGetTypeID"] - fn MDQueryGetTypeID() -> CFTypeID; - #[link_name = "\u{1}_MDQueryExecute"] - fn MDQueryExecute(query: MDQueryRef, optionFlags: CFOptionFlags) -> Boolean; - #[link_name = "\u{1}_MDQueryGetResultCount"] - fn MDQueryGetResultCount(query: MDQueryRef) -> CFIndex; - #[link_name = "\u{1}_MDQueryGetResultAtIndex"] - fn MDQueryGetResultAtIndex( - query: MDQueryRef, - idx: CFIndex, - ) -> *const ::std::os::raw::c_void; - #[link_name = "\u{1}_MDItemCreate"] - fn MDItemCreate(allocator: CFAllocatorRef, path: CFStringRef) -> MDItemRef; - #[link_name = "\u{1}_MDItemGetTypeID"] - pub fn MDItemGetTypeID() -> CFTypeID; - #[link_name = "\u{1}_MDItemCopyAttribute"] - fn MDItemCopyAttribute(item: MDItemRef, name: CFStringRef) -> CFTypeRef; - #[link_name = "\u{1}_kMDItemPath"] - static mut kMDItemPath: CFStringRef; - } - - struct MDQuery(MDQueryRef); - - impl MDQuery { - pub fn create(query_string: &str) -> Result { - let cf_query_string = CFString::new(&query_string); - let query = unsafe { - MDQueryCreate( - kCFAllocatorDefault, - ctref(&cf_query_string), - ptr::null(), - ptr::null(), - ) - }; - if query.is_null() { - return Err(failure::err_msg("MDQueryCreate failed")); - } - unsafe { Ok(MDQuery::wrap_under_create_rule(query)) } - } - pub fn execute(&self) -> Result { - if unsafe { MDQueryExecute(ctref(self), kMDQuerySynchronous) } == FALSE { - return Err(failure::err_msg("MDQueryExecute failed")); - } - unsafe { Ok(MDQueryGetResultCount(ctref(self))) } - } - } - impl Drop for MDQuery { - fn drop(&mut self) { - unsafe { CFRelease(self.as_CFTypeRef()) } - } - } - impl_TCFType!(MDQuery, MDQueryRef, MDQueryGetTypeID); - - struct MDItem(MDItemRef); - impl Drop for MDItem { - fn drop(&mut self) { - unsafe { CFRelease(self.as_CFTypeRef()) } - } - } - impl_TCFType!(MDItem, MDItemRef, MDItemGetTypeID); - - #[inline] - fn ctref(t: &T) -> C - where - T: TCFType, - { - t.as_concrete_TypeRef() - } - - fn cftype_to_string(cft: CFType) -> Result { - if !cft.instance_of::<_, CFString>() { - return Err(failure::err_msg("Not a string")); - } - let cf_string = unsafe { CFString::wrap_under_get_rule(ctref(&cft) as CFStringRef) }; - Ok(cf_string.to_string()) - } - - /// Attempt to locate the Mach-O file inside a dSYM matching `uuid` using spotlight. - fn spotlight_locate_dsym_bundle(uuid: Uuid) -> Result { - let uuid = uuid.to_hyphenated().to_string().to_uppercase(); - let query_string = format!("com_apple_xcode_dsym_uuids == {}", uuid); - let query = MDQuery::create(&query_string)?; - let count = query.execute()?; - for i in 0..count { - let item = unsafe { MDQueryGetResultAtIndex(ctref(&query), i) as MDItemRef }; - let attr = unsafe { CFString::wrap_under_get_rule(kMDItemPath) }; - let cf_attr = unsafe { MDItemCopyAttribute(item, ctref(&attr)) }; - if cf_attr.is_null() { - return Err(failure::err_msg("MDItemCopyAttribute failed")); - } - let cf_attr = unsafe { CFType::wrap_under_get_rule(cf_attr) }; - if let Ok(path) = cftype_to_string(cf_attr) { - return Ok(path); - } - } - Err(failure::err_msg("dSYM not found")) - } - - /// Get the path to the Mach-O file containing DWARF debug info inside `bundle`. - fn spotlight_get_dsym_path(bundle: &str) -> Result { - let cf_bundle_string = CFString::new(bundle); - let bundle_item = unsafe { MDItemCreate(kCFAllocatorDefault, ctref(&cf_bundle_string)) }; - if bundle_item.is_null() { - return Err(failure::err_msg("MDItemCreate failed")); - } - let bundle_item = unsafe { MDItem::wrap_under_create_rule(bundle_item) }; - let attr = CFString::from_static_string("com_apple_xcode_dsym_paths"); - let cf_attr = unsafe { - CFType::wrap_under_get_rule(MDItemCopyAttribute(ctref(&bundle_item), ctref(&attr))) - }; - if !cf_attr.instance_of::<_, CFArray>() { - return Err(failure::err_msg("dsym_paths attribute not an array")); - } - let cf_array = unsafe { CFArray::wrap_under_get_rule(ctref(&cf_attr) as CFArrayRef) }; - if let Some(cf_item) = cf_array.iter().nth(0) { - let cf_item = unsafe { CFType::wrap_under_get_rule(cf_item) }; - return cftype_to_string(cf_item); - } - Err(failure::err_msg("dsym_paths array is empty")) - } - - pub fn locate(_path: &Path, uuid: Uuid) -> Result { - let bundle = spotlight_locate_dsym_bundle(uuid)?; - Ok(Path::new(&bundle).join(spotlight_get_dsym_path(&bundle)?)) - } -} - -#[cfg(not(target_os = "macos"))] -mod dsym { - use failure::{self, Error}; - use std::path::{Path, PathBuf}; - use uuid::Uuid; - - /// Attempt to find the DWARF-containing file inside a dSYM bundle for the Mach-O binary - /// at `path` using simple path manipulation. - pub fn locate(path: &Path, _uuid: Uuid) -> Result { - let filename = path.file_name().ok_or(failure::err_msg("Bad path"))?; - let mut dsym = filename.to_owned(); - dsym.push(".dSYM"); - let f = path - .with_file_name(&dsym) - .join("Contents/Resources/DWARF") - .join(filename); - if f.exists() { - Ok(f) - } else { +cfg_if::cfg_if! { + if #[cfg(target_os = "macos")] { + mod macos; + use crate::macos::locate_dsym_using_spotlight; + } else { + fn locate_dsym_using_spotlight(_uuid: uuid::Uuid) -> Result { Err(failure::err_msg("Could not locate dSYM")) } } @@ -245,7 +68,7 @@ pub fn locate_dsym(path: T, uuid: Uuid) -> Result where T: AsRef, { - dsym::locate(path.as_ref(), uuid) + locate_dsym_using_spotlight(uuid) } /// Attempt to locate the separate debug symbol file for the object file at `path` with diff --git a/src/macos.rs b/src/macos.rs new file mode 100644 index 0000000..00294b0 --- /dev/null +++ b/src/macos.rs @@ -0,0 +1,157 @@ +use core_foundation::array::{CFArray, CFArrayRef}; +use core_foundation::base::{CFType, CFTypeRef, TCFType}; +use core_foundation::impl_TCFType; +use core_foundation::string::CFString; +use core_foundation_sys::base::{ + kCFAllocatorDefault, CFAllocatorRef, CFIndex, CFOptionFlags, CFRelease, CFTypeID, +}; +use core_foundation_sys::string::CFStringRef; +use failure::{self, Error}; +use libc::c_void; +use std::path::{Path, PathBuf}; +use std::ptr; +use uuid::Uuid; + +type Boolean = ::std::os::raw::c_uchar; +//const TRUE: Boolean = 1; +const FALSE: Boolean = 0; +#[repr(C)] +struct __MDQuery(c_void); +type MDQueryRef = *mut __MDQuery; +#[repr(C)] +struct __MDItem(c_void); +type MDItemRef = *mut __MDItem; + +#[allow(non_upper_case_globals)] +const kMDQuerySynchronous: CFOptionFlags = 1; +#[link(name = "CoreServices", kind = "framework")] +extern "C" { + #[link_name = "\u{1}_MDQueryCreate"] + fn MDQueryCreate( + allocator: CFAllocatorRef, + queryString: CFStringRef, + valueListAttrs: CFArrayRef, + sortingAttrs: CFArrayRef, + ) -> MDQueryRef; + #[link_name = "\u{1}_MDQueryGetTypeID"] + fn MDQueryGetTypeID() -> CFTypeID; + #[link_name = "\u{1}_MDQueryExecute"] + fn MDQueryExecute(query: MDQueryRef, optionFlags: CFOptionFlags) -> Boolean; + #[link_name = "\u{1}_MDQueryGetResultCount"] + fn MDQueryGetResultCount(query: MDQueryRef) -> CFIndex; + #[link_name = "\u{1}_MDQueryGetResultAtIndex"] + fn MDQueryGetResultAtIndex(query: MDQueryRef, idx: CFIndex) -> *const ::std::os::raw::c_void; + #[link_name = "\u{1}_MDItemCreate"] + fn MDItemCreate(allocator: CFAllocatorRef, path: CFStringRef) -> MDItemRef; + #[link_name = "\u{1}_MDItemGetTypeID"] + pub fn MDItemGetTypeID() -> CFTypeID; + #[link_name = "\u{1}_MDItemCopyAttribute"] + fn MDItemCopyAttribute(item: MDItemRef, name: CFStringRef) -> CFTypeRef; + #[link_name = "\u{1}_kMDItemPath"] + static mut kMDItemPath: CFStringRef; +} + +struct MDQuery(MDQueryRef); + +impl MDQuery { + pub fn create(query_string: &str) -> Result { + let cf_query_string = CFString::new(&query_string); + let query = unsafe { + MDQueryCreate( + kCFAllocatorDefault, + ctref(&cf_query_string), + ptr::null(), + ptr::null(), + ) + }; + if query.is_null() { + return Err(failure::err_msg("MDQueryCreate failed")); + } + unsafe { Ok(MDQuery::wrap_under_create_rule(query)) } + } + pub fn execute(&self) -> Result { + if unsafe { MDQueryExecute(ctref(self), kMDQuerySynchronous) } == FALSE { + return Err(failure::err_msg("MDQueryExecute failed")); + } + unsafe { Ok(MDQueryGetResultCount(ctref(self))) } + } +} +impl Drop for MDQuery { + fn drop(&mut self) { + unsafe { CFRelease(self.as_CFTypeRef()) } + } +} +impl_TCFType!(MDQuery, MDQueryRef, MDQueryGetTypeID); + +struct MDItem(MDItemRef); +impl Drop for MDItem { + fn drop(&mut self) { + unsafe { CFRelease(self.as_CFTypeRef()) } + } +} +impl_TCFType!(MDItem, MDItemRef, MDItemGetTypeID); + +#[inline] +fn ctref(t: &T) -> C +where + T: TCFType, +{ + t.as_concrete_TypeRef() +} + +fn cftype_to_string(cft: CFType) -> Result { + if !cft.instance_of::<_, CFString>() { + return Err(failure::err_msg("Not a string")); + } + let cf_string = unsafe { CFString::wrap_under_get_rule(ctref(&cft) as CFStringRef) }; + Ok(cf_string.to_string()) +} + +/// Attempt to locate the Mach-O file inside a dSYM matching `uuid` using spotlight. +fn spotlight_locate_dsym_bundle(uuid: Uuid) -> Result { + let uuid = uuid.to_hyphenated().to_string().to_uppercase(); + let query_string = format!("com_apple_xcode_dsym_uuids == {}", uuid); + let query = MDQuery::create(&query_string)?; + let count = query.execute()?; + for i in 0..count { + let item = unsafe { MDQueryGetResultAtIndex(ctref(&query), i) as MDItemRef }; + let attr = unsafe { CFString::wrap_under_get_rule(kMDItemPath) }; + let cf_attr = unsafe { MDItemCopyAttribute(item, ctref(&attr)) }; + if cf_attr.is_null() { + return Err(failure::err_msg("MDItemCopyAttribute failed")); + } + let cf_attr = unsafe { CFType::wrap_under_get_rule(cf_attr) }; + if let Ok(path) = cftype_to_string(cf_attr) { + return Ok(path); + } + } + Err(failure::err_msg("dSYM not found")) +} + +/// Get the path to the Mach-O file containing DWARF debug info inside `bundle`. +fn spotlight_get_dsym_path(bundle: &str) -> Result { + let cf_bundle_string = CFString::new(bundle); + let bundle_item = unsafe { MDItemCreate(kCFAllocatorDefault, ctref(&cf_bundle_string)) }; + if bundle_item.is_null() { + return Err(failure::err_msg("MDItemCreate failed")); + } + let bundle_item = unsafe { MDItem::wrap_under_create_rule(bundle_item) }; + let attr = CFString::from_static_string("com_apple_xcode_dsym_paths"); + let cf_attr = unsafe { + CFType::wrap_under_get_rule(MDItemCopyAttribute(ctref(&bundle_item), ctref(&attr))) + }; + if !cf_attr.instance_of::<_, CFArray>() { + return Err(failure::err_msg("dsym_paths attribute not an array")); + } + let cf_array = unsafe { CFArray::wrap_under_get_rule(ctref(&cf_attr) as CFArrayRef) }; + if let Some(cf_item) = cf_array.iter().nth(0) { + let cf_item = unsafe { CFType::wrap_under_get_rule(cf_item) }; + return cftype_to_string(cf_item); + } + Err(failure::err_msg("dsym_paths array is empty")) +} + +pub fn locate_dsym_using_spotlight(uuid: uuid::Uuid) -> Result { + let bundle = spotlight_locate_dsym_bundle(uuid)?; + Ok(Path::new(&bundle).join(spotlight_get_dsym_path(&bundle)?)) +} From c7b520febf01477037693681a0cd1338e39da7cd Mon Sep 17 00:00:00 2001 From: bjorn3 Date: Sun, 14 Apr 2019 18:03:39 +0200 Subject: [PATCH 5/6] Add a fastpath for locating dSYM files when everything is in cargo's target dir --- src/lib.rs | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 9b3d741..dde51c4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -35,6 +35,83 @@ cfg_if::cfg_if! { } } +/// On macOS it can take some time for spotlight to index the dSYM file and on other OSes it is +/// impossible to use spotlight. When built by cargo, we can likely find the dSYM file in +/// target//deps or target//examples. Otherwise it can likely be found at +/// .dSYM. This function will try to find it there. +/// +/// # Arguments +/// +/// * Parsed version of the object file which needs its debuginfo. +/// * Path to the object file. +fn locate_dsym_fastpath(path: &Path, uuid: Uuid) -> Option { + // Canonicalize the path to make sure the fastpath also works when current working + // dir is inside target/ + let path = path.canonicalize().ok()?; + + // First try .dSYM + let mut dsym = path.file_name()?.to_owned(); + dsym.push(".dSYM"); + let dsym_dir = path.with_file_name(&dsym); + if let Some(f) = try_match_dsym(&dsym_dir, uuid) { + return Some(f); + } + + // Get the path to the target dir of the current build channel. + let mut target_channel_dir = &*path; + loop { + let parent = target_channel_dir.parent()?; + target_channel_dir = parent; + + if target_channel_dir.parent().and_then(Path::file_name) + == Some(std::ffi::OsStr::new("target")) + { + break; // target_dir = ???/target/ + } + } + + // Check every entry in /deps and /examples + for dir in fs::read_dir(target_channel_dir.join("deps")) + .unwrap() + .chain(fs::read_dir(target_channel_dir.join("examples")).unwrap()) + { + let dir = dir.unwrap().path(); + + // If not a dSYM dir, try next entry. + if dir.extension() != Some(std::ffi::OsStr::new("dSYM")) { + continue; + } + + if let Some(debug_file_name) = try_match_dsym(&dir, uuid) { + return Some(debug_file_name); + } + } + + None +} + +fn try_match_dsym(dsym_dir: &Path, uuid: Uuid) -> Option { + // Get path to inner object file. + let mut dir_iter = fs::read_dir(dsym_dir.join("Contents/Resources/DWARF")).ok()?; + + let debug_file_name = dir_iter.next()?.ok()?.path(); + + if dir_iter.next().is_some() { + return None; // There should only be one file in the `DWARF` directory. + } + + // Parse inner object file. + let file = fs::read(&debug_file_name).ok()?; + let dsym = object::File::parse(&file).ok()?; + + // Make sure the dSYM file matches the object file to find debuginfo for. + if dsym.mach_uuid() == Some(uuid) { + Some(debug_file_name.to_owned()) + } else { + None + } +} + /// Attempt to locate the path to separate debug symbols for `object` at `path`. /// /// If `object` does not contain information that can be used to locate debug symbols for it, @@ -68,6 +145,9 @@ pub fn locate_dsym(path: T, uuid: Uuid) -> Result where T: AsRef, { + if let Some(dsym_path) = locate_dsym_fastpath(path.as_ref(), uuid) { + return Ok(dsym_path); + } locate_dsym_using_spotlight(uuid) } From b974836ccbe54633fd9a5b8a7bd9be4e93985071 Mon Sep 17 00:00:00 2001 From: bjorn3 Date: Sat, 20 Apr 2019 16:21:00 +0200 Subject: [PATCH 6/6] Update core-foundation --- Cargo.toml | 4 ++-- src/macos.rs | 31 +++++++++++++++---------------- 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7a0f696..89789d2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,5 +15,5 @@ object = "0.11.0" uuid = "0.7" [target.'cfg(target_os="macos")'.dependencies] -core-foundation = "0.4.6" -core-foundation-sys = "0.4.6" +core-foundation = "0.6.2" +core-foundation-sys = "0.6.2" diff --git a/src/macos.rs b/src/macos.rs index 00294b0..d42bef5 100644 --- a/src/macos.rs +++ b/src/macos.rs @@ -1,5 +1,5 @@ use core_foundation::array::{CFArray, CFArrayRef}; -use core_foundation::base::{CFType, CFTypeRef, TCFType}; +use core_foundation::base::{CFType, CFTypeRef, TCFType, TCFTypeRef}; use core_foundation::impl_TCFType; use core_foundation::string::CFString; use core_foundation_sys::base::{ @@ -92,19 +92,21 @@ impl Drop for MDItem { impl_TCFType!(MDItem, MDItemRef, MDItemGetTypeID); #[inline] -fn ctref(t: &T) -> C +fn ctref(t: &T) -> T::Ref where - T: TCFType, + T: TCFType, { t.as_concrete_TypeRef() } -fn cftype_to_string(cft: CFType) -> Result { - if !cft.instance_of::<_, CFString>() { - return Err(failure::err_msg("Not a string")); +fn cast(t: &T) -> Result where T: TCFType, U: TCFType { + if !t.instance_of::() { + return Err(failure::err_msg("dsym_paths attribute not an array")); } - let cf_string = unsafe { CFString::wrap_under_get_rule(ctref(&cft) as CFStringRef) }; - Ok(cf_string.to_string()) + + let t: *const c_void = t.as_concrete_TypeRef().as_void_ptr(); + + Ok(unsafe { U::wrap_under_get_rule(U::Ref::from_void_ptr(t)) }) } /// Attempt to locate the Mach-O file inside a dSYM matching `uuid` using spotlight. @@ -121,8 +123,8 @@ fn spotlight_locate_dsym_bundle(uuid: Uuid) -> Result { return Err(failure::err_msg("MDItemCopyAttribute failed")); } let cf_attr = unsafe { CFType::wrap_under_get_rule(cf_attr) }; - if let Ok(path) = cftype_to_string(cf_attr) { - return Ok(path); + if let Ok(path) = cast::(&cf_attr) { + return Ok(path.to_string()); } } Err(failure::err_msg("dSYM not found")) @@ -140,13 +142,10 @@ fn spotlight_get_dsym_path(bundle: &str) -> Result { let cf_attr = unsafe { CFType::wrap_under_get_rule(MDItemCopyAttribute(ctref(&bundle_item), ctref(&attr))) }; - if !cf_attr.instance_of::<_, CFArray>() { - return Err(failure::err_msg("dsym_paths attribute not an array")); - } - let cf_array = unsafe { CFArray::wrap_under_get_rule(ctref(&cf_attr) as CFArrayRef) }; + let cf_array = cast::>(&cf_attr)?; if let Some(cf_item) = cf_array.iter().nth(0) { - let cf_item = unsafe { CFType::wrap_under_get_rule(cf_item) }; - return cftype_to_string(cf_item); + let cf_item = unsafe { CFType::wrap_under_get_rule(ctref(&*cf_item)) }; + return cast::(&cf_item).map(|s| s.to_string()); } Err(failure::err_msg("dsym_paths array is empty")) }