Move mingw dlltool invocation to cg_ssa

This commit is contained in:
bjorn3 2024-07-25 20:17:59 +00:00
parent 3c987cbe02
commit 216686bfa5
6 changed files with 172 additions and 159 deletions

View File

@ -1,23 +1,13 @@
codegen_llvm_copy_bitcode = failed to copy bitcode to object file: {$err} codegen_llvm_copy_bitcode = failed to copy bitcode to object file: {$err}
codegen_llvm_dlltool_fail_import_library =
Dlltool could not create import library with {$dlltool_path} {$dlltool_args}:
{$stdout}
{$stderr}
codegen_llvm_dynamic_linking_with_lto = codegen_llvm_dynamic_linking_with_lto =
cannot prefer dynamic linking when performing LTO cannot prefer dynamic linking when performing LTO
.note = only 'staticlib', 'bin', and 'cdylib' outputs are supported with LTO .note = only 'staticlib', 'bin', and 'cdylib' outputs are supported with LTO
codegen_llvm_error_calling_dlltool =
Error calling dlltool '{$dlltool_path}': {$error}
codegen_llvm_error_creating_import_library = codegen_llvm_error_creating_import_library =
Error creating import library for {$lib_name}: {$error} Error creating import library for {$lib_name}: {$error}
codegen_llvm_error_writing_def_file =
Error writing .DEF file: {$error}
codegen_llvm_fixed_x18_invalid_arch = the `-Zfixed-x18` flag is not supported on the `{$arch}` architecture codegen_llvm_fixed_x18_invalid_arch = the `-Zfixed-x18` flag is not supported on the `{$arch}` architecture
codegen_llvm_from_llvm_diag = {$message} codegen_llvm_from_llvm_diag = {$message}

View File

