about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_codegen_cranelift/src/archive.rs9
-rw-r--r--compiler/rustc_codegen_llvm/src/back/archive.rs83
-rw-r--r--compiler/rustc_codegen_llvm/src/llvm/ffi.rs34
-rw-r--r--compiler/rustc_codegen_ssa/src/back/archive.rs9
-rw-r--r--compiler/rustc_codegen_ssa/src/back/link.rs67
-rw-r--r--compiler/rustc_codegen_ssa/src/lib.rs9
-rw-r--r--compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp52
-rw-r--r--compiler/rustc_metadata/src/native_libs.rs72
-rw-r--r--compiler/rustc_middle/src/middle/cstore.rs7
-rw-r--r--src/test/run-make/raw-dylib/Makefile21
-rw-r--r--src/test/run-make/raw-dylib/driver.rs5
-rw-r--r--src/test/run-make/raw-dylib/extern_1.c16
-rw-r--r--src/test/run-make/raw-dylib/extern_2.c6
-rw-r--r--src/test/run-make/raw-dylib/lib.rs22
-rw-r--r--src/test/run-make/raw-dylib/output.txt3
-rw-r--r--src/test/ui/feature-gates/feature-gate-raw-dylib-2.rs (renamed from src/test/ui/rfc-2627-raw-dylib/feature-gate-raw-dylib-2.rs)0
-rw-r--r--src/test/ui/feature-gates/feature-gate-raw-dylib-2.stderr (renamed from src/test/ui/rfc-2627-raw-dylib/feature-gate-raw-dylib-2.stderr)0
-rw-r--r--src/test/ui/feature-gates/feature-gate-raw-dylib-windows-gnu.rs8
-rw-r--r--src/test/ui/feature-gates/feature-gate-raw-dylib-windows-gnu.stderr18
-rw-r--r--src/test/ui/feature-gates/feature-gate-raw-dylib-windows-msvc.rs (renamed from src/test/ui/rfc-2627-raw-dylib/feature-gate-raw-dylib.rs)2
-rw-r--r--src/test/ui/feature-gates/feature-gate-raw-dylib-windows-msvc.stderr (renamed from src/test/ui/rfc-2627-raw-dylib/feature-gate-raw-dylib.stderr)2
-rw-r--r--src/test/ui/manual/manual-link-unsupported-kind.rs5
-rw-r--r--src/test/ui/manual/manual-link-unsupported-kind.stderr2
-rw-r--r--src/test/ui/rfc-2627-raw-dylib/raw-dylib-msvc-only.rs8
-rw-r--r--src/test/ui/rfc-2627-raw-dylib/raw-dylib-msvc-only.stderr17
-rw-r--r--src/test/ui/rfc-2627-raw-dylib/raw-dylib-windows-only.rs7
-rw-r--r--src/test/ui/rfc-2627-raw-dylib/raw-dylib-windows-only.stderr17
27 files changed, 481 insertions, 20 deletions
diff --git a/compiler/rustc_codegen_cranelift/src/archive.rs b/compiler/rustc_codegen_cranelift/src/archive.rs
index bd54adc53ee..22897c43e7e 100644
--- a/compiler/rustc_codegen_cranelift/src/archive.rs
+++ b/compiler/rustc_codegen_cranelift/src/archive.rs
@@ -254,6 +254,15 @@ impl<'a> ArchiveBuilder<'a> for ArArchiveBuilder<'a> {
             }
         }
     }
+
+    fn inject_dll_import_lib(
+        &mut self,
+        _lib_name: &str,
+        _dll_imports: &[rustc_middle::middle::cstore::DllImport],
+        _tmpdir: &rustc_data_structures::temp_dir::MaybeTempDir,
+    ) {
+        bug!("injecting dll imports is not supported");
+    }
 }
 
 impl<'a> ArArchiveBuilder<'a> {
diff --git a/compiler/rustc_codegen_llvm/src/back/archive.rs b/compiler/rustc_codegen_llvm/src/back/archive.rs
index 261affe2c42..64416bced31 100644
--- a/compiler/rustc_codegen_llvm/src/back/archive.rs
+++ b/compiler/rustc_codegen_llvm/src/back/archive.rs
@@ -8,9 +8,11 @@ use std::ptr;
 use std::str;
 
 use crate::llvm::archive_ro::{ArchiveRO, Child};
-use crate::llvm::{self, ArchiveKind};
+use crate::llvm::{self, ArchiveKind, LLVMMachineType, LLVMRustCOFFShortExport};
 use rustc_codegen_ssa::back::archive::{find_library, ArchiveBuilder};
 use rustc_codegen_ssa::{looks_like_rust_object_file, METADATA_FILENAME};
+use rustc_data_structures::temp_dir::MaybeTempDir;
+use rustc_middle::middle::cstore::DllImport;
 use rustc_session::Session;
 use rustc_span::symbol::Symbol;
 
@@ -61,6 +63,17 @@ fn archive_config<'a>(sess: &'a Session, output: &Path, input: Option<&Path>) ->
     }
 }
 
