From 0f8432af72f33a81cae7dfb452ce618c56cf4bad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20M=C3=BCller?= Date: Wed, 11 Oct 2023 14:17:52 -0700 Subject: [PATCH] Continue --- include/blazesym.h | 33 +++------ src/c_api/normalize.rs | 136 ++++++++++++------------------------ src/normalize/meta.rs | 40 +++++------ src/normalize/mod.rs | 8 +-- src/normalize/normalizer.rs | 25 ++++--- src/normalize/user.rs | 87 ++++++++--------------- tests/blazesym.rs | 2 +- 7 files changed, 117 insertions(+), 214 deletions(-) diff --git a/include/blazesym.h b/include/blazesym.h index 27dfbf133..9dfe57b6c 100644 --- a/include/blazesym.h +++ b/include/blazesym.h @@ -35,9 +35,9 @@ typedef enum blaze_user_meta_kind { */ BLAZE_USER_META_UNKNOWN, /** - * [`blaze_user_meta_variant::apk_elf`] is valid. + * [`blaze_user_meta_variant::apk`] is valid. */ - BLAZE_USER_META_APK_ELF, + BLAZE_USER_META_APK, /** * [`blaze_user_meta_variant::elf`] is valid. */ @@ -118,27 +118,15 @@ typedef struct blaze_inspect_elf_src { } blaze_inspect_elf_src; /** - * C compatible version of [`ApkElf`]. + * C compatible version of [`Apk`]. */ -typedef struct blaze_user_meta_apk_elf { +typedef struct blaze_user_meta_apk { /** * The canonical absolute path to the APK, including its name. * This member is always present. */ - char *apk_path; - /** - * The relative path to the ELF file inside the APK. - */ - char *elf_path; - /** - * The length of the build ID, in bytes. - */ - size_t elf_build_id_len; - /** - * The optional build ID of the ELF file, if found. - */ - uint8_t *elf_build_id; -} blaze_user_meta_apk_elf; + char *path; +} blaze_user_meta_apk; /** * C compatible version of [`Elf`]. @@ -173,9 +161,9 @@ typedef struct blaze_user_meta_unknown { */ typedef union blaze_user_meta_variant { /** - * Valid on [`blaze_user_meta_kind::BLAZE_USER_META_APK_ELF`]. + * Valid on [`blaze_user_meta_kind::BLAZE_USER_META_APK`]. */ - struct blaze_user_meta_apk_elf apk_elf; + struct blaze_user_meta_apk apk; /** * Valid on [`blaze_user_meta_kind::BLAZE_USER_META_ELF`]. */ @@ -201,8 +189,9 @@ typedef struct blaze_user_meta { } blaze_user_meta; /** - * A normalized address along with an index into the associated - * [`blaze_user_meta`] array (such as [`blaze_normalized_user_output::metas`]). + * A file offset or non-normalized address along with an index into the + * associated [`blaze_user_meta`] array (such as + * [`blaze_normalized_user_output::metas`]). */ typedef struct blaze_normalized_output { /** diff --git a/src/c_api/normalize.rs b/src/c_api/normalize.rs index f51fc3d5e..cd874f95a 100644 --- a/src/c_api/normalize.rs +++ b/src/c_api/normalize.rs @@ -11,7 +11,7 @@ use std::ptr; use std::slice; use crate::log::error; -use crate::normalize::ApkElf; +use crate::normalize::Apk; use crate::normalize::Elf; use crate::normalize::Normalizer; use crate::normalize::Unknown; @@ -51,12 +51,13 @@ pub unsafe extern "C" fn blaze_normalizer_free(normalizer: *mut Normalizer) { } -/// A normalized address along with an index into the associated -/// [`blaze_user_meta`] array (such as [`blaze_normalized_user_output::metas`]). +/// A file offset or non-normalized address along with an index into the +/// associated [`blaze_user_meta`] array (such as +/// [`blaze_normalized_user_output::metas`]). #[repr(C)] #[derive(Debug)] pub struct blaze_normalized_output { - /// The normalized address. + /// The file offset or non-normalized address. pub output: u64, /// The index into the associated [`blaze_user_meta`] array. pub meta_idx: usize, @@ -75,83 +76,44 @@ impl From<(u64, usize)> for blaze_normalized_output { pub enum blaze_user_meta_kind { /// [`blaze_user_meta_variant::unknown`] is valid. BLAZE_USER_META_UNKNOWN, - /// [`blaze_user_meta_variant::apk_elf`] is valid. - BLAZE_USER_META_APK_ELF, + /// [`blaze_user_meta_variant::apk`] is valid. + BLAZE_USER_META_APK, /// [`blaze_user_meta_variant::elf`] is valid. BLAZE_USER_META_ELF, } -/// C compatible version of [`ApkElf`]. +/// C compatible version of [`Apk`]. #[repr(C)] #[derive(Debug)] -pub struct blaze_user_meta_apk_elf { +pub struct blaze_user_meta_apk { /// The canonical absolute path to the APK, including its name. /// This member is always present. - pub apk_path: *mut c_char, - /// The relative path to the ELF file inside the APK. - pub elf_path: *mut c_char, - /// The length of the build ID, in bytes. - pub elf_build_id_len: usize, - /// The optional build ID of the ELF file, if found. - pub elf_build_id: *mut u8, + pub path: *mut c_char, } -impl From for blaze_user_meta_apk_elf { - fn from(other: ApkElf) -> Self { - let ApkElf { - apk_path, - elf_path, - elf_build_id, +impl From for blaze_user_meta_apk { + fn from(other: Apk) -> Self { + let Apk { + path, _non_exhaustive: (), } = other; Self { - apk_path: CString::new(apk_path.into_os_string().into_vec()) - .expect("encountered path with NUL bytes") - .into_raw(), - elf_path: CString::new(elf_path.into_os_string().into_vec()) + path: CString::new(path.into_os_string().into_vec()) .expect("encountered path with NUL bytes") .into_raw(), - elf_build_id_len: elf_build_id - .as_ref() - .map(|build_id| build_id.len()) - .unwrap_or(0), - elf_build_id: elf_build_id - .map(|build_id| { - // SAFETY: We know the pointer is valid because it - // came from a `Box`. - unsafe { - Box::into_raw(build_id.into_boxed_slice()) - .as_mut() - .unwrap() - .as_mut_ptr() - } - }) - .unwrap_or_else(ptr::null_mut), } } } -impl From for ApkElf { - fn from(other: blaze_user_meta_apk_elf) -> Self { - let blaze_user_meta_apk_elf { - apk_path, - elf_path, - elf_build_id_len, - elf_build_id, - } = other; +impl From for Apk { + fn from(other: blaze_user_meta_apk) -> Self { + let blaze_user_meta_apk { path } = other; - ApkElf { - apk_path: PathBuf::from(OsString::from_vec( - unsafe { CString::from_raw(apk_path) }.into_bytes(), - )), - elf_path: PathBuf::from(OsString::from_vec( - unsafe { CString::from_raw(elf_path) }.into_bytes(), + Apk { + path: PathBuf::from(OsString::from_vec( + unsafe { CString::from_raw(path) }.into_bytes(), )), - elf_build_id: (!elf_build_id.is_null()).then(|| unsafe { - Box::<[u8]>::from_raw(slice::from_raw_parts_mut(elf_build_id, elf_build_id_len)) - .into_vec() - }), _non_exhaustive: (), } } @@ -252,8 +214,8 @@ impl From for Unknown { /// The actual variant data in [`blaze_user_meta`]. #[repr(C)] pub union blaze_user_meta_variant { - /// Valid on [`blaze_user_meta_kind::BLAZE_USER_META_APK_ELF`]. - pub apk_elf: ManuallyDrop, + /// Valid on [`blaze_user_meta_kind::BLAZE_USER_META_APK`]. + pub apk: ManuallyDrop, /// Valid on [`blaze_user_meta_kind::BLAZE_USER_META_ELF`]. pub elf: ManuallyDrop, /// Valid on [`blaze_user_meta_kind::BLAZE_USER_META_UNKNOWN`]. @@ -280,10 +242,10 @@ pub struct blaze_user_meta { impl From for blaze_user_meta { fn from(other: UserAddrMeta) -> Self { match other { - UserAddrMeta::ApkElf(apk_elf) => Self { - kind: blaze_user_meta_kind::BLAZE_USER_META_APK_ELF, + UserAddrMeta::Apk(apk) => Self { + kind: blaze_user_meta_kind::BLAZE_USER_META_APK, variant: blaze_user_meta_variant { - apk_elf: ManuallyDrop::new(blaze_user_meta_apk_elf::from(apk_elf)), + apk: ManuallyDrop::new(blaze_user_meta_apk::from(apk)), }, }, UserAddrMeta::Elf(elf) => Self { @@ -305,9 +267,9 @@ impl From for blaze_user_meta { impl From for UserAddrMeta { fn from(other: blaze_user_meta) -> Self { match other.kind { - blaze_user_meta_kind::BLAZE_USER_META_APK_ELF => { - UserAddrMeta::ApkElf(ApkElf::from(ManuallyDrop::into_inner(unsafe { - other.variant.apk_elf + blaze_user_meta_kind::BLAZE_USER_META_APK => { + UserAddrMeta::Apk(Apk::from(ManuallyDrop::into_inner(unsafe { + other.variant.apk }))) } blaze_user_meta_kind::BLAZE_USER_META_ELF => { @@ -509,19 +471,13 @@ mod tests { "blaze_normalized_output { output: 4919, meta_idx: 1 }" ); - let meta_kind = blaze_user_meta_kind::BLAZE_USER_META_APK_ELF; - assert_eq!(format!("{meta_kind:?}"), "BLAZE_USER_META_APK_ELF"); + let meta_kind = blaze_user_meta_kind::BLAZE_USER_META_APK; + assert_eq!(format!("{meta_kind:?}"), "BLAZE_USER_META_APK"); - let apk_elf = blaze_user_meta_apk_elf { - apk_path: ptr::null_mut(), - elf_path: ptr::null_mut(), - elf_build_id_len: 0, - elf_build_id: ptr::null_mut(), + let apk = blaze_user_meta_apk { + path: ptr::null_mut(), }; - assert_eq!( - format!("{apk_elf:?}"), - "blaze_user_meta_apk_elf { apk_path: 0x0, elf_path: 0x0, elf_build_id_len: 0, elf_build_id: 0x0 }", - ); + assert_eq!(format!("{apk:?}"), "blaze_user_meta_apk { path: 0x0 }",); let elf = blaze_user_meta_elf { path: ptr::null_mut(), @@ -578,31 +534,27 @@ mod tests { assert_eq!(meta_new, meta); } - /// Check that we can convert an [`ApkElf`] into a - /// [`blaze_user_meta_apk_elf`] and back. + /// Check that we can convert an [`Apk`] into a [`blaze_user_meta_apk`] and + /// back. #[test] - fn apk_elf_conversion() { - let apk = ApkElf { - apk_path: PathBuf::from("/tmp/archive.apk"), - elf_path: PathBuf::from("file.so"), - elf_build_id: Some(vec![0x01, 0x02, 0x03, 0x04]), + fn apk_conversion() { + let apk = Apk { + path: PathBuf::from("/tmp/archive.apk"), _non_exhaustive: (), }; - let apk_new = ApkElf::from(blaze_user_meta_apk_elf::from(apk.clone())); + let apk_new = Apk::from(blaze_user_meta_apk::from(apk.clone())); assert_eq!(apk_new, apk); - let apk = ApkElf { - apk_path: PathBuf::new(), - elf_path: PathBuf::new(), - elf_build_id: None, + let apk = Apk { + path: PathBuf::new(), _non_exhaustive: (), }; - let apk_new = ApkElf::from(blaze_user_meta_apk_elf::from(apk.clone())); + let apk_new = Apk::from(blaze_user_meta_apk::from(apk.clone())); assert_eq!(apk_new, apk); - let meta = UserAddrMeta::ApkElf(apk_new); + let meta = UserAddrMeta::Apk(apk_new); let meta_new = UserAddrMeta::from(blaze_user_meta::from(meta.clone())); assert_eq!(meta_new, meta); } diff --git a/src/normalize/meta.rs b/src/normalize/meta.rs index 06cd84498..14e9bca08 100644 --- a/src/normalize/meta.rs +++ b/src/normalize/meta.rs @@ -5,11 +5,11 @@ use std::path::PathBuf; type BuildId = Vec; -/// Meta information about an ELF file inside an APK. +/// Meta information about an APK. /// -/// The corresponding normalized address is normalized to the ELF file, -/// not the APK container. I.e., it is suitable for usage with ELF -/// symbolization: +/// The corresponding file offset is normalized only to the APK container, not +/// any potential internal ELF files. In order to symbolize the address ... XXX +/// XXX FINISH /// ```no_run /// # use std::path::Path; /// # use blazesym::Pid; @@ -32,19 +32,15 @@ type BuildId = Vec; /// let src = symbolize::Source::from(symbolize::Elf::new(elf_path)); /// let symbolizer = symbolize::Symbolizer::new(); /// let sym = symbolizer -/// .symbolize_single(&src, symbolize::Input::VirtOffset(output)) +/// .symbolize_single(&src, symbolize::Input::FileOffset(output)) /// .unwrap() /// .into_sym() /// .unwrap(); /// ``` #[derive(Clone, Debug, PartialEq)] -pub struct ApkElf { +pub struct Apk { /// The canonical absolute path to the APK, including its name. - pub apk_path: PathBuf, - /// The relative path to the ELF file inside the APK. - pub elf_path: PathBuf, - /// The ELF file's build ID, if available. - pub elf_build_id: Option, + pub path: PathBuf, /// The struct is non-exhaustive and open to extension. #[doc(hidden)] pub(crate) _non_exhaustive: (), @@ -85,8 +81,8 @@ impl From for UserAddrMeta { #[derive(Clone, Debug, PartialEq)] #[non_exhaustive] pub enum UserAddrMeta { - /// The address belongs to an ELF file residing in an APK. - ApkElf(ApkElf), + /// The address belongs to an APK file. + Apk(Apk), /// The address belongs to an ELF file. Elf(Elf), /// The address' origin is unknown. @@ -94,10 +90,10 @@ pub enum UserAddrMeta { } impl UserAddrMeta { - /// Retrieve the [`ApkElf`] of this enum, if this variant is active. - pub fn apk_elf(&self) -> Option<&ApkElf> { + /// Retrieve the [`Apk`] of this enum, if this variant is active. + pub fn apk(&self) -> Option<&Apk> { match self { - Self::ApkElf(entry) => Some(entry), + Self::Apk(entry) => Some(entry), _ => None, } } @@ -129,13 +125,11 @@ mod tests { /// [`UserAddrMeta`] via the accessor functions. #[test] fn user_addr_meta_accessors() { - let meta = UserAddrMeta::ApkElf(ApkElf { - apk_path: PathBuf::from("/tmp/archive.apk"), - elf_path: PathBuf::from("object.so"), - elf_build_id: None, + let meta = UserAddrMeta::Apk(Apk { + path: PathBuf::from("/tmp/archive.apk"), _non_exhaustive: (), }); - assert!(meta.apk_elf().is_some()); + assert!(meta.apk().is_some()); assert!(meta.elf().is_none()); assert!(meta.unknown().is_none()); @@ -144,14 +138,14 @@ mod tests { build_id: None, _non_exhaustive: (), }); - assert!(meta.apk_elf().is_none()); + assert!(meta.apk().is_none()); assert!(meta.elf().is_some()); assert!(meta.unknown().is_none()); let meta = UserAddrMeta::Unknown(Unknown { _non_exhaustive: (), }); - assert!(meta.apk_elf().is_none()); + assert!(meta.apk().is_none()); assert!(meta.elf().is_none()); assert!(meta.unknown().is_some()); } diff --git a/src/normalize/mod.rs b/src/normalize/mod.rs index 0789c3717..a59e9a5b0 100644 --- a/src/normalize/mod.rs +++ b/src/normalize/mod.rs @@ -21,11 +21,11 @@ //! let normalized = normalizer.normalize_user_addrs(&addrs, pid).unwrap(); //! assert_eq!(normalized.outputs.len(), 1); //! -//! let (output, meta_idx) = normalized.outputs[0]; -//! // fopen (0x7f5f8e23a790) corresponds to address 0x77790 within +//! let (file_offset, meta_idx) = normalized.outputs[0]; +//! // fopen (0x7f5f8e23a790) corresponds to file offset 0x77790 within //! // Elf(Elf { path: "/usr/lib64/libc.so.6", build_id: Some([...]), ... }) //! println!( -//! "fopen ({fopen_addr:#x}) corresponds to address {output:#x} within {:?}", +//! "fopen ({fopen_addr:#x}) corresponds to file offset {file_offset:#x} within {:?}", //! normalized.meta[meta_idx] //! ); //! ``` @@ -35,7 +35,7 @@ mod meta; mod normalizer; mod user; -pub use meta::ApkElf; +pub use meta::Apk; pub use meta::Elf; pub use meta::Unknown; pub use meta::UserAddrMeta; diff --git a/src/normalize/normalizer.rs b/src/normalize/normalizer.rs index 16486e00a..e162fdc0a 100644 --- a/src/normalize/normalizer.rs +++ b/src/normalize/normalizer.rs @@ -17,9 +17,14 @@ pub struct Output { /// Outputs along with an index into `meta` for retrieval of the /// corresponding meta information. /// - /// A normalized address is one as it would appear in a binary or debug - /// symbol file, i.e., one excluding any relocations. - pub outputs: Vec<(Addr, usize)>, + /// The output is a file offset when normalization was successful and the + /// unnormalized input address otherwise. Normalization errors are indicated + /// by an index referencing a [`Unknown`][crate::normalize::Unknown] object. + /// + /// A file offset is one as it would appear in a binary or debug symbol + /// file, i.e., one excluding any relocations. The data reported here can be + /// used with the [`symbolize::Input::FileOffset`] variant. + pub outputs: Vec<(u64, usize)>, /// Meta information about the normalized outputs. pub meta: Vec, } @@ -137,7 +142,6 @@ mod tests { use std::mem::transmute; use std::path::Path; - use std::path::PathBuf; use test_log::test; @@ -146,7 +150,7 @@ mod tests { use crate::inspect::SymType; use crate::mmap::Mmap; use crate::normalize::buildid::read_elf_build_id; - use crate::normalize::ApkElf; + use crate::normalize::Apk; use crate::normalize::Elf; use crate::normalize::Unknown; use crate::normalize::UserAddrMeta; @@ -330,16 +334,11 @@ mod tests { let output = normalized.outputs[0]; assert_eq!(output.0, sym.addr); let meta = &normalized.meta[output.1]; - let so_path = Path::new(&env!("CARGO_MANIFEST_DIR")) - .join("data") - .join(so_name); - let expected = ApkElf { - apk_path: test_zip, - elf_path: PathBuf::from(so_name), - elf_build_id: Some(read_elf_build_id(&so_path).unwrap().unwrap()), + let expected = Apk { + path: test_zip, _non_exhaustive: (), }; - assert_eq!(meta, &UserAddrMeta::ApkElf(expected)); + assert_eq!(meta, &UserAddrMeta::Apk(expected)); } test("libtest-so.so"); diff --git a/src/normalize/user.rs b/src/normalize/user.rs index 1bad32203..e4adf4a79 100644 --- a/src/normalize/user.rs +++ b/src/normalize/user.rs @@ -20,9 +20,8 @@ use crate::Result; use super::buildid::BuildIdFn; use super::buildid::BuildIdReader; use super::buildid::DefaultBuildIdReader; -use super::buildid::ElfBuildIdFn; use super::buildid::NoBuildIdReader; -use super::meta::ApkElf; +use super::meta::Apk; use super::meta::Elf; use super::meta::Unknown; use super::meta::UserAddrMeta; @@ -82,20 +81,13 @@ fn make_elf_meta(entry: &PathMapsEntry, get_build_id: &BuildIdFn) -> Result Result { - let apk = ApkElf { - elf_build_id: get_build_id(elf_parser)?, - apk_path: entry.path.symbolic_path.to_path_buf(), - elf_path, +/// Make a [`UserAddrMeta::Apk`] variant. +fn make_apk_meta(entry: &PathMapsEntry) -> Result { + let apk = Apk { + path: entry.path.symbolic_path.to_path_buf(), _non_exhaustive: (), }; - let meta = UserAddrMeta::ApkElf(apk); + let meta = UserAddrMeta::Apk(apk); Ok(meta) } @@ -192,10 +184,10 @@ impl UserOutput { Some(unknown_idx) } - /// Add a normalized address to this object. - fn add_normalized_addr( + /// Add a (normalized) file offset to this object. + fn add_normalized_offset( &mut self, - norm_addr: Addr, + file_offset: Addr, key: &Path, meta_lookup: &mut HashMap, create_meta: F, @@ -213,7 +205,7 @@ impl UserOutput { meta_idx }; - let () = self.outputs.push((norm_addr, meta_idx)); + let () = self.outputs.push((file_offset, meta_idx)); Ok(()) } } @@ -256,41 +248,6 @@ impl NormalizationHandler { } } -impl NormalizationHandler -where - R: BuildIdReader, -{ - /// Normalize a virtual address belonging to an APK and create and add the - /// correct [`UserAddrMeta`] meta information. - fn normalize_and_add_apk_addr(&mut self, virt_addr: Addr, entry: &PathMapsEntry) -> Result<()> { - let file_off = virt_addr - entry.range.start + entry.offset; - let apk_path = &entry.path.symbolic_path; - let (norm_addr, elf_path, elf_parser) = normalize_apk_offset(file_off, apk_path)?; - let key = create_apk_elf_path(apk_path, &elf_path)?; - let () = - self.normalized - .add_normalized_addr(norm_addr, &key, &mut self.meta_lookup, || { - make_apk_elf_meta(entry, elf_path, &elf_parser, &R::read_build_id) - })?; - - Ok(()) - } - - /// Normalize a virtual address belonging to an ELF file and create and add - /// the correct [`UserAddrMeta`] meta information. - fn normalize_and_add_elf_addr(&mut self, virt_addr: Addr, entry: &PathMapsEntry) -> Result<()> { - let norm_addr = normalize_elf_addr(virt_addr, entry)?; - let () = self.normalized.add_normalized_addr( - norm_addr, - &entry.path.symbolic_path, - &mut self.meta_lookup, - || make_elf_meta(entry, &R::read_build_id_from_elf), - )?; - - Ok(()) - } -} - impl Handler for NormalizationHandler where R: BuildIdReader, @@ -302,14 +259,25 @@ where } fn handle_entry_addr(&mut self, addr: Addr, entry: &PathMapsEntry) -> Result<()> { + let file_off = addr - entry.range.start + entry.offset; let ext = entry .path .symbolic_path .extension() .unwrap_or_else(|| OsStr::new("")); match ext.to_str() { - Some("apk") | Some("zip") => self.normalize_and_add_apk_addr(addr, entry), - _ => self.normalize_and_add_elf_addr(addr, entry), + Some("apk") | Some("zip") => self.normalized.add_normalized_offset( + file_off, + &entry.path.symbolic_path, + &mut self.meta_lookup, + || make_apk_meta(entry), + ), + _ => self.normalized.add_normalized_offset( + file_off, + &entry.path.symbolic_path, + &mut self.meta_lookup, + || make_elf_meta(entry, &R::read_build_id_from_elf), + ), } } } @@ -380,8 +348,9 @@ where Ok(handler) } -/// Normalize all `addrs` in a given process. The `addrs` array has to -/// be sorted in ascending order or an error will be returned. +/// Normalize all `addrs` in a given process to the corresponding file offsets, +/// which are suitable for later symbolization. The `addrs` array has to be +/// sorted in ascending order or an error will be returned. /// /// Unknown addresses are not normalized. They are reported as /// [`Unknown`] meta entries in the returned [`UserOutput`] @@ -394,8 +363,8 @@ where /// /// The process' ID should be provided in `pid`. /// -/// Normalized addresses are reported in the exact same order in which the -/// non-normalized ones were provided. +/// File offsets are reported in the exact same order in which the +/// non-normalized addresses ones were provided. pub(super) fn normalize_user_addrs_sorted_impl( addrs: A, pid: Pid, diff --git a/tests/blazesym.rs b/tests/blazesym.rs index b9b4d4d4c..76ad5dc91 100644 --- a/tests/blazesym.rs +++ b/tests/blazesym.rs @@ -359,7 +359,7 @@ fn normalize_elf_addr() { let src = symbolize::Source::Elf(elf); let symbolizer = Symbolizer::new(); let result = symbolizer - .symbolize_single(&src, symbolize::Input::VirtOffset(output.0)) + .symbolize_single(&src, symbolize::Input::FileOffset(output.0)) .unwrap() .into_sym() .unwrap();