Skip to content
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

Improve handling of dllexport #27416

Merged
merged 2 commits into from
Aug 11, 2015
Merged

Conversation

alexcrichton
Copy link
Member

These two commits are aimed at "fixing" our usage of dllexport in the compiler. Currently we blanket apply this attribute to everything public in a crate, but this ends up with a few downsides:

  • Executables are larger than the should be as a result of thinking they should export everything
  • Native libraries aren't handled correctly because technically a statically included native library should be exported from a DLL in some cases.
  • Symbols don't actually need to be exported if they never end up in a DLL.

The first commit adds a new unstable attribute, #[linked_from], which is a way to tell the compiler what native library a block of symbols comes from. This is used to inform the compiler what set of native libraries are statically included in the rlib (or other output). This information is later used to export them from a DLL if necessary. Currently this is only used in a few places (such as the LLVM bindings) to get the compiler to link correctly.

The second commit stops adding dllexport to all items in LLVM and instead explicitly telling the linker what symbols should be exported. We only need to do this when building a dynamic library, and otherwise we can avoid adding dllexport or telling the linker about exported symbols.

As a testament to this change, the size of "Hello World" on MSVC drops from 1.2MB to 67KB as a result of this patch. This is because the linker can much more aggressively remove unused code.

These commits do not yet attempt to fix our story with dllimport, and I'll leave that to a future PR and issue, for now though I'm going to say that this

Closes #7196

@alexcrichton
Copy link
Member Author

r? @brson

cc @retep998

@rust-highfive rust-highfive assigned brson and unassigned pnkfelix Jul 31, 2015
@rust-highfive
Copy link
Collaborator

r? @pnkfelix

(rust_highfive has picked a reviewer for you, use r? to override)

@retep998
Copy link
Member

As a testament to this change, the size of "Hello World" on MSVC drops from 1.2MB to 67KB as a result of this patch.

👍

Also for that matter, how much does this decrease the size of the DLLs bundled with the Rust distribution?
EDIT: Tried building this myself. Doesn't seem to be any discernible difference for Rust's DLLs, but regular binaries produced by Rust with statically linked libraries benefit massively.

@bors
Copy link
Contributor

bors commented Aug 3, 2015