+/// Map machine type strings to values of LLVM's MachineTypes enum.
+fn llvm_machine_type(cpu: &str) -> LLVMMachineType {
+    match cpu {
+        "x86_64" => LLVMMachineType::AMD64,
+        "x86" => LLVMMachineType::I386,
+        "aarch64" => LLVMMachineType::ARM64,
+        "arm" => LLVMMachineType::ARM,
+        _ => panic!("unsupported cpu type {}", cpu),
+    }
+}
+
 impl<'a> ArchiveBuilder<'a> for LlvmArchiveBuilder<'a> {
     /// Creates a new static archive, ready for modifying the archive specified
     /// by `config`.
@@ -175,6 +188,74 @@ impl<'a> ArchiveBuilder<'a> for LlvmArchiveBuilder<'a> {
             self.config.sess.fatal(&format!("failed to build archive: {}", e));
         }
     }
+
+    fn inject_dll_import_lib(
+        &mut self,
+        lib_name: &str,
+        dll_imports: &[DllImport],
+        tmpdir: &MaybeTempDir,
+    ) {
+        let output_path = {
+            let mut output_path: PathBuf = tmpdir.as_ref().to_path_buf();
+            output_path.push(format!("{}_imports", lib_name));
+            output_path.with_extension("lib")
+        };
+
+        // we've checked for \0 characters in the library name already
+        let dll_name_z = CString::new(lib_name).unwrap();
+        // All import names are Rust identifiers and therefore cannot contain \0 characters.
+        // FIXME: when support for #[link_name] implemented, ensure that import.name values don't
+        // have any \0 characters
+        let import_name_vector: Vec<CString> = dll_imports
+            .iter()
+            .map(if self.config.sess.target.arch == "x86" {
+                |import: &DllImport| CString::new(format!("_{}", import.name.to_string())).unwrap()
+            } else {
+                |import: &DllImport| CString::new(import.name.to_string()).unwrap()
+            })
+            .collect();
+
+        let output_path_z = rustc_fs_util::path_to_c_string(&output_path);
+
+        tracing::trace!("invoking LLVMRustWriteImportLibrary");
+        tracing::trace!("  dll_name {:#?}", dll_name_z);
+        tracing::trace!("  output_path {}", output_path.display());
+        tracing::trace!(
+            "  import names: {}",
+            dll_imports.iter().map(|import| import.name.to_string()).collect::<Vec<_>>().join(", "),
+        );
+
+        let ffi_exports: Vec<LLVMRustCOFFShortExport> = import_name_vector
+            .iter()
+            .map(|name_z| LLVMRustCOFFShortExport::from_name(name_z.as_ptr()))
+            .collect();
+        let result = unsafe {
+            crate::llvm::LLVMRustWriteImportLibrary(
+                dll_name_z.as_ptr(),
+                output_path_z.as_ptr(),
+                ffi_exports.as_ptr(),
+                ffi_exports.len(),
+                llvm_machine_type(&self.config.sess.target.arch) as u16,
+                !self.config.sess.target.is_like_msvc,
+            )
+        };
+
+        if result == crate::llvm::LLVMRustResult::Failure {
+            self.config.sess.fatal(&format!(
+                "Error creating import library for {}: {}",
+                lib_name,
+                llvm::last_error().unwrap_or("unknown LLVM error".to_string())
+            ));
+        }
+
+        self.add_archive(&output_path, |_| false).unwrap_or_else(|e| {
+            self.config.sess.fatal(&format!(
+                "failed to add native library {}: {}",
+                output_path.display(),
+                e
+            ));
+        });
+    }
 }
 
 impl<'a> LlvmArchiveBuilder<'a> {
diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
index 115aa74dcc4..91923251018 100644
--- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
+++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
@@ -29,6 +29,31 @@ pub enum LLVMRustResult {
     Success,
     Failure,
 }
+
+// Rust version of the C struct with the same name in rustc_llvm/llvm-wrapper/RustWrapper.cpp.
+#[repr(C)]
+pub struct LLVMRustCOFFShortExport {
+    pub name: *const c_char,
+}
+
+impl LLVMRustCOFFShortExport {
+    pub fn from_name(name: *const c_char) -> LLVMRustCOFFShortExport {
+        LLVMRustCOFFShortExport { name }
+    }
+}
+
+/// Translation of LLVM's MachineTypes enum, defined in llvm\include\llvm\BinaryFormat\COFF.h.
+///
+/// We include only architectures supported on Windows.
+#[derive(Copy, Clone, PartialEq)]
+#[repr(C)]
+pub enum LLVMMachineType {
+    AMD64 = 0x8664,
+    I386 = 0x14c,
+    ARM64 = 0xaa64,
+    ARM = 0x01c0,
+}
+
 // Consts for the LLVM CallConv type, pre-cast to usize.
 
 /// LLVM CallingConv::ID. Should we wrap this?
@@ -2265,6 +2290,15 @@ extern "C" {
     ) -> &'a mut RustArchiveMember<'a>;
     pub fn LLVMRustArchiveMemberFree(Member: &'a mut RustArchiveMember<'a>);
 
+    pub fn LLVMRustWriteImportLibrary(
+        ImportName: *const c_char,
+        Path: *const c_char,
+        Exports: *const LLVMRustCOFFShortExport,
+        NumExports: usize,
+        Machine: u16,
+        MinGW: bool,
+    ) -> LLVMRustResult;
+
     pub fn LLVMRustSetDataLayoutFromTargetMachine(M: &'a Module, TM: &'a TargetMachine);
 
     pub fn LLVMRustBuildOperandBundleDef(
diff --git a/compiler/rustc_codegen_ssa/src/back/archive.rs b/compiler/rustc_codegen_ssa/src/back/archive.rs
index c197d48d4ea..63f457bb979 100644
--- a/compiler/rustc_codegen_ssa/src/back/archive.rs
+++ b/compiler/rustc_codegen_ssa/src/back/archive.rs
@@ -1,3 +1,5 @@
+use rustc_data_structures::temp_dir::MaybeTempDir;
+use rustc_middle::middle::cstore::DllImport;
 use rustc_session::Session;
 use rustc_span::symbol::Symbol;
 
@@ -57,4 +59,11 @@ pub trait ArchiveBuilder<'a> {
     fn update_symbols(&mut self);
 
     fn build(self);
+
+    fn inject_dll_import_lib(
+        &mut self,
+        lib_name: &str,
+        dll_imports: &[DllImport],
+        tmpdir: &MaybeTempDir,
+    );
 }
diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs
index 87eb51342d4..aeaf435e4c0 100644
--- a/compiler/rustc_codegen_ssa/src/back/link.rs
+++ b/compiler/rustc_codegen_ssa/src/back/link.rs
@@ -1,9 +1,9 @@
-use rustc_data_structures::fx::FxHashSet;
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_data_structures::temp_dir::MaybeTempDir;
 use rustc_errors::Handler;
 use rustc_fs_util::fix_windows_verbatim_for_gcc;
 use rustc_hir::def_id::CrateNum;
-use rustc_middle::middle::cstore::LibSource;
+use rustc_middle::middle::cstore::{DllImport, LibSource};
 use rustc_middle::middle::dependency_format::Linkage;
 use rustc_session::config::{self, CFGuard, CrateType, DebugInfo};
 use rustc_session::config::{OutputFilenames, OutputType, PrintRequest};
@@ -34,6 +34,7 @@ use object::write::Object;
 use object::{Architecture, BinaryFormat, Endianness, FileFlags, SectionFlags, SectionKind};
 use tempfile::Builder as TempFileBuilder;
 
+use std::cmp::Ordering;
 use std::ffi::OsString;
 use std::path::{Path, PathBuf};
 use std::process::{ExitStatus, Output, Stdio};
@@ -343,6 +344,12 @@ fn link_rlib<'a, B: ArchiveBuilder<'a>>(
         }
     }
 
