From 4c0a5ecc0370b1735b8da771ef4ac81411057176 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 39ef4276faf..590a7374d11 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 00000000000..bf811395981 --- /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 00000000000..b3a7539abae --- /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 00000000000..154f9682ef8 --- /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 00000000000..af439391c75 --- /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 ab42a20db5dbf2ae98f5466529586597590df6ef 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 | 35 +++------ 2 files changed, 81 insertions(+), 29 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 02e21e74fad..5144319752a 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -544,10 +544,36 @@ 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 bf811395981..faa45243104 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 - -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): +all: $(RUSTC) -C prefer-dynamic bar.rs - $(RUSTC) foo.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 + $(CC) $(CFLAGS) foo.c $(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 + + $(call RUN,foo) From 6bc500117e25447b325546b0f93a8b04cbf6e7ac 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 590a7374d11..72b208a7132 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 631dd0a2146..0743392b19a 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 faa45243104..fd76f6c5578 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 b520f92d5ac465da7c28ccf891f7f35af4b1336d 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 fd76f6c5578..c3570a46327 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 d1333f51870f2d363c0e39c988666f9cbd63050b 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 c3570a46327..08e4fa5d3aa 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 3f6a2bcc7860a42fb72c39fb9d48666ddc88d266 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 08e4fa5d3aa..a1e86a7ce4b 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