Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 1 addition & 7 deletions compiler/rustc_codegen_llvm/src/callee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,8 @@ pub(crate) fn get_fn<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, instance: Instance<'t
let llfn = if tcx.sess.target.arch == Arch::X86
&& let Some(dllimport) = crate::common::get_dllimport(tcx, instance_def_id, sym)
{
// When calling functions in generated import libraries,
// LLVM/ar_archive_writer needs the fully decorated name
// (as would have been in the declaring object file), but dlltool
// wants the name as exported (as would be in the def file)
// which may be missing decorations.
let using_dlltool = common::is_using_dlltool(&tcx.sess.target);
let llfn = cx.declare_fn(
&common::i686_decorated_name(dllimport, using_dlltool, true, !using_dlltool),
&common::i686_decorated_name(dllimport, true, true),
fn_abi,
Some(instance),
);
Expand Down
7 changes: 0 additions & 7 deletions compiler/rustc_codegen_llvm/src/consts.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use std::ops::Range;

use rustc_abi::{Align, ExternAbi, HasDataLayout, Primitive, Scalar, Size, WrappingRange};
use rustc_codegen_ssa::common;
use rustc_codegen_ssa::traits::*;
use rustc_hir::attrs::Linkage;
use rustc_hir::attrs::lang_items::LangItem;
Expand All @@ -17,7 +16,6 @@ use rustc_middle::ty::layout::{HasTypingEnv, LayoutOf};
use rustc_middle::ty::{self, Instance};
use rustc_middle::{bug, span_bug};
use rustc_span::Symbol;
use rustc_target::spec::Arch;
use tracing::{debug, instrument, trace};

use crate::common::CodegenCx;
Expand Down Expand Up @@ -266,11 +264,6 @@ fn check_and_apply_linkage<'ll, 'tcx>(
llvm::set_initializer(g2, initializer);

g2
} else if cx.tcx.sess.target.arch == Arch::X86
&& common::is_using_dlltool(&cx.tcx.sess.target)
&& let Some(dllimport) = crate::common::get_dllimport(cx.tcx, def_id, sym)
{
cx.declare_global(&common::i686_decorated_name(dllimport, true, true, false), llty)
} else {
// Generate an external declaration.
// FIXME(nagisa): investigate whether it can be changed into define_global
Expand Down
238 changes: 50 additions & 188 deletions compiler/rustc_codegen_ssa/src/back/archive.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
use std::borrow::Cow;
use std::env;
use std::error::Error;
use std::ffi::OsString;
use std::fs::{self, File};
use std::io::{self, BufWriter, Write};
use std::path::{Path, PathBuf};
Expand All @@ -24,13 +22,9 @@ use tracing::trace;
use super::metadata::{create_compressed_metadata_file, search_for_section};
use super::rmeta_link::{self, RmetaLinkCache};
use super::symbol_edit::{apply_edits, collect_internal_names};
use crate::common;
// Public for ArchiveBuilderBuilder::extract_bundled_libs
pub use crate::diagnostics::ExtractBundledLibsError;
use crate::diagnostics::{
ArchiveBuildFailure, DlltoolFailImportLibrary, ErrorCallingDllTool, ErrorCreatingImportLibrary,
ErrorWritingDEFFile, UnknownArchiveKind,
};
use crate::diagnostics::{ArchiveBuildFailure, ErrorCreatingImportLibrary, UnknownArchiveKind};

/// An item to be included in an import library.
/// This is a slimmed down version of `COFFShortExport` from `ar-archive-writer`.
Expand Down Expand Up @@ -87,66 +81,57 @@ pub trait ArchiveBuilderBuilder {
items: Vec<ImportLibraryItem>,
output_path: &Path,
) {
if common::is_using_dlltool(&sess.target) {
// The binutils linker used on -windows-gnu targets cannot read the import
// libraries generated by LLVM: in our attempts, the linker produced an .EXE
// that loaded but crashed with an AV upon calling one of the imported
// functions. Therefore, use binutils to create the import library instead,
// by writing a .DEF file to the temp dir and calling binutils's dlltool.
create_mingw_dll_import_lib(sess, lib_name, items, output_path);
} else {
trace!("creating import library");
trace!(" dll_name {:#?}", lib_name);
trace!(" output_path {}", output_path.display());
trace!(
" import names: {}",
items
.iter()
.map(|ImportLibraryItem { name, .. }| name.clone())
.collect::<Vec<_>>()
.join(", "),
);

// All import names are Rust identifiers and therefore cannot contain \0 characters.
// FIXME: when support for #[link_name] is implemented, ensure that the import names
// still don't contain any \0 characters. Also need to check that the names don't
// contain substrings like " @" or "NONAME" that are keywords or otherwise reserved
// in definition files.

let mut file = match fs::File::create_new(&output_path) {
Ok(file) => file,
Err(error) => sess
.dcx()
.emit_fatal(ErrorCreatingImportLibrary { lib_name, error: error.to_string() }),
};
trace!("creating import library");
trace!(" dll_name {:#?}", lib_name);
trace!(" output_path {}", output_path.display());
trace!(
" import names: {}",
items
.iter()
.map(|ImportLibraryItem { name, .. }| name.clone())
.collect::<Vec<_>>()
.join(", "),
);

// All import names are Rust identifiers and therefore cannot contain \0 characters.
// FIXME: when support for #[link_name] is implemented, ensure that the import names
// still don't contain any \0 characters. Also need to check that the names don't
// contain substrings like " @" or "NONAME" that are keywords or otherwise reserved
// in definition files.

let mut file = match fs::File::create_new(&output_path) {
Ok(file) => file,
Err(error) => sess
.dcx()
.emit_fatal(ErrorCreatingImportLibrary { lib_name, error: error.to_string() }),
};

let exports =
items.into_iter().map(|item| item.into_coff_short_export(sess)).collect::<Vec<_>>();
let machine = match &sess.target.arch {
Arch::X86_64 => MachineTypes::AMD64,
Arch::X86 => MachineTypes::I386,
Arch::AArch64 => MachineTypes::ARM64,
Arch::Arm64EC => MachineTypes::ARM64EC,
Arch::Arm => MachineTypes::ARMNT,
cpu => panic!("unsupported cpu type {cpu}"),
};
let exports =
items.into_iter().map(|item| item.into_coff_short_export(sess)).collect::<Vec<_>>();
let machine = match &sess.target.arch {
Arch::X86_64 => MachineTypes::AMD64,
Arch::X86 => MachineTypes::I386,
Arch::AArch64 => MachineTypes::ARM64,
Arch::Arm64EC => MachineTypes::ARM64EC,
Arch::Arm => MachineTypes::ARMNT,
cpu => panic!("unsupported cpu type {cpu}"),
};

if let Err(error) = ar_archive_writer::write_import_library(
&mut file,
lib_name,
&exports,
machine,
!sess.target.is_like_msvc,
// Enable compatibility with MSVC's `/WHOLEARCHIVE` flag.
// Without this flag a duplicate symbol error would be emitted
// when linking a rust staticlib using `/WHOLEARCHIVE`.
// See #129020
true,
&[],
) {
sess.dcx()
.emit_fatal(ErrorCreatingImportLibrary { lib_name, error: error.to_string() });
}
if let Err(error) = ar_archive_writer::write_import_library(
&mut file,
lib_name,
&exports,
machine,
!sess.target.is_like_msvc,
// Enable compatibility with MSVC's `/WHOLEARCHIVE` flag.
// Without this flag a duplicate symbol error would be emitted
// when linking a rust staticlib using `/WHOLEARCHIVE`.
// See #129020
true,
&[],
) {
sess.dcx()
.emit_fatal(ErrorCreatingImportLibrary { lib_name, error: error.to_string() });
}
}

Expand Down Expand Up @@ -187,129 +172,6 @@ pub trait ArchiveBuilderBuilder {
}
}

fn create_mingw_dll_import_lib(
sess: &Session,
lib_name: &str,
items: Vec<ImportLibraryItem>,
output_path: &Path,
) {
let def_file_path = output_path.with_extension("def");

let def_file_content = format!(
"EXPORTS\n{}",
items
.into_iter()
.map(|ImportLibraryItem { 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);
// temp_prefix doesn't handle paths with spaces so
// use a relative path and set the current working directory
let cwd = output_path.parent().unwrap_or(output_path);
let temp_prefix = lib_name;
// 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 {
Arch::X86_64 => ("i386:x86-64", "--64"),
Arch::X86 => ("i386", "--32"),
Arch::AArch64 => ("arm64", "--64"),
Arch::Arm => ("arm", "--32"),
arch => panic!("unsupported arch {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)
.current_dir(cwd);

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 {
Arch::X86_64 => "x86_64-w64-mingw32-dlltool",
Arch::X86 => "i686-w64-mingw32-dlltool",
Arch::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 enum AddArchiveKind<'a> {
Rlib(&'a mut RmetaLinkCache, /*skip*/ &'a dyn Fn(&str, ArchiveEntryKind) -> bool),
Other,
Expand Down
10 changes: 4 additions & 6 deletions compiler/rustc_codegen_ssa/src/back/link/raw_dylib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,18 +73,16 @@ pub(super) fn create_raw_dylib_dll_import_libs<'a>(
let name_suffix = if is_direct_dependency { "_imports" } else { "_imports_indirect" };
let output_path = tmpdir.join(format!("{raw_dylib_name}{name_suffix}.lib"));

let using_dlltool = common::is_using_dlltool(&sess.target);

let items: Vec<ImportLibraryItem> = raw_dylib_imports
.iter()
.map(|import: &DllImport| {
if sess.target.arch == Arch::X86 {
ImportLibraryItem {
name: common::i686_decorated_name(import, using_dlltool, false, false),
name: common::i686_decorated_name(import, false, false),
ordinal: import.ordinal(),
symbol_name: import.is_missing_decorations().then(|| {
common::i686_decorated_name(import, using_dlltool, false, true)
}),
symbol_name: import
.is_missing_decorations()
.then(|| common::i686_decorated_name(import, false, true)),
is_data: import.symbol_type != DllImportSymbolType::Function,
}
} else {
Expand Down
12 changes: 2 additions & 10 deletions compiler/rustc_codegen_ssa/src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ use rustc_middle::ty::layout::TyAndLayout;
use rustc_middle::ty::{self, Instance, ScalarInt, TyCtxt};
use rustc_middle::{bug, span_bug};
use rustc_span::Span;
use rustc_target::spec::{CfgAbi, Env, Os, Target};

use crate::traits::*;

Expand Down Expand Up @@ -200,13 +199,8 @@ pub fn asm_const_ptr_clean<'tcx>(tcx: TyCtxt<'tcx>, scalar: Scalar) -> Scalar {
}
}

pub fn is_using_dlltool(target: &Target) -> bool {
target.os == Os::Windows && target.env == Env::Gnu && target.cfg_abi == CfgAbi::Unspecified
}

pub fn i686_decorated_name(
dll_import: &DllImport,
mingw: bool,
disable_name_mangling: bool,
force_fully_decorated: bool,
) -> String {
Expand All @@ -232,12 +226,10 @@ pub fn i686_decorated_name(
let prefix = if add_prefix && dll_import.symbol_type == DllImportSymbolType::Function {
match dll_import.calling_convention {
DllCallingConvention::C | DllCallingConvention::Vectorcall(_) => None,
DllCallingConvention::Stdcall(_) => (!mingw
|| dll_import.import_name_type == Some(PeImportNameType::Decorated))
.then_some('_'),
DllCallingConvention::Stdcall(_) => Some('_'),
DllCallingConvention::Fastcall(_) => Some('@'),
}
} else if dll_import.symbol_type != DllImportSymbolType::Function && !mingw {
} else if dll_import.symbol_type != DllImportSymbolType::Function {
// For static variables, prefix with '_' on MSVC.
Some('_')
} else {
Expand Down
Loading
Loading