From 3a1ef50b34d3c88ddc423411be4521499cf9def5 Mon Sep 17 00:00:00 2001 From: Daniel Paoliello Date: Wed, 12 Oct 2022 14:44:01 -0700 Subject: [PATCH] Support raw-dylib functions being used inside inlined functions --- .../rustc_codegen_cranelift/src/archive.rs | 1 + compiler/rustc_codegen_gcc/src/archive.rs | 1 + .../rustc_codegen_llvm/src/back/archive.rs | 7 +++- .../rustc_codegen_ssa/src/back/archive.rs | 1 + compiler/rustc_codegen_ssa/src/back/link.rs | 41 ++++++++++++++++--- .../raw-dylib-inline-cross-dylib/Makefile | 31 ++++++++++++++ .../raw-dylib-inline-cross-dylib/driver.rs | 21 ++++++++++ .../raw-dylib-inline-cross-dylib/extern_1.c | 11 +++++ .../raw-dylib-inline-cross-dylib/extern_2.c | 6 +++ .../raw-dylib-inline-cross-dylib/lib.rs | 21 ++++++++++ .../lib_wrapper.rs | 6 +++ .../raw-dylib-inline-cross-dylib/output.txt | 6 +++ 12 files changed, 146 insertions(+), 7 deletions(-) create mode 100644 src/test/run-make/raw-dylib-inline-cross-dylib/Makefile create mode 100644 src/test/run-make/raw-dylib-inline-cross-dylib/driver.rs create mode 100644 src/test/run-make/raw-dylib-inline-cross-dylib/extern_1.c create mode 100644 src/test/run-make/raw-dylib-inline-cross-dylib/extern_2.c create mode 100644 src/test/run-make/raw-dylib-inline-cross-dylib/lib.rs create mode 100644 src/test/run-make/raw-dylib-inline-cross-dylib/lib_wrapper.rs create mode 100644 src/test/run-make/raw-dylib-inline-cross-dylib/output.txt diff --git a/compiler/rustc_codegen_cranelift/src/archive.rs b/compiler/rustc_codegen_cranelift/src/archive.rs index 31d3d0e06156b..f2e3bf16e6184 100644 --- a/compiler/rustc_codegen_cranelift/src/archive.rs +++ b/compiler/rustc_codegen_cranelift/src/archive.rs @@ -38,6 +38,7 @@ impl ArchiveBuilderBuilder for ArArchiveBuilderBuilder { _lib_name: &str, _dll_imports: &[rustc_session::cstore::DllImport], _tmpdir: &Path, + _is_direct_dependency: bool, ) -> PathBuf { bug!("creating dll imports is not supported"); } diff --git a/compiler/rustc_codegen_gcc/src/archive.rs b/compiler/rustc_codegen_gcc/src/archive.rs index ac0342f6b80a0..f18ae7ea5e9b0 100644 --- a/compiler/rustc_codegen_gcc/src/archive.rs +++ b/compiler/rustc_codegen_gcc/src/archive.rs @@ -47,6 +47,7 @@ impl ArchiveBuilderBuilder for ArArchiveBuilderBuilder { _lib_name: &str, _dll_imports: &[DllImport], _tmpdir: &Path, + _is_direct_dependency: bool, ) -> PathBuf { unimplemented!(); } diff --git a/compiler/rustc_codegen_llvm/src/back/archive.rs b/compiler/rustc_codegen_llvm/src/back/archive.rs index 20a063f80fd00..082665bba3802 100644 --- a/compiler/rustc_codegen_llvm/src/back/archive.rs +++ b/compiler/rustc_codegen_llvm/src/back/archive.rs @@ -165,10 +165,12 @@ impl ArchiveBuilderBuilder for LlvmArchiveBuilderBuilder { lib_name: &str, dll_imports: &[DllImport], tmpdir: &Path, + is_direct_dependency: bool, ) -> PathBuf { + let name_suffix = if is_direct_dependency { "_imports" } else { "_imports_indirect" }; let output_path = { let mut output_path: PathBuf = tmpdir.to_path_buf(); - output_path.push(format!("{}_imports", lib_name)); + output_path.push(format!("{}{}", lib_name, name_suffix)); output_path.with_extension("lib") }; @@ -195,7 +197,8 @@ impl ArchiveBuilderBuilder for LlvmArchiveBuilderBuilder { // 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. - let def_file_path = tmpdir.join(format!("{}_imports", lib_name)).with_extension("def"); + let def_file_path = + tmpdir.join(format!("{}{}", lib_name, name_suffix)).with_extension("def"); let def_file_content = format!( "EXPORTS\n{}", diff --git a/compiler/rustc_codegen_ssa/src/back/archive.rs b/compiler/rustc_codegen_ssa/src/back/archive.rs index bad58d0a8a0a1..bb76ca5d2b941 100644 --- a/compiler/rustc_codegen_ssa/src/back/archive.rs +++ b/compiler/rustc_codegen_ssa/src/back/archive.rs @@ -25,6 +25,7 @@ pub trait ArchiveBuilderBuilder { lib_name: &str, dll_imports: &[DllImport], tmpdir: &Path, + is_direct_dependency: bool, ) -> PathBuf; fn extract_bundled_libs( diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index a0b5e3b6daf44..0dc0dee862c74 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -391,13 +391,14 @@ fn link_rlib<'a>( } for (raw_dylib_name, raw_dylib_imports) in - collate_raw_dylibs(sess, &codegen_results.crate_info.used_libraries)? + 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, ); ab.add_archive(&output_path, Box::new(|_| false)).unwrap_or_else(|error| { @@ -449,9 +450,9 @@ fn link_rlib<'a>( /// then the CodegenResults value contains one NativeLib instance for each block. However, the /// linker appears to expect only a single import library for each library used, so we need to /// collate the symbols together by library name before generating the import libraries. -fn collate_raw_dylibs( - sess: &Session, - used_libraries: &[NativeLib], +fn collate_raw_dylibs<'a, 'b>( + sess: &'a Session, + used_libraries: impl IntoIterator, ) -> Result)>, ErrorGuaranteed> { // Use index maps to preserve original order of imports and libraries. let mut dylib_table = FxIndexMap::>::default(); @@ -2068,13 +2069,43 @@ fn linker_with_args<'a>( // 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)? + 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, + )); + } + // 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* + // handling the raw-dylib symbols in the current crate to make sure that those are chosen first + // by the linker. + let (_, dependency_linkage) = codegen_results + .crate_info + .dependency_formats + .iter() + .find(|(ty, _)| *ty == crate_type) + .expect("failed to find crate type in dependency format list"); + let native_libraries_from_nonstatics = codegen_results + .crate_info + .native_libraries + .iter() + .filter_map(|(cnum, libraries)| { + (dependency_linkage[cnum.as_usize() - 1] != Linkage::Static).then(|| libraries) + }) + .flatten(); + 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, )); } diff --git a/src/test/run-make/raw-dylib-inline-cross-dylib/Makefile b/src/test/run-make/raw-dylib-inline-cross-dylib/Makefile new file mode 100644 index 0000000000000..9e603f9583598 --- /dev/null +++ b/src/test/run-make/raw-dylib-inline-cross-dylib/Makefile @@ -0,0 +1,31 @@ +# Regression test for calling an inline function that uses a raw-dylib function. + +# only-windows + +include ../../run-make-fulldeps/tools.mk + +all: + $(RUSTC) --crate-type dylib --crate-name raw_dylib_test lib.rs -C prefer-dynamic + $(RUSTC) --crate-type dylib --crate-name raw_dylib_test_wrapper lib_wrapper.rs -C prefer-dynamic + $(RUSTC) --crate-type bin driver.rs -L "$(TMPDIR)" -C prefer-dynamic + # Make sure we don't find an import to the functions we expect to be inlined. + "$(LLVM_BIN_DIR)"/llvm-objdump -p $(TMPDIR)/driver.exe | $(CGREP) -v -e "inline_library_function" + "$(LLVM_BIN_DIR)"/llvm-objdump -p $(TMPDIR)/driver.exe | $(CGREP) -v -e "inline_library_function_calls_inline" + # Make sure we do find an import to the functions we expect to be imported. + "$(LLVM_BIN_DIR)"/llvm-objdump -p $(TMPDIR)/driver.exe | $(CGREP) -e "library_function" + $(call COMPILE_OBJ,"$(TMPDIR)"/extern_1.obj,extern_1.c) + $(call COMPILE_OBJ,"$(TMPDIR)"/extern_2.obj,extern_2.c) +ifdef IS_MSVC + $(CC) "$(TMPDIR)"/extern_1.obj -link -dll -out:"$(TMPDIR)"/extern_1.dll -noimplib + $(CC) "$(TMPDIR)"/extern_2.obj -link -dll -out:"$(TMPDIR)"/extern_2.dll -noimplib +else + $(CC) "$(TMPDIR)"/extern_1.obj -shared -o "$(TMPDIR)"/extern_1.dll + $(CC) "$(TMPDIR)"/extern_2.obj -shared -o "$(TMPDIR)"/extern_2.dll +endif + $(call RUN,driver) > "$(TMPDIR)"/output.txt + +ifdef RUSTC_BLESS_TEST + cp "$(TMPDIR)"/output.txt output.txt +else + $(DIFF) output.txt "$(TMPDIR)"/output.txt +endif diff --git a/src/test/run-make/raw-dylib-inline-cross-dylib/driver.rs b/src/test/run-make/raw-dylib-inline-cross-dylib/driver.rs new file mode 100644 index 0000000000000..f72ded7d9f638 --- /dev/null +++ b/src/test/run-make/raw-dylib-inline-cross-dylib/driver.rs @@ -0,0 +1,21 @@ +#![feature(raw_dylib)] + +extern crate raw_dylib_test; +extern crate raw_dylib_test_wrapper; + +#[link(name = "extern_2", kind = "raw-dylib")] +extern { + fn extern_fn_2(); +} + +fn main() { + // NOTE: The inlined call to `extern_fn_2` links against the function in extern_2.dll instead + // of extern_1.dll since raw-dylib symbols from the current crate are passed to the linker + // first, so any ambiguous names will prefer the current crate's definition. + raw_dylib_test::inline_library_function(); + raw_dylib_test::library_function(); + raw_dylib_test_wrapper::inline_library_function_calls_inline(); + unsafe { + extern_fn_2(); + } +} diff --git a/src/test/run-make/raw-dylib-inline-cross-dylib/extern_1.c b/src/test/run-make/raw-dylib-inline-cross-dylib/extern_1.c new file mode 100644 index 0000000000000..e5baaf5f04007 --- /dev/null +++ b/src/test/run-make/raw-dylib-inline-cross-dylib/extern_1.c @@ -0,0 +1,11 @@ +#include + +__declspec(dllexport) void extern_fn_1() { + printf("extern_fn_1\n"); + fflush(stdout); +} + +__declspec(dllexport) void extern_fn_2() { + printf("extern_fn_2 in extern_1\n"); + fflush(stdout); +} diff --git a/src/test/run-make/raw-dylib-inline-cross-dylib/extern_2.c b/src/test/run-make/raw-dylib-inline-cross-dylib/extern_2.c new file mode 100644 index 0000000000000..30aa4692238bc --- /dev/null +++ b/src/test/run-make/raw-dylib-inline-cross-dylib/extern_2.c @@ -0,0 +1,6 @@ +#include + +__declspec(dllexport) void extern_fn_2() { + printf("extern_fn_2 in extern_2\n"); + fflush(stdout); +} diff --git a/src/test/run-make/raw-dylib-inline-cross-dylib/lib.rs b/src/test/run-make/raw-dylib-inline-cross-dylib/lib.rs new file mode 100644 index 0000000000000..00c2c1c42d15f --- /dev/null +++ b/src/test/run-make/raw-dylib-inline-cross-dylib/lib.rs @@ -0,0 +1,21 @@ +#![feature(raw_dylib)] + +#[link(name = "extern_1", kind = "raw-dylib")] +extern { + fn extern_fn_1(); + fn extern_fn_2(); +} + +#[inline] +pub fn inline_library_function() { + unsafe { + extern_fn_1(); + extern_fn_2(); + } +} + +pub fn library_function() { + unsafe { + extern_fn_2(); + } +} diff --git a/src/test/run-make/raw-dylib-inline-cross-dylib/lib_wrapper.rs b/src/test/run-make/raw-dylib-inline-cross-dylib/lib_wrapper.rs new file mode 100644 index 0000000000000..47191b8de219c --- /dev/null +++ b/src/test/run-make/raw-dylib-inline-cross-dylib/lib_wrapper.rs @@ -0,0 +1,6 @@ +extern crate raw_dylib_test; + +#[inline] +pub fn inline_library_function_calls_inline() { + raw_dylib_test::inline_library_function(); +} diff --git a/src/test/run-make/raw-dylib-inline-cross-dylib/output.txt b/src/test/run-make/raw-dylib-inline-cross-dylib/output.txt new file mode 100644 index 0000000000000..e7009baa0d4c3 --- /dev/null +++ b/src/test/run-make/raw-dylib-inline-cross-dylib/output.txt @@ -0,0 +1,6 @@ +extern_fn_1 +extern_fn_2 in extern_2 +extern_fn_2 in extern_1 +extern_fn_1 +extern_fn_2 in extern_2 +extern_fn_2 in extern_2