/// Representation of a demangled symbol name. pubstruct Demangle<'a> {
inner: &'a str, /// The number of ::-separated elements in the original name.
elements: usize,
}
/// De-mangles a Rust symbol into a more readable version /// /// All Rust symbols by default are mangled as they contain characters that /// cannot be represented in all object files. The mangling mechanism is similar /// to C++'s, but Rust has a few specifics to handle items like lifetimes in /// symbols. /// /// This function will take a **mangled** symbol and return a value. When printed, /// the de-mangled version will be written. If the symbol does not look like /// a mangled symbol, the original value will be written instead. /// /// # Examples /// /// ``` /// use rustc_demangle::demangle; /// /// assert_eq!(demangle("_ZN4testE").to_string(), "test"); /// assert_eq!(demangle("_ZN3foo3barE").to_string(), "foo::bar"); /// assert_eq!(demangle("foo").to_string(), "foo"); /// ```
// All Rust symbols are in theory lists of "::"-separated identifiers. Some // assemblers, however, can't handle these characters in symbol names. To get // around this, we use C++-style mangling. The mangling method is: // // 1. Prefix the symbol with "_ZN" // 2. For each element of the path, emit the length plus the element // 3. End the path with "E" // // For example, "_ZN4testE" => "test" and "_ZN3foo3barE" => "foo::bar". // // We're the ones printing our backtraces, so we can't rely on anything else to // demangle our symbols. It's *much* nicer to look at demangled symbols, so // this function is implemented to give us nice pretty output. // // Note that this demangler isn't quite as fancy as it could be. We have lots // of other information in our symbols like hashes, version, type information, // etc. Additionally, this doesn't handle glue symbols at all. pubfn demangle(s: &str) -> Result<(Demangle, &str), ()> { // First validate the symbol. If it doesn't look like anything we're // expecting, we just print it literally. Note that we must handle non-Rust // symbols because we could have any function in the backtrace. let inner = if s.starts_with("_ZN") {
&s[3..]
} elseif s.starts_with("ZN") { // On Windows, dbghelp strips leading underscores, so we accept "ZN...E" // form too.
&s[2..]
} elseif s.starts_with("__ZN") { // On OSX, symbols are prefixed with an extra _
&s[4..]
} else { return Err(());
};
// only work with ascii text if inner.bytes().any(|c| c & 0x80 != 0) { return Err(());
}
letmut elements = 0; letmut chars = inner.chars(); letmut c = chars.next().ok_or(())?; while c != 'E' { // Decode an identifier element's length. if !c.is_digit(10) { return Err(());
} letmut len = 0usize; whilelet Some(d) = c.to_digit(10) {
len = len
.checked_mul(10)
.and_then(|len| len.checked_add(d as usize))
.ok_or(())?;
c = chars.next().ok_or(())?;
}
// `c` already contains the first character of this identifier, skip it and // all the other characters of this identifier, to reach the next element. for _ in0..len {
c = chars.next().ok_or(())?;
}
elements += 1;
}
Ok((Demangle { inner, elements }, chars.as_str()))
}
// Rust hashes are hex digits with an `h` prepended. fn is_rust_hash(s: &str) -> bool {
s.starts_with('h') && s[1..].chars().all(|c| c.is_digit(16))
}
impl<'a> fmt::Display for Demangle<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { // Alright, let's do this. letmut inner = self.inner; for element in0..self.elements { letmut rest = inner; while rest.chars().next().unwrap().is_digit(10) {
rest = &rest[1..];
} let i: usize = inner[..(inner.len() - rest.len())].parse().unwrap();
inner = &rest[i..];
rest = &rest[..i]; // Skip printing the hash if alternate formatting // was requested. if f.alternate() && element + 1 == self.elements && is_rust_hash(&rest) { break;
} if element != 0 {
f.write_str("::")?;
} if rest.starts_with("_$") {
rest = &rest[1..];
} loop { if rest.starts_with('.') { iflet Some('.') = rest[1..].chars().next() {
f.write_str("::")?;
rest = &rest[2..];
} else {
f.write_str(".")?;
rest = &rest[1..];
}
} elseif rest.starts_with('$') { let (escape, after_escape) = iflet Some(end) = rest[1..].find('$') {
(&rest[1..=end], &rest[end + 2..])
} else { break;
};
// see src/librustc_codegen_utils/symbol_names/legacy.rs for these mappings let unescaped = match escape { "SP" => "@", "BP" => "*", "RF" => "&", "LT" => "<", "GT" => ">", "LP" => "(", "RP" => ")", "C" => ",",
_ => { if escape.starts_with('u') { let digits = &escape[1..]; let all_lower_hex = digits.chars().all(|c| match c { '0'..='9' | 'a'..='f' => true,
_ => false,
}); let c = u32::from_str_radix(digits, 16)
.ok()
.and_then(char::from_u32); iflet (true, Some(c)) = (all_lower_hex, c) { // FIXME(eddyb) do we need to filter out control codepoints? if !c.is_control() {
c.fmt(f)?;
rest = after_escape; continue;
}
}
} break;
}
};
f.write_str(unescaped)?;
rest = after_escape;
} elseiflet Some(i) = rest.find(|c| c == '$' || c == '.') {
f.write_str(&rest[..i])?;
rest = &rest[i..];
} else { break;
}
}
f.write_str(rest)?;
}
#[test] fn demangle_without_hash() { let s = "_ZN3foo17h05af221e174051e9E";
t!(s, "foo::h05af221e174051e9");
t_nohash!(s, "foo");
}
#[test] fn demangle_without_hash_edgecases() { // One element, no hash.
t_nohash!("_ZN3fooE", "foo"); // Two elements, no hash.
t_nohash!("_ZN3foo3barE", "foo::bar"); // Longer-than-normal hash.
t_nohash!("_ZN3foo20h05af221e174051e9abcE", "foo"); // Shorter-than-normal hash.
t_nohash!("_ZN3foo5h05afE", "foo"); // Valid hash, but not at the end.
t_nohash!("_ZN17h05af221e174051e93fooE", "h05af221e174051e9::foo"); // Not a valid hash, missing the 'h'.
t_nohash!("_ZN3foo16ffaf221e174051e9E", "foo::ffaf221e174051e9"); // Not a valid hash, has a non-hex-digit.
t_nohash!("_ZN3foo17hg5af221e174051e9E", "foo::hg5af221e174051e9");
}
#[test] fn demangle_thinlto() { // One element, no hash.
t!("_ZN3fooE.llvm.9D1C9369", "foo");
t!("_ZN3fooE.llvm.9D1C9369@@16", "foo");
t_nohash!( "_ZN9backtrace3foo17hbb467fcdaea5d79bE.llvm.A5310EB9", "backtrace::foo"
);
}
#[test] fn demangle_llvm_ir_branch_labels() {
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");
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");
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.