1From 290b3379f60c0578174861c6ac8fb93f1ce83a3f Mon Sep 17 00:00:00 2001 2From: Matthew Maurer <[email protected]> 3Date: Tue, 17 Aug 2021 10:50:56 -0700 4Subject: [PATCH] Adjust API to match __cxa_demangle 5 6Bug: 178565008 7Test: cargo test + generate tombstone 8Change-Id: Iaf76af67a7f3a6323e926075a8ecd08b1d381db0 9--- 10 include/rustc_demangle.h | 9 +- 11 src/lib.rs | 339 +++++++++++++++++++++++++++------------ 12 2 files changed, 238 insertions(+), 110 deletions(-) 13 14diff --git a/include/rustc_demangle.h b/include/rustc_demangle.h 15index 61c4aa1..e7ee2ca 100644 16--- a/include/rustc_demangle.h 17+++ b/include/rustc_demangle.h 18@@ -5,11 +5,10 @@ 19 extern "C" { 20 #endif 21 22-// Demangles symbol given in `mangled` argument into `out` buffer 23-// 24-// Returns 0 if `mangled` is not Rust symbol or if `out` buffer is too small 25-// Returns 1 otherwise 26-int rustc_demangle(const char *mangled, char *out, size_t out_size); 27+// For size_t 28+#include <stddef.h> 29+ 30+char *rustc_demangle(const char *mangled, char *out, size_t *len, int *status); 31 32 #ifdef __cplusplus 33 } 34diff --git a/src/lib.rs b/src/lib.rs 35index 51e3103..7610145 100644 36--- a/src/lib.rs 37+++ b/src/lib.rs 38@@ -1,160 +1,289 @@ 39 extern crate rustc_demangle; 40 41+use std::alloc::{GlobalAlloc, Layout, System}; 42 use std::io::Write; 43 use std::os::raw::{c_char, c_int}; 44+use std::ptr; 45+use std::result; 46+ 47+type Result<T> = result::Result<T, Status>; 48+ 49+/// Convenience function to set return status if a location was provided. 50+unsafe fn set_status(status: *mut c_int, val: c_int) { 51+ if !status.is_null() { 52+ *status = val; 53+ } 54+} 55+ 56+/// Region from the system allocator for demangler output. We use the 57+/// system allocator because the intended client is C/C++ code which 58+/// may not be using the Rust allocator. 59+struct SystemBuffer { 60+ buf: *mut u8, 61+ size: usize, 62+ size_out: *mut usize, 63+} 64+ 65+impl SystemBuffer { 66+ const DEFAULT_BUFFER_SIZE: usize = 1024; 67+ fn new(size: usize) -> Result<Self> { 68+ let buf = unsafe { System.alloc_zeroed(Layout::from_size_align_unchecked(size, 1)) }; 69+ if buf.is_null() { 70+ Err(Status::AllocFailure) 71+ } else { 72+ Ok(Self { 73+ buf, 74+ size, 75+ size_out: ptr::null_mut(), 76+ }) 77+ } 78+ } 79+ /// Safety: If buf is non-null, size must be non-null and point to the 80+ /// non-zero size of the buffer provided in buf. 81+ /// Takes ownership of the buffer passed in (and may reallocate it). 82+ /// size must outlive the resulting buffer if non-null. 83+ unsafe fn from_raw(buf: *mut c_char, size: *mut usize) -> Result<Self> { 84+ if buf.is_null() { 85+ if !size.is_null() { 86+ *size = Self::DEFAULT_BUFFER_SIZE; 87+ } 88+ let fresh = Self::new(Self::DEFAULT_BUFFER_SIZE)?; 89+ Ok(Self { 90+ size_out: size, 91+ ..fresh 92+ }) 93+ } else { 94+ Ok(Self { 95+ buf: buf as *mut u8, 96+ size: *size, 97+ size_out: size, 98+ }) 99+ } 100+ } 101+ fn as_mut_slice(&mut self) -> &mut [u8] { 102+ unsafe { std::slice::from_raw_parts_mut(self.buf, self.size) } 103+ } 104+ fn resize(&mut self) -> Result<()> { 105+ let new_size = self.size * 2; 106+ let new_buf = unsafe { 107+ System.realloc( 108+ self.buf, 109+ Layout::from_size_align_unchecked(self.size, 1), 110+ new_size, 111+ ) 112+ }; 113+ if new_buf.is_null() { 114+ Err(Status::AllocFailure) 115+ } else { 116+ self.buf = new_buf; 117+ self.size = new_size; 118+ if !self.size_out.is_null() { 119+ unsafe { 120+ *self.size_out = new_size; 121+ } 122+ } 123+ Ok(()) 124+ } 125+ } 126+} 127 128 /// C-style interface for demangling. 129-/// Demangles symbol given in `mangled` argument into `out` buffer 130+/// Demangles symbol given in `mangled` argument into `out` buffer. 131+/// 132+/// This interface is a drop-in replacement for `__cxa_demangle`, but for 133+/// Rust demangling. 134+/// 135+/// If `out` is null, a buffer will be allocated using the system allocator 136+/// to contain the results. 137+/// If `out` is non-null, `out_size` must be a pointer to the current size 138+/// of the buffer, and `out` must come from the system allocator. 139+/// If `out_size` is non-null, the size of the output buffer will be written 140+/// to it. 141+/// 142+/// If `status` is non-null, it will be set to one of the following values: 143+/// * 0: Demangling succeeded 144+/// * -1: Allocation failure 145+/// * -2: Name did not demangle 146+/// * -3: Invalid arguments 147+/// 148+/// Returns null if `mangled` is not Rust symbol or demangling failed. 149+/// Returns the buffer containing the demangled symbol name otherwise. 150 /// 151 /// Unsafe as it handles buffers by raw pointers. 152 /// 153-/// Returns 0 if `mangled` is not Rust symbol or if `out` buffer is too small 154-/// Returns 1 otherwise 155+/// For non-null `out`, `out_size` represents a slight deviation from the 156+/// `__cxa_demangle` behavior. For `__cxa_demangle`, the buffer must be at 157+/// *least* the provided size. For `rustc_demangle`, it must be the exact 158+/// buffer size because it is used in the reconstruction of the `Layout` 159+/// for use with `::realloc`. 160 #[no_mangle] 161 pub unsafe extern "C" fn rustc_demangle( 162 mangled: *const c_char, 163 out: *mut c_char, 164- out_size: usize, 165-) -> c_int { 166+ out_size: *mut usize, 167+ status: *mut c_int, 168+) -> *mut c_char { 169+ match rustc_demangle_native(mangled, out, out_size) { 170+ Ok(demangled) => { 171+ set_status(status, 0); 172+ demangled 173+ } 174+ Err(e) => { 175+ set_status(status, e as c_int); 176+ ptr::null_mut() 177+ } 178+ } 179+} 180+ 181+enum Status { 182+ AllocFailure = -1, 183+ DemangleFailure = -2, 184+ InvalidArgs = -3, 185+} 186+ 187+unsafe fn rustc_demangle_native( 188+ mangled: *const c_char, 189+ out: *mut c_char, 190+ out_size: *mut usize, 191+) -> Result<*mut c_char> { 192+ if mangled.is_null() { 193+ return Err(Status::InvalidArgs); 194+ } 195 let mangled_str = match std::ffi::CStr::from_ptr(mangled).to_str() { 196 Ok(s) => s, 197- Err(_) => return 0, 198+ Err(_) => return Err(Status::InvalidArgs), 199 }; 200+ 201+ if !out.is_null() { 202+ if out_size.is_null() { 203+ return Err(Status::InvalidArgs); 204+ } 205+ if *out_size == 0 { 206+ return Err(Status::InvalidArgs); 207+ } 208+ } 209+ 210+ let mut out_buf = SystemBuffer::from_raw(out, out_size)?; 211+ 212 match rustc_demangle::try_demangle(mangled_str) { 213 Ok(demangle) => { 214- let mut out_slice = std::slice::from_raw_parts_mut(out as *mut u8, out_size); 215- match write!(out_slice, "{:#}\0", demangle) { 216- Ok(_) => return 1, 217- Err(_) => return 0, 218+ while write!(out_buf.as_mut_slice(), "{:#}\0", demangle).is_err() { 219+ out_buf.resize()?; 220 } 221+ Ok(out_buf.as_mut_slice().as_mut_ptr() as *mut c_char) 222 } 223- Err(_) => return 0, 224+ Err(_) => Err(Status::DemangleFailure), 225 } 226 } 227 228 #[cfg(test)] 229 mod tests { 230- use std; 231- use std::os::raw::c_char; 232+ use std::alloc::{GlobalAlloc, Layout, System}; 233+ use std::os::raw::{c_char, c_int}; 234+ use std::ptr; 235+ 236+ struct DemangleResult { 237+ out_buf: *mut u8, 238+ out_size: usize, 239+ status: c_int, 240+ } 241+ 242+ impl Drop for DemangleResult { 243+ fn drop(&mut self) { 244+ if !self.out_buf.is_null() { 245+ unsafe { 246+ System.dealloc( 247+ self.out_buf, 248+ Layout::from_size_align_unchecked(self.out_size, 1), 249+ ); 250+ } 251+ } 252+ } 253+ } 254+ 255+ impl DemangleResult { 256+ fn as_slice(&self) -> &[u8] { 257+ unsafe { std::slice::from_raw_parts(self.out_buf, self.out_size) } 258+ } 259+ } 260+ 261+ fn demangle(mangled: &str, alloc_size: usize) -> DemangleResult { 262+ unsafe { raw_demangle(mangled.as_ptr() as *const c_char, alloc_size) } 263+ } 264+ 265+ unsafe fn raw_demangle(mangled: *const c_char, alloc_size: usize) -> DemangleResult { 266+ let mut out_size: usize = alloc_size; 267+ let mut status: c_int = 0; 268+ let out_buf: *mut c_char = if out_size != 0 { 269+ System.alloc(Layout::from_size_align_unchecked(out_size, 1)) as *mut c_char 270+ } else { 271+ ptr::null_mut() 272+ }; 273+ ptr::write_bytes(out_buf, '*' as u8, out_size); 274+ 275+ let res = super::rustc_demangle(mangled, out_buf, &mut out_size, &mut status); 276+ DemangleResult { 277+ out_buf: res as *mut u8, 278+ out_size, 279+ status, 280+ } 281+ } 282+ 283 #[test] 284 fn demangle_c_str_large() { 285- let mangled = "_ZN4testE\0"; 286- let mut out_buf: Vec<u8> = vec![42; 8]; 287- let res = unsafe { 288- super::rustc_demangle( 289- mangled.as_ptr() as *const c_char, 290- out_buf.as_mut_ptr() as *mut c_char, 291- 8, 292- ) 293- }; 294- assert_eq!(res, 1); 295- let out_str = std::str::from_utf8(&out_buf[..5]).unwrap(); 296+ let res = demangle("_ZN4testE\0", 8); 297+ assert_eq!(res.status, 0); 298+ let out_str = core::str::from_utf8(&res.as_slice()[..5]).unwrap(); 299 assert_eq!(out_str, "test\0"); 300 } 301 302 #[test] 303 fn demangle_c_str_exact() { 304- let mangled = "_ZN4testE\0"; 305- let mut out_buf: Vec<u8> = vec![42; 8]; 306- let res = unsafe { 307- super::rustc_demangle( 308- mangled.as_ptr() as *const c_char, 309- out_buf.as_mut_ptr() as *mut c_char, 310- 5, 311- ) 312- }; 313- assert_eq!(res, 1); 314- let out_str = std::str::from_utf8(&out_buf).unwrap(); 315+ let res = demangle("_ZN4testE\0", 8); 316+ assert_eq!(res.status, 0); 317+ // No reallocation necessary, so our * fill should be present 318+ let out_str = core::str::from_utf8(res.as_slice()).unwrap(); 319 assert_eq!(out_str, "test\0***"); 320 } 321 322 #[test] 323 fn demangle_c_str_small() { 324- let mangled = "_ZN4testE\0"; 325- let mut out_buf: Vec<u8> = vec![42; 8]; 326- let res = unsafe { 327- super::rustc_demangle( 328- mangled.as_ptr() as *const c_char, 329- out_buf.as_mut_ptr() as *mut c_char, 330- 4, 331- ) 332- }; 333- assert_eq!(res, 0); 334- let out_str = std::str::from_utf8(&out_buf[4..]).unwrap(); 335- assert_eq!(out_str, "****"); 336- } 337- 338- #[test] 339- fn demangle_c_str_smaller() { 340- let mangled = "_ZN4testE\0"; 341- let mut out_buf: Vec<u8> = vec![42; 8]; 342- let res = unsafe { 343- super::rustc_demangle( 344- mangled.as_ptr() as *const c_char, 345- out_buf.as_mut_ptr() as *mut c_char, 346- 3, 347- ) 348- }; 349- assert_eq!(res, 0); 350- let out_str = std::str::from_utf8(&out_buf[3..]).unwrap(); 351- assert_eq!(out_str, "*****"); 352+ let res = demangle("_ZN4testE\0", 4); 353+ assert_eq!(res.status, 0); 354+ // demangle should have realloced 355+ assert_ne!(res.out_size, 4); 356+ // Only check the start, since the reallocation means our * fill may 357+ // be absent. 358+ let out_str = core::str::from_utf8(&res.as_slice()[..5]).unwrap(); 359+ assert_eq!(out_str, "test\0"); 360 } 361 362 #[test] 363- fn demangle_c_str_zero() { 364- let mangled = "_ZN4testE\0"; 365- let mut out_buf: Vec<u8> = vec![42; 8]; 366- let res = unsafe { 367- super::rustc_demangle( 368- mangled.as_ptr() as *const c_char, 369- out_buf.as_mut_ptr() as *mut c_char, 370- 0, 371- ) 372- }; 373- assert_eq!(res, 0); 374- let out_str = std::str::from_utf8(&out_buf).unwrap(); 375- assert_eq!(out_str, "********"); 376+ fn demangle_c_str_alloc() { 377+ let res = demangle("_ZN4testE\0", 0); 378+ assert_eq!(res.status, 0); 379+ // demangle should have allocated 380+ assert_ne!(res.out_size, 0); 381+ let out_str = core::str::from_utf8(&res.as_slice()[..5]).unwrap(); 382+ assert_eq!(out_str, "test\0"); 383 } 384 385 #[test] 386 fn demangle_c_str_not_rust_symbol() { 387- let mangled = "la la la\0"; 388- let mut out_buf: Vec<u8> = vec![42; 8]; 389- let res = unsafe { 390- super::rustc_demangle( 391- mangled.as_ptr() as *const c_char, 392- out_buf.as_mut_ptr() as *mut c_char, 393- 8, 394- ) 395- }; 396- assert_eq!(res, 0); 397+ let res = demangle("la la la\0", 8); 398+ assert_eq!(res.status, -2); 399 } 400 401 #[test] 402 fn demangle_c_str_null() { 403- let mangled = "\0"; 404- let mut out_buf: Vec<u8> = vec![42; 8]; 405- let res = unsafe { 406- super::rustc_demangle( 407- mangled.as_ptr() as *const c_char, 408- out_buf.as_mut_ptr() as *mut c_char, 409- 8, 410- ) 411- }; 412- assert_eq!(res, 0); 413+ let res = demangle("\0", 8); 414+ assert_eq!(res.status, -2); 415 } 416 417 #[test] 418 fn demangle_c_str_invalid_utf8() { 419 let mangled = [116, 101, 115, 116, 165, 0]; 420- let mut out_buf: Vec<u8> = vec![42; 8]; 421- let res = unsafe { 422- super::rustc_demangle( 423- mangled.as_ptr() as *const c_char, 424- out_buf.as_mut_ptr() as *mut c_char, 425- 8, 426- ) 427- }; 428- assert_eq!(res, 0); 429+ let res = unsafe { raw_demangle(mangled.as_ptr() as *const c_char, 8) }; 430+ assert_eq!(res.status, -2); 431 } 432 } 433-- 4342.33.0.rc1.237.g0d66db33f3-goog 435 436