1 extern crate rustc_demangle; 2 3 use std::alloc::{GlobalAlloc, Layout, System}; 4 use std::io::Write; 5 use std::os::raw::{c_char, c_int}; 6 use std::ptr; 7 use std::result; 8 9 type Result<T> = result::Result<T, Status>; 10 11 /// Convenience function to set return status if a location was provided. set_status(status: *mut c_int, val: c_int)12 unsafe fn set_status(status: *mut c_int, val: c_int) { 13 if !status.is_null() { 14 *status = val; 15 } 16 } 17 18 /// Region from the system allocator for demangler output. We use the 19 /// system allocator because the intended client is C/C++ code which 20 /// may not be using the Rust allocator. 21 struct SystemBuffer { 22 buf: *mut u8, 23 size: usize, 24 size_out: *mut usize, 25 } 26 27 impl SystemBuffer { 28 const DEFAULT_BUFFER_SIZE: usize = 1024; new(size: usize) -> Result<Self>29 fn new(size: usize) -> Result<Self> { 30 let buf = unsafe { System.alloc_zeroed(Layout::from_size_align_unchecked(size, 1)) }; 31 if buf.is_null() { 32 Err(Status::AllocFailure) 33 } else { 34 Ok(Self { 35 buf, 36 size, 37 size_out: ptr::null_mut(), 38 }) 39 } 40 } 41 /// Safety: If buf is non-null, size must be non-null and point to the 42 /// non-zero size of the buffer provided in buf. 43 /// Takes ownership of the buffer passed in (and may reallocate it). 44 /// size must outlive the resulting buffer if non-null. from_raw(buf: *mut c_char, size: *mut usize) -> Result<Self>45 unsafe fn from_raw(buf: *mut c_char, size: *mut usize) -> Result<Self> { 46 if buf.is_null() { 47 if !size.is_null() { 48 *size = Self::DEFAULT_BUFFER_SIZE; 49 } 50 let fresh = Self::new(Self::DEFAULT_BUFFER_SIZE)?; 51 Ok(Self { 52 size_out: size, 53 ..fresh 54 }) 55 } else { 56 Ok(Self { 57 buf: buf as *mut u8, 58 size: *size, 59 size_out: size, 60 }) 61 } 62 } as_mut_slice(&mut self) -> &mut [u8]63 fn as_mut_slice(&mut self) -> &mut [u8] { 64 unsafe { std::slice::from_raw_parts_mut(self.buf, self.size) } 65 } resize(&mut self) -> Result<()>66 fn resize(&mut self) -> Result<()> { 67 let new_size = self.size * 2; 68 let new_buf = unsafe { 69 System.realloc( 70 self.buf, 71 Layout::from_size_align_unchecked(self.size, 1), 72 new_size, 73 ) 74 }; 75 if new_buf.is_null() { 76 Err(Status::AllocFailure) 77 } else { 78 self.buf = new_buf; 79 self.size = new_size; 80 if !self.size_out.is_null() { 81 unsafe { 82 *self.size_out = new_size; 83 } 84 } 85 Ok(()) 86 } 87 } 88 } 89 90 /// C-style interface for demangling. 91 /// Demangles symbol given in `mangled` argument into `out` buffer. 92 /// 93 /// This interface is a drop-in replacement for `__cxa_demangle`, but for 94 /// Rust demangling. 95 /// 96 /// If `out` is null, a buffer will be allocated using the system allocator 97 /// to contain the results. 98 /// If `out` is non-null, `out_size` must be a pointer to the current size 99 /// of the buffer, and `out` must come from the system allocator. 100 /// If `out_size` is non-null, the size of the output buffer will be written 101 /// to it. 102 /// 103 /// If `status` is non-null, it will be set to one of the following values: 104 /// * 0: Demangling succeeded 105 /// * -1: Allocation failure 106 /// * -2: Name did not demangle 107 /// * -3: Invalid arguments 108 /// 109 /// Returns null if `mangled` is not Rust symbol or demangling failed. 110 /// Returns the buffer containing the demangled symbol name otherwise. 111 /// 112 /// Unsafe as it handles buffers by raw pointers. 113 /// 114 /// For non-null `out`, `out_size` represents a slight deviation from the 115 /// `__cxa_demangle` behavior. For `__cxa_demangle`, the buffer must be at 116 /// *least* the provided size. For `rustc_demangle`, it must be the exact 117 /// buffer size because it is used in the reconstruction of the `Layout` 118 /// for use with `::realloc`. 119 #[no_mangle] rustc_demangle( mangled: *const c_char, out: *mut c_char, out_size: *mut usize, status: *mut c_int, ) -> *mut c_char120 pub unsafe extern "C" fn rustc_demangle( 121 mangled: *const c_char, 122 out: *mut c_char, 123 out_size: *mut usize, 124 status: *mut c_int, 125 ) -> *mut c_char { 126 match rustc_demangle_native(mangled, out, out_size) { 127 Ok(demangled) => { 128 set_status(status, 0); 129 demangled 130 } 131 Err(e) => { 132 set_status(status, e as c_int); 133 ptr::null_mut() 134 } 135 } 136 } 137 138 enum Status { 139 AllocFailure = -1, 140 DemangleFailure = -2, 141 InvalidArgs = -3, 142 } 143 rustc_demangle_native( mangled: *const c_char, out: *mut c_char, out_size: *mut usize, ) -> Result<*mut c_char>144 unsafe fn rustc_demangle_native( 145 mangled: *const c_char, 146 out: *mut c_char, 147 out_size: *mut usize, 148 ) -> Result<*mut c_char> { 149 if mangled.is_null() { 150 return Err(Status::InvalidArgs); 151 } 152 let mangled_str = match std::ffi::CStr::from_ptr(mangled).to_str() { 153 Ok(s) => s, 154 Err(_) => return Err(Status::InvalidArgs), 155 }; 156 157 if !out.is_null() { 158 if out_size.is_null() { 159 return Err(Status::InvalidArgs); 160 } 161 if *out_size == 0 { 162 return Err(Status::InvalidArgs); 163 } 164 } 165 166 match rustc_demangle::try_demangle(mangled_str) { 167 Ok(demangle) => { 168 let mut out_buf = SystemBuffer::from_raw(out, out_size)?; 169 while write!(out_buf.as_mut_slice(), "{:#}\0", demangle).is_err() { 170 out_buf.resize()?; 171 } 172 Ok(out_buf.as_mut_slice().as_mut_ptr() as *mut c_char) 173 } 174 Err(_) => Err(Status::DemangleFailure), 175 } 176 } 177 178 #[cfg(test)] 179 mod tests { 180 use std::alloc::{GlobalAlloc, Layout, System}; 181 use std::os::raw::{c_char, c_int}; 182 use std::ptr; 183 184 struct DemangleResult { 185 out_buf: *mut u8, 186 out_size: usize, 187 status: c_int, 188 } 189 190 impl Drop for DemangleResult { drop(&mut self)191 fn drop(&mut self) { 192 if !self.out_buf.is_null() { 193 unsafe { 194 System.dealloc( 195 self.out_buf, 196 Layout::from_size_align_unchecked(self.out_size, 1), 197 ); 198 } 199 } 200 } 201 } 202 203 impl DemangleResult { as_slice(&self) -> &[u8]204 fn as_slice(&self) -> &[u8] { 205 unsafe { std::slice::from_raw_parts(self.out_buf, self.out_size) } 206 } 207 } 208 demangle(mangled: &str, alloc_size: usize) -> DemangleResult209 fn demangle(mangled: &str, alloc_size: usize) -> DemangleResult { 210 unsafe { raw_demangle(mangled.as_ptr() as *const c_char, alloc_size) } 211 } 212 raw_demangle(mangled: *const c_char, alloc_size: usize) -> DemangleResult213 unsafe fn raw_demangle(mangled: *const c_char, alloc_size: usize) -> DemangleResult { 214 let mut out_size: usize = alloc_size; 215 let mut status: c_int = 0; 216 let out_buf: *mut c_char = if out_size != 0 { 217 System.alloc(Layout::from_size_align_unchecked(out_size, 1)) as *mut c_char 218 } else { 219 ptr::null_mut() 220 }; 221 ptr::write_bytes(out_buf, '*' as u8, out_size); 222 223 let res = super::rustc_demangle(mangled, out_buf, &mut out_size, &mut status); 224 DemangleResult { 225 out_buf: res as *mut u8, 226 out_size, 227 status, 228 } 229 } 230 231 #[test] demangle_c_str_large()232 fn demangle_c_str_large() { 233 let res = demangle("_ZN4testE\0", 8); 234 assert_eq!(res.status, 0); 235 let out_str = core::str::from_utf8(&res.as_slice()[..5]).unwrap(); 236 assert_eq!(out_str, "test\0"); 237 } 238 239 #[test] demangle_c_str_exact()240 fn demangle_c_str_exact() { 241 let res = demangle("_ZN4testE\0", 8); 242 assert_eq!(res.status, 0); 243 // No reallocation necessary, so our * fill should be present 244 let out_str = core::str::from_utf8(res.as_slice()).unwrap(); 245 assert_eq!(out_str, "test\0***"); 246 } 247 248 #[test] demangle_c_str_small()249 fn demangle_c_str_small() { 250 let res = demangle("_ZN4testE\0", 4); 251 assert_eq!(res.status, 0); 252 // demangle should have realloced 253 assert_ne!(res.out_size, 4); 254 // Only check the start, since the reallocation means our * fill may 255 // be absent. 256 let out_str = core::str::from_utf8(&res.as_slice()[..5]).unwrap(); 257 assert_eq!(out_str, "test\0"); 258 } 259 260 #[test] demangle_c_str_alloc()261 fn demangle_c_str_alloc() { 262 let res = demangle("_ZN4testE\0", 0); 263 assert_eq!(res.status, 0); 264 // demangle should have allocated 265 assert_ne!(res.out_size, 0); 266 let out_str = core::str::from_utf8(&res.as_slice()[..5]).unwrap(); 267 assert_eq!(out_str, "test\0"); 268 } 269 270 #[test] demangle_c_str_not_rust_symbol()271 fn demangle_c_str_not_rust_symbol() { 272 let res = demangle("la la la\0", 8); 273 assert_eq!(res.status, -2); 274 } 275 276 #[test] demangle_c_str_null()277 fn demangle_c_str_null() { 278 let res = demangle("\0", 8); 279 assert_eq!(res.status, -2); 280 } 281 282 #[test] demangle_c_str_invalid_utf8()283 fn demangle_c_str_invalid_utf8() { 284 let mangled = [116, 101, 115, 116, 165, 0]; 285 let res = unsafe { raw_demangle(mangled.as_ptr() as *const c_char, 8) }; 286 assert_eq!(res.status, -2); 287 } 288 } 289