// private version of memrchr to avoid _GNU_SOURCE staticvoid *demangle_memrchr(constvoid *s, int c, size_t n) { const uint8_t *s_ = s; for (; n != 0; n--) { if (s_[n-1] == c) { return (void*)&s_[n-1];
}
} return NULL;
}
staticbool unicode_iscontrol(uint32_t ch) { // this is *technically* a unicode table, but // some unicode properties are simpler than you might think return ch < 0x20 || (ch >= 0x7f && ch < 0xa0);
}
// "good enough" tables, the only consequence is that when printing // *constant strings*, some characters are printed as `\u{abcd}` rather than themselves. // // I'm leaving these here to allow easily replacing them with actual // tables if desired. staticbool unicode_isprint(uint32_t ch) { if (ch < 0x20) { returnfalse;
} if (ch < 0x7f) { returntrue;
} returnfalse;
}
struct parser { // the parser assumes that `sym` has a safe "terminating byte". It might be NUL, // but it might also be something else if a symbol is "truncated". constchar *sym;
size_t sym_len;
size_t next;
uint32_t depth;
};
struct printer {
demangle_status status; // if status == 0 parser is valid struct parser parser; char *out; // NULL for no output [in which case out_len is not decremented]
size_t out_len;
uint32_t bound_lifetime_depth; bool alternate;
};
NODISCARD static demangle_status rust_demangle_v0_demangle(constchar *s, size_t s_len, struct demangle_v0 *res, constchar **rest) { if (s_len > strlen(s)) { // s_len only exists to shorten the string, this is not a buffer API return DemangleInvalid;
}
constchar *inner;
size_t inner_len; if (s_len >= 2 && !strncmp(s, "_R", strlen("_R"))) {
inner = s+2;
inner_len = s_len - 2;
} elseif (s_len >= 1 && !strncmp(s, "R", strlen("R"))) { // On Windows, dbghelp strips leading underscores, so we accept "R..." // form too.
inner = s+1;
inner_len = s_len - 1;
} elseif (s_len >= 3 && !strncmp(s, "__R", strlen("__R"))) { // On OSX, symbols are prefixed with an extra _
inner = s+3;
inner_len = s_len - 3;
} else { return DemangleInvalid;
}
// Paths always start with uppercase characters. if (*inner < 'A' || *inner > 'Z') { return DemangleInvalid;
}
if (!str_isascii(inner, inner_len)) { return DemangleInvalid;
}
if (ascii_len > SMALL_PUNYCODE_LEN) { return PunycodeError;
} for (size_t i = 0; i < ascii_len; i++) {
out[i] = start[i];
}
size_t len = ascii_len;
size_t base = 36, t_min = 1, t_max = 26, skew = 38, damp = 700, bias = 72, i = 0, n = 0x80; for (;;) {
size_t delta = 0, w = 1, k = 0; for (;;) {
k += base;
size_t biased = k < bias ? 0 : k - bias;
size_t t = MIN(MAX(biased, t_min), t_max);
size_t d; if (punycode_len == 0) { return PunycodeError;
} char nx = *punycode_start++;
punycode_len--; if ('a' <= nx && nx <= 'z') {
d = nx - 'a';
} elseif ('0' <= nx && nx <= '9') {
d = 26 + (nx - '0');
} else { return PunycodeError;
} if (w == 0 || d > SIZE_MAX / w || d*w > SIZE_MAX - delta) { return PunycodeError;
}
delta += d * w; if (d < t) { break;
} if (base < t || w == 0 || (base - t) > SIZE_MAX / w) { return PunycodeError;
}
w *= (base - t);
}
len += 1; if (i > SIZE_MAX - delta) { return PunycodeError;
}
i += delta; if (n > SIZE_MAX - i / len) { return PunycodeError;
}
n += i / len;
i %= len;
// insert new character if (len > SMALL_PUNYCODE_LEN) { return PunycodeError;
}
memmove(out + i + 1, out + i, (len - i - 1) * sizeof(uint32_t));
out[i] = (uint32_t)n;
// start i index at incremented position
i++;
// If there are no more deltas, decoding is complete. if (punycode_len == 0) {
*out_len = len; return PunycodeOk;
}
// conv_buf is full here if possible, process 1 UTF-8 character
uint32_t ch = 0;
size_t consumed = utf8_next_char(conv_buf, &ch); if (consumed > conv_buf_len) { // either SIZE_MAX (invalid UTF-8) or finished input buffer and // there are still bytes remaining, in both cases invalid return NtsInvalid;
}
// "consume" the character
memmove(conv_buf, conv_buf+consumed, conv_buf_len-consumed);
conv_buf_len -= consumed;
char escaped_buf[ESCAPED_SIZE];
size_t escaped_len = char_to_string(ch, '"', first, &escaped_buf); if (out != NULL) { if (cur_out_len < escaped_len) { return NtsOverflow;
}
memcpy(out, escaped_buf, escaped_len);
out += escaped_len;
cur_out_len -= escaped_len;
}
first = false;
}
// write ending quote if (out != NULL) { if (cur_out_len == 0) { return NtsOverflow;
}
*out++ = quote;
cur_out_len--;
*out_len -= cur_out_len; // subtract remaining space to get used space
}
static uint8_t parser_peek(struct parser const *parser) { if (parser->next == parser->sym_len) { return 0; // add a "pseudo nul terminator" to avoid peeking past the end of a symbol
} else { return parser->sym[parser->next];
}
}
staticbool parser_eat(struct parser *parser, uint8_t ch) { if (parser_peek(parser) == ch) { if (ch != 0) { // safety: make sure we don't skip past the NUL terminator
parser->next++;
} returntrue;
} else { returnfalse;
}
}
static uint8_t parser_next(struct parser *parser) { // don't advance after end of input, and return an imaginary NUL terminator if (parser->next == parser->sym_len) { return 0;
} else { return parser->sym[parser->next++];
}
}
static NODISCARD demangle_status parser_ch(struct parser *parser, uint8_t *next) { // don't advance after end of input if (parser->next == parser->sym_len) { return DemangleInvalid;
} else {
*next = parser->sym[parser->next++]; return DemangleOk;
}
}
struct parser orig_parser = printer->parser;
demangle_status orig_status = printer->status; // fixme not sure this is needed match for Ok on the Rust side
printer->parser = backref;
printer->status = DemangleOk;
overflow_status status = func(printer, arg);
printer->parser = orig_parser;
printer->status = orig_status;
// HACK(eddyb) if the parser is already marked as having errored, // `parse!` below will print a `?` without its preceding `::` // (because printing the `::` is skipped in certain conditions, // i.e. a lowercase namespace with an empty identifier), // so in order to get `::?`, the `::` has to be printed here. if (printer->status != DemangleOk) {
PRINT_STR(printer, "::");
}
size_t out_len = SIZE_MAX;
nibbles_to_string_status nts_status = nibbles_to_string(hex.start, hex.len, NULL, &out_len); switch (nts_status) { case NtsOk: if (printer->out != NULL) {
out_len = printer->out_len;
nts_status = nibbles_to_string(hex.start, hex.len, (uint8_t*)printer->out, &out_len); if (nts_status != NtsOk) { return OverflowOverflow;
}
printer->out += out_len;
printer->out_len -= out_len;
} return OverflowOk; case NtsOverflow: // technically if there is a string of size `SIZE_MAX/6` whose escaped version overflows // SIZE_MAX but has an invalid char, this will be a "fake" overflow. In practice, // that is not going to happen and a fuzzer will not generate strings of this length. return OverflowOverflow; case NtsInvalid: default:
INVALID(printer);
}
}
if (opened_brace) {
PRINT_STR(printer, "}");
}
printer_pop_depth(printer);
return OverflowOk;
}
/// A trait in a trait object may have some "existential projections" /// (i.e. associated type bindings) after it, which should be printed /// in the `<...>` of the trait, e.g. `dyn Trait<T, U, Assoc=X>`. /// To this end, this method will keep the `<...>` of an 'I' path /// open, by omitting the `>`, and return `Ok(true)` in that case. static NODISCARD overflow_status printer_print_maybe_open_generics(struct printer *printer, bool *open) { if (printer_eat(printer, 'B')) { // NOTE(eddyb) the closure may not run if printing is being skipped, // but in that case the returned boolean doesn't matter.
*open = false; return printer_print_backref(printer, printer_print_maybe_open_generics, open);
} elseif(printer_eat(printer, 'I')) {
PRINT(printer_print_path(printer, false));
PRINT_STR(printer, "<");
PRINT_SEP_LIST(printer, PRINT(printer_print_generic_arg(printer)), ", ");
*open = true; return OverflowOk;
} else {
PRINT(printer_print_path(printer, false));
*open = false; return OverflowOk;
}
}
if (printer_eat(printer, 'u')) { // Skip printing the return type if it's 'u', i.e. `()`.
} else {
PRINT_STR(printer, " -> ");
PRINT(printer_print_type(printer));
}
if (!printer_eat(printer, 'L')) {
INVALID(printer);
}
PARSE(printer, parser_integer_62, <);
if (lt != 0) {
PRINT_STR(printer, " + ");
PRINT(printer_print_lifetime_from_index(printer, lt));
} break; case'B':
PRINT(printer_print_backref(printer, printer_print_type_backref, NULL)); break; default: // Go back to the tag, so `print_path` also sees it. if (printer->status == DemangleOk && /* safety */ printer->parser.next > 0) {
printer->parser.next--;
}
PRINT(printer_print_path(printer, false));
}
printer_pop_depth(printer); return OverflowOk;
}
NODISCARD static demangle_status rust_demangle_legacy_demangle(constchar *s, size_t s_len, struct demangle_legacy *res, constchar **rest)
{ if (s_len > strlen(s)) { // s_len only exists to shorten the string, this is not a buffer API return DemangleInvalid;
}
constchar *inner;
size_t inner_len; if (s_len >= 3 && !strncmp(s, "_ZN", 3)) {
inner = s + 3;
inner_len = s_len - 3;
} elseif (s_len >= 2 && !strncmp(s, "ZN", 2)) { // On Windows, dbghelp strips leading underscores, so we accept "ZN...E" // form too.
inner = s + 2;
inner_len = s_len - 2;
} elseif (s_len >= 4 && !strncmp(s, "__ZN", 4)) { // On OSX, symbols are prefixed with an extra _
inner = s + 4;
inner_len = s_len - 4;
} else { return DemangleInvalid;
}
if (!str_isascii(inner, inner_len)) { return DemangleInvalid;
}
size_t elements = 0; constchar *chars = inner;
size_t chars_len = inner_len; if (chars_len == 0) { return DemangleInvalid;
} char c; while ((c = *chars) != 'E') { // Decode an identifier element's length if (c < '0' || c > '9') { return DemangleInvalid;
}
size_t len = 0; while (c >= '0' && c <= '9') {
size_t d = c - '0'; if (len > SIZE_MAX / 10) { return DemangleInvalid;
}
len *= 10; if (len > SIZE_MAX - d) { return DemangleInvalid;
}
len += d;
chars++;
chars_len--; if (chars_len == 0) { return DemangleInvalid;
}
c = *chars;
}
// Advance by the length if (chars_len <= len) { return DemangleInvalid;
}
chars += len;
chars_len -= len;
elements++;
}
*res = (struct demangle_legacy) { inner, inner_len, elements };
*rest = chars + 1; return DemangleOk;
}
for (size_t i = 1; i < len; i++) { if (!((s[i] >= '0' && s[i] <= '9') || (s[i] >= 'a' && s[i] <= 'f') || (s[i] >= 'A' && s[i] <= 'F'))) { returnfalse;
}
}
returntrue;
}
NODISCARD static overflow_status rust_demangle_legacy_display_demangle(struct demangle_legacy res, char *out, size_t len, bool alternate)
{ struct printer printer = { // not actually using the parser part of the printer, just keeping it to share the format functions
DemangleOk,
{ NULL },
out,
len,
0,
alternate
}; constchar *inner = res.mangled; for (size_t element = 0; element < res.elements; element++) {
size_t i = 0; constchar *rest; for (rest = inner; rest < res.mangled + res.mangled_len && *rest >= '0' && *rest <= '9'; rest++) {
i *= 10;
i += *rest - '0';
} if ((size_t)(res.mangled + res.mangled_len - rest) < i) { // safety: shouldn't reach this place if the input string is validated. bail out. // safety: we knwo rest <= res.mangled + res.mangled_len from the for-loop above break;
}
size_t len = i;
inner = rest + len;
// From here on, inner contains a pointer to the next element, rest[:len] to the current one if (alternate && element + 1 == res.elements && is_rust_hash(rest, i)) { break;
} if (element != 0) {
PRINT_STR(&printer, "::");
}
staticbool is_symbol_like(constchar *s, size_t len) { // rust-demangle definition of symbol like: control characters and space are not symbol-like, all else is for (size_t i = 0; i < len; i++) { char ch = s[i]; if (!(ch >= 0x21 && ch <= 0x7e)) { returnfalse;
}
} returntrue;
}
void rust_demangle_demangle(constchar *s, struct demangle *res)
{ // During ThinLTO LLVM may import and rename internal symbols, so strip out // those endings first as they're one of the last manglings applied to symbol // names. constchar *llvm = ".llvm."; constchar *found_llvm = strstr(s, llvm);
size_t s_len = strlen(s); if (found_llvm) { constchar *all_hex_ptr = found_llvm + strlen(".llvm."); bool all_hex = true; for (;*all_hex_ptr;all_hex_ptr++) { if (!(('0' <= *all_hex_ptr && *all_hex_ptr <= '9') ||
('A' <= *all_hex_ptr && *all_hex_ptr <= 'F') ||
*all_hex_ptr == '@')) {
all_hex = false; break;
}
}
// Output like LLVM IR adds extra period-delimited words. See if // we are in that case and save the trailing words if so. if (res->suffix_len) { if (res->suffix[0] == '.' && is_symbol_like(res->suffix, res->suffix_len)) { // Keep the suffix
} else { // Reset the suffix and invalidate the demangling
res->style = DemangleStyleUnknown;
res->suffix_len = 0;
}
}
}
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.