1use std::alloc::alloc;
2use std::alloc::dealloc;
3use std::alloc::Layout;
4use std::borrow::Cow;
5use std::ffi::CString;
6use std::ffi::OsString;
7use std::fmt::Debug;
8use std::fmt::Formatter;
9use std::fmt::Result as FmtResult;
10use std::mem;
11use std::mem::size_of;
12use std::mem::ManuallyDrop;
13use std::os::raw::c_char;
14use std::os::unix::ffi::OsStringExt as _;
15use std::path::PathBuf;
16use std::ptr;
17use std::slice;
18
19use blazesym::normalize::Apk;
20use blazesym::normalize::Elf;
21use blazesym::normalize::NormalizeOpts;
22use blazesym::normalize::Normalizer;
23use blazesym::normalize::Reason;
24use blazesym::normalize::Unknown;
25use blazesym::normalize::UserMeta;
26use blazesym::normalize::UserOutput;
27use blazesym::symbolize::Sym;
28use blazesym::Addr;
29
30use crate::blaze_err;
31#[cfg(doc)]
32use crate::blaze_err_last;
33use crate::blaze_sym;
34use crate::blaze_symbolize_inlined_fn;
35use crate::convert_sym;
36use crate::set_last_err;
37use crate::util::slice_from_user_array;
38use crate::util::DynSize as _;
39
40
41pub type blaze_normalizer = Normalizer;
43
44
45#[repr(C)]
47#[derive(Debug)]
48pub struct blaze_normalizer_opts {
49 pub type_size: usize,
54 pub use_procmap_query: bool,
73 pub cache_vmas: bool,
80 pub build_ids: bool,
86 pub cache_build_ids: bool,
89 pub reserved: [u8; 20],
92}
93
94impl Default for blaze_normalizer_opts {
95 fn default() -> Self {
96 Self {
97 type_size: size_of::<Self>(),
98 use_procmap_query: false,
99 cache_vmas: false,
100 build_ids: false,
101 cache_build_ids: false,
102 reserved: [0; 20],
103 }
104 }
105}
106
107
108#[repr(C)]
110#[derive(Debug)]
111pub struct blaze_normalize_opts {
112 pub type_size: usize,
117 pub sorted_addrs: bool,
123 pub map_files: bool,
133 pub apk_to_elf: bool,
139 pub reserved: [u8; 21],
142}
143
144impl Default for blaze_normalize_opts {
145 fn default() -> Self {
146 Self {
147 type_size: size_of::<Self>(),
148 sorted_addrs: false,
149 map_files: false,
150 apk_to_elf: false,
151 reserved: [0; 21],
152 }
153 }
154}
155
156impl From<blaze_normalize_opts> for NormalizeOpts {
157 fn from(opts: blaze_normalize_opts) -> Self {
158 let blaze_normalize_opts {
159 type_size: _,
160 sorted_addrs,
161 map_files,
162 apk_to_elf,
163 reserved: _,
164 } = opts;
165 Self {
166 sorted_addrs,
167 map_files,
168 apk_to_elf,
169 _non_exhaustive: (),
170 }
171 }
172}
173
174
175#[no_mangle]
189pub extern "C" fn blaze_normalizer_new() -> *mut blaze_normalizer {
190 let normalizer = Normalizer::new();
191 let normalizer_box = Box::new(normalizer);
192 let () = set_last_err(blaze_err::OK);
193 Box::into_raw(normalizer_box)
194}
195
196
197#[no_mangle]
210pub unsafe extern "C" fn blaze_normalizer_new_opts(
211 opts: *const blaze_normalizer_opts,
212) -> *mut blaze_normalizer {
213 if !input_zeroed!(opts, blaze_normalizer_opts) {
214 let () = set_last_err(blaze_err::INVALID_INPUT);
215 return ptr::null_mut()
216 }
217 let opts = input_sanitize!(opts, blaze_normalizer_opts);
218
219 let blaze_normalizer_opts {
220 type_size: _,
221 use_procmap_query,
222 cache_vmas,
223 build_ids,
224 cache_build_ids,
225 reserved: _,
226 } = opts;
227
228 let normalizer = Normalizer::builder()
229 .enable_procmap_query(use_procmap_query)
230 .enable_vma_caching(cache_vmas)
231 .enable_build_ids(build_ids)
232 .enable_build_id_caching(cache_build_ids)
233 .build();
234 let normalizer_box = Box::new(normalizer);
235 let () = set_last_err(blaze_err::OK);
236 Box::into_raw(normalizer_box)
237}
238
239
240#[no_mangle]
249pub unsafe extern "C" fn blaze_normalizer_free(normalizer: *mut blaze_normalizer) {
250 if !normalizer.is_null() {
251 drop(unsafe { Box::from_raw(normalizer) });
254 }
255}
256
257
258#[repr(C)]
262#[derive(Debug)]
263pub struct blaze_normalized_output {
264 pub output: u64,
266 pub meta_idx: usize,
268 pub reserved: [u8; 16],
271}
272
273impl From<(u64, usize)> for blaze_normalized_output {
274 fn from((output, meta_idx): (u64, usize)) -> Self {
275 Self {
276 output,
277 meta_idx,
278 reserved: [0; 16],
279 }
280 }
281}
282
283
284#[repr(transparent)]
286#[derive(Copy, Clone, Debug, Eq, PartialEq)]
287pub struct blaze_user_meta_kind(u8);
288
289impl blaze_user_meta_kind {
290 pub const UNKNOWN: blaze_user_meta_kind = blaze_user_meta_kind(0);
292 pub const APK: blaze_user_meta_kind = blaze_user_meta_kind(1);
294 pub const ELF: blaze_user_meta_kind = blaze_user_meta_kind(2);
296 pub const SYM: blaze_user_meta_kind = blaze_user_meta_kind(3);
298
299 #[deprecated]
302 pub const BLAZE_USER_META_UNKNOWN: blaze_user_meta_kind = blaze_user_meta_kind(0);
303 #[deprecated]
305 pub const BLAZE_USER_META_APK: blaze_user_meta_kind = blaze_user_meta_kind(1);
306 #[deprecated]
308 pub const BLAZE_USER_META_ELF: blaze_user_meta_kind = blaze_user_meta_kind(2);
309}
310
311
312#[repr(C)]
314#[derive(Debug)]
315pub struct blaze_user_meta_apk {
316 pub path: *mut c_char,
319 pub reserved: [u8; 16],
321}
322
323impl blaze_user_meta_apk {
324 fn from(other: Apk) -> ManuallyDrop<Self> {
325 let Apk {
326 path,
327 _non_exhaustive: (),
328 } = other;
329
330 let slf = Self {
331 path: CString::new(path.into_os_string().into_vec())
332 .expect("encountered path with NUL bytes")
333 .into_raw(),
334 reserved: [0; 16],
335 };
336 ManuallyDrop::new(slf)
337 }
338
339 unsafe fn free(self) {
340 let Self { path, reserved: _ } = self;
341
342 let _apk = Apk {
343 path: PathBuf::from(OsString::from_vec(
344 unsafe { CString::from_raw(path) }.into_bytes(),
345 )),
346 _non_exhaustive: (),
347 };
348 }
349}
350
351
352#[repr(C)]
354#[derive(Debug)]
355pub struct blaze_user_meta_elf {
356 pub path: *mut c_char,
364 pub build_id_len: usize,
366 pub build_id: *mut u8,
368 pub reserved: [u8; 16],
370}
371
372impl blaze_user_meta_elf {
373 fn from(other: Elf) -> ManuallyDrop<Self> {
374 let Elf {
375 path,
376 build_id,
377 _non_exhaustive: (),
378 } = other;
379
380 let slf = Self {
381 path: CString::new(path.into_os_string().into_vec())
382 .expect("encountered path with NUL bytes")
383 .into_raw(),
384 build_id_len: build_id
385 .as_ref()
386 .map(|build_id| build_id.len())
387 .unwrap_or(0),
388 build_id: build_id
389 .map(|build_id| {
390 unsafe {
393 Box::into_raw(build_id.to_vec().into_boxed_slice())
394 .as_mut()
395 .unwrap()
396 .as_mut_ptr()
397 }
398 })
399 .unwrap_or_else(ptr::null_mut),
400 reserved: [0; 16],
401 };
402 ManuallyDrop::new(slf)
403 }
404
405 unsafe fn free(self) {
406 let blaze_user_meta_elf {
407 path,
408 build_id_len,
409 build_id,
410 reserved: _,
411 } = self;
412
413 let _elf = Elf {
414 path: PathBuf::from(OsString::from_vec(
415 unsafe { CString::from_raw(path) }.into_bytes(),
416 )),
417 build_id: (!build_id.is_null()).then(|| unsafe {
418 Cow::Owned(
419 Box::<[u8]>::from_raw(slice::from_raw_parts_mut(build_id, build_id_len))
420 .into_vec(),
421 )
422 }),
423 _non_exhaustive: (),
424 };
425 }
426}
427
428
429#[repr(C)]
431#[derive(Debug)]
432pub struct blaze_user_meta_sym {
433 pub sym: *const blaze_sym,
435 pub reserved: [u8; 16],
437}
438
439impl blaze_user_meta_sym {
440 fn from(sym: Sym) -> ManuallyDrop<Self> {
441 let strtab_size = sym.c_str_size();
442 let buf_size = mem::size_of::<u64>()
443 + mem::size_of::<blaze_sym>()
444 + sym.inlined.len() * mem::size_of::<blaze_symbolize_inlined_fn>()
445 + strtab_size;
446 let buf = unsafe { alloc(Layout::from_size_align(buf_size, 8).unwrap()) };
447 assert!(!buf.is_null());
451
452 unsafe { *(buf as *mut u64) = buf_size as u64 };
454
455 let sym_buf = unsafe { buf.add(mem::size_of::<u64>()) }.cast::<blaze_sym>();
456 let mut inlined_last = unsafe { sym_buf.add(1) }.cast::<blaze_symbolize_inlined_fn>();
457 let mut cstr_last = unsafe { inlined_last.add(sym.inlined.len()) }.cast::<c_char>();
458 let sym_ref = unsafe { &mut *sym_buf };
459 let () = convert_sym(&sym, sym_ref, &mut inlined_last, &mut cstr_last);
460
461 let slf = Self {
462 sym: sym_buf,
463 reserved: [0; 16],
464 };
465 ManuallyDrop::new(slf)
466 }
467
468 unsafe fn free(self) {
469 let blaze_user_meta_sym { sym, reserved: _ } = self;
470
471 let buf = unsafe { sym.byte_sub(mem::size_of::<u64>()).cast::<u8>().cast_mut() };
472 let size = unsafe { *(buf as *mut u64) } as usize;
473 let () = unsafe { dealloc(buf, Layout::from_size_align(size, 8).unwrap()) };
474 }
475}
476
477
478#[repr(transparent)]
484#[derive(Copy, Clone, Debug, Eq, PartialEq)]
485pub struct blaze_normalize_reason(u8);
486
487impl blaze_normalize_reason {
488 pub const UNMAPPED: blaze_normalize_reason = blaze_normalize_reason(0);
491 pub const MISSING_COMPONENT: blaze_normalize_reason = blaze_normalize_reason(1);
494 pub const UNSUPPORTED: blaze_normalize_reason = blaze_normalize_reason(2);
496}
497
498impl From<Reason> for blaze_normalize_reason {
499 fn from(reason: Reason) -> Self {
500 match reason {
501 Reason::Unmapped => blaze_normalize_reason::UNMAPPED,
502 Reason::MissingComponent => blaze_normalize_reason::MISSING_COMPONENT,
503 Reason::Unsupported => blaze_normalize_reason::UNSUPPORTED,
504 _ => unreachable!(),
505 }
506 }
507}
508
509
510#[no_mangle]
512pub extern "C" fn blaze_normalize_reason_str(err: blaze_normalize_reason) -> *const c_char {
513 match err {
514 blaze_normalize_reason::UNMAPPED => Reason::Unmapped.as_bytes().as_ptr().cast(),
515 blaze_normalize_reason::MISSING_COMPONENT => {
516 Reason::MissingComponent.as_bytes().as_ptr().cast()
517 }
518 blaze_normalize_reason::UNSUPPORTED => Reason::Unsupported.as_bytes().as_ptr().cast(),
519 _ => b"unknown reason\0".as_ptr().cast(),
520 }
521}
522
523
524#[repr(C)]
526#[derive(Debug)]
527pub struct blaze_user_meta_unknown {
528 pub reason: blaze_normalize_reason,
533 pub reserved: [u8; 15],
535}
536
537impl blaze_user_meta_unknown {
538 fn from(other: Unknown) -> ManuallyDrop<Self> {
539 let Unknown {
540 reason,
541 _non_exhaustive: (),
542 } = other;
543
544 let slf = Self {
545 reason: reason.into(),
546 reserved: [0; 15],
547 };
548 ManuallyDrop::new(slf)
549 }
550
551 fn free(self) {
552 let blaze_user_meta_unknown {
553 reason: _,
554 reserved: _,
555 } = self;
556 }
557}
558
559
560#[repr(C)]
562pub union blaze_user_meta_variant {
563 pub apk: ManuallyDrop<blaze_user_meta_apk>,
565 pub elf: ManuallyDrop<blaze_user_meta_elf>,
567 pub sym: ManuallyDrop<blaze_user_meta_sym>,
569 pub unknown: ManuallyDrop<blaze_user_meta_unknown>,
571}
572
573impl Debug for blaze_user_meta_variant {
574 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
575 f.debug_struct(stringify!(blaze_user_meta_variant)).finish()
576 }
577}
578
579
580#[repr(C)]
582#[derive(Debug)]
583pub struct blaze_user_meta {
584 pub kind: blaze_user_meta_kind,
586 pub unused: [u8; 7],
588 pub variant: blaze_user_meta_variant,
590 pub reserved: [u8; 16],
593}
594
595impl blaze_user_meta {
596 fn from(other: UserMeta) -> ManuallyDrop<Self> {
597 let slf = match other {
598 UserMeta::Apk(apk) => Self {
599 kind: blaze_user_meta_kind::APK,
600 unused: [0; 7],
601 variant: blaze_user_meta_variant {
602 apk: blaze_user_meta_apk::from(apk),
603 },
604 reserved: [0; 16],
605 },
606 UserMeta::Elf(elf) => Self {
607 kind: blaze_user_meta_kind::ELF,
608 unused: [0; 7],
609 variant: blaze_user_meta_variant {
610 elf: blaze_user_meta_elf::from(elf),
611 },
612 reserved: [0; 16],
613 },
614 UserMeta::Sym(sym) => Self {
615 kind: blaze_user_meta_kind::SYM,
616 unused: [0; 7],
617 variant: blaze_user_meta_variant {
618 sym: blaze_user_meta_sym::from(sym),
619 },
620 reserved: [0; 16],
621 },
622 UserMeta::Unknown(unknown) => Self {
623 kind: blaze_user_meta_kind::UNKNOWN,
624 unused: [0; 7],
625 variant: blaze_user_meta_variant {
626 unknown: blaze_user_meta_unknown::from(unknown),
627 },
628 reserved: [0; 16],
629 },
630 _ => unreachable!(),
631 };
632 ManuallyDrop::new(slf)
633 }
634
635 unsafe fn free(self) {
636 match self.kind {
637 blaze_user_meta_kind::APK => unsafe {
638 ManuallyDrop::into_inner(self.variant.apk).free()
639 },
640 blaze_user_meta_kind::ELF => unsafe {
641 ManuallyDrop::into_inner(self.variant.elf).free()
642 },
643 blaze_user_meta_kind::SYM => unsafe {
644 ManuallyDrop::into_inner(self.variant.sym).free()
645 },
646 blaze_user_meta_kind::UNKNOWN => {
647 ManuallyDrop::into_inner(unsafe { self.variant.unknown }).free()
648 }
649 _ => {
650 debug_assert!(false)
651 }
652 }
653 }
654}
655
656
657#[repr(C)]
661#[derive(Debug)]
662pub struct blaze_normalized_user_output {
663 pub meta_cnt: usize,
665 pub metas: *mut blaze_user_meta,
667 pub output_cnt: usize,
669 pub outputs: *mut blaze_normalized_output,
671 pub reserved: [u8; 16],
673}
674
675impl blaze_normalized_user_output {
676 fn from(other: UserOutput) -> ManuallyDrop<Self> {
677 let slf = Self {
678 meta_cnt: other.meta.len(),
679 metas: unsafe {
680 Box::into_raw(
681 other
682 .meta
683 .into_iter()
684 .map(blaze_user_meta::from)
685 .map(ManuallyDrop::into_inner)
686 .collect::<Vec<_>>()
687 .into_boxed_slice(),
688 )
689 .as_mut()
690 .unwrap()
691 .as_mut_ptr()
692 },
693 output_cnt: other.outputs.len(),
694 outputs: unsafe {
695 Box::into_raw(
696 other
697 .outputs
698 .into_iter()
699 .map(blaze_normalized_output::from)
700 .collect::<Vec<_>>()
701 .into_boxed_slice(),
702 )
703 .as_mut()
704 .unwrap()
705 .as_mut_ptr()
706 },
707 reserved: [0; 16],
708 };
709 ManuallyDrop::new(slf)
710 }
711}
712
713
714unsafe fn blaze_normalize_user_addrs_impl(
715 normalizer: *const blaze_normalizer,
716 pid: u32,
717 addrs: *const Addr,
718 addr_cnt: usize,
719 opts: &NormalizeOpts,
720) -> *mut blaze_normalized_user_output {
721 let normalizer = unsafe { &*normalizer };
724 let addrs = unsafe { slice_from_user_array(addrs, addr_cnt) };
727 let result = normalizer.normalize_user_addrs_opts(pid.into(), &addrs, opts);
728 match result {
729 Ok(output) => {
730 let output_box = Box::new(ManuallyDrop::into_inner(
731 blaze_normalized_user_output::from(output),
732 ));
733 let () = set_last_err(blaze_err::OK);
734 Box::into_raw(output_box)
735 }
736 Err(err) => {
737 let () = set_last_err(err.kind().into());
738 ptr::null_mut()
739 }
740 }
741}
742
743
744#[no_mangle]
762pub unsafe extern "C" fn blaze_normalize_user_addrs(
763 normalizer: *const blaze_normalizer,
764 pid: u32,
765 addrs: *const Addr,
766 addr_cnt: usize,
767) -> *mut blaze_normalized_user_output {
768 let opts = NormalizeOpts::default();
769
770 unsafe { blaze_normalize_user_addrs_impl(normalizer, pid, addrs, addr_cnt, &opts) }
771}
772
773
774#[no_mangle]
794pub unsafe extern "C" fn blaze_normalize_user_addrs_opts(
795 normalizer: *const blaze_normalizer,
796 pid: u32,
797 addrs: *const Addr,
798 addr_cnt: usize,
799 opts: *const blaze_normalize_opts,
800) -> *mut blaze_normalized_user_output {
801 if !input_zeroed!(opts, blaze_normalize_opts) {
802 let () = set_last_err(blaze_err::INVALID_INPUT);
803 return ptr::null_mut()
804 }
805 let opts = input_sanitize!(opts, blaze_normalize_opts);
806 let opts = NormalizeOpts::from(opts);
807
808 unsafe { blaze_normalize_user_addrs_impl(normalizer, pid, addrs, addr_cnt, &opts) }
809}
810
811
812#[no_mangle]
820pub unsafe extern "C" fn blaze_user_output_free(output: *mut blaze_normalized_user_output) {
821 if output.is_null() {
822 return
823 }
824
825 let user_output = unsafe { Box::from_raw(output) };
828 let addr_metas = unsafe {
829 Box::<[blaze_user_meta]>::from_raw(slice::from_raw_parts_mut(
830 user_output.metas,
831 user_output.meta_cnt,
832 ))
833 }
834 .into_vec();
835 let _norm_addrs = unsafe {
836 Box::<[blaze_normalized_output]>::from_raw(slice::from_raw_parts_mut(
837 user_output.outputs,
838 user_output.output_cnt,
839 ))
840 }
841 .into_vec();
842
843 for addr_meta in addr_metas {
844 let () = unsafe { addr_meta.free() };
845 }
846}
847
848
849#[cfg(test)]
850mod tests {
851 use super::*;
852
853 use std::ffi::CStr;
854 use std::io;
855 use std::path::Path;
856
857 use blazesym::helper::read_elf_build_id;
858 use blazesym::Mmap;
859 use blazesym::__private::find_the_answer_fn;
860 use blazesym::__private::zip;
861
862 use test_tag::tag;
863
864 use crate::blaze_err_last;
865
866
867 #[tag(miri)]
869 #[test]
870 #[cfg(target_pointer_width = "64")]
871 fn type_sizes() {
872 assert_eq!(size_of::<blaze_normalizer_opts>(), 32);
873 assert_eq!(size_of::<blaze_normalize_opts>(), 32);
874 assert_eq!(size_of::<blaze_user_meta_apk>(), 24);
875 assert_eq!(size_of::<blaze_user_meta_elf>(), 40);
876 assert_eq!(size_of::<blaze_user_meta_sym>(), 24);
877 assert_eq!(size_of::<blaze_user_meta_unknown>(), 16);
878 }
879
880 #[tag(miri)]
882 #[test]
883 fn debug_repr() {
884 let output = blaze_normalized_output {
885 output: 0x1337,
886 meta_idx: 1,
887 reserved: [0; 16],
888 };
889 assert_eq!(
890 format!("{output:?}"),
891 "blaze_normalized_output { output: 4919, meta_idx: 1, reserved: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }"
892 );
893
894 let meta_kind = blaze_user_meta_kind::APK;
895 assert_eq!(format!("{meta_kind:?}"), "blaze_user_meta_kind(1)");
896
897 let apk = blaze_user_meta_apk {
898 path: ptr::null_mut(),
899 reserved: [0; 16],
900 };
901 assert_eq!(
902 format!("{apk:?}"),
903 "blaze_user_meta_apk { path: 0x0, reserved: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }",
904 );
905
906 let elf = blaze_user_meta_elf {
907 path: ptr::null_mut(),
908 build_id_len: 0,
909 build_id: ptr::null_mut(),
910 reserved: [0; 16],
911 };
912 assert_eq!(
913 format!("{elf:?}"),
914 "blaze_user_meta_elf { path: 0x0, build_id_len: 0, build_id: 0x0, reserved: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }",
915 );
916
917 let unknown = blaze_user_meta_unknown {
918 reason: blaze_normalize_reason::UNMAPPED,
919 reserved: [0; 15],
920 };
921 assert_eq!(
922 format!("{unknown:?}"),
923 "blaze_user_meta_unknown { reason: blaze_normalize_reason(0), reserved: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }",
924 );
925
926 let meta = blaze_user_meta {
927 kind: blaze_user_meta_kind::UNKNOWN,
928 unused: [0; 7],
929 variant: blaze_user_meta_variant {
930 unknown: ManuallyDrop::new(blaze_user_meta_unknown {
931 reason: blaze_normalize_reason::UNMAPPED,
932 reserved: [0; 15],
933 }),
934 },
935 reserved: [0; 16],
936 };
937 assert_eq!(
938 format!("{meta:?}"),
939 "blaze_user_meta { kind: blaze_user_meta_kind(0), unused: [0, 0, 0, 0, 0, 0, 0], variant: blaze_user_meta_variant, reserved: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }",
940 );
941
942 let normalized = blaze_normalized_user_output {
943 meta_cnt: 0,
944 metas: ptr::null_mut(),
945 output_cnt: 0,
946 outputs: ptr::null_mut(),
947 reserved: [0; 16],
948 };
949 assert_eq!(
950 format!("{normalized:?}"),
951 "blaze_normalized_user_output { meta_cnt: 0, metas: 0x0, output_cnt: 0, outputs: 0x0, reserved: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }",
952 );
953 }
954
955 #[tag(miri)]
957 #[test]
958 fn reason_stringification() {
959 let data = [
960 (Reason::Unmapped, blaze_normalize_reason::UNMAPPED),
961 (
962 Reason::MissingComponent,
963 blaze_normalize_reason::MISSING_COMPONENT,
964 ),
965 (Reason::Unsupported, blaze_normalize_reason::UNSUPPORTED),
966 ];
967
968 for (reason, expected) in data {
969 assert_eq!(blaze_normalize_reason::from(reason), expected);
970 let cstr = unsafe { CStr::from_ptr(blaze_normalize_reason_str(expected)) };
971 let expected = CStr::from_bytes_with_nul(reason.as_bytes()).unwrap();
972 assert_eq!(cstr, expected);
973 }
974 }
975
976 #[tag(miri)]
979 #[test]
980 fn unknown_conversion() {
981 let unknown = Unknown {
982 reason: Reason::Unsupported,
983 _non_exhaustive: (),
984 };
985
986 let unknown_c = blaze_user_meta_unknown::from(unknown.clone());
987 let () = ManuallyDrop::into_inner(unknown_c).free();
988
989 let meta = UserMeta::Unknown(unknown);
990 let meta_c = blaze_user_meta::from(meta);
991 let () = unsafe { ManuallyDrop::into_inner(meta_c).free() };
992 }
993
994 #[tag(miri)]
997 #[test]
998 fn apk_conversion() {
999 let apk = Apk {
1000 path: PathBuf::from("/tmp/archive.apk"),
1001 _non_exhaustive: (),
1002 };
1003
1004 let apk_c = blaze_user_meta_apk::from(apk.clone());
1005 let () = unsafe { ManuallyDrop::into_inner(apk_c).free() };
1006
1007 let meta = UserMeta::Apk(apk);
1008 let meta_c = blaze_user_meta::from(meta);
1009 let () = unsafe { ManuallyDrop::into_inner(meta_c).free() };
1010 }
1011
1012 #[tag(miri)]
1015 #[test]
1016 fn elf_conversion() {
1017 let elf = Elf {
1018 path: PathBuf::from("/tmp/file.so"),
1019 build_id: Some(Cow::Borrowed(&[0x01, 0x02, 0x03, 0x04])),
1020 _non_exhaustive: (),
1021 };
1022
1023 let elf_c = blaze_user_meta_elf::from(elf.clone());
1024 let () = unsafe { ManuallyDrop::into_inner(elf_c).free() };
1025
1026 let meta = UserMeta::Elf(elf);
1027 let meta_c = blaze_user_meta::from(meta);
1028 let () = unsafe { ManuallyDrop::into_inner(meta_c).free() };
1029 }
1030
1031 #[tag(miri)]
1033 #[test]
1034 fn normalizer_creation() {
1035 let normalizer = blaze_normalizer_new();
1036 let () = unsafe { blaze_normalizer_free(normalizer) };
1037 }
1038
1039 #[test]
1041 fn normalize_user_addrs() {
1042 fn test(normalizer: *const blaze_normalizer) {
1043 let addrs = [
1044 0x0,
1045 libc::atexit as Addr,
1046 libc::chdir as Addr,
1047 libc::fopen as Addr,
1048 elf_conversion as Addr,
1049 normalize_user_addrs as Addr,
1050 ];
1051
1052 let result = unsafe {
1053 blaze_normalize_user_addrs(normalizer, 0, addrs.as_slice().as_ptr(), addrs.len())
1054 };
1055 assert_ne!(result, ptr::null_mut());
1056
1057 let normalized = unsafe { &*result };
1058 assert_eq!(normalized.meta_cnt, 3);
1059 assert_eq!(normalized.output_cnt, 6);
1060
1061 let meta = unsafe { normalized.metas.read() };
1062 assert_eq!(meta.kind, blaze_user_meta_kind::UNKNOWN);
1063 assert_eq!(
1064 unsafe { meta.variant.unknown.reason },
1065 blaze_normalize_reason::UNMAPPED
1066 );
1067
1068 let () = unsafe { blaze_user_output_free(result) };
1069 }
1070
1071 let normalizer = blaze_normalizer_new();
1072 assert_ne!(normalizer, ptr::null_mut());
1073 test(normalizer);
1074 let () = unsafe { blaze_normalizer_free(normalizer) };
1075
1076 let opts = blaze_normalizer_opts {
1077 cache_vmas: true,
1078 ..Default::default()
1079 };
1080 let normalizer = unsafe { blaze_normalizer_new_opts(&opts) };
1081 assert_ne!(normalizer, ptr::null_mut());
1082 test(normalizer);
1083 test(normalizer);
1084 let () = unsafe { blaze_normalizer_free(normalizer) };
1085 }
1086
1087 fn test_normalize_user_addrs_sorted(use_procmap_query: bool) {
1088 let mut addrs = [
1089 libc::atexit as Addr,
1090 libc::chdir as Addr,
1091 libc::fopen as Addr,
1092 elf_conversion as Addr,
1093 normalize_user_addrs as Addr,
1094 ];
1095 let () = addrs.sort();
1096
1097 let opts = blaze_normalizer_opts {
1098 use_procmap_query,
1099 ..Default::default()
1100 };
1101 let normalizer = unsafe { blaze_normalizer_new_opts(&opts) };
1102 assert_ne!(normalizer, ptr::null_mut());
1103
1104 let opts = blaze_normalize_opts {
1105 sorted_addrs: true,
1106 ..Default::default()
1107 };
1108 let result = unsafe {
1109 blaze_normalize_user_addrs_opts(
1110 normalizer,
1111 0,
1112 addrs.as_slice().as_ptr(),
1113 addrs.len(),
1114 &opts,
1115 )
1116 };
1117 assert_ne!(result, ptr::null_mut());
1118
1119 let normalized = unsafe { &*result };
1120 assert_eq!(normalized.meta_cnt, 2);
1121 assert_eq!(normalized.output_cnt, 5);
1122
1123 let () = unsafe { blaze_user_output_free(result) };
1124 let () = unsafe { blaze_normalizer_free(normalizer) };
1125 }
1126
1127 #[test]
1129 fn normalize_user_addrs_sorted_proc_maps() {
1130 test_normalize_user_addrs_sorted(false)
1131 }
1132
1133 #[test]
1136 #[ignore = "test requires PROCMAP_QUERY ioctl kernel support"]
1137 fn normalize_user_addrs_sorted_ioctl() {
1138 test_normalize_user_addrs_sorted(true)
1139 }
1140
1141 #[test]
1144 fn normalize_user_addrs_unsorted_failure() {
1145 let mut addrs = [
1146 libc::atexit as Addr,
1147 libc::chdir as Addr,
1148 libc::fopen as Addr,
1149 elf_conversion as Addr,
1150 normalize_user_addrs as Addr,
1151 ];
1152 let () = addrs.sort_by(|addr1, addr2| addr1.cmp(addr2).reverse());
1153
1154 let normalizer = blaze_normalizer_new();
1155 assert_ne!(normalizer, ptr::null_mut());
1156
1157 let opts = blaze_normalize_opts {
1158 sorted_addrs: true,
1159 ..Default::default()
1160 };
1161 let result = unsafe {
1162 blaze_normalize_user_addrs_opts(
1163 normalizer,
1164 0,
1165 addrs.as_slice().as_ptr(),
1166 addrs.len(),
1167 &opts,
1168 )
1169 };
1170 assert_eq!(result, ptr::null_mut());
1171 assert_eq!(blaze_err_last(), blaze_err::INVALID_INPUT);
1172
1173 let () = unsafe { blaze_normalizer_free(normalizer) };
1174 }
1175
1176 #[test]
1178 #[cfg_attr(
1179 not(target_pointer_width = "64"),
1180 ignore = "loads 64 bit shared object"
1181 )]
1182 fn normalize_build_id_reading() {
1183 fn test(read_build_ids: bool) {
1184 let test_so = Path::new(&env!("CARGO_MANIFEST_DIR"))
1185 .join("..")
1186 .join("data")
1187 .join("libtest-so.so")
1188 .canonicalize()
1189 .unwrap();
1190 let so_cstr = CString::new(test_so.clone().into_os_string().into_vec()).unwrap();
1191 let handle = unsafe { libc::dlopen(so_cstr.as_ptr(), libc::RTLD_NOW) };
1192 assert!(!handle.is_null());
1193
1194 let the_answer_addr = unsafe { libc::dlsym(handle, "the_answer\0".as_ptr().cast()) };
1195 assert!(!the_answer_addr.is_null());
1196
1197 let opts = blaze_normalizer_opts {
1198 build_ids: read_build_ids,
1199 ..Default::default()
1200 };
1201
1202 let normalizer = unsafe { blaze_normalizer_new_opts(&opts) };
1203 assert!(!normalizer.is_null());
1204
1205 let opts = blaze_normalize_opts {
1206 sorted_addrs: true,
1207 ..Default::default()
1208 };
1209 let addrs = [the_answer_addr as Addr];
1210 let result = unsafe {
1211 blaze_normalize_user_addrs_opts(
1212 normalizer,
1213 0,
1214 addrs.as_slice().as_ptr(),
1215 addrs.len(),
1216 &opts,
1217 )
1218 };
1219 assert!(!result.is_null());
1220
1221 let normalized = unsafe { &*result };
1222 assert_eq!(normalized.meta_cnt, 1);
1223 assert_eq!(normalized.output_cnt, 1);
1224
1225 let rc = unsafe { libc::dlclose(handle) };
1226 assert_eq!(rc, 0, "{}", io::Error::last_os_error());
1227
1228 let output = unsafe { &*normalized.outputs.add(0) };
1229 let meta = unsafe { &*normalized.metas.add(output.meta_idx) };
1230 assert_eq!(meta.kind, blaze_user_meta_kind::ELF);
1231
1232 let elf = unsafe { &meta.variant.elf };
1233
1234 assert!(!elf.path.is_null());
1235 let path = unsafe { CStr::from_ptr(elf.path) };
1236 assert_eq!(path, so_cstr.as_ref());
1237
1238 if read_build_ids {
1239 let expected = read_elf_build_id(&test_so).unwrap().unwrap();
1240 let build_id = unsafe { slice_from_user_array(elf.build_id, elf.build_id_len) };
1241 assert_eq!(build_id, expected.as_ref());
1242 } else {
1243 assert!(elf.build_id.is_null());
1244 }
1245
1246 let () = unsafe { blaze_user_output_free(result) };
1247 let () = unsafe { blaze_normalizer_free(normalizer) };
1248 }
1249
1250 test(true);
1251 test(false);
1252 }
1253
1254 #[test]
1257 fn normalize_custom_so_in_zip() {
1258 let test_zip = Path::new(&env!("CARGO_MANIFEST_DIR"))
1259 .join("..")
1260 .join("data")
1261 .join("test.zip");
1262 let so_name = "libtest-so.so";
1263
1264 let mmap = Mmap::builder().exec().open(&test_zip).unwrap();
1265 let archive = zip::Archive::with_mmap(mmap.clone()).unwrap();
1266 let so = archive
1267 .entries()
1268 .find_map(|entry| {
1269 let entry = entry.unwrap();
1270 (entry.path == Path::new(so_name)).then_some(entry)
1271 })
1272 .unwrap();
1273
1274 let elf_mmap = mmap
1275 .constrain(so.data_offset..so.data_offset + so.data.len() as u64)
1276 .unwrap();
1277 let (_sym, the_answer_addr) = find_the_answer_fn(&elf_mmap);
1278
1279 let normalizer = blaze_normalizer_new();
1280 assert!(!normalizer.is_null());
1281
1282 let addrs = [the_answer_addr];
1283 let opts = blaze_normalize_opts {
1284 apk_to_elf: true,
1285 ..Default::default()
1286 };
1287 let result = unsafe {
1288 blaze_normalize_user_addrs_opts(
1289 normalizer,
1290 0,
1291 addrs.as_slice().as_ptr(),
1292 addrs.len(),
1293 &opts,
1294 )
1295 };
1296 assert_ne!(result, ptr::null_mut());
1297
1298 let normalized = unsafe { &*result };
1299 assert_eq!(normalized.meta_cnt, 1);
1300 assert_eq!(normalized.output_cnt, 1);
1301
1302 let output = unsafe { &*normalized.outputs.add(0) };
1303 let meta = unsafe { &*normalized.metas.add(output.meta_idx) };
1304 assert_eq!(meta.kind, blaze_user_meta_kind::ELF);
1305
1306 let elf = unsafe { &meta.variant.elf };
1307 let path = unsafe { CStr::from_ptr(elf.path) };
1308 assert!(path.to_str().unwrap().ends_with(so_name), "{path:?}");
1309
1310 let () = unsafe { blaze_user_output_free(result) };
1311 let () = unsafe { blaze_normalizer_free(normalizer) };
1312 }
1313
1314 #[cfg(linux)]
1317 #[cfg(target_pointer_width = "64")]
1319 #[test]
1320 fn normalize_local_vdso_address() {
1321 use libc::gettimeofday;
1322
1323 let addrs = [normalize_local_vdso_address as Addr, gettimeofday as Addr];
1324 let normalizer = blaze_normalizer_new();
1325 assert!(!normalizer.is_null());
1326
1327 let result = unsafe {
1328 blaze_normalize_user_addrs(normalizer, 0, addrs.as_slice().as_ptr(), addrs.len())
1329 };
1330 assert_ne!(result, ptr::null_mut());
1331
1332 let normalized = unsafe { &*result };
1333 assert_eq!(normalized.meta_cnt, 2);
1334 assert_eq!(normalized.output_cnt, 2);
1335
1336 let output = unsafe { &*normalized.outputs.add(1) };
1337 let meta = unsafe { &*normalized.metas.add(output.meta_idx) };
1338 assert_eq!(meta.kind, blaze_user_meta_kind::SYM);
1339
1340 let sym = unsafe { &*meta.variant.sym.sym };
1341 let name = unsafe { CStr::from_ptr(sym.name) };
1342 assert!(name.to_str().unwrap().ends_with("gettimeofday"), "{name:?}");
1343
1344 let () = unsafe { blaze_user_output_free(result) };
1345 let () = unsafe { blaze_normalizer_free(normalizer) };
1346 }
1347}