-
Notifications
You must be signed in to change notification settings - Fork 1.6k
RFC: -C export-executable-symbols #2841
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Mark-Simulacrum
merged 2 commits into
rust-lang:master
from
MaulingMonkey:pr/export-executable-symbols
Apr 13, 2021
Merged
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
- Feature Name: export-executable-symbols | ||
- Start Date: 2019-12-28 | ||
- RFC PR: [rust-lang/rfcs#2841](https://github.com/rust-lang/rfcs/pull/2841) | ||
- Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000) | ||
|
||
# Summary | ||
[summary]: #summary | ||
|
||
Add the ability to export symbols from executables, not just dylibs, via a new | ||
compiler flag: `-C export-executable-symbols`. | ||
|
||
# Motivation | ||
[motivation]: #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: | ||
* Load multiple language runtimes into the same process (Rust + C# + Java + Lua anyone? Only one of them can be the entry executable...) | ||
* Display user-friendly error messages if language runtimes are missing (maybe even a download link!) | ||
* [#[test] Java/Rust interop via cargo test.](https://github.com/MaulingMonkey/jerk/blob/04250c9d1b6ccc292eb27663f70919345c31007f/example-hello-world-jar/src/Global.rs) | ||
|
||
For this last case, I | ||
[manually export](https://github.com/MaulingMonkey/jerk/blob/04250c9d1b6ccc292eb27663f70919345c31007f/example-hello-world-jar/exports.def) | ||
executable symbols via | ||
[LINK](https://github.com/MaulingMonkey/jerk/blob/04250c9d1b6ccc292eb27663f70919345c31007f/example-hello-world-jar/build.rs#L4). | ||
This is ugly, brittle, and rustc | ||
[already knows](https://github.com/rust-lang/rust/blob/a916ac22b9f7f1f0f7aba0a41a789b3ecd765018/src/librustc_codegen_ssa/back/linker.rs#L706-L717) | ||
how to do this automatically, across more platforms, and better. | ||
|
||
# Guide-level explanation | ||
[guide-level-explanation]: #guide-level-explanation | ||
|
||
https://doc.rust-lang.org/rustc/codegen-options/index.html could gain: | ||
|
||
```md | ||
## 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](https://github.com/MaulingMonkey/jerk/blob/master/Readme.md#quick-start) | ||
would start recommending a `.cargo/config` with: | ||
```toml | ||
[build] | ||
rustflags = ["-C", "export-executable-symbols"] | ||
``` | ||
|
||
# Reference-level explanation | ||
[reference-level-explanation]: #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 | ||
[drawbacks]: #drawbacks | ||
|
||
* Options bloat | ||
* The burden of supporting a niche use-case in hideously platform specific code | ||
|
||
# Rationale and alternatives | ||
[rationale-and-alternatives]: #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: | ||
|
||
- Unconditionally export symbols from executables instead of introducing a new compiler flag. | ||
- Introduce a crate-level attribute instead of a compiler flag (`#![export_all_symbols]`? `#![export_symbols]`?) | ||
- Write *yet another* cargo subcommand to install/remember for interop testing instead of using cargo test. | ||
- Write interop tests exclusively as integration tests, in an entirely separate crate, that can load the testee as a dylib. | ||
- Continue abusing LINK, writing a tool to auto-generate .defs via build scripts - possibly by reading metadata from other tools. | ||
- Use nightly link-args instead of LINK, but still write a .def generator. | ||
- Remember to always cargo build a dylib copy of a crate manually before cargo test ing, and load that instead. | ||
(That would also add a whole second copy of all functions and static vars in the same unit test process!) | ||
|
||
# Prior art | ||
[prior-art]: #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 | ||
[unresolved-questions]: #unresolved-questions | ||
|
||
- Is this a good name for it? | ||
- Should it be more general and export when limit_rdylib_exports or crate_type == ProcMacro? | ||
|
||
# Future possibilities | ||
[future-possibilities]: #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. |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's also
-rdynamic
ingcc
/clang
(or--export-dynamic
at the linker) which exports all (visible) symbols from the constructed dynamic ELF (I assume it's a no-op when building an.so
).Possibly interesting as prior art since it's a universal modifier (i.e. not per-symbol) which matches the RFC proposal.