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
This commit is contained in:
commit
355eb9c79f
15 changed files with 332 additions and 317 deletions
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -4,7 +4,9 @@ use rustc_hir::LangItem;
|
|||
use rustc_middle::ty::layout::TyAndLayout;
|
||||
use rustc_middle::ty::{self, Instance, TyCtxt};
|
||||
use rustc_middle::{bug, mir, span_bug};
|
||||
use rustc_session::cstore::{DllCallingConvention, DllImport, PeImportNameType};
|
||||
use rustc_span::Span;
|
||||
use rustc_target::spec::Target;
|
||||
|
||||
use crate::traits::*;
|
||||
|
||||
|
@ -176,3 +178,66 @@ pub fn asm_const_to_str<'tcx>(
|
|||
_ => span_bug!(sp, "asm const has bad type {}", ty_and_layout.ty),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_mingw_gnu_toolchain(target: &Target) -> bool {
|
||||
target.vendor == "pc" && target.os == "windows" && target.env == "gnu" && target.abi.is_empty()
|
||||
}
|
||||
|
||||
pub fn i686_decorated_name(
|
||||
dll_import: &DllImport,
|
||||
mingw: bool,
|
||||
disable_name_mangling: bool,
|
||||
) -> String {
|
||||
let name = dll_import.name.as_str();
|
||||
|
||||
let (add_prefix, add_suffix) = match dll_import.import_name_type {
|
||||
Some(PeImportNameType::NoPrefix) => (false, true),
|
||||
Some(PeImportNameType::Undecorated) => (false, false),
|
||||
_ => (true, true),
|
||||
};
|
||||
|
||||
// Worst case: +1 for disable name mangling, +1 for prefix, +4 for suffix (@@__).
|
||||
let mut decorated_name = String::with_capacity(name.len() + 6);
|
||||
|
||||
if disable_name_mangling {
|
||||
// LLVM uses a binary 1 ('\x01') prefix to a name to indicate that mangling needs to be disabled.
|
||||
decorated_name.push('\x01');
|
||||
}
|
||||
|
||||
let prefix = if add_prefix && dll_import.is_fn {
|
||||
match dll_import.calling_convention {
|
||||
DllCallingConvention::C | DllCallingConvention::Vectorcall(_) => None,
|
||||
DllCallingConvention::Stdcall(_) => (!mingw
|
||||
|| dll_import.import_name_type == Some(PeImportNameType::Decorated))
|
||||
.then_some('_'),
|
||||
DllCallingConvention::Fastcall(_) => Some('@'),
|
||||
}
|
||||
} else if !dll_import.is_fn && !mingw {
|
||||
// For static variables, prefix with '_' on MSVC.
|
||||
Some('_')
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if let Some(prefix) = prefix {
|
||||
decorated_name.push(prefix);
|
||||
}
|
||||
|
||||
decorated_name.push_str(name);
|
||||
|
||||
if add_suffix && dll_import.is_fn {
|
||||
use std::fmt::Write;
|
||||
|
||||
match dll_import.calling_convention {
|
||||
DllCallingConvention::C => {}
|
||||
DllCallingConvention::Stdcall(arg_list_size)
|
||||
| DllCallingConvention::Fastcall(arg_list_size) => {
|
||||
write!(&mut decorated_name, "@{arg_list_size}").unwrap();
|
||||
}
|
||||
DllCallingConvention::Vectorcall(arg_list_size) => {
|
||||
write!(&mut decorated_name, "@@{arg_list_size}").unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
decorated_name
|
||||
}
|
||||
|
|
|
@ -1026,6 +1026,28 @@ pub struct FailedToGetLayout<'tcx> {
|
|||
pub err: LayoutError<'tcx>,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(codegen_ssa_dlltool_fail_import_library)]
|
||||
pub(crate) struct DlltoolFailImportLibrary<'a> {
|
||||
pub dlltool_path: Cow<'a, str>,
|
||||
pub dlltool_args: String,
|
||||
pub stdout: Cow<'a, str>,
|
||||
pub stderr: Cow<'a, str>,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(codegen_ssa_error_writing_def_file)]
|
||||
pub(crate) struct ErrorWritingDEFFile {
|
||||
pub error: std::io::Error,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(codegen_ssa_error_calling_dlltool)]
|
||||
pub(crate) struct ErrorCallingDllTool<'a> {
|
||||
pub dlltool_path: Cow<'a, str>,
|
||||
pub error: std::io::Error,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(codegen_ssa_error_creating_remark_dir)]
|
||||
pub struct ErrorCreatingRemarkDir {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue