RFC 2627: raw-dylib-kind

lang (linkage | windows)

Summary

Extend the #[link] attribute by adding a new kind kind="raw-dylib" for use on Windows which emits idata sections for the items in the attached extern block, so they may be linked against without linking against an import library. Also add a #[link_ordinal] attribute for specifying symbols that are actually ordinals.

Motivation

Traditionally, to link against a dll, the program must actually link against an import library. For example to depend on some symbols from kernel32.dll the program links to kernel32.lib. However, this requires that the correct import libraries be available to link against, and for third party libraries that are only distributed as a dll creating an import library can be quite difficult, especially given that lib.exe is incapable of creating an import library that links to stdcall symbols.

A real advantage of this feature, however, is the fact that symbols will be guaranteed to come from the specified dll. Currently, linking is a very finnicky process where if multiple libraries provide the same symbol the linker will choose one of them to provide the symbol and the user has little control over it. With kind="raw-dylib" the user is ensured that the symbol will come from the specified dll.

Sometimes, a crate may know exactly which dll it wants to link against, but which import library it ends up linking against is unknown. In particular the d3dcompiler.lib provided by the Windows SDK can link to several different versions of the d3dcompiler dll depending on which version of the Windows SDK the user has installed. kind="raw-dylib" would allow winapi to link to a specific version of that dll and ensure the symbols are correct for that version.

This would also allow winapi to not have to bundle import libraries for the pc-windows-gnu targets, saving on bandwidth and disk space for users.

Guide-level explanation

When trying to link to a Windows dll, the dylib kind may sometimes be unsuitable, and kind="raw-dylib" can be used instead. A central requirement of kind="raw-dylib" is that the dll has a stable ABI. Here are some examples of valid reasons to use kind="raw-dylib":

Here is an example of usage:

#[cfg(windows)]
#[link(name = "kernel32.dll", kind = "raw-dylib")]
#[allow(non_snake_case)]
extern "system" {
    fn GetStdHandle(nStdHandle: u32) -> *mut u8;
}

Some symbols are only exported by ordinal from the dll in which case #[link_ordinal(..)] may be used:

#[cfg(windows)]
#[link(name = "ws2_32.dll", kind = "raw-dylib")]
#[allow(non_snake_case)]
extern "system" {
    #[link_ordinal(116)]
    fn WSACleanup() -> i32;
}

Reference-level explanation

Add a new attribute #[link_ordinal] taking a single unsuffixed integer value, such as #[link_ordinal(116)]. It can only be specified on symbols in an extern block using kind="raw-dylib".

Add a new possible value raw-dylib to the kind property of the link attribute. When this kind is specified, the name must explicitly include the extension. In addition, for all items in the associated extern block, Rust will keep the symbol mangled, instead of having an unmangled symbol. Rust will emit an idata section that maps from the mangled symbol to a symbol in the specified dll. The symbol in the dll that the idata section maps to depends on which attributes are specified on the item in question:

The idata section that is produced is equivalent to the idata sections found in import libraries, and should result in identical code generation by the linker.

Drawbacks

Additional complexity in the language through a new kind and a new attribute for specifying ordinals.

Rationale and alternatives

The RFC as proposed would allow for full control over linking to symbols from dlls with syntax as close as possible to existing extern blocks.

No alternatives are currently known other than the status quo.

Prior art

Many non-native languages have the ability to import symbols from dlls, but this uses runtime loading by the language runtime and is not the same as what is being proposed here.

Delphi is a native language that has the ability to import symbols from dlls without import libraries.

Unresolved questions

Whether there are any unresolved questions is an unresolved question.

Future possibilities