RFC 2841: Support exporting symbols from executables

lang | compiler (ffi)

Summary

Add the ability to export symbols from executables, not just dylibs, via a new compiler flag: -C export-executable-symbols.

Motivation

Java and C# can't statically link against C/Rust code. Both require dylib symbols for their common native interop solution. Which is fine if you let their executables call your dylib, but is a problem if you want your Rust executable to load a JVM instance, and let it call back into your executable. You might want to do this to allow you to:

For this last case, I manually export executable symbols via LINK. This is ugly, brittle, and rustc already knows how to do this automatically, across more platforms, and better.

Guide-level explanation

https://doc.rust-lang.org/rustc/codegen-options/index.html could gain:

## export-executable-symbols

This flag causes `rustc` to export symbols from executables, as if they were dynamic libraries.

You might use this to allow the JVM or MSCLR to call back into your executable's
Rust code from Java/C# when embedding their runtimes into your Rust executable.

rustc -C help could gain:

    -C    export-executable-symbols -- export symbols from executables, as if they were dynamic libraries.

My Java interop Quick Start would start recommending a .cargo/config with:

[build]
rustflags = ["-C", "export-executable-symbols"]

Reference-level explanation

On a technical level, this just involves preventing an early bailout when calling fn export_symbols on executables with MSVC or GNU linker backends. Other linker backends (EmLinker, WasmLd, PtxLinker) do not have this early bailout in the first place, and remain unaffected.

Drawbacks

Rationale and alternatives

This is very simple to implement, leverages existing code to enable it to do exactly what it was meant to do, and has few drawbacks.

Alternatives:

Prior art

C and C++ compilers can already do this via __declspec(dllexport) annotations. Most people don't really notice it, for good or for ill.

Unresolved questions

Future possibilities

We could introduce a new source annotation, #[export]. For backwards compatibility with current behavior, #[no_mangle] symbols could be exported by default - and possibly disabled with #[export(false)]. This would reduce the need to hide this change to compiler/linker behavior behind a compiler flag or crate annotation.

Maybe other options to control what symbols get exported? Although I'd fear turning rustc into yet another linker script implementation, so maybe not.

My own building atop this in the wider language ecosystem would be for improved Java/Rust interop/testing, with the eventual goal of improved Android API support for Rust. Many APIs are only exposed via Java, and I'd like said APIs to be usable in a safe and sound fashion.