Skip to content

Commit 735451f

Browse files
arielb1Ariel Ben-Yehuda
and
Ariel Ben-Yehuda
authored
Add a native C demangler (#75)
This is intended for use by external projects that aren't able to take a dependency on Rust toolchains at this time. Co-authored-by: Ariel Ben-Yehuda <[email protected]>
1 parent f053741 commit 735451f

File tree

11 files changed

+2429
-2
lines changed

11 files changed

+2429
-2
lines changed

.github/workflows/main.yml

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ jobs:
1414
run: rustup update ${{ matrix.rust }} && rustup default ${{ matrix.rust }}
1515
- run: cargo build --all
1616
- run: cargo test --all
17+
- run: cd crates/native-c && cargo test --all
1718
- run: cargo build --features std
1819

1920
fuzz_targets:

Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ rustc-dep-of-std = ['core', 'compiler_builtins']
2323
std = []
2424

2525
[profile.release]
26-
lto = true
26+
#lto = true
2727

2828
[package.metadata.docs.rs]
2929
features = ["std"]

README.md

+9
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,15 @@ You'll then find `target/release/librustc_demangle.a` and
3030
platform). These objects implement the interface specified in
3131
`crates/capi/include/rustc_demangle.h`.
3232

33+
If your build system does not support Rust, there is also a mostly-identical
34+
C version in the `crates/native-c` which you can use via copy-paste or as
35+
a git submodule. Read `crates/native-c/README.md` for more details. It is
36+
likely to be less supported than the Rust version, so it is better to use
37+
the Rust version if your build system supports it.
38+
39+
Both the Rust and C versions don't require memory allocation or any other
40+
operating-system support.
41+
3342
# License
3443

3544
This project is licensed under either of

crates/native-c/Cargo.toml

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
[package]
2+
name = "rustc-demangle-native-c"
3+
version = "0.1.0"
4+
authors = ["automatically generated"]
5+
description = """
6+
Native C version of the rustc_demangle crate
7+
"""
8+
license = "MIT/Apache-2.0"
9+
repository = "https://github.com/rust-lang/rustc-demangle"
10+
11+
[lib]
12+
name = "rustc_demangle_native_c"
13+
14+
[build-dependencies]
15+
cc = "1"

crates/native-c/README

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
A portable native C demangler, which should mostly have byte-for-byte identical outputs to the Rust one, including in error cases.
2+
3+
This code is intended to be safe to run on untrusted inputs and has been fuzzed, but only it's author has tried to find security issues in it so a security review is probably wise before using it as a serious security barrier.
4+
5+
The only difference is that since it's hard to include up-to-date unicode tables in portable C code, strings in constants (do you know that feature exists?) have all non-ASCII characters escaped (as `\u{ABCD}`) rather than having only non-printable characters escaped. Unicode in identifiers is still translated as-is, allowing non-printable characters just like rustc. If you care, the code intentionally includes `unicode_isprint` and `unicode_isgraphemextend` that can be replaced with actual Unicode tables.
6+
7+
This has a Cargo.toml to make it easy to test, but people whose build systems can use Rust are expected to use the `rustc-demangle-capi` crate which uses the Rust `rustc-demangle` implementation instead. Since the crate is intended only for users with weird build systems, there is no build system provided.

crates/native-c/build.rs

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
fn main() {
2+
cc::Build::new()
3+
.file("src/demangle.c")
4+
.include("include")
5+
.compile("demangle_native_c");
6+
println!("cargo::rerun-if-changed=src/demangle.c");
7+
println!("cargo::rerun-if-changed=include/demangle.h");
8+
}

crates/native-c/include/demangle.h

+80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
#ifndef _H_DEMANGLE_V0_H
2+
#define _H_DEMANGLE_V0_H
3+
4+
#ifdef __cplusplus
5+
extern "C" {
6+
#endif
7+
8+
#include <stddef.h>
9+
10+
#if defined(__GNUC__) || defined(__clang__)
11+
#define DEMANGLE_NODISCARD __attribute__((warn_unused_result))
12+
#else
13+
#define DEMANGLE_NODISCARD
14+
#endif
15+
16+
typedef enum {
17+
OverflowOk,
18+
OverflowOverflow
19+
} overflow_status;
20+
21+
enum demangle_style {
22+
DemangleStyleUnknown = 0,
23+
DemangleStyleLegacy,
24+
DemangleStyleV0,
25+
};
26+
27+
// Not using a union here to make the struct easier to copy-paste if needed.
28+
struct demangle {
29+
enum demangle_style style;
30+
// points to the "mangled" part of the name,
31+
// not including `ZN` or `R` prefixes.
32+
const char *mangled;
33+
size_t mangled_len;
34+
// In DemangleStyleLegacy, is the number of path elements
35+
size_t elements;
36+
// while it's called "original", it will not contain `.llvm.9D1C9369@@16` suffixes
37+
// that are to be ignored.
38+
const char *original;
39+
size_t original_len;
40+
// Contains the part after the mangled name that is to be outputted,
41+
// which can be `.exit.i.i` suffixes LLVM sometimes adds.
42+
const char *suffix;
43+
size_t suffix_len;
44+
};
45+
46+
// if the length of the output buffer is less than `output_len-OVERFLOW_MARGIN`,
47+
// the demangler will return `OverflowOverflow` even if there is no overflow.
48+
#define OVERFLOW_MARGIN 4
49+
50+
/// Demangle a C string that refers to a Rust symbol and put the demangle intermediate result in `res`.
51+
/// Beware that `res` contains references into `s`. If `s` is modified (or free'd) before calling
52+
/// `rust_demangle_display_demangle` behavior is undefined.
53+
///
54+
/// Use `rust_demangle_display_demangle` to convert it to an actual string.
55+
void rust_demangle_demangle(const char *s, struct demangle *res);
56+
57+
/// Write the string in a `struct demangle` into a buffer.
58+
///
59+
/// Return `OverflowOk` if the output buffer was sufficiently big, `OverflowOverflow` if it wasn't.
60+
/// This function is `O(n)` in the length of the input + *output* [$], but the demangled output of demangling a symbol can
61+
/// be exponentially[$$] large, therefore it is recommended to have a sane bound (`rust-demangle`
62+
/// uses 1,000,000 bytes) on `len`.
63+
///
64+
/// `alternate`, if true, uses the less verbose alternate formatting (Rust `{:#}`) is used, which does not show
65+
/// symbol hashes and types of constant ints.
66+
///
67+
/// [$] It's `O(n * MAX_DEPTH)`, but `MAX_DEPTH` is a constant 300 and therefore it's `O(n)`
68+
/// [$$] Technically, bounded by `O(n^MAX_DEPTH)`, but this is practically exponential.
69+
DEMANGLE_NODISCARD overflow_status rust_demangle_display_demangle(struct demangle const *res, char *out, size_t len, bool alternate);
70+
71+
/// Returns true if `res` refers to a known valid Rust demangling style, false if it's an unknown style.
72+
bool rust_demangle_is_known(struct demangle *res);
73+
74+
#undef DEMANGLE_NODISCARD
75+
76+
#ifdef __cplusplus
77+
}
78+
#endif
79+
80+
#endif

0 commit comments

Comments
 (0)