-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
Correctly handle dllimport on Windows #27438
Comments
This commit leverages the runtime support for DWARF exception info added in rust-lang#27210 to enable unwinding by default on 64-bit MSVC. This also additionally adds a few minor fixes here and there in the test harness and such to get `make check` entirely passing on 64-bit MSVC: * The invocation of `maketest.py` now works with spaces/quotes in CC * debuginfo tests are disabled on MSVC * A link error for librustc was hacked around (see rust-lang#27438)
This commit leverages the runtime support for DWARF exception info added in rust-lang#27210 to enable unwinding by default on 64-bit MSVC. This also additionally adds a few minor fixes here and there in the test harness and such to get `make check` entirely passing on 64-bit MSVC: * The invocation of `maketest.py` now works with spaces/quotes in CC * debuginfo tests are disabled on MSVC * A link error for librustc was hacked around (see rust-lang#27438)
So basically, with MSVC toolchain you are supposed to know whether you are going to be linking with a static lib or a dll version of the upstream library when compiling your crate. Here's some ideas that come to my mind:
|
I agree that strategies like I'm personally a bit up in the air about how to tackle this. I only know of one absolute failure mode today (#26591) and otherwise the drawbacks today are lots of linker warnings and some extra indirection (not so bad). In that sense it doesn't seem incredibly urgent to tackle this, but it's certainly a wart! |
Here's some links with useful information: Looks like you can use a module definition file to get the current strategy to at least work in all cases, although it wouldn't remove the unnecessary indirection. In general it seems impossible to get best performance in all situations, unless you either have two sets of binaries for dynamic vs static linking (as microsoft does with the standard library), or always build from source, and use compiler flags to fine-tune its behaviour. Only cargo really exists at a high enough level to be able to do that automatically. Is there any danger of actual runtime unsafety here, eg. the msdn article implies that getting this wrong can result in code which uses the address of the constant getting the address of the import table instead? |
@Diggsey that MSDN blog post is actually different than #26591 I believe, handling dllexport to the best of my knowledge is done 100% correctly now that #27416 has landed. The bug you referenced, #26591, has to do with applying
I'm unaware of any impending danger, but which MSDN article were you referencing here? I'm under the impression that the linker fixes up references to functions automatically, and it apparently also fixes up dllimported statics to not use dllimport if not necessary. |
@alexcrichton When attempting to link to some statics in the CRT directly from Rust, the code would link fine but the generated code was wrong. It would would follow the pointer to the static, but instead of accessing the static it then treated the static as another pointer and would segfault since the static wasn't supposed to be a pointer. I'm not sure whether this was the fault of |
The MS link's behavior to require an object file to be chosen for linking before starting to auto-insert Static library: __declspec(dllexport) int foo = 1;
int* _imp__foo = &foo;
__declspec(dllexport) int bar() { return 2; }
void* _imp__bar = &bar; Main executable: __declspec(dllimport) int foo;
__declspec(dllimport) int bar();
int main()
{
int f = foo;
int b = bar();
printf("%d %d\n", f, b);
return 0;
} If you comment out both |
@vadimcn That doesn't quite solve the issue if I need to link to statics from a library that I don't control, like a system library. |
@retep998, Yeah, this only solves the problem for Rust crate writers. |
Actually, I wonder if we could we use the |
@vadimcn, @retep998 to solve that issue (needed to bootstrap with LLVM) the compiler now has a #[linked_from = "foo"]
extern {
// ...
} This instructs the compiler that all the items in the block specified come from the native library Note that |
@alexcrichton: I am confused about the purpose of |
There's no connection between |
This seems a bit bizarre. |
True, but they're frequently not attached to the functions in question. It's pretty common to have a Many of these could probably be fixed with the advent of |
Here's my attempt at fixing the problem with data dllimports: vadimcn/rust@d5d7ac5 |
@vadimcn isn't that basically just applying |
@alexcrichton I think the idea is that if you |
Hm yeah I can see how that would solve our linkage problems (#26591), but I wouldn't consider it as closing this issue. There'd still be an extra level of indirection in many cases which we otherwise shouldn't have. We also unfortunately don't have the information to determine whether to apply For native libraries it'll certainly require user annotations, but that's what I was hoping to possibly stabilize the |
@alexcrichton: I think As you mention above, the determination of whether to apply For data, there isn't much choice, since marking For code, we can choose between:
|
For native libs, I think we should be able to use information from |
Ah interesting! So the I've toyed around with a few ideas to handle our Thinking this through though in the past I've convinced myself that we'll run into snags. I can't quite recall them at this time, however. In theory though this would enable us to actually properly apply Also yeah, for native libraries we always precisely know how they're being linked (statically, dynamically, framework, etc), so this isn't a problem in their case. We just need to know what symbols come from what library and that's what |
As an extreme solution to the problem of FFI currently has this working mostly thanks to |
On MSVC targets rustc will add symbols prefixed with `_imp_` to LLVM modules to "emulate" dllexported statics as that workaround is still in place after rust-lang#27438 hasn't been solved otherwise. These statics, however, were getting gc'd by ThinLTO accidentally which later would cause linking failures. This commit updates the location we add such symbols to happen just before codegen to ensure that (a) they're not eliminated by the optimizer and (b) the optimizer doesn't even worry about them. Closes rust-lang#45347
rustc: Add `_imp_` symbols later in compilation On MSVC targets rustc will add symbols prefixed with `_imp_` to LLVM modules to "emulate" dllexported statics as that workaround is still in place after #27438 hasn't been solved otherwise. These statics, however, were getting gc'd by ThinLTO accidentally which later would cause linking failures. This commit updates the location we add such symbols to happen just before codegen to ensure that (a) they're not eliminated by the optimizer and (b) the optimizer doesn't even worry about them. Closes #45347
MinGW: enable dllexport/dllimport Fixes (only when using LLD) rust-lang#50176 Fixes rust-lang#72319 This makes `windows-gnu` on pair with `windows-msvc` when it comes to symbol exporting. For MinGW it means both good things like correctly working dllimport/dllexport, ability to link with LLD and bad things like rust-lang#27438. Not sure but maybe this should land behind unstable compiler option (`-Z`) or environment variable?
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
I have been struggling with dllimport for the past week, and the current limitations, this is just meant as an overview so that other people that face this issue may understand the current status. Please correct me if I understood anything wrong.
Using the It is possible to rename the libraries from the build script, but it is hard to do so from dependent crates, as described in rust-lang/cargo#6519. The most viable way is using While it may not be feasible to automatically detect the symbols in which to apply dllimport when not using a
To summarize, the main issue right now is that linking to global variables doesn't work on Windows without using |
Would it be possible for the build script to set an env var with the name of the library and then use |
Is it possible to use
No, in our case |
I believe the specific issue of setting the import library name separately could be adequately solved by allowing a link // Without a `name`, only `kind` is allowed here
// This would be the equivalent of using `__declspec(dllimport)` in Windows C/C++
#[link(kind = "dylib")]
extern "C" {
...
}
// kind=static is redundant here but allowed for consistency
// other kinds are not allowed
#[link(kind = "static")]
extern "C" {
...
} From what I can see, putting a name on an import block does not in any way connect that name to the functions or statics used in the // `empty` is just a lib file with no contents that we add to the search path.
// Even more hackily, we could use a well known lib name like "kernel32" which almost certainly exists.
// Either way this allows the real lib name to be supplied separately.
#[link(name = "empty", kind = "dylib")] // The kind here is redundant
extern "C" {
...
} |
The issue that I can see with this would be how to override the linking type on the build script. If you have a name you can rename the library and change the type later with
That's not the worst workaround, but it relies on having certain libraries installed which may not be the case for all users. |
You can supply an
True, if you need to switch between dylib and static you'd need to use |
Oh actually, if you always override the name then it doesn't have to actually exist. I tried |
That's not a bad workaround actually, it beats linking directly to the |
Currently the compiler makes basically no attempt to correctly use
dllimport
. As a bit of a refresher, the Windows linker requires that if you're importing symbols from a DLL that they're tagged withdllimport
. This helps wire things up correctly at runtime and link-time. To help us out, though, the linker will patch up a few cases wheredllimport
is missing where it would otherwise be required. If a function in another DLL is linked to withoutdllimport
then the linker will inject a local shim which adds a bit of indirection and runtime overhead but allows the crate to link correctly. For importing constants from other DLLs, however, MSVC linker requires that dllimport is annotated correctly. MinGW linkers can sometimes workaround it (see this commit description.If we're targeting windows, then the compiler currently puts
dllimport
on all imported constants from external crates, regardless of whether it's actually being imported from another crate. We rely on the linker fixing up all imports of functions. This ends up meaning that some crates don't link correctly, however (see this comment: #26591 (comment)).We should fix the compiler's handling of dllimport in a few ways:
dllimport
where appropriatedllimport
where appropriatedllimport
if they're not actually being imported from a DLL.I currently have a few thoughts running around in my head for fixing this, but nothing seems plausible enough to push on.
EDIT: Updated as @mati865 requested here.
The text was updated successfully, but these errors were encountered: