1#![feature(fmt_helpers_for_derive)]
2use std::sync::OnceLock;
60
61#[cfg(not(target_arch = "x86_64"))]
62compile_error!("only supported on x86_64");
63
64struct Pos(*const u8);
65unsafe impl Send for Pos {}
66unsafe impl Sync for Pos {}
67static MATCHES: OnceLock<Vec<Pos>> = OnceLock::new();
68
69
70pub unsafe fn enable(on: bool) {
80 unsafe {
81 let matches = MATCHES.get_or_init(find_all);
82
83 for Pos(ptr) in matches {
84 let ptr = ptr.cast_mut();
85 let _prot = region::protect_with_handle(ptr, 1, region::Protection::READ_WRITE_EXECUTE)
86 .unwrap();
87 ptr.write(if on { 0 } else { 4 });
88 }
89 }
90}
91
92fn find_all() -> Vec<Pos> {
93 unsafe {
94 let mut out = Vec::new();
95 macro_rules! find {
96 ($name:path) => {
97 do_find(&mut out, stringify!($name), $name as *const () as *const u8);
98 };
99 }
100 find!(std::fmt::DebugTuple::field);
101 find!(std::fmt::DebugTuple::finish);
102 find!(std::fmt::DebugTuple::finish_non_exhaustive);
103 find!(std::fmt::Formatter::debug_tuple_field1_finish);
104 find!(std::fmt::Formatter::debug_tuple_field2_finish);
105 find!(std::fmt::Formatter::debug_tuple_field3_finish);
106 find!(std::fmt::Formatter::debug_tuple_field4_finish);
107 find!(std::fmt::Formatter::debug_tuple_field5_finish);
108 find!(std::fmt::Formatter::debug_tuple_fields_finish);
109
110 assert!(out.iter().all(|x| x.0 == out[0].0), "field offsets differ");
112
113 out.into_iter().map(|x| Pos(x.1)).collect()
114 }
115}
116
117unsafe fn do_find(out: &mut Vec<(u8, *const u8)>, name: &str, mut ptr: *const u8) {
120 let n = out.len();
121 loop {
122 if (ptr as usize & 0xF) == 0xF && (*ptr == 0xC3 || *ptr == 0xCC) {
123 break;
124 }
125 if *ptr == 0xF6 && *ptr.add(1) & 0xF0 == 0x40 && *ptr.add(3) == 0x04 {
126 out.push((*ptr.add(2), ptr.add(3)));
127 }
128 ptr = ptr.add(1);
129 }
130 assert!(out.len() > n, "no matches found for {name}");
131}
132
133#[test]
134fn test() {
135 #[derive(Debug)]
136 #[allow(dead_code)]
137 struct A(u32, u32);
138
139 #[allow(dead_code)]
140 #[derive(Debug)]
141 struct B {
142 x: u32,
143 y: u32,
144 }
145
146 let a = A(8, 32);
147 let b = B { x: 8, y: 32 };
148
149 assert_eq!(format!("{a:?}"), "A(8, 32)");
150 assert_eq!(format!("{a:#?}"), "A(\n 8,\n 32,\n)");
151 assert_eq!(format!("{b:?}"), "B { x: 8, y: 32 }");
152 assert_eq!(format!("{b:#?}"), "B {\n x: 8,\n y: 32,\n}");
153
154 unsafe { enable(true) };
155
156 assert_eq!(format!("{a:?}"), "A(8, 32)");
157 assert_eq!(format!("{a:#?}"), "A(8, 32)");
158 assert_eq!(format!("{b:?}"), "B { x: 8, y: 32 }");
159 assert_eq!(format!("{b:#?}"), "B {\n x: 8,\n y: 32,\n}");
160
161 unsafe { enable(false) };
162
163 assert_eq!(format!("{a:?}"), "A(8, 32)");
164 assert_eq!(format!("{a:#?}"), "A(\n 8,\n 32,\n)");
165 assert_eq!(format!("{b:?}"), "B { x: 8, y: 32 }");
166 assert_eq!(format!("{b:#?}"), "B {\n x: 8,\n y: 32,\n}");
167}