+    for (raw_dylib_name, raw_dylib_imports) in
+        collate_raw_dylibs(&codegen_results.crate_info.used_libraries)
+    {
+        ab.inject_dll_import_lib(&raw_dylib_name, &raw_dylib_imports, tmpdir);
+    }
+
     // After adding all files to the archive, we need to update the
     // symbol table of the archive.
     ab.update_symbols();
@@ -524,6 +531,57 @@ fn link_rlib<'a, B: ArchiveBuilder<'a>>(
     }
 }
 
+/// Extract all symbols defined in raw-dylib libraries, collated by library name.
+///
+/// If we have multiple extern blocks that specify symbols defined in the same raw-dylib library,
+/// then the CodegenResults value contains one NativeLib instance for each block.  However, the
+/// linker appears to expect only a single import library for each library used, so we need to
+/// collate the symbols together by library name before generating the import libraries.
+fn collate_raw_dylibs(used_libraries: &[NativeLib]) -> Vec<(String, Vec<DllImport>)> {
+    let mut dylib_table: FxHashMap<String, FxHashSet<Symbol>> = FxHashMap::default();
+
+    for lib in used_libraries {
+        if lib.kind == NativeLibKind::RawDylib {
+            let name = lib.name.unwrap_or_else(||
+                bug!("`link` attribute with kind = \"raw-dylib\" and no name should have caused error earlier")
+            );
+            let name = if matches!(lib.verbatim, Some(true)) {
+                name.to_string()
+            } else {
+                format!("{}.dll", name)
+            };
+            dylib_table
+                .entry(name)
+                .or_default()
+                .extend(lib.dll_imports.iter().map(|import| import.name));
+        }
+    }
+
+    // FIXME: when we add support for ordinals, fix this to propagate ordinals.  Also figure out
+    // what we should do if we have two DllImport values with the same name but different
+    // ordinals.
+    let mut result = dylib_table
+        .into_iter()
+        .map(|(lib_name, imported_names)| {
+            let mut names = imported_names
+                .iter()
+                .map(|name| DllImport { name: *name, ordinal: None })
+                .collect::<Vec<_>>();
+            names.sort_unstable_by(|a: &DllImport, b: &DllImport| {
+                match a.name.as_str().cmp(&b.name.as_str()) {
+                    Ordering::Equal => a.ordinal.cmp(&b.ordinal),
+                    x => x,
+                }
+            });
+            (lib_name, names)
+        })
+        .collect::<Vec<_>>();
+    result.sort_unstable_by(|a: &(String, Vec<DllImport>), b: &(String, Vec<DllImport>)| {
+        a.0.cmp(&b.0)
+    });
+    result
+}
+
 /// Create a static archive.
 ///
 /// This is essentially the same thing as an rlib, but it also involves adding all of the upstream
