1use std::collections::HashMap;
2
3use proc_macro::TokenStream;
4use quote::quote;
5
6fn construct_tests_from_config() -> Vec<proc_macro2::TokenStream> {
7 let meta = cargo_metadata::MetadataCommand::new().exec().unwrap();
8 let mut tests = HashMap::new();
9 let ws_meta = meta.workspace_metadata.as_object();
10 if let Some(v) = ws_meta.and_then(|v| v.get("malloc-tests")) {
11 for (name, cmd) in v.as_object().unwrap() {
12 let cmd = cmd.as_str().unwrap();
13 tests.insert(name.to_owned(), cmd.to_owned());
14 }
15 }
16 tests
17 .iter()
18 .map(|(k, v)| {
19 let s = format!(
20 r#"
21 #[test]
22 fn {}() {{
23 ::mallockit::util::testing::malloc::test(env!("CARGO_CRATE_NAME"), {:?});
24 }}
25 "#,
26 k, v,
27 );
28 s.parse().unwrap()
29 })
30 .collect()
31}
32
33#[proc_macro_attribute]
34pub fn plan(_attr: TokenStream, item: TokenStream) -> TokenStream {
35 let input = syn::parse_macro_input!(item as syn::DeriveInput);
36 let name = &input.ident;
37
38 let tests = construct_tests_from_config();
39
40 let result = quote! {
41 #input
42
43 mod __mallockit_plan {
44 type Plan = super::#name;
45
46 static PLAN: ::mallockit::util::Lazy<Plan> = ::mallockit::util::Lazy::new(|| <Plan as ::mallockit::Plan>::new());
47
48 #[cfg(feature = "malloc")]
49 #[::mallockit::ctor]
50 unsafe fn ctor() {
51 <<Plan as ::mallockit::Plan>::Mutator as ::mallockit::mutator::TLS>::current();
52 ::mallockit::util::sys::hooks::process_start(&*PLAN);
53 }
54
55 #[cfg(target_os = "macos")]
56 #[no_mangle]
57 extern "C" fn mallockit_initialize_macos_tls() -> *mut u8 {
58 use ::mallockit::mutator::TLS;
59 <Plan as ::mallockit::Plan>::Mutator::current() as *mut <Plan as ::mallockit::Plan>::Mutator as _
60 }
61
62 impl ::mallockit::plan::Singleton for super::#name {
63 fn singleton() -> &'static Self {
64 unsafe { &PLAN }
65 }
66 }
67
68 ::mallockit::export_malloc_api!(PLAN, super::super::#name);
69 ::mallockit::export_rust_global_alloc_api!(super::super::#name);
70 }
71
72 pub use __mallockit_plan::__mallockit_rust_api::Global;
73
74 #[cfg(test)]
75 mod tests {
76 #[cfg(feature = "malloc")]
77 mod malloc {
78 #(#tests)*
79 }
80 ::mallockit::rust_allocator_tests!(crate::Global);
81 }
82 };
83 result.into()
84}
85
86#[proc_macro_attribute]
87pub fn mutator(_attr: TokenStream, item: TokenStream) -> TokenStream {
88 let input = syn::parse_macro_input!(item as syn::DeriveInput);
89 let name = &input.ident;
90 let result = quote! {
91 #[repr(align(256))]
92 #input
93
94
95 mod __mallockit_mutator {
96 #[cfg(not(target_os = "macos"))]
97 fn init() -> super::#name {
98 ::mallockit::mutator::init_pthread_specific();
99 <super::#name as ::mallockit::Mutator>::new()
100 }
101
102 #[cfg(not(target_os = "macos"))]
103 #[thread_local]
104 pub(super) static mut MUTATOR: ::mallockit::util::Lazy<super::#name, ::mallockit::util::Local> = ::mallockit::util::Lazy::new(init);
105
106 #[no_mangle]
107 #[cfg(not(target_os = "macos"))]
108 extern "C" fn mallockit_pthread_destructor() {
109 unsafe {
110 MUTATOR.reset(init);
111 }
112 }
113
114 #[no_mangle]
115 #[cfg(target_os = "macos")]
116 extern "C" fn mallockit_pthread_destructor() {
117 use crate::mallockit::mutator::TLS;
118 <super::#name as ::mallockit::mutator::TLS>::current().reset();
119 }
120 }
121
122 impl ::mallockit::mutator::TLS for #name {
123 fn new() -> Self {
124 <Self as ::mallockit::Mutator>::new()
125 }
126
127 #[cfg(not(target_os = "macos"))]
128 fn current() -> &'static mut Self {
129 unsafe { &mut *__mallockit_mutator::MUTATOR }
130 }
131 }
132
133 };
134 result.into()
135}
136
137#[proc_macro_attribute]
138pub fn interpose(_attr: TokenStream, item: TokenStream) -> TokenStream {
139 let input = syn::parse_macro_input!(item as syn::ItemFn);
140 let name = &input.sig.ident;
141 let interpose_name = syn::Ident::new(&format!("_interpose_{}", name), name.span());
142 let result = quote! {
143 #[cfg(target_os = "macos")]
144 #[cfg(not(test))]
145 pub mod #name {
146 #[repr(C)]
147 pub struct Interpose {
148 _new: *const (),
149 _old: *const (),
150 }
151
152 #[no_mangle]
153 #[allow(non_upper_case_globals)]
154 #[link_section = "__DATA,__interpose"]
155 pub static mut #interpose_name: Interpose = Interpose {
156 _new: super::#name as *const (),
157 _old: #name as *const (),
158 };
159
160 extern {
161 pub fn #name();
162 }
163 }
164
165 #[cfg(target_os = "macos")]
166 #[cfg(not(test))]
167 #input
168
169 #[cfg(not(target_os = "macos"))]
170 #[cfg(not(test))]
171 #[no_mangle]
172 #input
173 };
174 result.into()
175}
176
177#[proc_macro_attribute]
178pub fn aligned_block(_attr: TokenStream, item: TokenStream) -> TokenStream {
179 let input = syn::parse_macro_input!(item as syn::DeriveInput);
180 let name = &input.ident;
181 let result = quote! {
182 #[repr(transparent)]
183 #input
184
185 mallockit::impl_aligned_block!(#name);
186 };
187 result.into()
188}