Join GitHub today
GitHub is home to over 31 million developers working together to host and review code, manage projects, and build software together.
Sign upCorrectly handle dllimport on Windows/MSVC #27438
Comments
alexcrichton
added
the
O-windows
label
Jul 31, 2015
alexcrichton
added a commit
to alexcrichton/rust
that referenced
this issue
Aug 11, 2015
alexcrichton
referenced this issue
Aug 11, 2015
Merged
trans: Re-enable unwinding on 64-bit MSVC #27676
alexcrichton
added a commit
to alexcrichton/rust
that referenced
this issue
Aug 11, 2015
alexcrichton
added a commit
to alexcrichton/rust
that referenced
this issue
Aug 11, 2015
alexcrichton
added a commit
to alexcrichton/rust
that referenced
this issue
Aug 12, 2015
alexcrichton
added a commit
to alexcrichton/rust
that referenced
this issue
Aug 12, 2015
This comment has been minimized.
This comment has been minimized.
|
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:
|
This comment has been minimized.
This comment has been minimized.
|
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! |
This comment has been minimized.
This comment has been minimized.
|
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? |
This comment has been minimized.
This comment has been minimized.
|
@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. |
This comment has been minimized.
This comment has been minimized.
|
@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 |
This comment has been minimized.
This comment has been minimized.
|
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 |
This comment has been minimized.
This comment has been minimized.
|
@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. |
This comment has been minimized.
This comment has been minimized.
|
@retep998, Yeah, this only solves the problem for Rust crate writers. |
This comment has been minimized.
This comment has been minimized.
|
Actually, I wonder if we could we use the |
This comment has been minimized.
This comment has been minimized.
|
@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 |
This comment has been minimized.
This comment has been minimized.
|
@alexcrichton: I am confused about the purpose of |
This comment has been minimized.
This comment has been minimized.
|
There's no connection between |
This comment has been minimized.
This comment has been minimized.
This seems a bit bizarre. |
This comment has been minimized.
This comment has been minimized.
|
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 |
This comment has been minimized.
This comment has been minimized.
|
Here's my attempt at fixing the problem with data dllimports: vadimcn/rust@d5d7ac5 |
This comment has been minimized.
This comment has been minimized.
|
@vadimcn isn't that basically just applying |
This comment has been minimized.
This comment has been minimized.
|
@alexcrichton I think the idea is that if you |
This comment has been minimized.
This comment has been minimized.
|
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 |
This comment has been minimized.
This comment has been minimized.
|
@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:
|
This comment has been minimized.
This comment has been minimized.
|
For native libs, I think we should be able to use information from |
This comment has been minimized.
This comment has been minimized.
|
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 |
This comment has been minimized.
This comment has been minimized.
|
The msvc solution is generally to treat it as part of the configuration (like debug vs release) rather than the target. That's why MSVC projects often have Release/ReleaseDLL or similar configurations. I vote for not messing with the target triples any more... |
This comment has been minimized.
This comment has been minimized.
|
To really do this right would require that Cargo has knowledge in advance of whether a dependency will be a plugin or not so that it can decide how to build it and its dependencies. |
This comment has been minimized.
This comment has been minimized.
I am indeed! (as I mentioned above as well)
Unfortunately I think the problem still comes up with build scripts. Both plugins and build scripts are always compiled for the host architecture, and build scripts are linked statically while plugins are dynamic, so if a build script and a plugin share a dependency it's the same problem. For normal dependencies, however, yes Cargo will compile it twice.
In theory, yeah, but this is something that'd probably need to be designed from both a Cargo and compiler perspective.
I agree in that this is the only solution I know of here. This is just a fundamental problem with the way Cargo compiles dependencies and how they're linked together. I'm wary of starting to do crazy windows-specific things without reconsidering how it affects other platforms (e.g. making tons of windows-specific or unix-specific bugs), but I don't mean to reject anything.
I agree that this is more of a compilation-option rather than a target-triple option, I would also prefer to not mess with triples. |
This comment has been minimized.
This comment has been minimized.
|
Alright, fair enough. I believe @retep998 meant something similar to treating them as two targets, just not actually messing with the triples. |
This comment has been minimized.
This comment has been minimized.
|
Thinking about the current situation, it is already incredibly hairy as soon as you start mixing in dylibs using Cargo because now every single dylib and binary has to make sure they use Are there any actual real world situations where Cargo is successfully being used with some dependencies being linked in as dylibs? Would it be okay if Cargo made mixing of static and dynamic linking illegal or is there a valid use case that we'd want to preserve? Also I think it is really silly that plugins are treated as normal dependencies, so Cargo has to assume that all dependencies will be linked in even if they actually get used as plugins instead. This really hamper's Cargo's ability to be smart about how to handle such dependencies. Also unfortunate that we can't easily add special support for plugins to |
This comment has been minimized.
This comment has been minimized.
|
I'm ok saying mixing dynamic and static linking is fine to forbid (so long as we have a good reason) because I highly doubt it happens in the wild today. It's quite rare to specifically request a dylib as an intermediate artifact, and otherwise you either get it because you're a plugin. Also, are you familiar with Cargo's support for plugins? Your claims that Cargo assumes plugins are linked and how Cargo can't be smart isn't quite correct. Cargo knows exactly which crates are plugins to know which chains of dependencies should be compiled for the host architecture instead of the target. |
This comment has been minimized.
This comment has been minimized.
|
Oh, there is a |
retep998
referenced this issue
Oct 24, 2015
Closed
Linking fails when only a lib's static variables are used and none of its functions are called. #29267
This comment has been minimized.
This comment has been minimized.
|
Cargo does not currently build dependencies of plugins as dylibs. So Cargo would first have to be modified to build all dependencies of plugins as dylibs. This would result in a shared dependency being built twice, once as an rlib for the binary, and again as a dylib for the plugin. Ideally rustc would be able to do it as a single compilation, only internally duplicating the code generation steps when there's a split between whether to use dllimport or not, but that could potentially be complicated so two separate rustc invocations is the easier way for now. Also Once Cargo has been modified then rustc can be taught to always use dllimport when referring to an upstream dylib and never use dllimport when referring to an upstream rlib. It would also be nice to store metadata on this so that rustc doesn't later try to link against the wrong thing as a sanity check. |
This comment has been minimized.
This comment has been minimized.
KindDragon
commented
Jul 12, 2016
•
|
JFI GCC suggest always use special C++ visibility attributes https://gcc.gnu.org/wiki/Visibility #if defined _WIN32 || defined __CYGWIN__
#ifdef BUILDING_DLL
#ifdef __GNUC__
#define DLL_PUBLIC __attribute__ ((dllexport))
#else
#define DLL_PUBLIC __declspec(dllexport) // Note: actually gcc seems to also supports this syntax.
#endif
#else
#ifdef __GNUC__
#define DLL_PUBLIC __attribute__ ((dllimport))
#else
#define DLL_PUBLIC __declspec(dllimport) // Note: actually gcc seems to also supports this syntax.
#endif
#endif
#define DLL_LOCAL
#else
#if __GNUC__ >= 4
#define DLL_PUBLIC __attribute__ ((visibility ("default")))
#define DLL_LOCAL __attribute__ ((visibility ("hidden")))
#else
#define DLL_PUBLIC
#define DLL_LOCAL
#endif
#endif |
This comment has been minimized.
This comment has been minimized.
FWIW, this is probably not true. Stylo extensively uses static variables to reference string atoms from Gecko side (see this script), so it could potentially be affected. Also, I see tons of warnings like
when linking Gecko with Servo on Windows. |
This comment has been minimized.
This comment has been minimized.
|
@upsuper Those warnings mean that static is being referenced with |
This comment has been minimized.
This comment has been minimized.
|
rust-lang/rfcs#1717 should fix this? (I didn't see it fixed) |
This comment has been minimized.
This comment has been minimized.
|
@Ericson2314 did you mean #31717? Since the issue you mentioned is about changing the precedence of |
This comment has been minimized.
This comment has been minimized.
|
Sorry, meant to link an RFC. Fixed now. |
This comment has been minimized.
This comment has been minimized.
|
@Ericson2314 It will fix the situation for FFI. However it doesn't apply to inter-crate linking as Rust still doesn't know whether a dependency will be linked as an rlib or a dylib until link time, when it needs to know it at code generation time. |
This was referenced Dec 10, 2016
This was referenced Mar 18, 2017
Mark-Simulacrum
added
the
C-bug
label
Jul 22, 2017
alexcrichton
added a commit
to alexcrichton/rust
that referenced
this issue
Oct 18, 2017
alexcrichton
referenced this issue
Oct 18, 2017
Merged
rustc: Add `_imp_` symbols later in compilation #45348
This comment has been minimized.
This comment has been minimized.
|
As an extreme solution to the problem of FFI currently has this working mostly thanks to |
alexcrichton commentedJul 31, 2015
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 wheredllimportis missing where it would otherwise be required. If a function in another DLL is linked to withoutdllimportthen 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, the linker requires thatdllimportis annotated correctly.If we're targeting MSVC then the compiler currently puts
dllimporton 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:
dllimportwhere appropriatedllimportwhere appropriatedllimportif 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.