☔ The latest upstream changes (presumably #27210) made this pull request unmergeable. Please resolve the merge conflicts.

@bors
Copy link
Contributor

bors commented Aug 5, 2015

☔ The latest upstream changes (presumably #27458) made this pull request unmergeable. Please resolve the merge conflicts.

@alexcrichton
Copy link
Member Author

Rebased, ping r? @brson


@retep998

EDIT: Tried building this myself. Doesn't seem to be any discernible difference for Rust's DLLs, but regular binaries produced by Rust with statically linked libraries benefit massively.

Yeah right now we don't have a great distinction between "dylib later linked into a Rust project" and "dylib only used to link into an external C-like project", so we have to continue to pessimistically export everything. If we add this distinction, however, then we could get the same wins on dylibs as we're getting on executables.

I'd also recommend avoiding editing github comments, unfortunately no notifications are sent out after an edit...

@brson
Copy link
Contributor

brson commented Aug 5, 2015

This will break dylibs on windows that, like rustllvm, export native symbols, right? On stable it also leaves no recourse for fixing the breakage. I wonder if this comes up anywhere in the wild. Maybe some of Cargo's bindings?

@alexcrichton
Copy link
Member Author

Thankfully this doesn't actually break any of them that weren't already broken. If the native library is compiled with dllexport then it'll still continue to be exported, and if it wasn't compiled with dllexport it will continue to not be exported.

LLVM doesn't support a mode of being built with dllexport, so we used a custom *.def file to export them previously, but the linker only accepts one *.def file and now that the compiler is providing its own we needed a mechanism for the compiler to emit those symbols as well.

In short, this doesn't actually change the story for any native library linked into an MSVC binary today. It still sucks that you have to remember to dllexport it yourself if you know it's going to be called through a DLL, but that's true both before and after this PR.

@retep998
Copy link
Member

@alexcrichton If we really want some wins with dynamic libraries we should respect the distinction between .dll and .lib where when Rust produces a dylib on Windows it'll produce both a .lib with all the Rust metadata and stuff for linking to and a .dll which only contains fully compiled and linked code rather than the current situation of the .dll containing both.

@brson
Copy link
Contributor

brson commented Aug 10, 2015

@bors r+

@bors
Copy link
Contributor

bors commented Aug 10, 2015

📌 Commit 80936de has been approved by brson

@alexcrichton
Copy link
Member Author

@retep998 that would only help binary size, right? I'm not too too worried about it because Rust dlls are so uncommon. This is another case where a distinction between a DLL-used-with-a-C-api and a DLL-used-as-a-rust-dependency would be useful, however.

@retep998
Copy link
Member

@alexcrichton It would help binary size when you are distributing DLLs with a Rust binary where nobody will link to those DLLs. For example the DLLs sitting next to rustc.exe are never linked to by users of the Rust distribution, so there is no need for them to have all that metadata.

@alexcrichton
Copy link
Member Author

Sure, but this is nothing that has to do specifically with Windows, this is a problem that plagues all platforms.

@bors
Copy link
Contributor

bors commented Aug 11, 2015

☔ The latest upstream changes (presumably #27338) made this pull request unmergeable. Please resolve the merge conflicts.

To correctly reexport statically included libraries from a DLL on Windows, the
compiler will soon need to have knowledge about what symbols are statically
included and which are not. To solve this problem a new unstable
`#[linked_from]` attribute is being added and recognized on `extern` blocks to
indicate which native library the symbols are coming from.

The compiler then keeps track of what the set of FFI symbols are that are
included statically. This information will be used in a future commit to
configure how we invoke the linker on Windows.
Rust's current compilation model makes it impossible on Windows to generate one
object file with a complete and final set of dllexport annotations. This is
because when an object is generated the compiler doesn't actually know if it
will later be included in a dynamic library or not. The compiler works around
this today by flagging *everything* as dllexport, but this has the drawback of
exposing too much.

Thankfully there are alternate methods of specifying the exported surface area
of a dll on Windows, one of which is passing a `*.def` file to the linker which
lists all public symbols of the dynamic library. This commit removes all
locations that add `dllexport` to LLVM variables and instead dynamically
generates a `*.def` file which is passed to the linker. This file will include
all the public symbols of the current object file as well as all upstream
libraries, and the crucial aspect is that it's only used when generating a
dynamic library. When generating an executable this file isn't generated, so all
the symbols aren't exported from an executable.

To ensure that statically included native libraries are reexported correctly,
the previously added support for the `#[linked_from]` attribute is used to
determine the set of FFI symbols that are exported from a dynamic library, and
this is required to get the compiler to link correctly.
@alexcrichton
Copy link
Member Author

@bors: r=brson e648c96

@bors
Copy link
Contributor

bors commented Aug 11, 2015

⌛ Testing commit e648c96 with merge 8b37055...

bors added a commit that referenced this pull request Aug 11, 2015
These two commits are aimed at "fixing" our usage of `dllexport` in the compiler. Currently we blanket apply this attribute to *everything* public in a crate, but this ends up with a few downsides:

* Executables are larger than the should be as a result of thinking they should export everything
* Native libraries aren't handled correctly because technically a statically included native library should be exported from a DLL in some cases.
* Symbols don't actually need to be exported if they never end up in a DLL.

The first commit adds a new unstable attribute, `#[linked_from]`, which is a way to tell the compiler what native library a block of symbols comes from. This is used to inform the compiler what set of native libraries are statically included in the rlib (or other output). This information is later used to export them from a DLL if necessary. Currently this is only used in a few places (such as the LLVM bindings) to get the compiler to link correctly.

The second commit stops adding `dllexport` to all items in LLVM and instead explicitly telling the linker what symbols should be exported. We only need to do this when building a dynamic library, and otherwise we can avoid adding `dllexport` or telling the linker about exported symbols.

As a testament to this change, the size of "Hello World" on MSVC drops from 1.2MB to 67KB as a result of this patch. This is because the linker can much more aggressively remove unused code.

These commits do not yet attempt to fix our story with `dllimport`, and I'll leave that to a future PR and issue, for now though I'm going to say that this

Closes #7196
@bors bors merged commit e648c96 into rust-lang:master Aug 11, 2015
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add support for DllExport on Windows
6 participants