@@ -2302,10 +2360,7 @@ fn add_upstream_native_libraries(
                 // already included them when we included the rust library
                 // previously
                 NativeLibKind::Static { bundle: None | Some(true), .. } => {}
-                NativeLibKind::RawDylib => {
-                    // FIXME(#58713): Proper handling for raw dylibs.
-                    bug!("raw_dylib feature not yet implemented");
-                }
+                NativeLibKind::RawDylib => {}
             }
         }
     }
diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs
index 12da3d9e155..48171ccd2fd 100644
--- a/compiler/rustc_codegen_ssa/src/lib.rs
+++ b/compiler/rustc_codegen_ssa/src/lib.rs
@@ -110,11 +110,18 @@ pub struct NativeLib {
     pub name: Option<Symbol>,
     pub cfg: Option<ast::MetaItem>,
     pub verbatim: Option<bool>,
+    pub dll_imports: Vec<cstore::DllImport>,
 }
 
 impl From<&cstore::NativeLib> for NativeLib {
     fn from(lib: &cstore::NativeLib) -> Self {
-        NativeLib { kind: lib.kind, name: lib.name, cfg: lib.cfg.clone(), verbatim: lib.verbatim }
+        NativeLib {
+            kind: lib.kind,
+            name: lib.name,
+            cfg: lib.cfg.clone(),
+            verbatim: lib.verbatim,
+            dll_imports: lib.dll_imports.clone(),
+        }
     }
 }
 
diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
index 548b3471ba7..9b757eb40c1 100644
--- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
+++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
@@ -6,6 +6,7 @@
 #include "llvm/IR/Instructions.h"
 #include "llvm/IR/Intrinsics.h"
 #include "llvm/Object/Archive.h"
+#include "llvm/Object/COFFImportFile.h"
 #include "llvm/Object/ObjectFile.h"
 #include "llvm/Bitcode/BitcodeWriterPass.h"
 #include "llvm/Support/Signals.h"
@@ -1722,3 +1723,54 @@ extern "C" LLVMValueRef
 LLVMRustBuildMaxNum(LLVMBuilderRef B, LLVMValueRef LHS, LLVMValueRef RHS) {
     return wrap(unwrap(B)->CreateMaxNum(unwrap(LHS),unwrap(RHS)));
 }
+
+// This struct contains all necessary info about a symbol exported from a DLL.
+// At the moment, it's just the symbol's name, but we use a separate struct to
+// make it easier to add other information like ordinal later.
+struct LLVMRustCOFFShortExport {
+  const char* name;
+};
+
+// Machine must be a COFF machine type, as defined in PE specs.
+extern "C" LLVMRustResult LLVMRustWriteImportLibrary(
+  const char* ImportName,
+  const char* Path,
+  const LLVMRustCOFFShortExport* Exports,
+  size_t NumExports,
+  uint16_t Machine,
+  bool MinGW)
+{
+  std::vector<llvm::object::COFFShortExport> ConvertedExports;
+  ConvertedExports.reserve(NumExports);
+
+  for (size_t i = 0; i < NumExports; ++i) {
+    ConvertedExports.push_back(llvm::object::COFFShortExport{
+      Exports[i].name,  // Name
+      std::string{},    // ExtName
+      std::string{},    // SymbolName
+      std::string{},    // AliasTarget
+      0,                // Ordinal
+      false,            // Noname
+      false,            // Data
+      false,            // Private
+      false             // Constant
+    });
+  }
+
+  auto Error = llvm::object::writeImportLibrary(
+    ImportName,
+    Path,
+    ConvertedExports,
+    static_cast<llvm::COFF::MachineTypes>(Machine),
+    MinGW);
+  if (Error) {
+    std::string errorString;
+    llvm::raw_string_ostream stream(errorString);
+    stream << Error;
+    stream.flush();
+    LLVMRustSetLastError(errorString.c_str());
+    return LLVMRustResult::Failure;
+  } else {
+    return LLVMRustResult::Success;
+  }
+}
diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs
index bc342119efb..cd4c394ae14 100644
--- a/compiler/rustc_metadata/src/native_libs.rs
+++ b/compiler/rustc_metadata/src/native_libs.rs
@@ -3,7 +3,7 @@ use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::struct_span_err;
 use rustc_hir as hir;
 use rustc_hir::itemlikevisit::ItemLikeVisitor;
