From 9a580168b5c824831c5c6683fae568c9b88ea72b Mon Sep 17 00:00:00 2001 From: Diggory Blake Date: Wed, 8 Feb 2017 15:31:57 +0000 Subject: [PATCH] Put metadata in import library when building a dylib with MSVC Remove metadata objects from rlib import libraries Fix tidy issues Don't try to remove metadata before linking Undo change to dylib linking --- src/jemalloc | 2 +- src/librustc_metadata/locator.rs | 38 ++++++++++-------- src/librustc_trans/back/link.rs | 49 +++++++++++++++++++++++- src/librustc_trans/back/linker.rs | 2 +- src/librustc_trans/back/symbol_export.rs | 8 +++- 5 files changed, 79 insertions(+), 20 deletions(-) diff --git a/src/jemalloc b/src/jemalloc index 11bfb0dcf85f7..e058ca661692a 160000 --- a/src/jemalloc +++ b/src/jemalloc @@ -1 +1 @@ -Subproject commit 11bfb0dcf85f7aa92abd30524bb1e42e18d108c6 +Subproject commit e058ca661692a8d01f8cf9d35939dfe3105ce968 diff --git a/src/librustc_metadata/locator.rs b/src/librustc_metadata/locator.rs index de465ea92f6b8..4e770d79b698b 100644 --- a/src/librustc_metadata/locator.rs +++ b/src/librustc_metadata/locator.rs @@ -862,33 +862,41 @@ fn get_metadata_section_imp(target: &Target, if !filename.exists() { return Err(format!("no such file: '{}'", filename.display())); } - if flavor == CrateFlavor::Rlib { + if flavor == CrateFlavor::Rmeta { + let mut file = File::open(filename).map_err(|_| + format!("could not open file: '{}'", filename.display()))?; + let mut buf = vec![]; + file.read_to_end(&mut buf).map_err(|_| + format!("failed to read rlib metadata: '{}'", filename.display()))?; + let blob = MetadataBlob::Raw(buf); + verify_decompressed_encoding_version(&blob, filename)?; + return Ok(blob); + } else if flavor == CrateFlavor::Rlib || target.options.is_like_msvc { + // With MSVC, DLLs do not contain metadata. Instead, the import library + // for the DLL is itself an Rlib. + let rlib_filename = if flavor == CrateFlavor::Rlib { + filename.to_owned() + } else { + filename.with_extension("dll.rlib") + }; + // Use ArchiveRO for speed here, it's backed by LLVM and uses mmap // internally to read the file. We also avoid even using a memcpy by // just keeping the archive along while the metadata is in use. - let archive = match ArchiveRO::open(filename) { + let archive = match ArchiveRO::open(&rlib_filename) { Some(ar) => ar, None => { - debug!("llvm didn't like `{}`", filename.display()); - return Err(format!("failed to read rlib metadata: '{}'", filename.display())); + debug!("llvm didn't like `{}`", rlib_filename.display()); + return Err(format!("failed to read rlib metadata: '{}'", rlib_filename.display())); } }; return match ArchiveMetadata::new(archive).map(|ar| MetadataBlob::Archive(ar)) { - None => Err(format!("failed to read rlib metadata: '{}'", filename.display())), + None => Err(format!("failed to read rlib metadata: '{}'", rlib_filename.display())), Some(blob) => { - verify_decompressed_encoding_version(&blob, filename)?; + verify_decompressed_encoding_version(&blob, &rlib_filename)?; Ok(blob) } }; - } else if flavor == CrateFlavor::Rmeta { - let mut file = File::open(filename).map_err(|_| - format!("could not open file: '{}'", filename.display()))?; - let mut buf = vec![]; - file.read_to_end(&mut buf).map_err(|_| - format!("failed to read rlib metadata: '{}'", filename.display()))?; - let blob = MetadataBlob::Raw(buf); - verify_decompressed_encoding_version(&blob, filename)?; - return Ok(blob); } unsafe { let buf = common::path2cstr(filename); diff --git a/src/librustc_trans/back/link.rs b/src/librustc_trans/back/link.rs index 1da9fcb0e95e4..67d2469e8eb36 100644 --- a/src/librustc_trans/back/link.rs +++ b/src/librustc_trans/back/link.rs @@ -777,6 +777,29 @@ fn link_natively(sess: &Session, } info!("linker stderr:\n{}", escape_string(&prog.stderr[..])); info!("linker stdout:\n{}", escape_string(&prog.stdout[..])); + + + // When linking a dynamic library with MSVC, we put the metadata + // in the import library, so we add that here. + if crate_type == config::CrateTypeDylib || + crate_type == config::CrateTypeProcMacro { + if sess.target.target.options.is_like_msvc { + // Find the import library and add the metadata to it + let ref imp_lib_name = out_filename.with_extension("dll.lib"); + let ref imp_rlib_name = out_filename.with_extension("dll.rlib"); + + // Import library may not exist if there were no exports + if fs::metadata(imp_lib_name).is_ok() { + add_metadata_to_existing_lib(sess, trans, Some(imp_lib_name), + imp_rlib_name, tmpdir); + // Remove plain lib file + remove(sess, imp_lib_name); + } else { + add_metadata_to_existing_lib(sess, trans, None, imp_rlib_name, tmpdir); + } + } + } + }, Err(e) => { sess.struct_err(&format!("could not exec the linker `{}`: {}", pname, e)) @@ -803,6 +826,22 @@ fn link_natively(sess: &Session, } } +fn add_metadata_to_existing_lib(sess: &Session, + trans: &CrateTranslation, + input_lib: Option<&Path>, + output_rlib: &Path, + tmpdir: &Path) { + + info!("adding metadata to {:?} to build {:?}", input_lib, output_rlib); + let mut ab = ArchiveBuilder::new(archive_config(sess, output_rlib, input_lib)); + ab.update_symbols(); + + let metadata = tmpdir.join(sess.cstore.metadata_filename()); + emit_metadata(sess, trans, &metadata); + ab.add_file(&metadata); + ab.build(); +} + fn link_args(cmd: &mut Linker, sess: &Session, crate_type: config::CrateType, @@ -844,7 +883,11 @@ fn link_args(cmd: &mut Linker, // object file, so we link that in here. if crate_type == config::CrateTypeDylib || crate_type == config::CrateTypeProcMacro { - cmd.add_object(&outputs.with_extension(METADATA_OBJ_NAME)); + // We store the metadata in the import library instead of the DLL + // when targetting MSVC. + if !sess.target.target.options.is_like_msvc { + cmd.add_object(&outputs.with_extension(METADATA_OBJ_NAME)); + } } // Try to strip as much out of the generated object by removing unused @@ -1157,6 +1200,9 @@ fn add_upstream_rust_crates(cmd: &mut Linker, lib.kind == NativeLibraryKind::NativeStatic && !relevant_lib(sess, lib) }); + // If we're not doing LTO, don't need to remove metadata, and don't + // want to skip specific object files, then we can take the fast path, + // and pass the library directly to the linker without modification. if !sess.lto() && crate_type != config::CrateTypeDylib && !skip_native { cmd.link_rlib(&fix_windows_verbatim_for_gcc(cratepath)); return @@ -1240,6 +1286,7 @@ fn add_upstream_rust_crates(cmd: &mut Linker, if let Some(dir) = parent { cmd.include_path(&fix_windows_verbatim_for_gcc(dir)); } + let filestem = cratepath.file_stem().unwrap().to_str().unwrap(); cmd.link_rust_dylib(&unlib(&sess.target, filestem), parent.unwrap_or(Path::new(""))); diff --git a/src/librustc_trans/back/linker.rs b/src/librustc_trans/back/linker.rs index 830d1d0d3a558..9e6880b9958e2 100644 --- a/src/librustc_trans/back/linker.rs +++ b/src/librustc_trans/back/linker.rs @@ -353,7 +353,7 @@ impl<'a> Linker for MsvcLinker<'a> { // `foo.lib` file if the dll doesn't actually export any symbols, so we // check to see if the file is there and just omit linking to it if it's // not present. - let name = format!("{}.dll.lib", lib); + let name = format!("{}.dll.rlib", lib); if fs::metadata(&path.join(&name)).is_ok() { self.cmd.arg(name); } diff --git a/src/librustc_trans/back/symbol_export.rs b/src/librustc_trans/back/symbol_export.rs index bea3ca8df70e0..041d05b64a74a 100644 --- a/src/librustc_trans/back/symbol_export.rs +++ b/src/librustc_trans/back/symbol_export.rs @@ -71,8 +71,12 @@ impl ExportedSymbols { } if scx.sess().crate_types.borrow().contains(&config::CrateTypeDylib) { - local_crate.push((scx.metadata_symbol_name(), - SymbolExportLevel::Rust)); + // When targetting MSVC, metadata is stored in import library + // so don't add an exported symbol for it. + if !scx.sess().target.target.options.is_like_msvc { + local_crate.push((scx.metadata_symbol_name(), + SymbolExportLevel::Rust)); + } } let mut exports = FxHashMap();