From 09e1fae118bce2530ef34e50f666b46cc2070e41 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Wed, 28 Dec 2022 17:10:21 +0000 Subject: [PATCH 1/6] Support linking to rust dylibs from a staticlib --- .../rustc_metadata/src/dependency_format.rs | 23 +++++++--------- .../run-make/staticlib-dylib-linkage/Makefile | 27 +++++++++++++++++++ tests/run-make/staticlib-dylib-linkage/bar.rs | 5 ++++ tests/run-make/staticlib-dylib-linkage/foo.c | 10 +++++++ tests/run-make/staticlib-dylib-linkage/foo.rs | 13 +++++++++ 5 files changed, 65 insertions(+), 13 deletions(-) create mode 100644 tests/run-make/staticlib-dylib-linkage/Makefile create mode 100644 tests/run-make/staticlib-dylib-linkage/bar.rs create mode 100644 tests/run-make/staticlib-dylib-linkage/foo.c create mode 100644 tests/run-make/staticlib-dylib-linkage/foo.rs diff --git a/compiler/rustc_metadata/src/dependency_format.rs b/compiler/rustc_metadata/src/dependency_format.rs index 39ef4276faf10..590a7374d116d 100644 --- a/compiler/rustc_metadata/src/dependency_format.rs +++ b/compiler/rustc_metadata/src/dependency_format.rs @@ -89,11 +89,12 @@ fn calculate_type(tcx: TyCtxt<'_>, ty: CrateType) -> DependencyList { // to try to eagerly statically link all dependencies. This is normally // done for end-product dylibs, not intermediate products. // - // Treat cdylibs similarly. If `-C prefer-dynamic` is set, the caller may - // be code-size conscious, but without it, it makes sense to statically - // link a cdylib. - CrateType::Dylib | CrateType::Cdylib if !sess.opts.cg.prefer_dynamic => Linkage::Static, - CrateType::Dylib | CrateType::Cdylib => Linkage::Dynamic, + // Treat cdylibs and staticlibs similarly. If `-C prefer-dynamic` is set, + // the caller may be code-size conscious, but without it, it makes sense + // to statically link a cdylib or staticlib. + CrateType::Dylib | CrateType::Cdylib | CrateType::Staticlib => { + if sess.opts.cg.prefer_dynamic { Linkage::Dynamic } else { Linkage::Static } + } // If the global prefer_dynamic switch is turned off, or the final // executable will be statically linked, prefer static crate linkage. @@ -108,9 +109,6 @@ fn calculate_type(tcx: TyCtxt<'_>, ty: CrateType) -> DependencyList { // No linkage happens with rlibs, we just needed the metadata (which we // got long ago), so don't bother with anything. CrateType::Rlib => Linkage::NotLinked, - - // staticlibs must have all static dependencies. - CrateType::Staticlib => Linkage::Static, }; match preferred_linkage { @@ -123,12 +121,11 @@ fn calculate_type(tcx: TyCtxt<'_>, ty: CrateType) -> DependencyList { return v; } - // Staticlibs and static executables must have all static dependencies. + // Static executables must have all static dependencies. // If any are not found, generate some nice pretty errors. - if ty == CrateType::Staticlib - || (ty == CrateType::Executable - && sess.crt_static(Some(ty)) - && !sess.target.crt_static_allows_dylibs) + if ty == CrateType::Executable + && sess.crt_static(Some(ty)) + && !sess.target.crt_static_allows_dylibs { for &cnum in tcx.crates(()).iter() { if tcx.dep_kind(cnum).macros_only() { diff --git a/tests/run-make/staticlib-dylib-linkage/Makefile b/tests/run-make/staticlib-dylib-linkage/Makefile new file mode 100644 index 0000000000000..bf811395981e5 --- /dev/null +++ b/tests/run-make/staticlib-dylib-linkage/Makefile @@ -0,0 +1,27 @@ +include ../tools.mk + +TARGET_SYSROOT := $(shell $(RUSTC) --print sysroot)/lib/rustlib/$(TARGET)/lib + +ifdef IS_MSVC +LIBSTD := $(wildcard $(TARGET_SYSROOT)/libstd-*.dll.lib) +else +LIBSTD := $(wildcard $(TARGET_SYSROOT)/$(call DYLIB_GLOB,std)) +STD := $(basename $(patsubst lib%,%, $(notdir $(LIBSTD)))) +endif + +all: $(call RUN_BINFILE,foo) + $(call RUN,foo) + +ifdef IS_MSVC +CLIBS := $(TMPDIR)/foo.lib $(TMPDIR)/bar.dll.lib $(LIBSTD) +$(call RUN_BINFILE,foo): $(call STATICLIB,foo) + $(CC) $(CFLAGS) foo.c $(CLIBS) $(call OUT_EXE,foo) +else +CLIBS := $(TMPDIR)/libfoo.a -lbar -l$(STD) -L $(TMPDIR) -L $(TARGET_SYSROOT) +$(call RUN_BINFILE,foo): $(call STATICLIB,foo) + $(CC) $(CFLAGS) foo.c $(CLIBS) -o $(call RUN_BINFILE,foo) +endif + +$(call STATICLIB,foo): + $(RUSTC) -C prefer-dynamic bar.rs + $(RUSTC) foo.rs diff --git a/tests/run-make/staticlib-dylib-linkage/bar.rs b/tests/run-make/staticlib-dylib-linkage/bar.rs new file mode 100644 index 0000000000000..b3a7539abaeff --- /dev/null +++ b/tests/run-make/staticlib-dylib-linkage/bar.rs @@ -0,0 +1,5 @@ +#![crate_type = "dylib"] + +pub fn bar() { + println!("hello!"); +} diff --git a/tests/run-make/staticlib-dylib-linkage/foo.c b/tests/run-make/staticlib-dylib-linkage/foo.c new file mode 100644 index 0000000000000..154f9682ef8f1 --- /dev/null +++ b/tests/run-make/staticlib-dylib-linkage/foo.c @@ -0,0 +1,10 @@ +#include + +extern void foo(); +extern unsigned bar(unsigned a, unsigned b); + +int main() { + foo(); + assert(bar(1, 2) == 3); + return 0; +} diff --git a/tests/run-make/staticlib-dylib-linkage/foo.rs b/tests/run-make/staticlib-dylib-linkage/foo.rs new file mode 100644 index 0000000000000..af439391c757e --- /dev/null +++ b/tests/run-make/staticlib-dylib-linkage/foo.rs @@ -0,0 +1,13 @@ +#![crate_type = "staticlib"] + +extern crate bar; + +#[no_mangle] +pub extern "C" fn foo() { + bar::bar(); +} + +#[no_mangle] +pub extern "C" fn bar(a: u32, b: u32) -> u32 { + a + b +} From 39ba9dadeed12f67e44ae00ebdcbcebc0edf3d61 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Wed, 28 Dec 2022 18:13:19 +0000 Subject: [PATCH 2/6] Support `--print native-static-libs` with rust dylibs --- compiler/rustc_codegen_ssa/src/back/link.rs | 75 +++++++++++++++++-- .../run-make/staticlib-dylib-linkage/Makefile | 25 ++----- 2 files changed, 76 insertions(+), 24 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 02e21e74fadc8..5144319752a4c 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -544,12 +544,38 @@ fn link_staticlib<'a>( ab.build(out_filename); - if !all_native_libs.is_empty() { - if sess.opts.prints.contains(&PrintRequest::NativeStaticLibs) { - print_native_static_libs(sess, &all_native_libs); + let crates = codegen_results.crate_info.used_crates.iter(); + + let fmts = codegen_results + .crate_info + .dependency_formats + .iter() + .find_map(|&(ty, ref list)| if ty == CrateType::Staticlib { Some(list) } else { None }) + .expect("no dependency formats for staticlib"); + + let mut all_rust_dylibs = vec![]; + for &cnum in crates { + match fmts.get(cnum.as_usize() - 1) { + Some(&Linkage::Dynamic) => {} + _ => continue, + } + let crate_name = codegen_results.crate_info.crate_name[&cnum]; + let used_crate_source = &codegen_results.crate_info.used_crate_source[&cnum]; + if let Some((path, _)) = &used_crate_source.dylib { + all_rust_dylibs.push(&**path); + } else { + if used_crate_source.rmeta.is_some() { + sess.emit_fatal(errors::LinkRlibError::OnlyRmetaFound { crate_name }); + } else { + sess.emit_fatal(errors::LinkRlibError::NotFound { crate_name }); + } } } + if sess.opts.prints.contains(&PrintRequest::NativeStaticLibs) { + print_native_static_libs(sess, &all_native_libs, &all_rust_dylibs); + } + Ok(()) } @@ -1291,8 +1317,12 @@ enum RlibFlavor { StaticlibBase, } -fn print_native_static_libs(sess: &Session, all_native_libs: &[NativeLib]) { - let lib_args: Vec<_> = all_native_libs +fn print_native_static_libs( + sess: &Session, + all_native_libs: &[NativeLib], + all_rust_dylibs: &[&Path], +) { + let mut lib_args: Vec<_> = all_native_libs .iter() .filter(|l| relevant_lib(sess, l)) .filter_map(|lib| { @@ -1322,6 +1352,41 @@ fn print_native_static_libs(sess: &Session, all_native_libs: &[NativeLib]) { } }) .collect(); + for path in all_rust_dylibs { + // FIXME deduplicate with add_dynamic_crate + + // Just need to tell the linker about where the library lives and + // what its name is + let parent = path.parent(); + if let Some(dir) = parent { + let dir = fix_windows_verbatim_for_gcc(dir); + if sess.target.is_like_msvc { + let mut arg = String::from("/LIBPATH:"); + arg.push_str(&dir.display().to_string()); + lib_args.push(arg); + } else { + lib_args.push("-L".to_owned()); + lib_args.push(dir.display().to_string()); + } + } + let stem = path.file_stem().unwrap().to_str().unwrap(); + // Convert library file-stem into a cc -l argument. + let prefix = if stem.starts_with("lib") && !sess.target.is_like_windows { 3 } else { 0 }; + let lib = &stem[prefix..]; + let path = parent.unwrap_or_else(|| Path::new("")); + if sess.target.is_like_msvc { + // When producing a dll, the MSVC linker may not actually emit a + // `foo.lib` file if the dll doesn't actually export any symbols, so we + // check to see if the file is there and just omit linking to it if it's + // not present. + let name = format!("{}.dll.lib", lib); + if path.join(&name).exists() { + lib_args.push(name); + } + } else { + lib_args.push(format!("-l{}", lib)); + } + } if !lib_args.is_empty() { sess.emit_note(errors::StaticLibraryNativeArtifacts); // Prefix for greppability diff --git a/tests/run-make/staticlib-dylib-linkage/Makefile b/tests/run-make/staticlib-dylib-linkage/Makefile index bf811395981e5..faa4524310478 100644 --- a/tests/run-make/staticlib-dylib-linkage/Makefile +++ b/tests/run-make/staticlib-dylib-linkage/Makefile @@ -1,27 +1,14 @@ include ../tools.mk -TARGET_SYSROOT := $(shell $(RUSTC) --print sysroot)/lib/rustlib/$(TARGET)/lib +all: + $(RUSTC) -C prefer-dynamic bar.rs + $(RUSTC) foo.rs --crate-type staticlib --print native-static-libs 2>&1 | grep 'note: native-static-libs: ' | sed 's/note: native-static-libs: \(.*\)/\1/' > $(TMPDIR)/libs.txt + cat $(TMPDIR)/libs.txt ifdef IS_MSVC -LIBSTD := $(wildcard $(TARGET_SYSROOT)/libstd-*.dll.lib) + $(CC) $(CFLAGS) foo.c $(TMPDIR)/foo.lib $$(cat $(TMPDIR)/libs.txt) $(call OUT_EXE,foo) else -LIBSTD := $(wildcard $(TARGET_SYSROOT)/$(call DYLIB_GLOB,std)) -STD := $(basename $(patsubst lib%,%, $(notdir $(LIBSTD)))) + $(CC) $(CFLAGS) foo.c -L $(TMPDIR) -lfoo $$(cat $(TMPDIR)/libs.txt) -o $(call RUN_BINFILE,foo) endif -all: $(call RUN_BINFILE,foo) $(call RUN,foo) - -ifdef IS_MSVC -CLIBS := $(TMPDIR)/foo.lib $(TMPDIR)/bar.dll.lib $(LIBSTD) -$(call RUN_BINFILE,foo): $(call STATICLIB,foo) - $(CC) $(CFLAGS) foo.c $(CLIBS) $(call OUT_EXE,foo) -else -CLIBS := $(TMPDIR)/libfoo.a -lbar -l$(STD) -L $(TMPDIR) -L $(TARGET_SYSROOT) -$(call RUN_BINFILE,foo): $(call STATICLIB,foo) - $(CC) $(CFLAGS) foo.c $(CLIBS) -o $(call RUN_BINFILE,foo) -endif - -$(call STATICLIB,foo): - $(RUSTC) -C prefer-dynamic bar.rs - $(RUSTC) foo.rs From 83f96e81423b63af89d04899e4710ca883c716f2 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Wed, 4 Jan 2023 20:24:03 +0000 Subject: [PATCH 3/6] Add unstable feature flags --- .../rustc_metadata/src/dependency_format.rs | 26 ++++++++++++++----- compiler/rustc_session/src/options.rs | 4 +++ .../run-make/staticlib-dylib-linkage/Makefile | 4 ++- 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_metadata/src/dependency_format.rs b/compiler/rustc_metadata/src/dependency_format.rs index 590a7374d116d..72b208a713276 100644 --- a/compiler/rustc_metadata/src/dependency_format.rs +++ b/compiler/rustc_metadata/src/dependency_format.rs @@ -91,9 +91,22 @@ fn calculate_type(tcx: TyCtxt<'_>, ty: CrateType) -> DependencyList { // // Treat cdylibs and staticlibs similarly. If `-C prefer-dynamic` is set, // the caller may be code-size conscious, but without it, it makes sense - // to statically link a cdylib or staticlib. - CrateType::Dylib | CrateType::Cdylib | CrateType::Staticlib => { - if sess.opts.cg.prefer_dynamic { Linkage::Dynamic } else { Linkage::Static } + // to statically link a cdylib or staticlib. For staticlibs we use + // `-Z staticlib-prefer-dynamic` for now. This may be merged into + // `-C prefer-dynamic` in the future. + CrateType::Dylib | CrateType::Cdylib => { + if sess.opts.cg.prefer_dynamic { + Linkage::Dynamic + } else { + Linkage::Static + } + } + CrateType::Staticlib => { + if sess.opts.unstable_opts.staticlib_prefer_dynamic { + Linkage::Dynamic + } else { + Linkage::Static + } } // If the global prefer_dynamic switch is turned off, or the final @@ -123,9 +136,10 @@ fn calculate_type(tcx: TyCtxt<'_>, ty: CrateType) -> DependencyList { // Static executables must have all static dependencies. // If any are not found, generate some nice pretty errors. - if ty == CrateType::Executable - && sess.crt_static(Some(ty)) - && !sess.target.crt_static_allows_dylibs + if (ty == CrateType::Staticlib && !sess.opts.unstable_opts.staticlib_allow_rdylib_deps) + || (ty == CrateType::Executable + && sess.crt_static(Some(ty)) + && !sess.target.crt_static_allows_dylibs) { for &cnum in tcx.crates(()).iter() { if tcx.dep_kind(cnum).macros_only() { diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 631dd0a2146e8..0743392b19a58 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -1709,6 +1709,10 @@ options! { #[rustc_lint_opt_deny_field_access("use `Session::stack_protector` instead of this field")] stack_protector: StackProtector = (StackProtector::None, parse_stack_protector, [TRACKED], "control stack smash protection strategy (`rustc --print stack-protector-strategies` for details)"), + staticlib_allow_rdylib_deps: bool = (false, parse_bool, [TRACKED], + "allow staticlibs to have rust dylib dependencies"), + staticlib_prefer_dynamic: bool = (false, parse_bool, [TRACKED], + "prefer dynamic linking to static linking for staticlibs (default: no)"), strict_init_checks: bool = (false, parse_bool, [TRACKED], "control if mem::uninitialized and mem::zeroed panic on more UB"), strip: Strip = (Strip::None, parse_strip, [UNTRACKED], diff --git a/tests/run-make/staticlib-dylib-linkage/Makefile b/tests/run-make/staticlib-dylib-linkage/Makefile index faa4524310478..fd76f6c55784d 100644 --- a/tests/run-make/staticlib-dylib-linkage/Makefile +++ b/tests/run-make/staticlib-dylib-linkage/Makefile @@ -2,7 +2,9 @@ include ../tools.mk all: $(RUSTC) -C prefer-dynamic bar.rs - $(RUSTC) foo.rs --crate-type staticlib --print native-static-libs 2>&1 | grep 'note: native-static-libs: ' | sed 's/note: native-static-libs: \(.*\)/\1/' > $(TMPDIR)/libs.txt + $(RUSTC) foo.rs --crate-type staticlib --print native-static-libs \ + -Z staticlib-allow-rdylib-deps 2>&1 | grep 'note: native-static-libs: ' \ + | sed 's/note: native-static-libs: \(.*\)/\1/' > $(TMPDIR)/libs.txt cat $(TMPDIR)/libs.txt ifdef IS_MSVC From d6fc3667dd3a3cd71897eba33c952077c24a65bd Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sat, 4 Mar 2023 11:06:33 +0000 Subject: [PATCH 4/6] Ignore test on MSVC for now I can't figure out how to link with the MSVC toolchain --- tests/run-make/staticlib-dylib-linkage/Makefile | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/run-make/staticlib-dylib-linkage/Makefile b/tests/run-make/staticlib-dylib-linkage/Makefile index fd76f6c55784d..c3570a4632774 100644 --- a/tests/run-make/staticlib-dylib-linkage/Makefile +++ b/tests/run-make/staticlib-dylib-linkage/Makefile @@ -1,5 +1,7 @@ include ../tools.mk +# ignore-msvc FIXME(bjorn3) can't figure out how to link with the MSVC toolchain + all: $(RUSTC) -C prefer-dynamic bar.rs $(RUSTC) foo.rs --crate-type staticlib --print native-static-libs \ @@ -8,7 +10,8 @@ all: cat $(TMPDIR)/libs.txt ifdef IS_MSVC - $(CC) $(CFLAGS) foo.c $(TMPDIR)/foo.lib $$(cat $(TMPDIR)/libs.txt) $(call OUT_EXE,foo) + $(CC) $(CFLAGS) /c foo.c /Fo:$(TMPDIR)/foo.o + $(RUSTC_LINKER) $(TMPDIR)/foo.o $(TMPDIR)/foo.lib $$(cat $(TMPDIR)/libs.txt) $(call OUT_EXE,foo) else $(CC) $(CFLAGS) foo.c -L $(TMPDIR) -lfoo $$(cat $(TMPDIR)/libs.txt) -o $(call RUN_BINFILE,foo) endif From 2a3ef11e3b21d6b16b50cac5198c0ba6d70f954e Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Wed, 19 Apr 2023 19:09:18 +0000 Subject: [PATCH 5/6] Ignore test on wasm as dylibs aren't supported --- tests/run-make/staticlib-dylib-linkage/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/run-make/staticlib-dylib-linkage/Makefile b/tests/run-make/staticlib-dylib-linkage/Makefile index c3570a4632774..08e4fa5d3aae3 100644 --- a/tests/run-make/staticlib-dylib-linkage/Makefile +++ b/tests/run-make/staticlib-dylib-linkage/Makefile @@ -1,6 +1,7 @@ include ../tools.mk # ignore-msvc FIXME(bjorn3) can't figure out how to link with the MSVC toolchain +# ignore-wasm wasm doesn't support dynamic libraries all: $(RUSTC) -C prefer-dynamic bar.rs From 47be0605a8bf2aec090322a0c604ebbf290206aa Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 27 Apr 2023 18:22:06 +0000 Subject: [PATCH 6/6] Ignore test when cross compiling --- tests/run-make/staticlib-dylib-linkage/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/run-make/staticlib-dylib-linkage/Makefile b/tests/run-make/staticlib-dylib-linkage/Makefile index 08e4fa5d3aae3..a1e86a7ce4b65 100644 --- a/tests/run-make/staticlib-dylib-linkage/Makefile +++ b/tests/run-make/staticlib-dylib-linkage/Makefile @@ -1,5 +1,6 @@ include ../tools.mk +# ignore-cross-compile # ignore-msvc FIXME(bjorn3) can't figure out how to link with the MSVC toolchain # ignore-wasm wasm doesn't support dynamic libraries