-
Notifications
You must be signed in to change notification settings - Fork 12.9k
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
Partial support for raw-dylib linkage #84171
Conversation
Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @petrochenkov (or someone else) soon. If any changes to this PR are deemed necessary, please add them as extra commits. This ensures that the reviewer can see what has changed since they last reviewed the code. Due to the way GitHub handles out-of-date commits, this should also make it reasonably obvious what issues have or haven't been addressed. Large or tricky changes may require several passes of review and changes. Please see the contribution instructions for more information. |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
I tested a build of this PR with the Windows crate it works wonderfully! 🎉 Here's the test branch. |
This comment has been minimized.
This comment has been minimized.
I also have a big question about the implementation strategy. In this PR the imports are added to The alternative strategy is to delay generation of the temporary Any specific reasons to choose the first strategy and not the second one? |
The use of a temporary file is primarily a consequence of my choice to use LLVM's function to generate the import library. The relevant function generates the import library and writes it to disk; I haven't yet found a way to perform the first action without the second without modifying LLVM. (An early look at the implementation of
Honestly, the possibility never occurred to me; I'm new enough to rustc that metadata wasn't really on my radar screen.
Yes, that is a use case that I've been aware of; I'm going to investigate tomorrow to see what happens there. (I'm also curious about what happens if one does the analogous actions with C static libraries linked into a single exe or dll. The only thing I've heard about that so far is that "nobody does that," so I'm just going to have to try it myself.) If I understand correctly, we won't be able to avoid the weird-hybrid-of-static-library-and-import-library in all cases, such as when someone compiles an rlib with |
This comment has been minimized.
This comment has been minimized.
As described in the RFC, the generated import library should map from the Rust mangled name to a dll + symbol tuple. Thus, if there are two places that both link to the same symbol but from different dlls, they should each correctly resolve to their specific dll as there will be no ambiguity between the mangled names. This is different from the traditional linker model where import libraries map from the unmangled (but still possibly calling convention decorated) name to a dll + symbol tuple, which would be ambiguous and result in linker shenanigans. |
@retep998 |
@ricobbe For example, if we build several dlls and executables using the same rlib with imports, then the idata will be generated only once and then reused. (*) Assuming it works in general, merging idata from different rlibs work, mixed static+import libraries don't confuse mingw linker, etc. UPD: We could potentially use the |
Taking the metadata from all rlibs and using them during the final linking step is how other linking kinds work (like dynamic or static-nobundle). |
Assuming the current solution works in at least some cases and doesn't completely break mingw, do you see a resolution to this question as necessary in order to complete this PR, or is this something that we could possibly address as follow-on work? If possible, I'd like to get the current implementation in (subject to caveats above, of course), so we could get some experience with the feature while continuing to discuss this issue. (We've got some folks here who are really ready to start using this functionality, so there are definitely opportunities for feedback.) It may be worth considering that the mingw platform does seem to be having some difficulties with this solution, although I haven't yet determined if this is due to an incompatibility between this implementation technique and the mingw linker, or if it's just because I haven't yet got my windows-gnu toolchain set up correctly. I intend to continue investigating that question. |
No, not necessary, can be done as a follow up work. |
Status update: the current implementation doesn't work with the windows-gnu toolchain. The test succeeds in creating the .exe file, but it AVs on execution, and the failure seems to occur when it tries to call one of the functions imported from a .dll. I've been working with @mati865 to address this, but it's a slow process (mostly because the time difference makes for high-latency communcation). The case where multiple .rlibs import functions from the same raw-dylib DLL and are linked into the same final EXE does work as intended with the current implementation on windows-msvc (and, IIRC, the linker does remove any duplicate symbols from the final import library), but I haven't yet been able to test this on windows-gnu. So my plan at this point is to switch to the implementation strategy suggested above: adding raw-dylib import information to .rlib metadata, and deferring the actual generation of the import library until we're linking the .exe, .dll, static lib, or generally anything that isn't an .rlib. @petrochenkov, you had some concerns about the performance consequences of this decision, because we'd be generating the import libraries potentially many times for a given rlib; do you think I need to investigate that further? I don't have hard performance numbers on that, but all of my experience so far is that generating the import libraries does not take much time. |
I'm not sure if that switch will actually help. Like I said in the private message LLVM generates MSVC style import libraries which are poorly supported by BFD:
|
Well, at least it will avoid mixed import-static libraries (*), which is one of the problems.
Are there multiple styles of import libraries? o_O Can rustc generate the same style as used in https://github.com/retep998/winapi-rs/tree/0.3/x86_64/lib, because that style is something that is known to work. (Also, is there a style that works for both msvc and gnu targets?) (*) Modulo the staticlib case, where there's no other choice, but in that case the mixed library is not an intermediate result like rlib, but a final product, so consuming it is not our problem. |
According to the spec there are "short" and "long" styles. The short format provides only the information needed for the linker to construct the A quick look at winapi suggests they create |
I just pushed a fix for the failure on i686; I figured I'd squash commits again after dealing with any review feedback for the change. |
FWIW, I made an attempt at a patch for LLVM to make it possible to produce GNU style long import libraries at some point, but I never finished and upstreamed it as my code left like a big mess, but if necessary I could try to dig it up. Overall, LLD should work with both formats. Binutils ld.bfd kinda works with the MSVC style but has known issues if trying to link more than one such library. MSVC link.exe kinda works with the GNU style, but there's vague gotchas (e.g. if you use an import library created as the output of With the rust generated internal import libraries, would you still end up linking against import libs from the SDK, or would it only use the generated ones? If linking against two different import libraries for the same DLL, you might run into problems - at least for GNU style import libraries. Or maybe it'd work fine and you'd just have two separate DLL import entries for the same DLL name?
There's indeed a vague difference between GNU tools there; with e.g. binutils dlltool (where you just create an import library from a def file and nothing else, and using the option When LLD links a DLL with stdcall symbols, the def file contains the symbol names without trailing decorations. After completing linking and finding all actual symbols, it does some amount of fixup, e.g. if the def file specifies to export the symbol But without actual symbols, there's nowhere to deduce the suffix from, so then only the dlltool style behaviour with the def file containing the decorated form would work. |
This comment has been minimized.
This comment has been minimized.
6a72c3a
to
688aa1d
Compare
…k(kind = "raw-dylib")]. This does not yet support #[link_name] attributes on functions, the #[link_ordinal] attribute, #[link(kind = "raw-dylib")] on extern blocks in bin crates, or stdcall functions on 32-bit x86.
688aa1d
to
6aa45b7
Compare
That depends on whether you specify |
@rustbot label -S-waiting-on-author +S-waiting-on-review |
@bors r+ |
📌 Commit 6aa45b7 has been approved by |
☀️ Test successful - checks-actions |
…enkov Partial support for raw-dylib linkage First cut of functionality for issue rust-lang#58713: add support for `#[link(kind = "raw-dylib")]` on `extern` blocks in lib crates compiled to .rlib files. Does not yet support `#[link_name]` attributes on functions, or the `#[link_ordinal]` attribute, or `#[link(kind = "raw-dylib")]` on `extern` blocks in bin crates; I intend to publish subsequent PRs to fill those gaps. It's also not yet clear whether this works for functions in `extern "stdcall"` blocks; I also intend to investigate that shortly and make any necessary changes as a follow-on PR. This implementation calls out to an LLVM function to construct the actual `.idata` sections as temporary `.lib` files on disk and then links those into the generated .rlib.
First cut of functionality for issue #58713: add support for
#[link(kind = "raw-dylib")]
onextern
blocks in lib crates compiled to .rlib files. Does not yet support#[link_name]
attributes on functions, or the#[link_ordinal]
attribute, or#[link(kind = "raw-dylib")]
onextern
blocks in bin crates; I intend to publish subsequent PRs to fill those gaps. It's also not yet clear whether this works for functions inextern "stdcall"
blocks; I also intend to investigate that shortly and make any necessary changes as a follow-on PR.This implementation calls out to an LLVM function to construct the actual
.idata
sections as temporary.lib
files on disk and then links those into the generated .rlib.