diff options
| author | Guillaume Gomez <guillaume1.gomez@gmail.com> | 2024-08-07 15:59:35 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-08-07 15:59:35 +0200 |
| commit | 355eb9c79fed8895c7f96cb401e254696375955e (patch) | |
| tree | 8349313e035b5de282fc0aeaa06590efa52e785a /compiler/rustc_codegen_ssa/src/back | |
| parent | 9bad7ba324099d124c77c5b06aebf68e11763f7b (diff) | |
| parent | f58e737554bebd34ac794d02b6e018bf20857788 (diff) | |
| download | rust-355eb9c79fed8895c7f96cb401e254696375955e.tar.gz rust-355eb9c79fed8895c7f96cb401e254696375955e.zip | |
Rollup merge of #128206 - bjorn3:import_lib_writing_refactor, r=jieyouxu
Make create_dll_import_lib easier to implement This will make it easier to implement raw-dylib support in cg_clif and cg_gcc. This PR doesn't yet include an create_dll_import_lib implementation for cg_clif as I need to correctly implement dllimport in cg_clif first before raw-dylib can work at all with cg_clif. Required for https://github.com/rust-lang/rustc_codegen_cranelift/issues/1345
Diffstat (limited to 'compiler/rustc_codegen_ssa/src/back')
| -rw-r--r-- | compiler/rustc_codegen_ssa/src/back/archive.rs | 135 | ||||
| -rw-r--r-- | compiler/rustc_codegen_ssa/src/back/link.rs | 98 |
2 files changed, 196 insertions, 37 deletions
diff --git a/compiler/rustc_codegen_ssa/src/back/archive.rs b/compiler/rustc_codegen_ssa/src/back/archive.rs index 4f93b7b23c6..ce55d99f506 100644 --- a/compiler/rustc_codegen_ssa/src/back/archive.rs +++ b/compiler/rustc_codegen_ssa/src/back/archive.rs @@ -1,4 +1,6 @@ +use std::env; use std::error::Error; +use std::ffi::OsString; use std::fs::{self, File}; use std::io::{self, Write}; use std::path::{Path, PathBuf}; @@ -9,7 +11,6 @@ use object::read::archive::ArchiveFile; use object::read::macho::FatArch; use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::memmap::Mmap; -use rustc_session::cstore::DllImport; use rustc_session::Session; use rustc_span::symbol::Symbol; use tempfile::Builder as TempFileBuilder; @@ -17,6 +18,7 @@ use tempfile::Builder as TempFileBuilder; use super::metadata::search_for_section; // Re-exporting for rustc_codegen_llvm::back::archive pub use crate::errors::{ArchiveBuildFailure, ExtractBundledLibsError, UnknownArchiveKind}; +use crate::errors::{DlltoolFailImportLibrary, ErrorCallingDllTool, ErrorWritingDEFFile}; pub trait ArchiveBuilderBuilder { fn new_archive_builder<'a>(&self, sess: &'a Session) -> Box<dyn ArchiveBuilder + 'a>; @@ -30,10 +32,9 @@ pub trait ArchiveBuilderBuilder { &self, sess: &Session, lib_name: &str, - dll_imports: &[DllImport], - tmpdir: &Path, - is_direct_dependency: bool, - ) -> PathBuf; + import_name_and_ordinal_vector: Vec<(String, Option<u16>)>, + output_path: &Path, + ); fn extract_bundled_libs<'a>( &'a self, @@ -72,6 +73,130 @@ pub trait ArchiveBuilderBuilder { } } +pub fn create_mingw_dll_import_lib( + sess: &Session, + lib_name: &str, + import_name_and_ordinal_vector: Vec<(String, Option<u16>)>, + output_path: &Path, +) { + let def_file_path = output_path.with_extension("def"); + + let def_file_content = format!( + "EXPORTS\n{}", + import_name_and_ordinal_vector + .into_iter() + .map(|(name, ordinal)| { + match ordinal { + Some(n) => format!("{name} @{n} NONAME"), + None => name, + } + }) + .collect::<Vec<String>>() + .join("\n") + ); + + match std::fs::write(&def_file_path, def_file_content) { + Ok(_) => {} + Err(e) => { + sess.dcx().emit_fatal(ErrorWritingDEFFile { error: e }); + } + }; + + // --no-leading-underscore: For the `import_name_type` feature to work, we need to be + // able to control the *exact* spelling of each of the symbols that are being imported: + // hence we don't want `dlltool` adding leading underscores automatically. + let dlltool = find_binutils_dlltool(sess); + let temp_prefix = { + let mut path = PathBuf::from(&output_path); + path.pop(); + path.push(lib_name); + path + }; + // dlltool target architecture args from: + // https://github.com/llvm/llvm-project-release-prs/blob/llvmorg-15.0.6/llvm/lib/ToolDrivers/llvm-dlltool/DlltoolDriver.cpp#L69 + let (dlltool_target_arch, dlltool_target_bitness) = match sess.target.arch.as_ref() { + "x86_64" => ("i386:x86-64", "--64"), + "x86" => ("i386", "--32"), + "aarch64" => ("arm64", "--64"), + "arm" => ("arm", "--32"), + _ => panic!("unsupported arch {}", sess.target.arch), + }; + let mut dlltool_cmd = std::process::Command::new(&dlltool); + dlltool_cmd + .arg("-d") + .arg(def_file_path) + .arg("-D") + .arg(lib_name) + .arg("-l") + .arg(&output_path) + .arg("-m") + .arg(dlltool_target_arch) + .arg("-f") + .arg(dlltool_target_bitness) + .arg("--no-leading-underscore") + .arg("--temp-prefix") + .arg(temp_prefix); + + match dlltool_cmd.output() { + Err(e) => { + sess.dcx().emit_fatal(ErrorCallingDllTool { + dlltool_path: dlltool.to_string_lossy(), + error: e, + }); + } + // dlltool returns '0' on failure, so check for error output instead. + Ok(output) if !output.stderr.is_empty() => { + sess.dcx().emit_fatal(DlltoolFailImportLibrary { + dlltool_path: dlltool.to_string_lossy(), + dlltool_args: dlltool_cmd + .get_args() + .map(|arg| arg.to_string_lossy()) + .collect::<Vec<_>>() + .join(" "), + stdout: String::from_utf8_lossy(&output.stdout), + stderr: String::from_utf8_lossy(&output.stderr), + }) + } + _ => {} + } +} + +fn find_binutils_dlltool(sess: &Session) -> OsString { + assert!(sess.target.options.is_like_windows && !sess.target.options.is_like_msvc); + if let Some(dlltool_path) = &sess.opts.cg.dlltool { + return dlltool_path.clone().into_os_string(); + } + + let tool_name: OsString = if sess.host.options.is_like_windows { + // If we're compiling on Windows, always use "dlltool.exe". + "dlltool.exe" + } else { + // On other platforms, use the architecture-specific name. + match sess.target.arch.as_ref() { + "x86_64" => "x86_64-w64-mingw32-dlltool", + "x86" => "i686-w64-mingw32-dlltool", + "aarch64" => "aarch64-w64-mingw32-dlltool", + + // For non-standard architectures (e.g., aarch32) fallback to "dlltool". + _ => "dlltool", + } + } + .into(); + + // NOTE: it's not clear how useful it is to explicitly search PATH. + for dir in env::split_paths(&env::var_os("PATH").unwrap_or_default()) { + let full_path = dir.join(&tool_name); + if full_path.is_file() { + return full_path.into_os_string(); + } + } + + // The user didn't specify the location of the dlltool binary, and we weren't able + // to find the appropriate one on the PATH. Just return the name of the tool + // and let the invocation fail with a hopefully useful error message. + tool_name +} + pub trait ArchiveBuilder { fn add_file(&mut self, path: &Path); diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 653895380be..45a92fd03d5 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -51,7 +51,8 @@ use super::linker::{self, Linker}; use super::metadata::{create_wrapper_file, MetadataPosition}; use super::rpath::{self, RPathConfig}; use crate::{ - errors, looks_like_rust_object_file, CodegenResults, CompiledModule, CrateInfo, NativeLib, + common, errors, looks_like_rust_object_file, CodegenResults, CompiledModule, CrateInfo, + NativeLib, }; pub fn ensure_removed(dcx: DiagCtxtHandle<'_>, path: &Path) { @@ -390,17 +391,13 @@ fn link_rlib<'a>( } } - for (raw_dylib_name, raw_dylib_imports) in - collate_raw_dylibs(sess, codegen_results.crate_info.used_libraries.iter())? - { - let output_path = archive_builder_builder.create_dll_import_lib( - sess, - &raw_dylib_name, - &raw_dylib_imports, - tmpdir.as_ref(), - true, - ); - + for output_path in create_dll_import_libs( + sess, + archive_builder_builder, + codegen_results.crate_info.used_libraries.iter(), + tmpdir.as_ref(), + true, + )? { ab.add_archive(&output_path, Box::new(|_| false)).unwrap_or_else(|error| { sess.dcx().emit_fatal(errors::AddNativeLibrary { library_path: output_path, error }); }); @@ -488,6 +485,47 @@ fn collate_raw_dylibs<'a>( .collect()) } +fn create_dll_import_libs<'a>( + sess: &Session, + archive_builder_builder: &dyn ArchiveBuilderBuilder, + used_libraries: impl IntoIterator<Item = &'a NativeLib>, + tmpdir: &Path, + is_direct_dependency: bool, +) -> Result<Vec<PathBuf>, ErrorGuaranteed> { + Ok(collate_raw_dylibs(sess, used_libraries)? + .into_iter() + .map(|(raw_dylib_name, raw_dylib_imports)| { + let name_suffix = if is_direct_dependency { "_imports" } else { "_imports_indirect" }; + let output_path = tmpdir.join(format!("{raw_dylib_name}{name_suffix}.lib")); + + let mingw_gnu_toolchain = common::is_mingw_gnu_toolchain(&sess.target); + + let import_name_and_ordinal_vector: Vec<(String, Option<u16>)> = raw_dylib_imports + .iter() + .map(|import: &DllImport| { + if sess.target.arch == "x86" { + ( + common::i686_decorated_name(import, mingw_gnu_toolchain, false), + import.ordinal(), + ) + } else { + (import.name.to_string(), import.ordinal()) + } + }) + .collect(); + + archive_builder_builder.create_dll_import_lib( + sess, + &raw_dylib_name, + import_name_and_ordinal_vector, + &output_path, + ); + + output_path + }) + .collect()) +} + /// Create a static archive. /// /// This is essentially the same thing as an rlib, but it also involves adding all of the upstream @@ -2305,16 +2343,14 @@ fn linker_with_args( ); // Link with the import library generated for any raw-dylib functions. - for (raw_dylib_name, raw_dylib_imports) in - collate_raw_dylibs(sess, codegen_results.crate_info.used_libraries.iter())? - { - cmd.add_object(&archive_builder_builder.create_dll_import_lib( - sess, - &raw_dylib_name, - &raw_dylib_imports, - tmpdir, - true, - )); + for output_path in create_dll_import_libs( + sess, + archive_builder_builder, + codegen_results.crate_info.used_libraries.iter(), + tmpdir, + true, + )? { + cmd.add_object(&output_path); } // As with add_upstream_native_libraries, we need to add the upstream raw-dylib symbols in case // they are used within inlined functions or instantiated generic functions. We do this *after* @@ -2339,16 +2375,14 @@ fn linker_with_args( .flatten() .collect::<Vec<_>>(); native_libraries_from_nonstatics.sort_unstable_by(|a, b| a.name.as_str().cmp(b.name.as_str())); - for (raw_dylib_name, raw_dylib_imports) in - collate_raw_dylibs(sess, native_libraries_from_nonstatics)? - { - cmd.add_object(&archive_builder_builder.create_dll_import_lib( - sess, - &raw_dylib_name, - &raw_dylib_imports, - tmpdir, - false, - )); + for output_path in create_dll_import_libs( + sess, + archive_builder_builder, + native_libraries_from_nonstatics, + tmpdir, + false, + )? { + cmd.add_object(&output_path); } // Library linking above uses some global state for things like `-Bstatic`/`-Bdynamic` to make |
