Skip to content

C port of the Rust symbol demangler (rustc-demangle)

License

Apache-2.0, MIT licenses found

Licenses found

Apache-2.0
LICENSE-APACHE
MIT
LICENSE-MIT
Notifications You must be signed in to change notification settings

LykenSol/rust-demangle.c

Repository files navigation

rust-demangle.c

This is a single-file C99 port of the official Rust symbol demangler (rustc-demangle, a Rust library).

Usecases

This C port is intended for situations in which a Rust dependency is hard to justify, or effectively impossible (e.g. platform toolchains, that would be used while building Rust, not the other way around).

If a Rust dependency is acceptable, using rustc-demangle from C (or other languages via FFI) is possible, and may be preferred over this C port.

Status

As this C port was originally (see the History section) only for the rustc-demangle code demangling the (new at the time) Rust RFC2603 (aka "v0") mangling scheme, it may lag behind rustc-demangle in functionality, for now.

The current port status by category is:

Notable differences (intentionally) introduced by porting:

  • rustc-demangle can't use the heap (as it's #![no_std]), but the C port does
    • this is mainly dictated by the ergonomics of the rust_demangle API, which requires malloc/realloc to return a new C string allocation
    • if there is demand for it, rust_demangle support could be made optional, forcing heap-less users to always use rust_demangle_with_callback instead
    • a subtler consequence is that rustc-demangle uses a fixed-size buffer on the stack for punycode decoding, while the C port can allocate it on the heap
  • Unicode support is always handrolled in the C port, and often simplified

Usage

Get rust-demangle.c and rust-demangle.h (via git submodule, vendoring, etc.), add them to your project's build system (as C source, and include path, respectively), then you can call rust_demangle with a symbol and some flags, e.g.:

#include <rust-demangle.h>
#include <stdio.h>

int main() {
    const char *sym = "_RNvNtCsbmNqQUJIY6D_4core3foo3bar";

    printf("demangle(%s) = %s\n", sym, rust_demangle(sym, 0));

    printf(
        "demangle(%s, VERBOSE) = %s\n", sym,
        rust_demangle(sym, RUST_DEMANGLE_FLAG_VERBOSE)
    );
}

which prints out, when ran:

demangle(_RNvNtCsbmNqQUJIY6D_4core3foo3bar) = core::foo::bar
demangle(_RNvNtCsbmNqQUJIY6D_4core3foo3bar, VERBOSE) = core[846817f741e54dfd]::foo::bar

Note that the example leaks the returned C strings, ideally you would free them.

Advanced usage (callback-based API)

If you want to avoid the cost of allocating the output in memory, you can also use rust_demangle_with_callback, which takes a "printing" callback instead, e.g.:

#include <rust-demangle.h>
#include <stdio.h>

static void fwrite_callback(const char *data, size_t len, void *opaque) {
    fwrite(data, len, 1, (FILE *)opaque);
}

int main() {
    const char *sym = "_RNvNtCsbmNqQUJIY6D_4core3foo3bar";

    printf("demangle(%s) = ", sym);
    rust_demangle_with_callback(sym, 0, fwrite_callback, stdout);
    printf("\n");

    printf("demangle(%s, VERBOSE) = ", sym);
    rust_demangle_with_callback(
        sym, RUST_DEMANGLE_FLAG_VERBOSE, fwrite_callback, stdout
    );
    printf("\n");
}

(with identical output to the simpler example)

Testing

cargo test will run built-in tests - it's implemented in Rust (in test-harness) so that it can depend on rustc-demangle itself for comparisons.

Additionally, cargo run -q --release --example check-csv-dataset path/to/syms/*.csv can be used to provide CSV files with additional mangled symbols test data, but such datasets aren't trivial to obtain (existing ones required building rust-lang/rust with a compiler patch that reacts to a custom environment variable). They're also quite large (~1GiB uncompressed) so none have been published anywhere yet.

History

This C port was started while the Rust RFC2603 (aka "v0") mangling scheme was still being developed, with the intent of upstreaming it into libiberty (which provides demangling for binutils, gdb, Linux perf, etc.) and other projects (e.g. valgrind) - you can see some of that upstreaming history on the v0 tracking issue.

At the time, the expectation was that most projects could either depend on libiberty, or vendor a copy of its code, so the C port focused on upstreaming to it, rather than producing an independent reusable C codebase.

That meant that instead of a git repository, the code revisions were only tracked by a gist, and the GNU code style was followed (including C89 comments and variable declarations).

However, the LGPL license of libiberty turned out to be a problem for adoption, compared to the typical MIT/Apache-2.0 dual licensing of Rust projects.

The rust-demangle.c fork

This repository started out as a fork of the original gist, at commit e2c30407516a87c0f8c3820cf152640bd08805dd, just before libiberty integration (which was in commit 0e6f57b0e86ccec4395f8850f4885b1e391a9f4b).

Any changes since that gist are either fresh C ports of the Rust rustc-demangle code, or completely new code, in order to maintain the MIT/Apache-2.0 dual licensing.

While this has the disadvantage of starting behind libiberty (which kept its Rust legacy demangler, and also got a few more features during/since upstreaming), the relationship may reverse eventually, where this port could get new features that would then have to be upstreamed into libiberty.

License

Like rustc-demangle, this project is licensed under either of

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in rust-demangle.c you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

About

C port of the Rust symbol demangler (rustc-demangle)

Resources

License

Apache-2.0, MIT licenses found

Licenses found

Apache-2.0
LICENSE-APACHE
MIT
LICENSE-MIT

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published