diff options
author | 2025-02-12 21:36:49 +0000 | |
---|---|---|
committer | 2025-02-19 17:39:32 +0000 | |
commit | 610eb1a1e299206cb59c0a4a7bc6ce05a028d537 (patch) | |
tree | bb402a5bd52150f9768841289f9db201e42dcf89 | |
parent | 14b1c563d1347c14a96cf1d14753f1276028586d (diff) |
rust: Propagate MTO libs linked whole through dylibs.
Because dylibs don't export symbols of whole-archive staticlibs, we
simulate this by propagating those static libs through to dependent
modules. Similarly, we must do the same for rlibs from cc_* modules used
to generate MTO staticlibs.
Bug: 395915782
Test: New soong tests
Test: m rust
Change-Id: I83d6a08234af04af5039764f5a80f659b24aa132
-rw-r--r-- | rust/library.go | 6 | ||||
-rw-r--r-- | rust/rust.go | 33 | ||||
-rw-r--r-- | rust/rust_test.go | 64 |
3 files changed, 94 insertions, 9 deletions
diff --git a/rust/library.go b/rust/library.go index 7f5861fe8..415785a16 100644 --- a/rust/library.go +++ b/rust/library.go @@ -744,10 +744,16 @@ func (library *libraryDecorator) compile(ctx ModuleContext, flags Flags, deps Pa } if library.rlib() { ccExporter.RustRlibDeps = append(ccExporter.RustRlibDeps, deps.reexportedCcRlibDeps...) + ccExporter.RustRlibDeps = append(ccExporter.RustRlibDeps, deps.reexportedWholeCcRlibDeps...) } android.SetProvider(ctx, cc.FlagExporterInfoProvider, ccExporter) } + if library.dylib() { + // reexport whole-static'd dependencies for dylibs. + library.flagExporter.wholeRustRlibDeps = append(library.flagExporter.wholeRustRlibDeps, deps.reexportedWholeCcRlibDeps...) + } + if library.shared() || library.stubs() { // Optimize out relinking against shared libraries whose interface hasn't changed by // depending on a table of contents file instead of the library itself. diff --git a/rust/rust.go b/rust/rust.go index 7a7b1064c..a02ca6073 100644 --- a/rust/rust.go +++ b/rust/rust.go @@ -496,8 +496,9 @@ type PathDeps struct { depLinkFlags []string // track cc static-libs that have Rlib dependencies - reexportedCcRlibDeps []cc.RustRlibDep - ccRlibDeps []cc.RustRlibDep + reexportedCcRlibDeps []cc.RustRlibDep + reexportedWholeCcRlibDeps []cc.RustRlibDep + ccRlibDeps []cc.RustRlibDep // linkDirs are link paths passed via -L to rustc. linkObjects are objects passed directly to the linker // Both of these are exported and propagate to dependencies. @@ -555,6 +556,7 @@ type flagExporter struct { staticLibObjects []string sharedLibObjects []string wholeStaticLibObjects []string + wholeRustRlibDeps []cc.RustRlibDep } func (flagExporter *flagExporter) exportLinkDirs(dirs ...string) { @@ -584,6 +586,7 @@ func (flagExporter *flagExporter) setRustProvider(ctx ModuleContext) { StaticLibObjects: flagExporter.staticLibObjects, WholeStaticLibObjects: flagExporter.wholeStaticLibObjects, SharedLibPaths: flagExporter.sharedLibObjects, + WholeRustRlibDeps: flagExporter.wholeRustRlibDeps, }) } @@ -600,6 +603,7 @@ type RustFlagExporterInfo struct { StaticLibObjects []string WholeStaticLibObjects []string SharedLibPaths []string + WholeRustRlibDeps []cc.RustRlibDep } var RustFlagExporterInfoProvider = blueprint.NewProvider[RustFlagExporterInfo]() @@ -1585,8 +1589,8 @@ func (mod *Module) depsToPaths(ctx android.ModuleContext) PathDeps { directSrcProvidersDeps = append(directSrcProvidersDeps, &dep) } + exportedRustInfo, _ := android.OtherModuleProvider(ctx, dep, RustFlagExporterInfoProvider) exportedInfo, _ := android.OtherModuleProvider(ctx, dep, RustFlagExporterInfoProvider) - //Append the dependencies exported objects, except for proc-macros which target a different arch/OS if depTag != procMacroDepTag { depPaths.depFlags = append(depPaths.depFlags, exportedInfo.Flags...) @@ -1595,6 +1599,11 @@ func (mod *Module) depsToPaths(ctx android.ModuleContext) PathDeps { depPaths.staticLibObjects = append(depPaths.staticLibObjects, exportedInfo.StaticLibObjects...) depPaths.wholeStaticLibObjects = append(depPaths.wholeStaticLibObjects, exportedInfo.WholeStaticLibObjects...) depPaths.linkDirs = append(depPaths.linkDirs, exportedInfo.LinkDirs...) + + depPaths.reexportedWholeCcRlibDeps = append(depPaths.reexportedWholeCcRlibDeps, exportedRustInfo.WholeRustRlibDeps...) + if !mod.Rlib() { + depPaths.ccRlibDeps = append(depPaths.ccRlibDeps, exportedRustInfo.WholeRustRlibDeps...) + } } if depTag == dylibDepTag || depTag == rlibDepTag || depTag == procMacroDepTag { @@ -1656,17 +1665,26 @@ func (mod *Module) depsToPaths(ctx android.ModuleContext) PathDeps { } } + exportedInfo, _ := android.OtherModuleProvider(ctx, dep, cc.FlagExporterInfoProvider) if cc.IsWholeStaticLib(depTag) { // Add whole staticlibs to wholeStaticLibObjects to propagate to Rust all dependents. depPaths.wholeStaticLibObjects = append(depPaths.wholeStaticLibObjects, ccLibPath.String()) + + // We also propagate forward whole-static'd cc staticlibs with rust_ffi_rlib dependencies + // We don't need to check a hypothetical exportedRustInfo.WholeRustRlibDeps because we + // wouldn't expect a rust_ffi_rlib to be listed in `static_libs` (Soong explicitly disallows this) + depPaths.reexportedWholeCcRlibDeps = append(depPaths.reexportedWholeCcRlibDeps, exportedInfo.RustRlibDeps...) } else { - // Otherwise add to staticLibObjects, which only propagate through rlibs to their dependents. + // If not whole_static, add to staticLibObjects, which only propagate through rlibs to their dependents. depPaths.staticLibObjects = append(depPaths.staticLibObjects, ccLibPath.String()) + + if mod.Rlib() { + // rlibs propagate their inherited rust_ffi_rlibs forward. + depPaths.reexportedCcRlibDeps = append(depPaths.reexportedCcRlibDeps, exportedInfo.RustRlibDeps...) + } } depPaths.linkDirs = append(depPaths.linkDirs, linkPath) - - exportedInfo, _ := android.OtherModuleProvider(ctx, dep, cc.FlagExporterInfoProvider) depPaths.depIncludePaths = append(depPaths.depIncludePaths, exportedInfo.IncludeDirs...) depPaths.depSystemIncludePaths = append(depPaths.depSystemIncludePaths, exportedInfo.SystemIncludeDirs...) depPaths.depClangFlags = append(depPaths.depClangFlags, exportedInfo.Flags...) @@ -1675,8 +1693,6 @@ func (mod *Module) depsToPaths(ctx android.ModuleContext) PathDeps { if !mod.Rlib() { // rlibs don't need to build the generated static library, so they don't need to track these. depPaths.ccRlibDeps = append(depPaths.ccRlibDeps, exportedInfo.RustRlibDeps...) - } else { - depPaths.reexportedCcRlibDeps = append(depPaths.reexportedCcRlibDeps, exportedInfo.RustRlibDeps...) } directStaticLibDeps = append(directStaticLibDeps, linkableInfo) @@ -1835,6 +1851,7 @@ func (mod *Module) depsToPaths(ctx android.ModuleContext) PathDeps { depPaths.depSystemIncludePaths = android.FirstUniquePaths(depPaths.depSystemIncludePaths) depPaths.depLinkFlags = android.FirstUniqueStrings(depPaths.depLinkFlags) depPaths.reexportedCcRlibDeps = android.FirstUniqueFunc(depPaths.reexportedCcRlibDeps, cc.EqRustRlibDeps) + depPaths.reexportedWholeCcRlibDeps = android.FirstUniqueFunc(depPaths.reexportedWholeCcRlibDeps, cc.EqRustRlibDeps) depPaths.ccRlibDeps = android.FirstUniqueFunc(depPaths.ccRlibDeps, cc.EqRustRlibDeps) return depPaths diff --git a/rust/rust_test.go b/rust/rust_test.go index fbb994752..f634bb5cd 100644 --- a/rust/rust_test.go +++ b/rust/rust_test.go @@ -456,6 +456,13 @@ func TestRustFFIRlibs(t *testing.T) { } rust_ffi_static { + name: "libfoo_from_rlib_whole", + crate_name: "foo_from_rlib_whole", + srcs: ["src/lib.rs"], + export_include_dirs: ["foo_includes"] + } + + rust_ffi_static { name: "libbuzz", crate_name: "buzz", srcs: ["src/lib.rs"], @@ -469,6 +476,13 @@ func TestRustFFIRlibs(t *testing.T) { export_include_dirs: ["buzz_includes"] } + rust_ffi_static { + name: "libbuzz_from_rlib_whole", + crate_name: "buzz_from_rlib_whole", + srcs: ["src/lib.rs"], + export_include_dirs: ["buzz_includes"] + } + cc_library_shared { name: "libcc_shared", srcs:["foo.c"], @@ -489,6 +503,13 @@ func TestRustFFIRlibs(t *testing.T) { whole_static_libs: ["libfoo_from_rlib"], } + cc_library_static { + name: "libcc_whole_static_from_rlib", + srcs:["foo.c"], + static_libs: ["libbuzz_from_rlib_whole"], + whole_static_libs: ["libfoo_from_rlib_whole"], + } + cc_binary { name: "ccBin", srcs:["foo.c"], @@ -500,6 +521,14 @@ func TestRustFFIRlibs(t *testing.T) { srcs:["src/foo.rs"], crate_name: "rs", static_libs: ["libcc_static_from_rlib"], + whole_static_libs: ["libcc_whole_static_from_rlib"], + } + + rust_library { + name: "librs2", + srcs:["src/foo.rs"], + crate_name: "rs", + rustlibs: ["librs"], } rust_binary { @@ -509,7 +538,7 @@ func TestRustFFIRlibs(t *testing.T) { rlibs: ["librs", "libbar"], static_libs: ["libcc_static"], } - `) + `) libbar := ctx.ModuleForTests(t, "libbar", "android_arm64_armv8-a_rlib_rlib-std").Rule("rustc") libcc_shared_rustc := ctx.ModuleForTests(t, "libcc_shared", "android_arm64_armv8-a_shared").Rule("rustc") @@ -521,6 +550,10 @@ func TestRustFFIRlibs(t *testing.T) { rustbin_genlib := ctx.ModuleForTests(t, "rsBin", "android_arm64_armv8-a").Output("generated_rust_staticlib/librustlibs.a") rustbin := ctx.ModuleForTests(t, "rsBin", "android_arm64_armv8-a").Output("unstripped/rsBin") librs_rlib := ctx.ModuleForTests(t, "librs", "android_arm64_armv8-a_rlib_dylib-std").MaybeOutput("generated_rust_staticlib/librustlibs.a") + librs2_rlib := ctx.ModuleForTests(t, "librs2", "android_arm64_armv8-a_rlib_dylib-std").MaybeOutput("generated_rust_staticlib/librustlibs.a") + librs_genlib := ctx.ModuleForTests(t, "librs", "android_arm64_armv8-a_dylib").Output("generated_rust_staticlib/librustlibs.a") + librs2_genlib := ctx.ModuleForTests(t, "librs2", "android_arm64_armv8-a_dylib").Output("generated_rust_staticlib/librustlibs.a") + librs2_dylib := ctx.ModuleForTests(t, "librs2", "android_arm64_armv8-a_dylib").Output("unstripped/librs2.dylib.so") if !strings.Contains(libbar.Args["rustcFlags"], "crate-type=rlib") { t.Errorf("missing crate-type for static variant, expecting %#v, rustcFlags: %#v", "rlib", libbar.Args["rustcFlags"]) @@ -578,6 +611,10 @@ func TestRustFFIRlibs(t *testing.T) { t.Errorf("missing generated static library in linker step libFlags in Rust module, expecting %#v, libFlags: %#v", "generated_rust_staticlib/librustlibs.a", rustbin.Args["libFlags"]) } + if !strings.Contains(librs2_dylib.Args["linkFlags"], "generated_rust_staticlib/librustlibs.a") { + t.Errorf("missing generated static library in linker step libFlags in Rust module, expecting %#v, libFlags: %#v", + "generated_rust_staticlib/librustlibs.a", librs2_dylib.Args["libFlags"]) + } // Make sure that direct dependencies and indirect whole static dependencies are // propagating correctly for the rlib -> cc_library_static -> rust_* generated library example. @@ -610,6 +647,31 @@ func TestRustFFIRlibs(t *testing.T) { if librs_rlib.Rule != nil { t.Error("rlibs should not be generating mto staticlibs", "rlib", libbar.Args["rustcFlags"]) } + if librs2_rlib.Rule != nil { + t.Error("rlibs should not be generating mto staticlibs", "rlib", libbar.Args["rustcFlags"]) + } + + // Make sure that direct whole static dependencies are propagating correctly downstream + // foo_from_rlib_whole --(ws)--> libcc_whole_static_from_rlib --(ws)--> librs + if !strings.Contains(librs_genlib.Args["libFlags"], "--extern foo_from_rlib_whole=") { + t.Errorf("Missing direct whole_static_lib dependency libfoo_from_rlib_whole from rust dylib when writing generated Rust staticlib: %#v", librs_genlib.Args["libFlags"]) + } + + // Make sure that indirect whole static dependencies are propagating correctly downstream + // foo_from_rlib_whole --(ws)--> libcc_whole_static_from_rlib --(ws)--> librs --> rust_* + if !strings.Contains(librs2_genlib.Args["libFlags"], "--extern foo_from_rlib_whole=") { + t.Errorf("Missing indirect whole_static_lib dependency libfoo_from_rlib_whole from rust dylib when writing generated Rust staticlib: %#v", librs2_genlib.Args["libFlags"]) + } + if !strings.Contains(rustbin_genlib.Args["libFlags"], "--extern foo_from_rlib_whole=") { + t.Errorf("Missing indirect whole_static_lib dependency libfoo_from_rlib_whole from rust dylib in rust binary when writing generated Rust staticlib: %#v", rustbin_genlib.Args["libFlags"]) + } + + // Make sure that normal static dependencies are not propagating through dylib dependencies + // buzz_from_rlib_whole --(s)--> libcc_whole_static_from_rlib --(ws)--> librs --> rust_* + if strings.Contains(librs2_genlib.Args["libFlags"], "--extern buzz_from_rlib_whole=") { + t.Errorf("dependency from indirect cc staticlib from direct dylib dep found in rust dylib when writing generated Rust staticlib: %#v", librs2_genlib.Args["libFlags"]) + } + } func assertString(t *testing.T, got, expected string) { |