Skip to content

Commit c2ce9c8

Browse files
committed
RFC: -C export-executable-symbols
1 parent 0fb14f2 commit c2ce9c8

File tree

1 file changed

+109
-0
lines changed

1 file changed

+109
-0
lines changed
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
- Feature Name: export-executable-symbols
2+
- Start Date: 2019-12-28
3+
- RFC PR: [rust-lang/rfcs#0000](https://github.com/rust-lang/rfcs/pull/0000)
4+
- Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000)
5+
6+
# Summary
7+
[summary]: #summary
8+
9+
Add the ability to export symbols from executables, not just dylibs, via a new
10+
compiler flag: `-C export-executable-symbols`.
11+
12+
# Motivation
13+
[motivation]: #motivation
14+
15+
Java and C# can't statically link against C/Rust code. Both require dylib
16+
symbols for their common native interop solution. Which is fine if you let
17+
their executables call your dylib, but is a problem if you want your Rust
18+
executable to load a JVM instance, and let it call back into your executable.
19+
You might want to do this to allow you to:
20+
* Load multiple language runtimes into the same process (Rust + C# + Java + Lua anyone? Only one of them can be the entry executable...)
21+
* Display user-friendly error messages if language runtimes are missing (maybe even a download link!)
22+
* [#[test] Java/Rust interop via cargo test.](https://github.com/MaulingMonkey/jerk/blob/04250c9d1b6ccc292eb27663f70919345c31007f/example-hello-world-jar/src/Global.rs)
23+
24+
For this last case, I
25+
[manually export](https://github.com/MaulingMonkey/jerk/blob/04250c9d1b6ccc292eb27663f70919345c31007f/example-hello-world-jar/exports.def)
26+
executable symbols via
27+
[LINK](https://github.com/MaulingMonkey/jerk/blob/04250c9d1b6ccc292eb27663f70919345c31007f/example-hello-world-jar/build.rs#L4).
28+
This is ugly, brittle, and rustc
29+
[already knows](https://github.com/rust-lang/rust/blob/a916ac22b9f7f1f0f7aba0a41a789b3ecd765018/src/librustc_codegen_ssa/back/linker.rs#L706-L717)
30+
how to do this automatically, across more platforms, and better.
31+
32+
# Guide-level explanation
33+
[guide-level-explanation]: #guide-level-explanation
34+
35+
https://doc.rust-lang.org/rustc/codegen-options/index.html could gain:
36+
37+
```md
38+
## export-executable-symbols
39+
40+
This flag causes `rustc` to export symbols from executables, as if they were dynamic libraries.
41+
42+
You might use this to allow the JVM or MSCLR to call back into your executable's
43+
Rust code from Java/C# when embedding their runtimes into your Rust executable.
44+
```
45+
46+
`rustc -C help` could gain:
47+
48+
```
49+
-C export-executable-symbols -- export symbols from executables, as if they were dynamic libraries.
50+
```
51+
52+
My Java interop [Quick Start](https://github.com/MaulingMonkey/jerk/blob/master/Readme.md#quick-start)
53+
would start recommending a `.cargo/config` with:
54+
```toml
55+
[build]
56+
rustflags = ["-C", "export-executable-symbols"]
57+
```
58+
59+
# Reference-level explanation
60+
[reference-level-explanation]: #reference-level-explanation
61+
62+
On a technical level, this just involves preventing an early bailout when
63+
calling `fn export_symbols` on executables with MSVC or GNU linker backends.
64+
Other linker backends (EmLinker, WasmLd, PtxLinker) do not have this early
65+
bailout in the first place, and remain unaffected.
66+
67+
# Drawbacks
68+
[drawbacks]: #drawbacks
69+
70+
* Options bloat
71+
* The burden of supporting a niche use-case in hideously platform specific code
72+
73+
# Rationale and alternatives
74+
[rationale-and-alternatives]: #rationale-and-alternatives
75+
76+
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.
77+
78+
Alternatives:
79+
80+
- Unconditionally export symbols from executables instead of introducing a new compiler flag.
81+
- Write *yet another* cargo subcommand to install/remember for interop testing instead of using cargo test.
82+
- Write interop tests exclusively as integration tests, in an entirely separate crate, that can load the testee as a dylib.
83+
- Continue abusing LINK, writing a tool to auto-generate .defs via build scripts - possibly by reading metadata from other tools.
84+
- Use nightly link-args instead of LINK, but still write a .def generator.
85+
- Remember to always cargo build a dylib copy of a crate manually before cargo test ing, and load that instead.
86+
(That would also add a whole second copy of all functions and static vars in the same unit test process!)
87+
88+
# Prior art
89+
[prior-art]: #prior-art
90+
91+
C and C++ compilers can already do this via `__declspec(dllexport)` annotations.
92+
Most people don't really notice it, for good or for ill.
93+
94+
# Unresolved questions
95+
[unresolved-questions]: #unresolved-questions
96+
97+
- Is this a good name for it?
98+
- Should it be more general and export when limit_rdylib_exports or crate_type == ProcMacro?
99+
100+
# Future possibilities
101+
[future-possibilities]: #future-possibilities
102+
103+
Maybe other options to control what symbols get exported? Although I'd fear
104+
turning rustc into yet another linker script implementation, so maybe not.
105+
106+
My own building atop this in the wider language ecosystem would be for improved
107+
Java/Rust interop/testing, with the eventual goal of improved Android API
108+
support for Rust. Many APIs are only exposed via Java, and I'd like said APIs
109+
to be usable in a safe and sound fashion.

0 commit comments

Comments
 (0)