@ -1,20 +1,19 @@
//! A helper class for dealing with static archives //! A helper class for dealing with static archives
use std::ffi::{c_char, c_void, CStr, CString, OsString}; use std::ffi::{c_char, c_void, CStr, CString};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::{env, io, mem, ptr, str}; use std::{io, mem, ptr, str};
use rustc_codegen_ssa::back::archive::{ use rustc_codegen_ssa::back::archive::{
try_extract_macho_fat_archive, ArArchiveBuilder, ArchiveBuildFailure, ArchiveBuilder, create_mingw_dll_import_lib, try_extract_macho_fat_archive, ArArchiveBuilder,
ArchiveBuilderBuilder, ObjectReader, UnknownArchiveKind, DEFAULT_OBJECT_READER, ArchiveBuildFailure, ArchiveBuilder, ArchiveBuilderBuilder, ObjectReader, UnknownArchiveKind,
DEFAULT_OBJECT_READER,
}; };
use rustc_codegen_ssa::common; use rustc_codegen_ssa::common;
use rustc_session::Session; use rustc_session::Session;
use tracing::trace; use tracing::trace;
use crate::errors::{ use crate::errors::ErrorCreatingImportLibrary;
DlltoolFailImportLibrary, ErrorCallingDllTool, ErrorCreatingImportLibrary, ErrorWritingDEFFile,
};
use crate::llvm::archive_ro::{ArchiveRO, Child}; use crate::llvm::archive_ro::{ArchiveRO, Child};
use crate::llvm::{self, ArchiveKind, LLVMMachineType, LLVMRustCOFFShortExport}; use crate::llvm::{self, ArchiveKind, LLVMMachineType, LLVMRustCOFFShortExport};
@ -121,95 +120,18 @@ fn create_dll_import_lib(
import_name_and_ordinal_vector: Vec<(String, Option<u16>)>, import_name_and_ordinal_vector: Vec<(String, Option<u16>)>,
output_path: &Path, output_path: &Path,
) { ) {
let target = &sess.target; if common::is_mingw_gnu_toolchain(&sess.target) {
let mingw_gnu_toolchain = common::is_mingw_gnu_toolchain(target);
if mingw_gnu_toolchain {
// The binutils linker used on -windows-gnu targets cannot read the import // The binutils linker used on -windows-gnu targets cannot read the import
// libraries generated by LLVM: in our attempts, the linker produced an .EXE // 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 // that loaded but crashed with an AV upon calling one of the imported
// functions. Therefore, use binutils to create the import library instead, // functions. Therefore, use binutils to create the import library instead,
// by writing a .DEF file to the temp dir and calling binutils's dlltool. // by writing a .DEF file to the temp dir and calling binutils's dlltool.
let def_file_path = output_path.with_extension("def"); create_mingw_dll_import_lib(
sess,
let def_file_content = format!( lib_name,
"EXPORTS\n{}", import_name_and_ordinal_vector,
import_name_and_ordinal_vector output_path,
.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),
})
}
_ => {}
}
} else { } else {
// we've checked for \0 characters in the library name already // we've checked for \0 characters in the library name already
let dll_name_z = CString::new(lib_name).unwrap(); let dll_name_z = CString::new(lib_name).unwrap();
@ -434,39 +356,3 @@ fn build_with_llvm(&mut self, output: &Path) -> io::Result<bool> {
fn string_to_io_error(s: String) -> io::Error { fn string_to_io_error(s: String) -> io::Error {
io::Error::new(io::ErrorKind::Other, format!("bad archive: {s}")) io::Error::new(io::ErrorKind::Other, format!("bad archive: {s}"))
} }
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
}

View File

@ -1,4 +1,3 @@
use std::borrow::Cow;
use std::ffi::CString; use std::ffi::CString;
use std::path::Path; use std::path::Path;
@ -71,28 +70,6 @@ pub(crate) struct InvalidMinimumAlignmentTooLarge {
#[diag(codegen_llvm_sanitizer_memtag_requires_mte)] #[diag(codegen_llvm_sanitizer_memtag_requires_mte)]
pub(crate) struct SanitizerMemtagRequiresMte; pub(crate) struct SanitizerMemtagRequiresMte;
#[derive(Diagnostic)]
#[diag(codegen_llvm_error_writing_def_file)]
pub(crate) struct ErrorWritingDEFFile {
pub error: std::io::Error,
}
#[derive(Diagnostic)]
#[diag(codegen_llvm_error_calling_dlltool)]
pub(crate) struct ErrorCallingDllTool<'a> {
pub dlltool_path: Cow<'a, str>,
pub error: std::io::Error,
}
#[derive(Diagnostic)]
#[diag(codegen_llvm_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)] #[derive(Diagnostic)]
#[diag(codegen_llvm_dynamic_linking_with_lto)] #[diag(codegen_llvm_dynamic_linking_with_lto)]
#[note] #[note]

View File

@ -25,8 +25,19 @@ codegen_ssa_copy_path_buf = unable to copy {$source_file} to {$output_path}: {$e
codegen_ssa_create_temp_dir = couldn't create a temp dir: {$error} codegen_ssa_create_temp_dir = couldn't create a temp dir: {$error}
codegen_ssa_dlltool_fail_import_library =
Dlltool could not create import library with {$dlltool_path} {$dlltool_args}:
{$stdout}
{$stderr}
codegen_ssa_error_calling_dlltool =
Error calling dlltool '{$dlltool_path}': {$error}
codegen_ssa_error_creating_remark_dir = failed to create remark directory: {$error} codegen_ssa_error_creating_remark_dir = failed to create remark directory: {$error}
codegen_ssa_error_writing_def_file =
Error writing .DEF file: {$error}
codegen_ssa_expected_used_symbol = expected `used`, `used(compiler)` or `used(linker)` codegen_ssa_expected_used_symbol = expected `used`, `used(compiler)` or `used(linker)`
codegen_ssa_extern_funcs_not_found = some `extern` functions couldn't be found; some native libraries may need to be installed or have their path specified codegen_ssa_extern_funcs_not_found = some `extern` functions couldn't be found; some native libraries may need to be installed or have their path specified

View File

@ -1,4 +1,6 @@
use std::env;
use std::error::Error; use std::error::Error;
use std::ffi::OsString;
use std::fs::{self, File}; use std::fs::{self, File};
use std::io::{self, Write}; use std::io::{self, Write};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
@ -16,6 +18,7 @@
use super::metadata::search_for_section; use super::metadata::search_for_section;
// Re-exporting for rustc_codegen_llvm::back::archive // Re-exporting for rustc_codegen_llvm::back::archive
pub use crate::errors::{ArchiveBuildFailure, ExtractBundledLibsError, UnknownArchiveKind}; pub use crate::errors::{ArchiveBuildFailure, ExtractBundledLibsError, UnknownArchiveKind};
use crate::errors::{DlltoolFailImportLibrary, ErrorCallingDllTool, ErrorWritingDEFFile};
pub trait ArchiveBuilderBuilder { pub trait ArchiveBuilderBuilder {
fn new_archive_builder<'a>(&self, sess: &'a Session) -> Box<dyn ArchiveBuilder + 'a>; fn new_archive_builder<'a>(&self, sess: &'a Session) -> Box<dyn ArchiveBuilder + 'a>;
@ -70,6 +73,130 @@ fn extract_bundled_libs<'a>(
} }
} }
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 { pub trait ArchiveBuilder {
fn add_file(&mut self, path: &Path); fn add_file(&mut self, path: &Path);

View File

@ -1024,6 +1024,28 @@ pub struct FailedToGetLayout<'tcx> {
pub err: LayoutError<'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)] #[derive(Diagnostic)]
#[diag(codegen_ssa_error_creating_remark_dir)] #[diag(codegen_ssa_error_creating_remark_dir)]
pub struct ErrorCreatingRemarkDir { pub struct ErrorCreatingRemarkDir {