-use rustc_middle::middle::cstore::NativeLib;
+use rustc_middle::middle::cstore::{DllImport, NativeLib};
 use rustc_middle::ty::TyCtxt;
 use rustc_session::parse::feature_err;
 use rustc_session::utils::NativeLibKind;
@@ -33,8 +33,8 @@ struct Collector<'tcx> {
 
 impl ItemLikeVisitor<'tcx> for Collector<'tcx> {
     fn visit_item(&mut self, it: &'tcx hir::Item<'tcx>) {
-        let abi = match it.kind {
-            hir::ItemKind::ForeignMod { abi, .. } => abi,
+        let (abi, foreign_mod_items) = match it.kind {
+            hir::ItemKind::ForeignMod { abi, items } => (abi, items),
             _ => return,
         };
 
@@ -57,6 +57,7 @@ impl ItemLikeVisitor<'tcx> for Collector<'tcx> {
                 foreign_module: Some(it.def_id.to_def_id()),
                 wasm_import_module: None,
                 verbatim: None,
+                dll_imports: Vec::new(),
             };
             let mut kind_specified = false;
 
@@ -196,6 +197,27 @@ impl ItemLikeVisitor<'tcx> for Collector<'tcx> {
                 .span_label(m.span, "missing `name` argument")
                 .emit();
             }
+
+            if lib.kind == NativeLibKind::RawDylib {
+                match abi {
+                    Abi::C { .. } => (),
+                    Abi::Cdecl => (),
+                    _ => {
+                        if sess.target.arch == "x86" {
+                            sess.span_fatal(
+                                it.span,
+                                r#"`#[link(kind = "raw-dylib")]` only supports C and Cdecl ABIs"#,
+                            );
+                        }
+                    }
+                };
+                lib.dll_imports.extend(
+                    foreign_mod_items
+                        .iter()
+                        .map(|child_item| DllImport { name: child_item.ident.name, ordinal: None }),
+                );
+            }
+
             self.register_native_lib(Some(m.span), lib);
         }
     }
@@ -253,15 +275,42 @@ impl Collector<'tcx> {
             )
             .emit();
         }
-        if lib.kind == NativeLibKind::RawDylib && !self.tcx.features().raw_dylib {
-            feature_err(
-                &self.tcx.sess.parse_sess,
-                sym::raw_dylib,
-                span.unwrap_or(rustc_span::DUMMY_SP),
-                "kind=\"raw-dylib\" is unstable",
-            )
-            .emit();
+        // this just unwraps lib.name; we already established that it isn't empty above.
+        if let (NativeLibKind::RawDylib, Some(lib_name)) = (lib.kind, lib.name) {
+            let span = match span {
+                Some(s) => s,
+                None => {
+                    bug!("raw-dylib libraries are not supported on the command line");
+                }
+            };
+
+            if !self.tcx.sess.target.options.is_like_windows {
+                self.tcx.sess.span_fatal(
+                    span,
+                    "`#[link(...)]` with `kind = \"raw-dylib\"` only supported on Windows",
+                );
+            } else if !self.tcx.sess.target.options.is_like_msvc {
+                self.tcx.sess.span_warn(
+                    span,
+                    "`#[link(...)]` with `kind = \"raw-dylib\"` not supported on windows-gnu",
+                );
+            }
+
+            if lib_name.as_str().contains('\0') {
+                self.tcx.sess.span_err(span, "library name may not contain NUL characters");
+            }
+
+            if !self.tcx.features().raw_dylib {
+                feature_err(
+                    &self.tcx.sess.parse_sess,
+                    sym::raw_dylib,
+                    span,
+                    "kind=\"raw-dylib\" is unstable",
+                )
+                .emit();
+            }
         }
+
         self.libs.push(lib);
     }
 
@@ -337,6 +386,7 @@ impl Collector<'tcx> {
                     foreign_module: None,
                     wasm_import_module: None,
                     verbatim: passed_lib.verbatim,
