1 use core::char; 2 use core::fmt; 3 4 /// Representation of a demangled symbol name. 5 pub struct Demangle<'a> { 6 inner: &'a str, 7 /// The number of ::-separated elements in the original name. 8 elements: usize, 9 } 10 11 /// De-mangles a Rust symbol into a more readable version 12 /// 13 /// All Rust symbols by default are mangled as they contain characters that 14 /// cannot be represented in all object files. The mangling mechanism is similar 15 /// to C++'s, but Rust has a few specifics to handle items like lifetimes in 16 /// symbols. 17 /// 18 /// This function will take a **mangled** symbol and return a value. When printed, 19 /// the de-mangled version will be written. If the symbol does not look like 20 /// a mangled symbol, the original value will be written instead. 21 /// 22 /// # Examples 23 /// 24 /// ``` 25 /// use rustc_demangle::demangle; 26 /// 27 /// assert_eq!(demangle("_ZN4testE").to_string(), "test"); 28 /// assert_eq!(demangle("_ZN3foo3barE").to_string(), "foo::bar"); 29 /// assert_eq!(demangle("foo").to_string(), "foo"); 30 /// ``` 31 32 // All Rust symbols are in theory lists of "::"-separated identifiers. Some 33 // assemblers, however, can't handle these characters in symbol names. To get 34 // around this, we use C++-style mangling. The mangling method is: 35 // 36 // 1. Prefix the symbol with "_ZN" 37 // 2. For each element of the path, emit the length plus the element 38 // 3. End the path with "E" 39 // 40 // For example, "_ZN4testE" => "test" and "_ZN3foo3barE" => "foo::bar". 41 // 42 // We're the ones printing our backtraces, so we can't rely on anything else to 43 // demangle our symbols. It's *much* nicer to look at demangled symbols, so 44 // this function is implemented to give us nice pretty output. 45 // 46 // Note that this demangler isn't quite as fancy as it could be. We have lots 47 // of other information in our symbols like hashes, version, type information, 48 // etc. Additionally, this doesn't handle glue symbols at all. demangle(s: &str) -> Result<(Demangle, &str), ()>49 pub fn demangle(s: &str) -> Result<(Demangle, &str), ()> { 50 // First validate the symbol. If it doesn't look like anything we're 51 // expecting, we just print it literally. Note that we must handle non-Rust 52 // symbols because we could have any function in the backtrace. 53 let inner = if s.starts_with("_ZN") { 54 &s[3..] 55 } else if s.starts_with("ZN") { 56 // On Windows, dbghelp strips leading underscores, so we accept "ZN...E" 57 // form too. 58 &s[2..] 59 } else if s.starts_with("__ZN") { 60 // On OSX, symbols are prefixed with an extra _ 61 &s[4..] 62 } else { 63 return Err(()); 64 }; 65 66 // only work with ascii text 67 if inner.bytes().any(|c| c & 0x80 != 0) { 68 return Err(()); 69 } 70 71 let mut elements = 0; 72 let mut chars = inner.chars(); 73 let mut c = chars.next().ok_or(())?; 74 while c != 'E' { 75 // Decode an identifier element's length. 76 if !c.is_digit(10) { 77 return Err(()); 78 } 79 let mut len = 0usize; 80 while let Some(d) = c.to_digit(10) { 81 len = len 82 .checked_mul(10) 83 .and_then(|len| len.checked_add(d as usize)) 84 .ok_or(())?; 85 c = chars.next().ok_or(())?; 86 } 87 88 // `c` already contains the first character of this identifier, skip it and 89 // all the other characters of this identifier, to reach the next element. 90 for _ in 0..len { 91 c = chars.next().ok_or(())?; 92 } 93 94 elements += 1; 95 } 96 97 Ok((Demangle { inner, elements }, chars.as_str())) 98 } 99 100 // Rust hashes are hex digits with an `h` prepended. is_rust_hash(s: &str) -> bool101 fn is_rust_hash(s: &str) -> bool { 102 s.starts_with('h') && s[1..].chars().all(|c| c.is_digit(16)) 103 } 104 105 impl<'a> fmt::Display for Demangle<'a> { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result106 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 107 // Alright, let's do this. 108 let mut inner = self.inner; 109 for element in 0..self.elements { 110 let mut rest = inner; 111 while rest.chars().next().unwrap().is_digit(10) { 112 rest = &rest[1..]; 113 } 114 let i: usize = inner[..(inner.len() - rest.len())].parse().unwrap(); 115 inner = &rest[i..]; 116 rest = &rest[..i]; 117 // Skip printing the hash if alternate formatting 118 // was requested. 119 if f.alternate() && element + 1 == self.elements && is_rust_hash(&rest) { 120 break; 121 } 122 if element != 0 { 123 f.write_str("::")?; 124 } 125 if rest.starts_with("_$") { 126 rest = &rest[1..]; 127 } 128 loop { 129 if rest.starts_with('.') { 130 if let Some('.') = rest[1..].chars().next() { 131 f.write_str("::")?; 132 rest = &rest[2..]; 133 } else { 134 f.write_str(".")?; 135 rest = &rest[1..]; 136 } 137 } else if rest.starts_with('$') { 138 let (escape, after_escape) = if let Some(end) = rest[1..].find('$') { 139 (&rest[1..=end], &rest[end + 2..]) 140 } else { 141 break; 142 }; 143 144 // see src/librustc_codegen_utils/symbol_names/legacy.rs for these mappings 145 let unescaped = match escape { 146 "SP" => "@", 147 "BP" => "*", 148 "RF" => "&", 149 "LT" => "<", 150 "GT" => ">", 151 "LP" => "(", 152 "RP" => ")", 153 "C" => ",", 154 155 _ => { 156 if escape.starts_with('u') { 157 let digits = &escape[1..]; 158 let all_lower_hex = digits.chars().all(|c| match c { 159 '0'..='9' | 'a'..='f' => true, 160 _ => false, 161 }); 162 let c = u32::from_str_radix(digits, 16) 163 .ok() 164 .and_then(char::from_u32); 165 if let (true, Some(c)) = (all_lower_hex, c) { 166 // FIXME(eddyb) do we need to filter out control codepoints? 167 if !c.is_control() { 168 c.fmt(f)?; 169 rest = after_escape; 170 continue; 171 } 172 } 173 } 174 break; 175 } 176 }; 177 f.write_str(unescaped)?; 178 rest = after_escape; 179 } else if let Some(i) = rest.find(|c| c == '$' || c == '.') { 180 f.write_str(&rest[..i])?; 181 rest = &rest[i..]; 182 } else { 183 break; 184 } 185 } 186 f.write_str(rest)?; 187 } 188 189 Ok(()) 190 } 191 } 192 193 #[cfg(test)] 194 mod tests { 195 use std::prelude::v1::*; 196 197 macro_rules! t { 198 ($a:expr, $b:expr) => { 199 assert!(ok($a, $b)) 200 }; 201 } 202 203 macro_rules! t_err { 204 ($a:expr) => { 205 assert!(ok_err($a)) 206 }; 207 } 208 209 macro_rules! t_nohash { 210 ($a:expr, $b:expr) => {{ 211 assert_eq!(format!("{:#}", ::demangle($a)), $b); 212 }}; 213 } 214 ok(sym: &str, expected: &str) -> bool215 fn ok(sym: &str, expected: &str) -> bool { 216 match ::try_demangle(sym) { 217 Ok(s) => { 218 if s.to_string() == expected { 219 true 220 } else { 221 println!("\n{}\n!=\n{}\n", s, expected); 222 false 223 } 224 } 225 Err(_) => { 226 println!("error demangling"); 227 false 228 } 229 } 230 } 231 ok_err(sym: &str) -> bool232 fn ok_err(sym: &str) -> bool { 233 match ::try_demangle(sym) { 234 Ok(_) => { 235 println!("succeeded in demangling"); 236 false 237 } 238 Err(_) => ::demangle(sym).to_string() == sym, 239 } 240 } 241 242 #[test] demangle()243 fn demangle() { 244 t_err!("test"); 245 t!("_ZN4testE", "test"); 246 t_err!("_ZN4test"); 247 t!("_ZN4test1a2bcE", "test::a::bc"); 248 } 249 250 #[test] demangle_dollars()251 fn demangle_dollars() { 252 t!("_ZN4$RP$E", ")"); 253 t!("_ZN8$RF$testE", "&test"); 254 t!("_ZN8$BP$test4foobE", "*test::foob"); 255 t!("_ZN9$u20$test4foobE", " test::foob"); 256 t!("_ZN35Bar$LT$$u5b$u32$u3b$$u20$4$u5d$$GT$E", "Bar<[u32; 4]>"); 257 } 258 259 #[test] demangle_many_dollars()260 fn demangle_many_dollars() { 261 t!("_ZN13test$u20$test4foobE", "test test::foob"); 262 t!("_ZN12test$BP$test4foobE", "test*test::foob"); 263 } 264 265 #[test] demangle_osx()266 fn demangle_osx() { 267 t!( 268 "__ZN5alloc9allocator6Layout9for_value17h02a996811f781011E", 269 "alloc::allocator::Layout::for_value::h02a996811f781011" 270 ); 271 t!("__ZN38_$LT$core..option..Option$LT$T$GT$$GT$6unwrap18_MSG_FILE_LINE_COL17haf7cb8d5824ee659E", "<core::option::Option<T>>::unwrap::_MSG_FILE_LINE_COL::haf7cb8d5824ee659"); 272 t!("__ZN4core5slice89_$LT$impl$u20$core..iter..traits..IntoIterator$u20$for$u20$$RF$$u27$a$u20$$u5b$T$u5d$$GT$9into_iter17h450e234d27262170E", "core::slice::<impl core::iter::traits::IntoIterator for &'a [T]>::into_iter::h450e234d27262170"); 273 } 274 275 #[test] demangle_windows()276 fn demangle_windows() { 277 t!("ZN4testE", "test"); 278 t!("ZN13test$u20$test4foobE", "test test::foob"); 279 t!("ZN12test$RF$test4foobE", "test&test::foob"); 280 } 281 282 #[test] demangle_elements_beginning_with_underscore()283 fn demangle_elements_beginning_with_underscore() { 284 t!("_ZN13_$LT$test$GT$E", "<test>"); 285 t!("_ZN28_$u7b$$u7b$closure$u7d$$u7d$E", "{{closure}}"); 286 t!("_ZN15__STATIC_FMTSTRE", "__STATIC_FMTSTR"); 287 } 288 289 #[test] demangle_trait_impls()290 fn demangle_trait_impls() { 291 t!( 292 "_ZN71_$LT$Test$u20$$u2b$$u20$$u27$static$u20$as$u20$foo..Bar$LT$Test$GT$$GT$3barE", 293 "<Test + 'static as foo::Bar<Test>>::bar" 294 ); 295 } 296 297 #[test] demangle_without_hash()298 fn demangle_without_hash() { 299 let s = "_ZN3foo17h05af221e174051e9E"; 300 t!(s, "foo::h05af221e174051e9"); 301 t_nohash!(s, "foo"); 302 } 303 304 #[test] demangle_without_hash_edgecases()305 fn demangle_without_hash_edgecases() { 306 // One element, no hash. 307 t_nohash!("_ZN3fooE", "foo"); 308 // Two elements, no hash. 309 t_nohash!("_ZN3foo3barE", "foo::bar"); 310 // Longer-than-normal hash. 311 t_nohash!("_ZN3foo20h05af221e174051e9abcE", "foo"); 312 // Shorter-than-normal hash. 313 t_nohash!("_ZN3foo5h05afE", "foo"); 314 // Valid hash, but not at the end. 315 t_nohash!("_ZN17h05af221e174051e93fooE", "h05af221e174051e9::foo"); 316 // Not a valid hash, missing the 'h'. 317 t_nohash!("_ZN3foo16ffaf221e174051e9E", "foo::ffaf221e174051e9"); 318 // Not a valid hash, has a non-hex-digit. 319 t_nohash!("_ZN3foo17hg5af221e174051e9E", "foo::hg5af221e174051e9"); 320 } 321 322 #[test] demangle_thinlto()323 fn demangle_thinlto() { 324 // One element, no hash. 325 t!("_ZN3fooE.llvm.9D1C9369", "foo"); 326 t!("_ZN3fooE.llvm.9D1C9369@@16", "foo"); 327 t_nohash!( 328 "_ZN9backtrace3foo17hbb467fcdaea5d79bE.llvm.A5310EB9", 329 "backtrace::foo" 330 ); 331 } 332 333 #[test] demangle_llvm_ir_branch_labels()334 fn demangle_llvm_ir_branch_labels() { 335 t!("_ZN4core5slice77_$LT$impl$u20$core..ops..index..IndexMut$LT$I$GT$$u20$for$u20$$u5b$T$u5d$$GT$9index_mut17haf9727c2edfbc47bE.exit.i.i", "core::slice::<impl core::ops::index::IndexMut<I> for [T]>::index_mut::haf9727c2edfbc47b.exit.i.i"); 336 t_nohash!("_ZN4core5slice77_$LT$impl$u20$core..ops..index..IndexMut$LT$I$GT$$u20$for$u20$$u5b$T$u5d$$GT$9index_mut17haf9727c2edfbc47bE.exit.i.i", "core::slice::<impl core::ops::index::IndexMut<I> for [T]>::index_mut.exit.i.i"); 337 } 338 339 #[test] demangle_ignores_suffix_that_doesnt_look_like_a_symbol()340 fn demangle_ignores_suffix_that_doesnt_look_like_a_symbol() { 341 t_err!("_ZN3fooE.llvm moocow"); 342 } 343 344 #[test] dont_panic()345 fn dont_panic() { 346 ::demangle("_ZN2222222222222222222222EE").to_string(); 347 ::demangle("_ZN5*70527e27.ll34csaғE").to_string(); 348 ::demangle("_ZN5*70527a54.ll34_$b.1E").to_string(); 349 ::demangle( 350 "\ 351 _ZN5~saäb4e\n\ 352 2734cOsbE\n\ 353 5usage20h)3\0\0\0\0\0\0\07e2734cOsbE\ 354 ", 355 ) 356 .to_string(); 357 } 358 359 #[test] invalid_no_chop()360 fn invalid_no_chop() { 361 t_err!("_ZNfooE"); 362 } 363 364 #[test] handle_assoc_types()365 fn handle_assoc_types() { 366 t!("_ZN151_$LT$alloc..boxed..Box$LT$alloc..boxed..FnBox$LT$A$C$$u20$Output$u3d$R$GT$$u20$$u2b$$u20$$u27$a$GT$$u20$as$u20$core..ops..function..FnOnce$LT$A$GT$$GT$9call_once17h69e8f44b3723e1caE", "<alloc::boxed::Box<alloc::boxed::FnBox<A, Output=R> + 'a> as core::ops::function::FnOnce<A>>::call_once::h69e8f44b3723e1ca"); 367 } 368 369 #[test] handle_bang()370 fn handle_bang() { 371 t!( 372 "_ZN88_$LT$core..result..Result$LT$$u21$$C$$u20$E$GT$$u20$as$u20$std..process..Termination$GT$6report17hfc41d0da4a40b3e8E", 373 "<core::result::Result<!, E> as std::process::Termination>::report::hfc41d0da4a40b3e8" 374 ); 375 } 376 377 #[test] demangle_utf8_idents()378 fn demangle_utf8_idents() { 379 t_nohash!( 380 "_ZN11utf8_idents157_$u10e1$$u10d0$$u10ed$$u10db$$u10d4$$u10da$$u10d0$$u10d3$_$u10d2$$u10d4$$u10db$$u10e0$$u10d8$$u10d4$$u10da$$u10d8$_$u10e1$$u10d0$$u10d3$$u10d8$$u10da$$u10d8$17h21634fd5714000aaE", 381 "utf8_idents::საჭმელად_გემრიელი_სადილი" 382 ); 383 } 384 385 #[test] demangle_issue_60925()386 fn demangle_issue_60925() { 387 t_nohash!( 388 "_ZN11issue_609253foo37Foo$LT$issue_60925..llv$u6d$..Foo$GT$3foo17h059a991a004536adE", 389 "issue_60925::foo::Foo<issue_60925::llvm::Foo>::foo" 390 ); 391 } 392 } 393