|
| 1 | +- Feature Name: dllimport |
| 2 | +- Start Date: 2016-08-13 |
| 3 | +- RFC PR: (leave this empty) |
| 4 | +- Rust Issue: (leave this empty) |
| 5 | + |
| 6 | +# Summary |
| 7 | +[summary]: #summary |
| 8 | + |
| 9 | +Make compiler aware of the association between library names adorning `extern` blocks |
| 10 | +and symbols defined within the block. Add attributes and command line switches that leverage |
| 11 | +this association. |
| 12 | + |
| 13 | +# Motivation |
| 14 | +[motivation]: #motivation |
| 15 | + |
| 16 | +Most of the time a linkage directive is only needed to inform the linker about |
| 17 | +what native libraries need to be linked into a program. On some platforms, |
| 18 | +however, the compiler needs more detailed knowledge about what's being linked |
| 19 | +from where in order to ensure that symbols are wired up correctly. |
| 20 | + |
| 21 | +On Windows, when a symbol is imported from a dynamic library, the code that accesses |
| 22 | +this symbol must be generated differently than for symbols imported from a static library. |
| 23 | + |
| 24 | +Currently the compiler is not aware of associations between the libraries and symbols |
| 25 | +imported from them, so it cannot alter code generation based on library kind. |
| 26 | + |
| 27 | +# Detailed design |
| 28 | +[design]: #detailed-design |
| 29 | + |
| 30 | +### Library <-> symbol association |
| 31 | + |
| 32 | +The compiler shall assume that symbols defined within extern block |
| 33 | +are imported from the library mentioned in the `#[link]` attribute adorning the block. |
| 34 | + |
| 35 | +### Changes to code generation |
| 36 | + |
| 37 | +On platforms other than Windows the above association will have no effect. |
| 38 | +On Windows, however, `#[link(..., kind="dylib")` shall be presumed to mean linking to a dll, |
| 39 | +whereas `#[link(..., kind="static")` shall mean static linking. In the former case, all symbols |
| 40 | +associated with that library will be marked with LLVM [dllimport][1] storage class. |
| 41 | + |
| 42 | +[1]: http://llvm.org/docs/LangRef.html#dll-storage-classes |
| 43 | + |
| 44 | +### Library name and kind variance |
| 45 | + |
| 46 | +Many native libraries are linked via the command line via `-l` which is passed |
| 47 | +in through Cargo build scripts instead of being written in the source code |
| 48 | +itself. As a recap, a native library may change names across platforms or |
| 49 | +distributions or it may be linked dynamically in some situations and |
| 50 | +statically in others which is why build scripts are leveraged to make these |
| 51 | +dynamic decisions. In order to support this kind of dynamism, the following |
| 52 | +modifications are proposed: |
| 53 | + |
| 54 | +- A new library kind, "abstract". An "abstract" library by itself does not |
| 55 | + cause any libraries to be linked. Its purpose is to establish an identifier, |
| 56 | + that may be later referred to from the command line flags. |
| 57 | +- Extend syntax of the `-l` flag to `-l [KIND=]lib[:NEWNAME]`. The `NEWNAME` |
| 58 | + part may be used to override name of a library specified in the source. |
| 59 | +- Add new meaning to the `KIND` part: if "lib" is already specified in the source, |
| 60 | + this will override its kind with KIND. Note that this override is possible only |
| 61 | + for libraries defined in the current crate. |
| 62 | + |
| 63 | +Example: |
| 64 | + |
| 65 | +```rust |
| 66 | +// mylib.rs |
| 67 | +#[link(name = "foo", kind="dylib")] |
| 68 | +extern { |
| 69 | + // dllimport applied |
| 70 | +} |
| 71 | + |
| 72 | +#[link(name = "bar", kind="static")] |
| 73 | +extern { |
| 74 | + // dllimport not applied |
| 75 | +} |
| 76 | + |
| 77 | +#[link(name = "baz", kind="abstract")] |
| 78 | +extern { |
| 79 | + // dllimport not applied, "baz" not linked |
| 80 | +} |
| 81 | +``` |
| 82 | + |
| 83 | +``` |
| 84 | +rustc mylib.rs -l static=foo # change foo's kind to "static", dllimport will not be applied |
| 85 | +rustc mylib.rs -l foo:newfoo # link newfoo instead of foo |
| 86 | +rustc mylib.rs -l dylib=baz:quoox # specify baz's kind as "dylib", change link name to quoox. |
| 87 | +``` |
| 88 | + |
| 89 | +### Unbundled static libs (optional) |
| 90 | + |
| 91 | +It had been pointed out that sometimes one may wish to link to a static system library |
| 92 | +(i.e. one that is always available to the linker) without bundling it into .lib's and .rlib's. |
| 93 | +For this use case we'll introduce another library "kind", "static-nobundle". |
| 94 | +Such libraries would be treated in the same way as "static", minus the bundling. |
| 95 | + |
| 96 | +# Drawbacks |
| 97 | +[drawbacks]: #drawbacks |
| 98 | + |
| 99 | +For libraries to work robustly on MSVC, the correct `#[link]` annotation will |
| 100 | +be required. Most cases will "just work" on MSVC due to the compiler strongly |
| 101 | +favoring static linkage, but any symbols imported from a dynamic library or |
| 102 | +exported as a Rust dynamic library will need to be tagged appropriately to |
| 103 | +ensure that they work in all situations. Worse still, the `#[link]` annotations |
| 104 | +on an `extern` block are not required on any other platform to work correctly, |
| 105 | +meaning that it will be common that these attributes are left off by accident. |
| 106 | + |
| 107 | + |
| 108 | +# Alternatives |
| 109 | +[alternatives]: #alternatives |
| 110 | + |
| 111 | +- Instead of enhancing `#[link]`, a `#[linked_from = "foo"]` annotation could be added. |
| 112 | + This has the drawback of not being able to handle native libraries whose |
| 113 | + name is unpredictable across platforms in an easy fashion, however. |
| 114 | + Additionally, it adds an extra attribute to the comipler that wasn't known |
| 115 | + previously. |
| 116 | + |
| 117 | +- Support a `#[dllimport]` on extern blocks (or individual symbols, or both). |
| 118 | + This has the following drawbacks, however: |
| 119 | + - This attribute would duplicate the information already provided by |
| 120 | + `#[link(kind="...")]`. |
| 121 | + - It is not always known whether `#[dllimport]` is needed. Native |
| 122 | + libraires are not always known whether they're linked dynamically or |
| 123 | + statically (e.g. that's what a build script decides), so `dllimport` |
| 124 | + will need to be guarded by `cfg_attr`. |
| 125 | + |
| 126 | +- When linking native libraries, the compiler could attempt to locate each |
| 127 | + library on the filesystem and probe the contents for what symbol names are |
| 128 | + exported from the native library. This list could then be cross-referenced |
| 129 | + with all symbols declared in the program locally to understand which symbols |
| 130 | + are coming from a dylib and which are being linked statically. Some downsides |
| 131 | + of this approach may include: |
| 132 | + |
| 133 | + - It's unclear whether this will be a performant operation and not cause |
| 134 | + undue runtime overhead during compiles. |
| 135 | + |
| 136 | + - On Windows linking to a DLL involves linking to its "import library", so |
| 137 | + it may be difficult to know whether a symbol truly comes from a DLL or |
| 138 | + not. |
| 139 | + |
| 140 | + - Locating libraries on the system may be difficult as the system linker |
| 141 | + often has search paths baked in that the compiler does not know about. |
| 142 | + |
| 143 | +- As was already mentioned, "kind" override can affect codegen of the current crate only. |
| 144 | + This overloading the `-l` flag for this purpose may be confusinfg to developers. |
| 145 | + A new codegen flag might be a better fit for this, for example `-C libkind=KIND=LIB`. |
| 146 | + |
| 147 | +# Unresolved questions |
| 148 | +[unresolved]: #unresolved-questions |
| 149 | + |
| 150 | +- Should un-overridden "abstract" kind cause an error, a warning, or be silently ignored? |
| 151 | +- Do we even need "abstract"? Since kind can be overridden, there's no harm in providing a default in the source. |
| 152 | +- Should we allow dropping a library specified in the source from linking via `-l lib:` (i.e. "rename to empty")? |
0 commit comments