+                    dll_imports: Vec::new(),
                 };
                 self.register_native_lib(None, lib);
             } else {
diff --git a/compiler/rustc_middle/src/middle/cstore.rs b/compiler/rustc_middle/src/middle/cstore.rs
index dd3b0a40d69..df578708931 100644
--- a/compiler/rustc_middle/src/middle/cstore.rs
+++ b/compiler/rustc_middle/src/middle/cstore.rs
@@ -95,6 +95,13 @@ pub struct NativeLib {
     pub foreign_module: Option<DefId>,
     pub wasm_import_module: Option<Symbol>,
     pub verbatim: Option<bool>,
+    pub dll_imports: Vec<DllImport>,
+}
+
+#[derive(Clone, Debug, Encodable, Decodable, HashStable)]
+pub struct DllImport {
+    pub name: Symbol,
+    pub ordinal: Option<u16>,
 }
 
 #[derive(Clone, TyEncodable, TyDecodable, HashStable, Debug)]
diff --git a/src/test/run-make/raw-dylib/Makefile b/src/test/run-make/raw-dylib/Makefile
new file mode 100644
index 00000000000..7ce46fd9331
--- /dev/null
+++ b/src/test/run-make/raw-dylib/Makefile
@@ -0,0 +1,21 @@
+# Test the behavior of #[link(.., kind = "raw-dylib")] on windows-msvc
+
+# only-windows
+# only-msvc
+
+-include ../../run-make-fulldeps/tools.mk
+
+all:
+	$(call COMPILE_OBJ,"$(TMPDIR)"/extern_1.obj,extern_1.c)
+	$(call COMPILE_OBJ,"$(TMPDIR)"/extern_2.obj,extern_2.c)
+	$(CC) "$(TMPDIR)"/extern_1.obj -link -dll -out:"$(TMPDIR)"/extern_1.dll
+	$(CC) "$(TMPDIR)"/extern_2.obj -link -dll -out:"$(TMPDIR)"/extern_2.dll
+	$(RUSTC) --crate-type lib --crate-name raw_dylib_test lib.rs
+	$(RUSTC) --crate-type bin driver.rs -L "$(TMPDIR)"
+	"$(TMPDIR)"/driver > "$(TMPDIR)"/output.txt
+
+ifdef RUSTC_BLESS_TEST
+	cp "$(TMPDIR)"/output.txt output.txt
+else
+	$(DIFF) output.txt "$(TMPDIR)"/output.txt
+endif
diff --git a/src/test/run-make/raw-dylib/driver.rs b/src/test/run-make/raw-dylib/driver.rs
new file mode 100644
index 00000000000..4059ede11fc
--- /dev/null
+++ b/src/test/run-make/raw-dylib/driver.rs
@@ -0,0 +1,5 @@
+extern crate raw_dylib_test;
+
+fn main() {
+    raw_dylib_test::library_function();
+}
diff --git a/src/test/run-make/raw-dylib/extern_1.c b/src/test/run-make/raw-dylib/extern_1.c
new file mode 100644
index 00000000000..72737c086eb
--- /dev/null
+++ b/src/test/run-make/raw-dylib/extern_1.c
@@ -0,0 +1,16 @@
+#include <stdio.h>
+
+__declspec(dllexport) void extern_fn_1() {
+    printf("extern_fn_1\n");
+    fflush(stdout);
+}
+
+__declspec(dllexport) void extern_fn_2() {
+    printf("extern_fn_2; didn't get the rename\n");
+    fflush(stdout);
+}
+
+__declspec(dllexport) void extern_fn_with_long_name() {
+    printf("extern_fn_with_long_name; got the rename\n");
+    fflush(stdout);
+}
diff --git a/src/test/run-make/raw-dylib/extern_2.c b/src/test/run-make/raw-dylib/extern_2.c
new file mode 100644
index 00000000000..ae87fc3f821
--- /dev/null
+++ b/src/test/run-make/raw-dylib/extern_2.c
@@ -0,0 +1,6 @@
+#include <stdio.h>
+
+__declspec(dllexport) void extern_fn_3() {
+    printf("extern_fn_3\n");
+    fflush(stdout);
+}
diff --git a/src/test/run-make/raw-dylib/lib.rs b/src/test/run-make/raw-dylib/lib.rs
new file mode 100644
index 00000000000..d8e6301f38e
--- /dev/null
+++ b/src/test/run-make/raw-dylib/lib.rs
@@ -0,0 +1,22 @@
+#![feature(raw_dylib, native_link_modifiers, native_link_modifiers_verbatim)]
+
+#[link(name = "extern_1.dll", kind = "raw-dylib", modifiers = "+verbatim")]
+extern {
+    fn extern_fn_1();
+}
+
+#[link(name = "extern_2", kind = "raw-dylib")]
+extern {
+    fn extern_fn_3();
+}
+
+pub fn library_function() {
+    #[link(name = "extern_1", kind = "raw-dylib")]
+    extern { fn extern_fn_2(); }
+
+    unsafe {
+        extern_fn_1();
+        extern_fn_2();
+        extern_fn_3();
+    }
+}
diff --git a/src/test/run-make/raw-dylib/output.txt b/src/test/run-make/raw-dylib/output.txt
new file mode 100644
index 00000000000..7800cba1872
--- /dev/null
+++ b/src/test/run-make/raw-dylib/output.txt
@@ -0,0 +1,3 @@
+extern_fn_1
+extern_fn_2; didn't get the rename
+extern_fn_3
diff --git a/src/test/ui/rfc-2627-raw-dylib/feature-gate-raw-dylib-2.rs b/src/test/ui/feature-gates/feature-gate-raw-dylib-2.rs
index 518aa20dd68..518aa20dd68 100644
--- a/src/test/ui/rfc-2627-raw-dylib/feature-gate-raw-dylib-2.rs
+++ b/src/test/ui/feature-gates/feature-gate-raw-dylib-2.rs
diff --git a/src/test/ui/rfc-2627-raw-dylib/feature-gate-raw-dylib-2.stderr b/src/test/ui/feature-gates/feature-gate-raw-dylib-2.stderr
index dbee5f316b0..dbee5f316b0 100644
--- a/src/test/ui/rfc-2627-raw-dylib/feature-gate-raw-dylib-2.stderr
+++ b/src/test/ui/feature-gates/feature-gate-raw-dylib-2.stderr
diff --git a/src/test/ui/feature-gates/feature-gate-raw-dylib-windows-gnu.rs b/src/test/ui/feature-gates/feature-gate-raw-dylib-windows-gnu.rs
new file mode 100644
index 00000000000..33f9c539313
--- /dev/null
+++ b/src/test/ui/feature-gates/feature-gate-raw-dylib-windows-gnu.rs
@@ -0,0 +1,8 @@
+// gate-test-raw_dylib
+// only-windows-gnu
+#[link(name = "foo", kind = "raw-dylib")]
+//~^ ERROR: kind="raw-dylib" is unstable
+//~| WARNING: `#[link(...)]` with `kind = "raw-dylib"` not supported on windows-gnu
+extern "C" {}
+
+fn main() {}
diff --git a/src/test/ui/feature-gates/feature-gate-raw-dylib-windows-gnu.stderr b/src/test/ui/feature-gates/feature-gate-raw-dylib-windows-gnu.stderr
new file mode 100644
index 00000000000..14dfadf4126
--- /dev/null
+++ b/src/test/ui/feature-gates/feature-gate-raw-dylib-windows-gnu.stderr
@@ -0,0 +1,18 @@
+warning: `#[link(...)]` with `kind = "raw-dylib"` not supported on windows-gnu
+  --> $DIR/feature-gate-raw-dylib-windows-gnu.rs:3:1
+   |
+LL | #[link(name = "foo", kind = "raw-dylib")]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0658]: kind="raw-dylib" is unstable
+  --> $DIR/feature-gate-raw-dylib-windows-gnu.rs:3:1
+   |
+LL | #[link(name = "foo", kind = "raw-dylib")]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #58713 <https://github.com/rust-lang/rust/issues/58713> for more information
+   = help: add `#![feature(raw_dylib)]` to the crate attributes to enable
+
+error: aborting due to previous error; 1 warning emitted
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/rfc-2627-raw-dylib/feature-gate-raw-dylib.rs b/src/test/ui/feature-gates/feature-gate-raw-dylib-windows-msvc.rs
index 29edd0f9ef9..49de24ea9ab 100644
--- a/src/test/ui/rfc-2627-raw-dylib/feature-gate-raw-dylib.rs
+++ b/src/test/ui/feature-gates/feature-gate-raw-dylib-windows-msvc.rs
@@ -1,3 +1,5 @@
+// gate-test-raw_dylib
+// only-windows-msvc
 #[link(name = "foo", kind = "raw-dylib")]
 //~^ ERROR: kind="raw-dylib" is unstable
 extern "C" {}
diff --git a/src/test/ui/rfc-2627-raw-dylib/feature-gate-raw-dylib.stderr b/src/test/ui/feature-gates/feature-gate-raw-dylib-windows-msvc.stderr
index a670b6c6c2a..11988080812 100644
--- a/src/test/ui/rfc-2627-raw-dylib/feature-gate-raw-dylib.stderr
+++ b/src/test/ui/feature-gates/feature-gate-raw-dylib-windows-msvc.stderr
@@ -1,5 +1,5 @@
 error[E0658]: kind="raw-dylib" is unstable
-  --> $DIR/feature-gate-raw-dylib.rs:1:1
+  --> $DIR/feature-gate-raw-dylib-windows-msvc.rs:3:1
    |
 LL | #[link(name = "foo", kind = "raw-dylib")]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/test/ui/manual/manual-link-unsupported-kind.rs b/src/test/ui/manual/manual-link-unsupported-kind.rs
new file mode 100644
index 00000000000..34814db593f
--- /dev/null
+++ b/src/test/ui/manual/manual-link-unsupported-kind.rs
@@ -0,0 +1,5 @@
+// compile-flags:-l raw-dylib=foo
+// error-pattern: unknown library kind `raw-dylib`, expected one of dylib, framework, or static
+
+fn main() {
+}
diff --git a/src/test/ui/manual/manual-link-unsupported-kind.stderr b/src/test/ui/manual/manual-link-unsupported-kind.stderr
new file mode 100644
index 00000000000..acb4463cb04
--- /dev/null
+++ b/src/test/ui/manual/manual-link-unsupported-kind.stderr
@@ -0,0 +1,2 @@
+error: unknown library kind `raw-dylib`, expected one of dylib, framework, or static
+
diff --git a/src/test/ui/rfc-2627-raw-dylib/raw-dylib-msvc-only.rs b/src/test/ui/rfc-2627-raw-dylib/raw-dylib-msvc-only.rs
new file mode 100644
index 00000000000..e9690f03f45
--- /dev/null
+++ b/src/test/ui/rfc-2627-raw-dylib/raw-dylib-msvc-only.rs
@@ -0,0 +1,8 @@
+// only-windows-gnu
+// check-pass
+// compile-flags: --crate-type lib
+#![feature(raw_dylib)]
+//~^ WARNING: the feature `raw_dylib` is incomplete
+#[link(name = "foo", kind = "raw-dylib")]
+//~^ WARNING: `#[link(...)]` with `kind = "raw-dylib"` not supported on windows-gnu
+extern "C" {}
diff --git a/src/test/ui/rfc-2627-raw-dylib/raw-dylib-msvc-only.stderr b/src/test/ui/rfc-2627-raw-dylib/raw-dylib-msvc-only.stderr
new file mode 100644
index 00000000000..6e24112b3c3
--- /dev/null
+++ b/src/test/ui/rfc-2627-raw-dylib/raw-dylib-msvc-only.stderr
@@ -0,0 +1,17 @@
+warning: the feature `raw_dylib` is incomplete and may not be safe to use and/or cause compiler crashes
+  --> $DIR/raw-dylib-msvc-only.rs:4:12
+   |
+LL | #![feature(raw_dylib)]
+   |            ^^^^^^^^^
+   |
+   = note: `#[warn(incomplete_features)]` on by default
+   = note: see issue #58713 <https://github.com/rust-lang/rust/issues/58713> for more information
+
+warning: `#[link(...)]` with `kind = "raw-dylib"` not supported on windows-gnu
+  --> $DIR/raw-dylib-msvc-only.rs:6:1
+   |
+LL | #[link(name = "foo", kind = "raw-dylib")]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+warning: 2 warnings emitted
+
diff --git a/src/test/ui/rfc-2627-raw-dylib/raw-dylib-windows-only.rs b/src/test/ui/rfc-2627-raw-dylib/raw-dylib-windows-only.rs
new file mode 100644
index 00000000000..7a5d7ac2934
--- /dev/null
+++ b/src/test/ui/rfc-2627-raw-dylib/raw-dylib-windows-only.rs
@@ -0,0 +1,7 @@
+// ignore-windows
+// compile-flags: --crate-type lib
+#![feature(raw_dylib)]
+//~^ WARNING: the feature `raw_dylib` is incomplete
+#[link(name = "foo", kind = "raw-dylib")]
+//~^ ERROR: `#[link(...)]` with `kind = "raw-dylib"` only supported on Windows
+extern "C" {}
diff --git a/src/test/ui/rfc-2627-raw-dylib/raw-dylib-windows-only.stderr b/src/test/ui/rfc-2627-raw-dylib/raw-dylib-windows-only.stderr
new file mode 100644
index 00000000000..f3879b63f91
--- /dev/null
+++ b/src/test/ui/rfc-2627-raw-dylib/raw-dylib-windows-only.stderr
@@ -0,0 +1,17 @@
+warning: the feature `raw_dylib` is incomplete and may not be safe to use and/or cause compiler crashes
+  --> $DIR/raw-dylib-windows-only.rs:3:12
+   |
+LL | #![feature(raw_dylib)]
+   |            ^^^^^^^^^
+   |
+   = note: `#[warn(incomplete_features)]` on by default
+   = note: see issue #58713 <https://github.com/rust-lang/rust/issues/58713> for more information
+
+error: `#[link(...)]` with `kind = "raw-dylib"` only supported on Windows
+  --> $DIR/raw-dylib-windows-only.rs:5:1
+   |
+LL | #[link(name = "foo", kind = "raw-dylib")]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error; 1 warning emitted
+