rust: made-to-order rust staticlibs

Whenever any two Rust static libraries are included
as static libraries anywhere in a CC dependency tree, we sometimes
get duplicate symbol errors. To avoid this, we no longer
directly link multiple rust static libs to CC modules.

Instead, we build rust_ffi_rlib modules and produce the actual
static library that gets linked against the CC module based on
that CC module's full list of Rust rlib dependencies.

This introduces a new static_rlibs property for cc modules to
define the rust_ffi_rlib dependencies, which are then used to
generate the module above.

This CL is intended to deprecate rust_ffi_static. It leaves
rust_ffi_static and rust_ffi static variants in place until
the remaining rust_ffi_static declarations and uses can be
removed. In the meantime, rust_ffi_static produces
rust_ffi_rlib variants as well to make the transition easier.

Bug: 254469782
Test: m # with no changes
Test: m libapexsupport # with static_rlibs
Test: m libunwindstack # with static_rlibs
Test: m netsimd # with static_rlibs, no duplicate symbols
Test: m blueprint_tests # New Soong tests

Change-Id: I47e27ac967ef0cad46d398ebf59d8275929ae28a
diff --git a/android/util.go b/android/util.go
index 698a856..de4ca4d 100644
--- a/android/util.go
+++ b/android/util.go
@@ -302,6 +302,24 @@
 	return removed, result
 }
 
+// FirstUniqueFunc returns all unique elements of a slice, keeping the first copy of
+// each.  It does not modify the input slice. The eq function should return true
+// if two elements can be considered equal.
+func FirstUniqueFunc[SortableList ~[]Sortable, Sortable any](list SortableList, eq func(a, b Sortable) bool) SortableList {
+	k := 0
+outer:
+	for i := 0; i < len(list); i++ {
+		for j := 0; j < k; j++ {
+			if eq(list[i], list[j]) {
+				continue outer
+			}
+		}
+		list[k] = list[i]
+		k++
+	}
+	return list[:k]
+}
+
 // FirstUniqueStrings returns all unique elements of a slice of strings, keeping the first copy of
 // each.  It does not modify the input slice.
 func FirstUniqueStrings(list []string) []string {
diff --git a/apex/apex.go b/apex/apex.go
index ef57d7e..0fc5a5e 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -702,7 +702,12 @@
 func addDependenciesForNativeModules(ctx android.BottomUpMutatorContext, nativeModules ApexNativeDependencies, target android.Target, imageVariation string) {
 	binVariations := target.Variations()
 	libVariations := append(target.Variations(), blueprint.Variation{Mutator: "link", Variation: "shared"})
-	rustLibVariations := append(target.Variations(), blueprint.Variation{Mutator: "rust_libraries", Variation: "dylib"})
+	rustLibVariations := append(
+		target.Variations(), []blueprint.Variation{
+			{Mutator: "rust_libraries", Variation: "dylib"},
+			{Mutator: "link", Variation: ""},
+		}...,
+	)
 
 	// Append "image" variation
 	binVariations = append(binVariations, blueprint.Variation{Mutator: "image", Variation: imageVariation})
diff --git a/cc/binary.go b/cc/binary.go
index 7aa8e20..3ff35de 100644
--- a/cc/binary.go
+++ b/cc/binary.go
@@ -18,6 +18,7 @@
 	"path/filepath"
 
 	"android/soong/android"
+
 	"github.com/google/blueprint"
 )
 
@@ -425,6 +426,10 @@
 	validations = append(validations, objs.tidyDepFiles...)
 	linkerDeps = append(linkerDeps, flags.LdFlagsDeps...)
 
+	if generatedLib := generateRustStaticlib(ctx, deps.RustRlibDeps); generatedLib != nil {
+		deps.StaticLibs = append(deps.StaticLibs, generatedLib)
+	}
+
 	// Register link action.
 	transformObjToDynamicBinary(ctx, objs.objFiles, sharedLibs, deps.StaticLibs,
 		deps.LateStaticLibs, deps.WholeStaticLibs, linkerDeps, deps.CrtBegin, deps.CrtEnd, true,
diff --git a/cc/builder.go b/cc/builder.go
index e78b8c0..3eed1f2 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -19,6 +19,7 @@
 // functions.
 
 import (
+	"fmt"
 	"path/filepath"
 	"runtime"
 	"strconv"
@@ -330,6 +331,15 @@
 			CommandDeps: []string{"$cxxExtractor", "$kytheVnames"},
 		},
 		"cFlags")
+
+	// Function pointer for producting staticlibs from rlibs. Corresponds to
+	// rust.TransformRlibstoStaticlib(), initialized in soong-rust (rust/builder.go init())
+	//
+	// This is required since soong-rust depends on soong-cc, so soong-cc cannot depend on soong-rust
+	// without resulting in a circular dependency. Setting this function pointer in soong-rust allows
+	// soong-cc to call into this particular function.
+	TransformRlibstoStaticlib (func(ctx android.ModuleContext, mainSrc android.Path, deps []RustRlibDep,
+		outputFile android.WritablePath) android.Path) = nil
 )
 
 func PwdPrefix() string {
@@ -774,6 +784,47 @@
 	}
 }
 
+// Generate a Rust staticlib from a list of rlibDeps. Returns nil if TransformRlibstoStaticlib is nil or rlibDeps is empty.
+func generateRustStaticlib(ctx android.ModuleContext, rlibDeps []RustRlibDep) android.Path {
+	if TransformRlibstoStaticlib == nil && len(rlibDeps) > 0 {
+		// This should only be reachable if a module defines static_rlibs and
+		// soong-rust hasn't been loaded alongside soong-cc (e.g. in soong-cc tests).
+		panic(fmt.Errorf("TransformRlibstoStaticlib is not set and static_rlibs is defined in %s", ctx.ModuleName()))
+	} else if len(rlibDeps) == 0 {
+		return nil
+	}
+
+	output := android.PathForModuleOut(ctx, "generated_rust_staticlib", "lib"+ctx.ModuleName()+"_rust_staticlib.a")
+	stemFile := output.ReplaceExtension(ctx, "rs")
+	crateNames := []string{}
+
+	// Collect crate names
+	for _, lib := range rlibDeps {
+		// Exclude libstd so this can support no_std builds.
+		if lib.CrateName != "libstd" {
+			crateNames = append(crateNames, lib.CrateName)
+		}
+	}
+
+	// Deduplicate any crateNames just to be safe
+	crateNames = android.FirstUniqueStrings(crateNames)
+
+	// Write the source file
+	android.WriteFileRule(ctx, stemFile, genRustStaticlibSrcFile(crateNames))
+
+	return TransformRlibstoStaticlib(ctx, stemFile, rlibDeps, output)
+}
+
+func genRustStaticlibSrcFile(crateNames []string) string {
+	lines := []string{
+		"// @Soong generated Source",
+	}
+	for _, crate := range crateNames {
+		lines = append(lines, fmt.Sprintf("extern crate %s;", crate))
+	}
+	return strings.Join(lines, "\n")
+}
+
 // Generate a rule for compiling multiple .o files, plus static libraries, whole static libraries,
 // and shared libraries, to a shared library (.so) or dynamic executable
 func transformObjToDynamicBinary(ctx android.ModuleContext,
diff --git a/cc/cc.go b/cc/cc.go
index e3954d7..6dd2682 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -99,6 +99,7 @@
 	StaticLibs, LateStaticLibs, WholeStaticLibs []string
 	HeaderLibs                                  []string
 	RuntimeLibs                                 []string
+	Rlibs                                       []string
 
 	// UnexportedStaticLibs are static libraries that are also passed to -Wl,--exclude-libs= to
 	// prevent automatically exporting symbols.
@@ -144,6 +145,17 @@
 	LlndkHeaderLibs []string
 }
 
+// A struct which to collect flags for rlib dependencies
+type RustRlibDep struct {
+	LibPath   android.Path // path to the rlib
+	LinkDirs  []string     // flags required for dependency (e.g. -L flags)
+	CrateName string       // crateNames associated with rlibDeps
+}
+
+func EqRustRlibDeps(a RustRlibDep, b RustRlibDep) bool {
+	return a.LibPath == b.LibPath
+}
+
 // PathDeps is a struct containing file paths to dependencies of a module.
 // It's constructed in depsToPath() by traversing the direct dependencies of the current module.
 // It's used to construct flags for various build statements (such as for compiling and linking).
@@ -156,6 +168,8 @@
 	SharedLibsDeps, EarlySharedLibsDeps, LateSharedLibsDeps android.Paths
 	// Paths to .a files
 	StaticLibs, LateStaticLibs, WholeStaticLibs android.Paths
+	// Paths and crateNames for RustStaticLib dependencies
+	RustRlibDeps []RustRlibDep
 
 	// Transitive static library dependencies of static libraries for use in ordering.
 	TranstiveStaticLibrariesForOrdering *android.DepSet[android.Path]
@@ -184,6 +198,7 @@
 	ReexportedFlags            []string
 	ReexportedGeneratedHeaders android.Paths
 	ReexportedDeps             android.Paths
+	ReexportedRustRlibDeps     []RustRlibDep
 
 	// Paths to crt*.o files
 	CrtBegin, CrtEnd android.Paths
@@ -297,6 +312,7 @@
 
 	AndroidMkSharedLibs       []string `blueprint:"mutated"`
 	AndroidMkStaticLibs       []string `blueprint:"mutated"`
+	AndroidMkRlibs            []string `blueprint:"mutated"`
 	AndroidMkRuntimeLibs      []string `blueprint:"mutated"`
 	AndroidMkWholeStaticLibs  []string `blueprint:"mutated"`
 	AndroidMkHeaderLibs       []string `blueprint:"mutated"`
@@ -653,6 +669,7 @@
 	headerLibraryDependency = iota
 	sharedLibraryDependency
 	staticLibraryDependency
+	rlibLibraryDependency
 )
 
 func (k libraryDependencyKind) String() string {
@@ -663,6 +680,8 @@
 		return "sharedLibraryDependency"
 	case staticLibraryDependency:
 		return "staticLibraryDependency"
+	case rlibLibraryDependency:
+		return "rlibLibraryDependency"
 	default:
 		panic(fmt.Errorf("unknown libraryDependencyKind %d", k))
 	}
@@ -740,6 +759,11 @@
 	return d.Kind == staticLibraryDependency
 }
 
+// rlib returns true if the libraryDependencyTag is tagging an rlib dependency.
+func (d libraryDependencyTag) rlib() bool {
+	return d.Kind == rlibLibraryDependency
+}
+
 func (d libraryDependencyTag) LicenseAnnotations() []android.LicenseAnnotation {
 	if d.shared() {
 		return []android.LicenseAnnotation{android.LicenseAnnotationSharedDependency}
@@ -1108,6 +1132,14 @@
 	return false
 }
 
+func (c *Module) CrateName() string {
+	panic(fmt.Errorf("CrateName called on non-Rust module: %q", c.BaseModuleName()))
+}
+
+func (c *Module) ExportedCrateLinkDirs() []string {
+	panic(fmt.Errorf("ExportedCrateLinkDirs called on non-Rust module: %q", c.BaseModuleName()))
+}
+
 func (c *Module) IsFuzzModule() bool {
 	if _, ok := c.compiler.(*fuzzBinary); ok {
 		return true
@@ -2300,6 +2332,7 @@
 
 	deps.WholeStaticLibs = android.LastUniqueStrings(deps.WholeStaticLibs)
 	deps.StaticLibs = android.LastUniqueStrings(deps.StaticLibs)
+	deps.Rlibs = android.LastUniqueStrings(deps.Rlibs)
 	deps.LateStaticLibs = android.LastUniqueStrings(deps.LateStaticLibs)
 	deps.SharedLibs = android.LastUniqueStrings(deps.SharedLibs)
 	deps.LateSharedLibs = android.LastUniqueStrings(deps.LateSharedLibs)
@@ -2607,6 +2640,15 @@
 		}, depTag, lib)
 	}
 
+	for _, lib := range deps.Rlibs {
+		depTag := libraryDependencyTag{Kind: rlibLibraryDependency}
+		actx.AddVariationDependencies([]blueprint.Variation{
+			{Mutator: "link", Variation: ""},
+			{Mutator: "rust_libraries", Variation: "rlib"},
+			{Mutator: "rust_stdlinkage", Variation: "rlib-std"},
+		}, depTag, lib)
+	}
+
 	// staticUnwinderDep is treated as staticDep for Q apexes
 	// so that native libraries/binaries are linked with static unwinder
 	// because Q libc doesn't have unwinder APIs
@@ -3216,6 +3258,14 @@
 				default:
 					panic(fmt.Errorf("unexpected library dependency order %d", libDepTag.Order))
 				}
+
+			case libDepTag.rlib():
+				rlibDep := RustRlibDep{LibPath: linkFile.Path(), CrateName: ccDep.CrateName(), LinkDirs: ccDep.ExportedCrateLinkDirs()}
+				depPaths.ReexportedRustRlibDeps = append(depPaths.ReexportedRustRlibDeps, rlibDep)
+				depPaths.RustRlibDeps = append(depPaths.RustRlibDeps, rlibDep)
+				depPaths.IncludeDirs = append(depPaths.IncludeDirs, depExporterInfo.IncludeDirs...)
+				depPaths.ReexportedDirs = append(depPaths.ReexportedDirs, depExporterInfo.IncludeDirs...)
+
 			case libDepTag.static():
 				staticLibraryInfo, isStaticLib := android.OtherModuleProvider(ctx, dep, StaticLibraryInfoProvider)
 				if !isStaticLib {
@@ -3268,6 +3318,12 @@
 						panic(fmt.Errorf("unexpected library dependency order %d", libDepTag.Order))
 					}
 				}
+
+				// We re-export the Rust static_rlibs so rlib dependencies don't need to be redeclared by cc_library_static dependents.
+				// E.g. libfoo (cc_library_static) depends on libfoo.ffi (a rust_ffi rlib), libbar depending on libfoo shouldn't have to also add libfoo.ffi to static_rlibs.
+				depPaths.ReexportedRustRlibDeps = append(depPaths.ReexportedRustRlibDeps, depExporterInfo.RustRlibDeps...)
+				depPaths.RustRlibDeps = append(depPaths.RustRlibDeps, depExporterInfo.RustRlibDeps...)
+
 				if libDepTag.unexportedSymbols {
 					depPaths.LdFlags = append(depPaths.LdFlags,
 						"-Wl,--exclude-libs="+staticLibraryInfo.StaticLibrary.Base())
@@ -3320,6 +3376,12 @@
 			depPaths.SystemIncludeDirs = append(depPaths.SystemIncludeDirs, depExporterInfo.SystemIncludeDirs...)
 			depPaths.GeneratedDeps = append(depPaths.GeneratedDeps, depExporterInfo.Deps...)
 			depPaths.Flags = append(depPaths.Flags, depExporterInfo.Flags...)
+			depPaths.RustRlibDeps = append(depPaths.RustRlibDeps, depExporterInfo.RustRlibDeps...)
+
+			// Only re-export RustRlibDeps for cc static libs
+			if c.static() {
+				depPaths.ReexportedRustRlibDeps = append(depPaths.ReexportedRustRlibDeps, depExporterInfo.RustRlibDeps...)
+			}
 
 			if libDepTag.reexportFlags {
 				reexportExporter(depExporterInfo)
@@ -3392,11 +3454,14 @@
 	depPaths.IncludeDirs = android.FirstUniquePaths(depPaths.IncludeDirs)
 	depPaths.SystemIncludeDirs = android.FirstUniquePaths(depPaths.SystemIncludeDirs)
 	depPaths.GeneratedDeps = android.FirstUniquePaths(depPaths.GeneratedDeps)
+	depPaths.RustRlibDeps = android.FirstUniqueFunc(depPaths.RustRlibDeps, EqRustRlibDeps)
+
 	depPaths.ReexportedDirs = android.FirstUniquePaths(depPaths.ReexportedDirs)
 	depPaths.ReexportedSystemDirs = android.FirstUniquePaths(depPaths.ReexportedSystemDirs)
 	depPaths.ReexportedFlags = android.FirstUniqueStrings(depPaths.ReexportedFlags)
 	depPaths.ReexportedDeps = android.FirstUniquePaths(depPaths.ReexportedDeps)
 	depPaths.ReexportedGeneratedHeaders = android.FirstUniquePaths(depPaths.ReexportedGeneratedHeaders)
+	depPaths.ReexportedRustRlibDeps = android.FirstUniqueFunc(depPaths.ReexportedRustRlibDeps, EqRustRlibDeps)
 
 	if c.sabi != nil {
 		c.sabi.Properties.ReexportedIncludes = android.FirstUniqueStrings(c.sabi.Properties.ReexportedIncludes)
diff --git a/cc/compiler.go b/cc/compiler.go
index 9a961cf..e52a6c3 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -772,6 +772,9 @@
 	// be added to the include path using -I
 	Local_include_dirs []string `android:"arch_variant,variant_prepend"`
 
+	// list of Rust static libraries.
+	Static_rlibs []string `android:"arch_variant,variant_prepend"`
+
 	// list of static libraries that provide headers for this binding.
 	Static_libs []string `android:"arch_variant,variant_prepend"`
 
diff --git a/cc/fuzz.go b/cc/fuzz.go
index 2436f33..2f00aa7 100644
--- a/cc/fuzz.go
+++ b/cc/fuzz.go
@@ -597,7 +597,7 @@
 	ctx.WalkDeps(func(child, parent android.Module) bool {
 
 		// If this is a Rust module which is not rust_ffi_shared, we still want to bundle any transitive
-		// shared dependencies (even for rust_ffi_static)
+		// shared dependencies (even for rust_ffi_rlib or rust_ffi_static)
 		if rustmod, ok := child.(LinkableInterface); ok && rustmod.RustLibraryInterface() && !rustmod.Shared() {
 			if recursed[ctx.OtherModuleName(child)] {
 				return false
diff --git a/cc/library.go b/cc/library.go
index a436649..2fd3ded 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -274,11 +274,12 @@
 type flagExporter struct {
 	Properties FlagExporterProperties
 
-	dirs       android.Paths // Include directories to be included with -I
-	systemDirs android.Paths // System include directories to be included with -isystem
-	flags      []string      // Exported raw flags.
-	deps       android.Paths
-	headers    android.Paths
+	dirs         android.Paths // Include directories to be included with -I
+	systemDirs   android.Paths // System include directories to be included with -isystem
+	flags        []string      // Exported raw flags.
+	deps         android.Paths
+	headers      android.Paths
+	rustRlibDeps []RustRlibDep
 }
 
 // exportedIncludes returns the effective include paths for this module and
@@ -339,6 +340,10 @@
 	f.deps = append(f.deps, deps...)
 }
 
+func (f *flagExporter) reexportRustStaticDeps(deps ...RustRlibDep) {
+	f.rustRlibDeps = append(f.rustRlibDeps, deps...)
+}
+
 // addExportedGeneratedHeaders does nothing but collects generated header files.
 // This can be differ to exportedDeps which may contain phony files to minimize ninja.
 func (f *flagExporter) addExportedGeneratedHeaders(headers ...android.Path) {
@@ -356,6 +361,8 @@
 		// Used sparingly, for extra files that need to be explicitly exported to dependers,
 		// or for phony files to minimize ninja.
 		Deps: f.deps,
+		// Used for exporting rlib deps of static libraries to dependents.
+		RustRlibDeps: f.rustRlibDeps,
 		// For exported generated headers, such as exported aidl headers, proto headers, or
 		// sysprop headers.
 		GeneratedHeaders: f.headers,
@@ -1132,9 +1139,14 @@
 	linkerDeps = append(linkerDeps, deps.EarlySharedLibsDeps...)
 	linkerDeps = append(linkerDeps, deps.SharedLibsDeps...)
 	linkerDeps = append(linkerDeps, deps.LateSharedLibsDeps...)
+
+	if generatedLib := generateRustStaticlib(ctx, deps.RustRlibDeps); generatedLib != nil {
+		deps.StaticLibs = append(deps.StaticLibs, generatedLib)
+	}
+
 	transformObjToDynamicBinary(ctx, objs.objFiles, sharedLibs,
-		deps.StaticLibs, deps.LateStaticLibs, deps.WholeStaticLibs,
-		linkerDeps, deps.CrtBegin, deps.CrtEnd, false, builderFlags, outputFile, implicitOutputs, objs.tidyDepFiles)
+		deps.StaticLibs, deps.LateStaticLibs, deps.WholeStaticLibs, linkerDeps, deps.CrtBegin,
+		deps.CrtEnd, false, builderFlags, outputFile, implicitOutputs, objs.tidyDepFiles)
 
 	objs.coverageFiles = append(objs.coverageFiles, deps.StaticLibObjs.coverageFiles...)
 	objs.coverageFiles = append(objs.coverageFiles, deps.WholeStaticLibObjs.coverageFiles...)
@@ -1594,6 +1606,10 @@
 	library.reexportDeps(deps.ReexportedDeps...)
 	library.addExportedGeneratedHeaders(deps.ReexportedGeneratedHeaders...)
 
+	if library.static() && len(deps.ReexportedRustRlibDeps) > 0 {
+		library.reexportRustStaticDeps(deps.ReexportedRustRlibDeps...)
+	}
+
 	// Optionally export aidl headers.
 	if Bool(library.Properties.Aidl.Export_aidl_headers) {
 		if library.baseCompiler.hasAidl(deps) {
@@ -2121,14 +2137,12 @@
 			// Header only
 		}
 
-	} else if library, ok := mctx.Module().(LinkableInterface); ok && library.CcLibraryInterface() {
-
+	} else if library, ok := mctx.Module().(LinkableInterface); ok && (library.CcLibraryInterface() || library.RustLibraryInterface()) {
 		// Non-cc.Modules may need an empty variant for their mutators.
 		variations := []string{}
 		if library.NonCcVariants() {
 			variations = append(variations, "")
 		}
-
 		isLLNDK := false
 		if m, ok := mctx.Module().(*Module); ok {
 			isLLNDK = m.IsLlndk()
diff --git a/cc/linkable.go b/cc/linkable.go
index 10cc38f..5579aae 100644
--- a/cc/linkable.go
+++ b/cc/linkable.go
@@ -73,6 +73,12 @@
 	// RustLibraryInterface returns true if this is a Rust library module
 	RustLibraryInterface() bool
 
+	// CrateName returns the crateName for a Rust library, panics if not a Rust library.
+	CrateName() string
+
+	// DepFlags returns a slice of Rustc string flags, panics if not a Rust library
+	ExportedCrateLinkDirs() []string
+
 	// BaseModuleName returns the android.ModuleBase.BaseModuleName() value for this module.
 	BaseModuleName() string
 
@@ -380,6 +386,7 @@
 	SystemIncludeDirs android.Paths // System include directories to be included with -isystem
 	Flags             []string      // Exported raw flags.
 	Deps              android.Paths
+	RustRlibDeps      []RustRlibDep
 	GeneratedHeaders  android.Paths
 }
 
diff --git a/cc/linker.go b/cc/linker.go
index 9686697..3896c78 100644
--- a/cc/linker.go
+++ b/cc/linker.go
@@ -39,6 +39,9 @@
 	// the dependency's .a file will be linked into this module using -Wl,--whole-archive.
 	Whole_static_libs []string `android:"arch_variant,variant_prepend"`
 
+	// list of Rust libs that should be statically linked into this module.
+	Static_rlibs []string `android:"arch_variant"`
+
 	// list of modules that should be statically linked into this module.
 	Static_libs []string `android:"arch_variant,variant_prepend"`
 
@@ -116,10 +119,14 @@
 			// product variant of the C/C++ module.
 			Static_libs []string
 
-			// list of ehader libs that only should be used to build vendor or product
+			// list of header libs that only should be used to build vendor or product
 			// variant of the C/C++ module.
 			Header_libs []string
 
+			// list of Rust libs that should be statically linked to build vendor or product
+			// variant.
+			Static_rlibs []string
+
 			// list of shared libs that should not be used to build vendor or
 			// product variant of the C/C++ module.
 			Exclude_shared_libs []string
@@ -148,6 +155,10 @@
 			// variant of the C/C++ module.
 			Static_libs []string
 
+			// list of Rust libs that should be statically linked to build the recovery
+			// variant.
+			Static_rlibs []string
+
 			// list of shared libs that should not be used to build
 			// the recovery variant of the C/C++ module.
 			Exclude_shared_libs []string
@@ -165,10 +176,14 @@
 			Exclude_runtime_libs []string
 		}
 		Ramdisk struct {
-			// list of static libs that only should be used to build the recovery
+			// list of static libs that only should be used to build the ramdisk
 			// variant of the C/C++ module.
 			Static_libs []string
 
+			// list of Rust libs that should be statically linked to build the ramdisk
+			// variant.
+			Static_rlibs []string
+
 			// list of shared libs that should not be used to build
 			// the ramdisk variant of the C/C++ module.
 			Exclude_shared_libs []string
@@ -183,9 +198,13 @@
 		}
 		Vendor_ramdisk struct {
 			// list of shared libs that should not be used to build
-			// the recovery variant of the C/C++ module.
+			// the vendor ramdisk variant of the C/C++ module.
 			Exclude_shared_libs []string
 
+			// list of Rust libs that should be statically linked to build the vendor ramdisk
+			// variant.
+			Static_rlibs []string
+
 			// list of static libs that should not be used to build
 			// the vendor ramdisk variant of the C/C++ module.
 			Exclude_static_libs []string
@@ -201,6 +220,10 @@
 			// variants.
 			Shared_libs []string
 
+			// list of Rust libs that should be statically linked to build the vendor ramdisk
+			// variant.
+			Static_rlibs []string
+
 			// list of ehader libs that only should be used to build platform variant of
 			// the C/C++ module.
 			Header_libs []string
@@ -291,6 +314,7 @@
 	deps.WholeStaticLibs = append(deps.WholeStaticLibs, linker.Properties.Whole_static_libs...)
 	deps.HeaderLibs = append(deps.HeaderLibs, linker.Properties.Header_libs...)
 	deps.StaticLibs = append(deps.StaticLibs, linker.Properties.Static_libs...)
+	deps.Rlibs = append(deps.Rlibs, linker.Properties.Static_rlibs...)
 	deps.SharedLibs = append(deps.SharedLibs, linker.Properties.Shared_libs...)
 	deps.RuntimeLibs = append(deps.RuntimeLibs, linker.Properties.Runtime_libs...)
 
@@ -333,6 +357,7 @@
 		deps.ReexportStaticLibHeaders = removeListFromList(deps.ReexportStaticLibHeaders, linker.Properties.Target.Vendor.Exclude_static_libs)
 		deps.WholeStaticLibs = removeListFromList(deps.WholeStaticLibs, linker.Properties.Target.Vendor.Exclude_static_libs)
 		deps.RuntimeLibs = removeListFromList(deps.RuntimeLibs, linker.Properties.Target.Vendor.Exclude_runtime_libs)
+		deps.Rlibs = append(deps.Rlibs, linker.Properties.Target.Vendor.Static_rlibs...)
 	}
 
 	if ctx.inProduct() {
@@ -345,6 +370,7 @@
 		deps.ReexportStaticLibHeaders = removeListFromList(deps.ReexportStaticLibHeaders, linker.Properties.Target.Product.Exclude_static_libs)
 		deps.WholeStaticLibs = removeListFromList(deps.WholeStaticLibs, linker.Properties.Target.Product.Exclude_static_libs)
 		deps.RuntimeLibs = removeListFromList(deps.RuntimeLibs, linker.Properties.Target.Product.Exclude_runtime_libs)
+		deps.Rlibs = append(deps.Rlibs, linker.Properties.Target.Product.Static_rlibs...)
 	}
 
 	if ctx.inRecovery() {
@@ -358,6 +384,7 @@
 		deps.ReexportStaticLibHeaders = removeListFromList(deps.ReexportStaticLibHeaders, linker.Properties.Target.Recovery.Exclude_static_libs)
 		deps.WholeStaticLibs = removeListFromList(deps.WholeStaticLibs, linker.Properties.Target.Recovery.Exclude_static_libs)
 		deps.RuntimeLibs = removeListFromList(deps.RuntimeLibs, linker.Properties.Target.Recovery.Exclude_runtime_libs)
+		deps.Rlibs = append(deps.Rlibs, linker.Properties.Target.Recovery.Static_rlibs...)
 	}
 
 	if ctx.inRamdisk() {
@@ -368,6 +395,7 @@
 		deps.ReexportStaticLibHeaders = removeListFromList(deps.ReexportStaticLibHeaders, linker.Properties.Target.Ramdisk.Exclude_static_libs)
 		deps.WholeStaticLibs = removeListFromList(deps.WholeStaticLibs, linker.Properties.Target.Ramdisk.Exclude_static_libs)
 		deps.RuntimeLibs = removeListFromList(deps.RuntimeLibs, linker.Properties.Target.Ramdisk.Exclude_runtime_libs)
+		deps.Rlibs = append(deps.Rlibs, linker.Properties.Target.Ramdisk.Static_rlibs...)
 	}
 
 	if ctx.inVendorRamdisk() {
@@ -377,6 +405,7 @@
 		deps.ReexportStaticLibHeaders = removeListFromList(deps.ReexportStaticLibHeaders, linker.Properties.Target.Vendor_ramdisk.Exclude_static_libs)
 		deps.WholeStaticLibs = removeListFromList(deps.WholeStaticLibs, linker.Properties.Target.Vendor_ramdisk.Exclude_static_libs)
 		deps.RuntimeLibs = removeListFromList(deps.RuntimeLibs, linker.Properties.Target.Vendor_ramdisk.Exclude_runtime_libs)
+		deps.Rlibs = append(deps.Rlibs, linker.Properties.Target.Vendor_ramdisk.Static_rlibs...)
 	}
 
 	if !ctx.useSdk() {
diff --git a/cc/testing.go b/cc/testing.go
index 20c435a..55d3e71 100644
--- a/cc/testing.go
+++ b/cc/testing.go
@@ -299,6 +299,7 @@
 			system_shared_libs: [],
 			stl: "none",
 			vendor_available: true,
+			vendor_ramdisk_available: true,
 			product_available: true,
 			recovery_available: true,
 			host_supported: true,
@@ -570,17 +571,17 @@
 
 	// Additional files needed in tests that disallow non-existent source.
 	android.MockFS{
-		"defaults/cc/common/libc.map.txt":      nil,
-		"defaults/cc/common/libdl.map.txt":     nil,
-		"defaults/cc/common/libft2.map.txt":    nil,
-		"defaults/cc/common/libm.map.txt":      nil,
-		"defaults/cc/common/ndk_libc++_shared": nil,
-		"defaults/cc/common/crtbegin_so.c":     nil,
-		"defaults/cc/common/crtbegin.c":        nil,
-		"defaults/cc/common/crtend_so.c":       nil,
-		"defaults/cc/common/crtend.c":          nil,
-		"defaults/cc/common/crtbrand.c":        nil,
-		"external/compiler-rt/lib/cfi/cfi_blocklist.txt":   nil,
+		"defaults/cc/common/libc.map.txt":                nil,
+		"defaults/cc/common/libdl.map.txt":               nil,
+		"defaults/cc/common/libft2.map.txt":              nil,
+		"defaults/cc/common/libm.map.txt":                nil,
+		"defaults/cc/common/ndk_libc++_shared":           nil,
+		"defaults/cc/common/crtbegin_so.c":               nil,
+		"defaults/cc/common/crtbegin.c":                  nil,
+		"defaults/cc/common/crtend_so.c":                 nil,
+		"defaults/cc/common/crtend.c":                    nil,
+		"defaults/cc/common/crtbrand.c":                  nil,
+		"external/compiler-rt/lib/cfi/cfi_blocklist.txt": nil,
 
 		"defaults/cc/common/libclang_rt.ubsan_minimal.android_arm64.a": nil,
 		"defaults/cc/common/libclang_rt.ubsan_minimal.android_arm.a":   nil,
diff --git a/rust/builder.go b/rust/builder.go
index 1281db5..abbf90d 100644
--- a/rust/builder.go
+++ b/rust/builder.go
@@ -21,6 +21,7 @@
 	"github.com/google/blueprint"
 
 	"android/soong/android"
+	"android/soong/cc"
 	"android/soong/rust/config"
 )
 
@@ -118,6 +119,7 @@
 
 func init() {
 	pctx.HostBinToolVariable("SoongZipCmd", "soong_zip")
+	cc.TransformRlibstoStaticlib = TransformRlibstoStaticlib
 }
 
 type transformProperties struct {
@@ -166,6 +168,48 @@
 	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, getTransformProperties(ctx, "rlib"))
 }
 
+func TransformRlibstoStaticlib(ctx android.ModuleContext, mainSrc android.Path, deps []cc.RustRlibDep,
+	outputFile android.WritablePath) android.Path {
+
+	var rustPathDeps PathDeps
+	var rustFlags Flags
+
+	for _, rlibDep := range deps {
+		rustPathDeps.RLibs = append(rustPathDeps.RLibs, RustLibrary{Path: rlibDep.LibPath, CrateName: rlibDep.CrateName})
+		rustPathDeps.linkDirs = append(rustPathDeps.linkDirs, rlibDep.LinkDirs...)
+	}
+
+	ccModule := ctx.(cc.ModuleContext).Module().(*cc.Module)
+	toolchain := config.FindToolchain(ctx.Os(), ctx.Arch())
+	t := transformProperties{
+		// Crate name can be a predefined value as this is a staticlib and
+		// it does not need to be unique. The crate name is used for name
+		// mangling, but it is mixed with the metadata for that purpose, which we
+		// already set to the module name.
+		crateName:       "generated_rust_staticlib",
+		is64Bit:         toolchain.Is64Bit(),
+		targetTriple:    toolchain.RustTriple(),
+		bootstrap:       ccModule.Bootstrap(),
+		inRecovery:      ccModule.InRecovery(),
+		inRamdisk:       ccModule.InRamdisk(),
+		inVendorRamdisk: ccModule.InVendorRamdisk(),
+
+		// crateType indicates what type of crate to build
+		crateType: "staticlib",
+
+		// synthetic indicates whether this is an actual Rust module or not
+		synthetic: true,
+	}
+
+	rustFlags = CommonDefaultFlags(ctx, toolchain, rustFlags)
+	rustFlags = CommonLibraryCompilerFlags(ctx, rustFlags)
+	rustFlags.GlobalRustFlags = append(rustFlags.GlobalRustFlags, "-C lto=thin")
+
+	rustFlags.EmitXrefs = false
+
+	return transformSrctoCrate(ctx, mainSrc, rustPathDeps, rustFlags, outputFile, t).outputFile
+}
+
 func TransformSrctoDylib(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
 	outputFile android.WritablePath) buildOutput {
 	flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto=thin")
diff --git a/rust/builder_test.go b/rust/builder_test.go
index 639f6d4..c093ac4 100644
--- a/rust/builder_test.go
+++ b/rust/builder_test.go
@@ -46,6 +46,9 @@
 }
 
 func TestCompilationOutputFiles(t *testing.T) {
+
+	// Note: Rustdoc output is produced for the PrimaryModule, so if the variant
+	// order changes, then it may be produced for a different variant.
 	ctx := testRust(t, `
 		rust_library {
 			name: "libfizz_buzz",
@@ -126,6 +129,16 @@
 			},
 		},
 		{
+			testName:   "rust_ffi rlib",
+			moduleName: "librust_ffi",
+			variant:    "android_arm64_armv8-a_rlib_rlib-std",
+			expectedFiles: []string{
+				"out/soong/.intermediates/librust_ffi/android_arm64_armv8-a_rlib_rlib-std/librust_ffi.rlib",
+				"out/soong/.intermediates/librust_ffi/android_arm64_armv8-a_rlib_rlib-std/librust_ffi.rlib.clippy",
+				"out/soong/.intermediates/librust_ffi/android_arm64_armv8-a_rlib_rlib-std/meta_lic",
+			},
+		},
+		{
 			testName:   "rust_ffi shared",
 			moduleName: "librust_ffi",
 			variant:    "android_arm64_armv8-a_shared",
diff --git a/rust/coverage.go b/rust/coverage.go
index 91a7806..e0e919c 100644
--- a/rust/coverage.go
+++ b/rust/coverage.go
@@ -47,7 +47,7 @@
 
 		// no_std modules are missing libprofiler_builtins which provides coverage, so we need to add it as a dependency.
 		if rustModule, ok := ctx.Module().(*Module); ok && rustModule.compiler.noStdlibs() {
-			ctx.AddVariationDependencies([]blueprint.Variation{{Mutator: "rust_libraries", Variation: "rlib"}}, rlibDepTag, ProfilerBuiltins)
+			ctx.AddVariationDependencies([]blueprint.Variation{{Mutator: "rust_libraries", Variation: "rlib"}, {Mutator: "link", Variation: ""}}, rlibDepTag, ProfilerBuiltins)
 		}
 	}
 
diff --git a/rust/fuzz_test.go b/rust/fuzz_test.go
index ee28c6d..0d4622a 100644
--- a/rust/fuzz_test.go
+++ b/rust/fuzz_test.go
@@ -120,13 +120,17 @@
 			}
 			cc_fuzz {
 				name: "fuzz_static_libtest",
+				static_rlibs: ["libtest_fuzzing"],
+			}
+			cc_fuzz {
+				name: "fuzz_staticffi_libtest",
 				static_libs: ["libtest_fuzzing"],
 			}
-
 	`)
 
 	fuzz_shared_libtest := ctx.ModuleForTests("fuzz_shared_libtest", "android_arm64_armv8-a_fuzzer").Module().(cc.LinkableInterface)
 	fuzz_static_libtest := ctx.ModuleForTests("fuzz_static_libtest", "android_arm64_armv8-a_fuzzer").Module().(cc.LinkableInterface)
+	fuzz_staticffi_libtest := ctx.ModuleForTests("fuzz_staticffi_libtest", "android_arm64_armv8-a_fuzzer").Module().(cc.LinkableInterface)
 
 	if !strings.Contains(fuzz_shared_libtest.FuzzSharedLibraries().String(), ":libcc_transitive_dep.so") {
 		t.Errorf("cc_fuzz does not contain the expected bundled transitive shared libs from rust_ffi_shared ('libcc_transitive_dep'): %#v", fuzz_shared_libtest.FuzzSharedLibraries().String())
@@ -134,4 +138,7 @@
 	if !strings.Contains(fuzz_static_libtest.FuzzSharedLibraries().String(), ":libcc_transitive_dep.so") {
 		t.Errorf("cc_fuzz does not contain the expected bundled transitive shared libs from rust_ffi_static ('libcc_transitive_dep'): %#v", fuzz_static_libtest.FuzzSharedLibraries().String())
 	}
+	if !strings.Contains(fuzz_staticffi_libtest.FuzzSharedLibraries().String(), ":libcc_transitive_dep.so") {
+		t.Errorf("cc_fuzz does not contain the expected bundled transitive shared libs from rust_ffi_rlib ('libcc_transitive_dep'): %#v", fuzz_staticffi_libtest.FuzzSharedLibraries().String())
+	}
 }
diff --git a/rust/image_test.go b/rust/image_test.go
index ba94906..71e271c 100644
--- a/rust/image_test.go
+++ b/rust/image_test.go
@@ -22,33 +22,45 @@
 	"android/soong/cc"
 )
 
-// Test that cc modules can link against vendor_available rust_ffi_static libraries.
+// Test that cc modules can link against vendor_available rust_ffi_rlib/rust_ffi_static libraries.
 func TestVendorLinkage(t *testing.T) {
 	ctx := testRust(t, `
 			cc_binary {
-				name: "fizz_vendor",
-				static_libs: ["libfoo_vendor"],
+				name: "fizz_vendor_available",
+				static_libs: ["libfoo_vendor_static"],
+				static_rlibs: ["libfoo_vendor"],
+				vendor_available: true,
+			}
+			cc_binary {
+				name: "fizz_soc_specific",
+				static_rlibs: ["libfoo_vendor"],
 				soc_specific: true,
 			}
-			rust_ffi_static {
+			rust_ffi_rlib {
 				name: "libfoo_vendor",
 				crate_name: "foo",
 				srcs: ["foo.rs"],
 				vendor_available: true,
 			}
+			rust_ffi_static {
+				name: "libfoo_vendor_static",
+				crate_name: "foo",
+				srcs: ["foo.rs"],
+				vendor_available: true,
+			}
 		`)
 
-	vendorBinary := ctx.ModuleForTests("fizz_vendor", "android_vendor_arm64_armv8-a").Module().(*cc.Module)
+	vendorBinary := ctx.ModuleForTests("fizz_vendor_available", "android_vendor_arm64_armv8-a").Module().(*cc.Module)
 
-	if !android.InList("libfoo_vendor.vendor", vendorBinary.Properties.AndroidMkStaticLibs) {
-		t.Errorf("vendorBinary should have a dependency on libfoo_vendor: %#v", vendorBinary.Properties.AndroidMkStaticLibs)
+	if !android.InList("libfoo_vendor_static.vendor", vendorBinary.Properties.AndroidMkStaticLibs) {
+		t.Errorf("vendorBinary should have a dependency on libfoo_vendor_static.vendor: %#v", vendorBinary.Properties.AndroidMkStaticLibs)
 	}
 }
 
 // Test that variants which use the vndk emit the appropriate cfg flag.
 func TestImageCfgFlag(t *testing.T) {
 	ctx := testRust(t, `
-			rust_ffi_static {
+			rust_ffi_shared {
 				name: "libfoo",
 				crate_name: "foo",
 				srcs: ["foo.rs"],
@@ -57,7 +69,7 @@
 			}
 		`)
 
-	vendor := ctx.ModuleForTests("libfoo", "android_vendor_arm64_armv8-a_static").Rule("rustc")
+	vendor := ctx.ModuleForTests("libfoo", "android_vendor_arm64_armv8-a_shared").Rule("rustc")
 
 	if !strings.Contains(vendor.Args["rustcFlags"], "--cfg 'android_vndk'") {
 		t.Errorf("missing \"--cfg 'android_vndk'\" for libfoo vendor variant, rustcFlags: %#v", vendor.Args["rustcFlags"])
@@ -69,7 +81,7 @@
 		t.Errorf("unexpected \"--cfg 'android_product'\" for libfoo vendor variant, rustcFlags: %#v", vendor.Args["rustcFlags"])
 	}
 
-	product := ctx.ModuleForTests("libfoo", "android_product_arm64_armv8-a_static").Rule("rustc")
+	product := ctx.ModuleForTests("libfoo", "android_product_arm64_armv8-a_shared").Rule("rustc")
 	if !strings.Contains(product.Args["rustcFlags"], "--cfg 'android_vndk'") {
 		t.Errorf("missing \"--cfg 'android_vndk'\" for libfoo product variant, rustcFlags: %#v", product.Args["rustcFlags"])
 	}
@@ -80,7 +92,7 @@
 		t.Errorf("missing \"--cfg 'android_product'\" for libfoo product variant, rustcFlags: %#v", product.Args["rustcFlags"])
 	}
 
-	system := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_static").Rule("rustc")
+	system := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Rule("rustc")
 	if strings.Contains(system.Args["rustcFlags"], "--cfg 'android_vndk'") {
 		t.Errorf("unexpected \"--cfg 'android_vndk'\" for libfoo system variant, rustcFlags: %#v", system.Args["rustcFlags"])
 	}
@@ -93,27 +105,34 @@
 
 }
 
-// Test that cc modules can link against vendor_ramdisk_available rust_ffi_static libraries.
+// Test that cc modules can link against vendor_ramdisk_available rust_ffi_rlib and rust_ffi_static libraries.
 func TestVendorRamdiskLinkage(t *testing.T) {
 	ctx := testRust(t, `
-			cc_library_static {
+			cc_library_shared {
 				name: "libcc_vendor_ramdisk",
-				static_libs: ["libfoo_vendor_ramdisk"],
+				static_rlibs: ["libfoo_vendor_ramdisk"],
+				static_libs: ["libfoo_static_vendor_ramdisk"],
 				system_shared_libs: [],
 				vendor_ramdisk_available: true,
 			}
-			rust_ffi_static {
+			rust_ffi_rlib {
 				name: "libfoo_vendor_ramdisk",
 				crate_name: "foo",
 				srcs: ["foo.rs"],
 				vendor_ramdisk_available: true,
 			}
+			rust_ffi_static {
+				name: "libfoo_static_vendor_ramdisk",
+				crate_name: "foo",
+				srcs: ["foo.rs"],
+				vendor_ramdisk_available: true,
+			}
 		`)
 
-	vendorRamdiskLibrary := ctx.ModuleForTests("libcc_vendor_ramdisk", "android_vendor_ramdisk_arm64_armv8-a_static").Module().(*cc.Module)
+	vendorRamdiskLibrary := ctx.ModuleForTests("libcc_vendor_ramdisk", "android_vendor_ramdisk_arm64_armv8-a_shared").Module().(*cc.Module)
 
-	if !android.InList("libfoo_vendor_ramdisk.vendor_ramdisk", vendorRamdiskLibrary.Properties.AndroidMkStaticLibs) {
-		t.Errorf("libcc_vendor_ramdisk should have a dependency on libfoo_vendor_ramdisk")
+	if !android.InList("libfoo_static_vendor_ramdisk.vendor_ramdisk", vendorRamdiskLibrary.Properties.AndroidMkStaticLibs) {
+		t.Errorf("libcc_vendor_ramdisk should have a dependency on libfoo_static_vendor_ramdisk")
 	}
 }
 
diff --git a/rust/library.go b/rust/library.go
index 7e5d4c4..7300001 100644
--- a/rust/library.go
+++ b/rust/library.go
@@ -37,10 +37,15 @@
 	android.RegisterModuleType("rust_library_host_rlib", RustLibraryRlibHostFactory)
 	android.RegisterModuleType("rust_ffi", RustFFIFactory)
 	android.RegisterModuleType("rust_ffi_shared", RustFFISharedFactory)
-	android.RegisterModuleType("rust_ffi_static", RustFFIStaticFactory)
+	android.RegisterModuleType("rust_ffi_rlib", RustFFIRlibFactory)
 	android.RegisterModuleType("rust_ffi_host", RustFFIHostFactory)
 	android.RegisterModuleType("rust_ffi_host_shared", RustFFISharedHostFactory)
-	android.RegisterModuleType("rust_ffi_host_static", RustFFIStaticHostFactory)
+	android.RegisterModuleType("rust_ffi_host_rlib", RustFFIRlibHostFactory)
+
+	// TODO: Remove when all instances of rust_ffi_static have been switched to rust_ffi_rlib
+	// Alias rust_ffi_static to the combined rust_ffi_rlib factory
+	android.RegisterModuleType("rust_ffi_static", RustFFIStaticRlibFactory)
+	android.RegisterModuleType("rust_ffi_host_static", RustFFIStaticRlibHostFactory)
 }
 
 type VariantLibraryProperties struct {
@@ -104,6 +109,8 @@
 	includeDirs       android.Paths
 	sourceProvider    SourceProvider
 
+	isFFI bool
+
 	// table-of-contents file for cdylib crates to optimize out relinking when possible
 	tocFile android.OptionalPath
 }
@@ -143,6 +150,8 @@
 	BuildOnlyShared()
 
 	toc() android.OptionalPath
+
+	isFFILibrary() bool
 }
 
 func (library *libraryDecorator) nativeCoverage() bool {
@@ -250,7 +259,7 @@
 }
 
 func (library *libraryDecorator) stdLinkage(ctx *depsContext) RustLinkage {
-	if library.static() || library.MutatedProperties.VariantIsStaticStd {
+	if library.static() || library.MutatedProperties.VariantIsStaticStd || (library.rlib() && library.isFFILibrary()) {
 		return RlibLinkage
 	} else if library.baseCompiler.preferRlib() {
 		return RlibLinkage
@@ -270,8 +279,8 @@
 	return module.Init()
 }
 
-// rust_ffi produces all FFI variants (rust_ffi_shared and
-// rust_ffi_static).
+// rust_ffi produces all FFI variants (rust_ffi_shared, rust_ffi_static, and
+// rust_ffi_rlib).
 func RustFFIFactory() android.Module {
 	module, library := NewRustLibrary(android.HostAndDeviceSupported)
 	library.BuildOnlyFFI()
@@ -300,14 +309,6 @@
 	return module.Init()
 }
 
-// rust_ffi_static produces a static library (Rust crate type
-// "staticlib").
-func RustFFIStaticFactory() android.Module {
-	module, library := NewRustLibrary(android.HostAndDeviceSupported)
-	library.BuildOnlyStatic()
-	return module.Init()
-}
-
 // rust_library_host produces all Rust variants for the host
 // (rust_library_dylib_host and rust_library_rlib_host).
 func RustLibraryHostFactory() android.Module {
@@ -317,7 +318,7 @@
 }
 
 // rust_ffi_host produces all FFI variants for the host
-// (rust_ffi_static_host and rust_ffi_shared_host).
+// (rust_ffi_rlib_host, rust_ffi_static_host, and rust_ffi_shared_host).
 func RustFFIHostFactory() android.Module {
 	module, library := NewRustLibrary(android.HostSupported)
 	library.BuildOnlyFFI()
@@ -340,14 +341,6 @@
 	return module.Init()
 }
 
-// rust_ffi_static_host produces a static library for the host (Rust
-// crate type "staticlib").
-func RustFFIStaticHostFactory() android.Module {
-	module, library := NewRustLibrary(android.HostSupported)
-	library.BuildOnlyStatic()
-	return module.Init()
-}
-
 // rust_ffi_shared_host produces an shared library for the host (Rust
 // crate type "cdylib").
 func RustFFISharedHostFactory() android.Module {
@@ -356,11 +349,51 @@
 	return module.Init()
 }
 
+// rust_ffi_rlib_host produces an rlib for the host (Rust crate
+// type "rlib").
+func RustFFIRlibHostFactory() android.Module {
+	module, library := NewRustLibrary(android.HostSupported)
+	library.BuildOnlyRlibStatic()
+
+	library.isFFI = true
+	return module.Init()
+}
+
+// rust_ffi_rlib produces an rlib (Rust crate type "rlib").
+func RustFFIRlibFactory() android.Module {
+	module, library := NewRustLibrary(android.HostAndDeviceSupported)
+	library.BuildOnlyRlib()
+
+	library.isFFI = true
+	return module.Init()
+}
+
+// rust_ffi_static produces a staticlib and an rlib variant
+func RustFFIStaticRlibFactory() android.Module {
+	module, library := NewRustLibrary(android.HostAndDeviceSupported)
+	library.BuildOnlyRlibStatic()
+
+	library.isFFI = true
+	return module.Init()
+}
+
+// rust_ffi_static_host produces a staticlib and an rlib variant for the host
+func RustFFIStaticRlibHostFactory() android.Module {
+	module, library := NewRustLibrary(android.HostSupported)
+	library.BuildOnlyRlibStatic()
+
+	library.isFFI = true
+	return module.Init()
+}
+
 func (library *libraryDecorator) BuildOnlyFFI() {
 	library.MutatedProperties.BuildDylib = false
-	library.MutatedProperties.BuildRlib = false
+	// we build rlibs for later static ffi linkage.
+	library.MutatedProperties.BuildRlib = true
 	library.MutatedProperties.BuildShared = true
 	library.MutatedProperties.BuildStatic = true
+
+	library.isFFI = true
 }
 
 func (library *libraryDecorator) BuildOnlyRust() {
@@ -384,11 +417,21 @@
 	library.MutatedProperties.BuildStatic = false
 }
 
+func (library *libraryDecorator) BuildOnlyRlibStatic() {
+	library.MutatedProperties.BuildDylib = false
+	library.MutatedProperties.BuildRlib = true
+	library.MutatedProperties.BuildShared = false
+	library.MutatedProperties.BuildStatic = true
+	library.isFFI = true
+}
+
 func (library *libraryDecorator) BuildOnlyStatic() {
 	library.MutatedProperties.BuildRlib = false
 	library.MutatedProperties.BuildDylib = false
 	library.MutatedProperties.BuildShared = false
 	library.MutatedProperties.BuildStatic = true
+
+	library.isFFI = true
 }
 
 func (library *libraryDecorator) BuildOnlyShared() {
@@ -396,6 +439,12 @@
 	library.MutatedProperties.BuildDylib = false
 	library.MutatedProperties.BuildStatic = false
 	library.MutatedProperties.BuildShared = true
+
+	library.isFFI = true
+}
+
+func (library *libraryDecorator) isFFILibrary() bool {
+	return library.isFFI
 }
 
 func NewRustLibrary(hod android.HostOrDeviceSupported) (*Module, *libraryDecorator) {
@@ -480,10 +529,11 @@
 
 	flags = CommonLibraryCompilerFlags(ctx, flags)
 
-	if library.shared() || library.static() {
+	if library.isFFI {
 		library.includeDirs = append(library.includeDirs, android.PathsForModuleSrc(ctx, library.Properties.Include_dirs)...)
 		library.includeDirs = append(library.includeDirs, android.PathsForModuleSrc(ctx, library.Properties.Export_include_dirs)...)
 	}
+
 	if library.shared() {
 		if ctx.Darwin() {
 			flags.LinkFlags = append(
@@ -509,6 +559,9 @@
 		deps.srcProviderFiles = append(deps.srcProviderFiles, library.sourceProvider.Srcs()...)
 	}
 
+	// Ensure link dirs are not duplicated
+	deps.linkDirs = android.FirstUniqueStrings(deps.linkDirs)
+
 	// Calculate output filename
 	if library.rlib() {
 		fileName = library.getStem(ctx) + ctx.toolchain().RlibSuffix()
@@ -564,9 +617,10 @@
 		library.flagExporter.exportLinkObjects(deps.linkObjects...)
 	}
 
-	if library.static() || library.shared() {
+	// Since we have FFI rlibs, we need to collect their includes as well
+	if library.static() || library.shared() || library.rlib() {
 		android.SetProvider(ctx, cc.FlagExporterInfoProvider, cc.FlagExporterInfo{
-			IncludeDirs: library.includeDirs,
+			IncludeDirs: android.FirstUniquePaths(library.includeDirs),
 		})
 	}
 
@@ -681,6 +735,11 @@
 		return
 	}
 
+	// Don't produce rlib/dylib/source variants for shared or static variants
+	if library.shared() || library.static() {
+		return
+	}
+
 	var variants []string
 	// The source variant is used for SourceProvider modules. The other variants (i.e. rlib and dylib)
 	// depend on this variant. It must be the first variant to be declared.
@@ -720,6 +779,9 @@
 			// The source variant does not produce any library.
 			// Disable the compilation steps.
 			v.(*Module).compiler.SetDisabled()
+		case "":
+			// if there's an empty variant, alias it so it is the default variant
+			mctx.AliasVariation("")
 		}
 	}
 
@@ -744,20 +806,29 @@
 		case libraryInterface:
 			// Only create a variant if a library is actually being built.
 			if library.rlib() && !library.sysroot() {
-				variants := []string{"rlib-std", "dylib-std"}
-				modules := mctx.CreateLocalVariations(variants...)
+				// If this is a rust_ffi variant it only needs rlib-std
+				if library.isFFILibrary() {
+					variants := []string{"rlib-std"}
+					modules := mctx.CreateLocalVariations(variants...)
+					rlib := modules[0].(*Module)
+					rlib.compiler.(libraryInterface).setRlibStd()
+					rlib.Properties.RustSubName += RlibStdlibSuffix
+				} else {
+					variants := []string{"rlib-std", "dylib-std"}
+					modules := mctx.CreateLocalVariations(variants...)
 
-				rlib := modules[0].(*Module)
-				dylib := modules[1].(*Module)
-				rlib.compiler.(libraryInterface).setRlibStd()
-				dylib.compiler.(libraryInterface).setDylibStd()
-				if dylib.ModuleBase.ImageVariation().Variation == android.VendorRamdiskVariation {
-					// TODO(b/165791368)
-					// Disable rlibs that link against dylib-std on vendor ramdisk variations until those dylib
-					// variants are properly supported.
-					dylib.Disable()
+					rlib := modules[0].(*Module)
+					dylib := modules[1].(*Module)
+					rlib.compiler.(libraryInterface).setRlibStd()
+					dylib.compiler.(libraryInterface).setDylibStd()
+					if dylib.ModuleBase.ImageVariation().Variation == android.VendorRamdiskVariation {
+						// TODO(b/165791368)
+						// Disable rlibs that link against dylib-std on vendor ramdisk variations until those dylib
+						// variants are properly supported.
+						dylib.Disable()
+					}
+					rlib.Properties.RustSubName += RlibStdlibSuffix
 				}
-				rlib.Properties.RustSubName += RlibStdlibSuffix
 			}
 		}
 	}
diff --git a/rust/library_test.go b/rust/library_test.go
index 7275b66..1133c28 100644
--- a/rust/library_test.go
+++ b/rust/library_test.go
@@ -37,9 +37,10 @@
 		}`)
 
 	// Test all variants are being built.
+	libfooStatic := ctx.ModuleForTests("libfoo.ffi", "linux_glibc_x86_64_static").Rule("rustc")
 	libfooRlib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_rlib_rlib-std").Rule("rustc")
 	libfooDylib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_dylib").Rule("rustc")
-	libfooStatic := ctx.ModuleForTests("libfoo.ffi", "linux_glibc_x86_64_static").Rule("rustc")
+	libfooFFIRlib := ctx.ModuleForTests("libfoo.ffi", "linux_glibc_x86_64_rlib_rlib-std").Rule("rustc")
 	libfooShared := ctx.ModuleForTests("libfoo.ffi", "linux_glibc_x86_64_shared").Rule("rustc")
 
 	rlibCrateType := "rlib"
@@ -62,6 +63,11 @@
 		t.Errorf("missing crate-type for static variant, expecting %#v, rustcFlags: %#v", staticCrateType, libfooStatic.Args["rustcFlags"])
 	}
 
+	// Test crate type for FFI rlibs is correct
+	if !strings.Contains(libfooFFIRlib.Args["rustcFlags"], "crate-type="+rlibCrateType) {
+		t.Errorf("missing crate-type for static variant, expecting %#v, rustcFlags: %#v", rlibCrateType, libfooFFIRlib.Args["rustcFlags"])
+	}
+
 	// Test crate type for C shared libraries is correct.
 	if !strings.Contains(libfooShared.Args["rustcFlags"], "crate-type="+sharedCrateType) {
 		t.Errorf("missing crate-type for shared variant, expecting %#v, got rustcFlags: %#v", sharedCrateType, libfooShared.Args["rustcFlags"])
@@ -182,15 +188,20 @@
 
 func TestStaticLibraryLinkage(t *testing.T) {
 	ctx := testRust(t, `
-		rust_ffi_static {
+		rust_ffi {
 			name: "libfoo",
 			srcs: ["foo.rs"],
 			crate_name: "foo",
 		}`)
 
-	libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_static")
+	libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_rlib_rlib-std")
+	libfooStatic := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_static")
 
 	if !android.InList("libstd", libfoo.Module().(*Module).Properties.AndroidMkRlibs) {
+		t.Errorf("Static libstd rlib expected to be a dependency of Rust rlib libraries. Rlib deps are: %#v",
+			libfoo.Module().(*Module).Properties.AndroidMkDylibs)
+	}
+	if !android.InList("libstd", libfooStatic.Module().(*Module).Properties.AndroidMkRlibs) {
 		t.Errorf("Static libstd rlib expected to be a dependency of Rust static libraries. Rlib deps are: %#v",
 			libfoo.Module().(*Module).Properties.AndroidMkDylibs)
 	}
@@ -198,6 +209,12 @@
 
 func TestNativeDependencyOfRlib(t *testing.T) {
 	ctx := testRust(t, `
+		rust_ffi_rlib {
+			name: "libffi_rlib",
+			crate_name: "ffi_rlib",
+			rlibs: ["librust_rlib"],
+			srcs: ["foo.rs"],
+		}
 		rust_ffi_static {
 			name: "libffi_static",
 			crate_name: "ffi_static",
@@ -224,10 +241,12 @@
 	rustRlibRlibStd := ctx.ModuleForTests("librust_rlib", "android_arm64_armv8-a_rlib_rlib-std")
 	rustRlibDylibStd := ctx.ModuleForTests("librust_rlib", "android_arm64_armv8-a_rlib_dylib-std")
 	ffiStatic := ctx.ModuleForTests("libffi_static", "android_arm64_armv8-a_static")
+	ffiRlib := ctx.ModuleForTests("libffi_rlib", "android_arm64_armv8-a_rlib_rlib-std")
 
 	modules := []android.TestingModule{
 		rustRlibRlibStd,
 		rustRlibDylibStd,
+		ffiRlib,
 		ffiStatic,
 	}
 
@@ -290,27 +309,28 @@
 
 	libfooRlib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_rlib_rlib-std")
 	libfooDylib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_dylib")
+	libfooFFIRlib := ctx.ModuleForTests("libfoo.ffi", "linux_glibc_x86_64_rlib_rlib-std")
 	libfooStatic := ctx.ModuleForTests("libfoo.ffi", "linux_glibc_x86_64_static")
 	libfooShared := ctx.ModuleForTests("libfoo.ffi", "linux_glibc_x86_64_shared")
 
-	for _, static := range []android.TestingModule{libfooRlib, libfooStatic} {
+	for _, static := range []android.TestingModule{libfooRlib, libfooStatic, libfooFFIRlib} {
 		if !android.InList("libbar.rlib-std", static.Module().(*Module).Properties.AndroidMkRlibs) {
-			t.Errorf("libbar not present as rlib dependency in static lib")
+			t.Errorf("libbar not present as rlib dependency in static lib: %s", static.Module().Name())
 		}
 		if android.InList("libbar", static.Module().(*Module).Properties.AndroidMkDylibs) {
-			t.Errorf("libbar present as dynamic dependency in static lib")
+			t.Errorf("libbar present as dynamic dependency in static lib: %s", static.Module().Name())
 		}
 	}
 
 	for _, dyn := range []android.TestingModule{libfooDylib, libfooShared} {
 		if !android.InList("libbar", dyn.Module().(*Module).Properties.AndroidMkDylibs) {
-			t.Errorf("libbar not present as dynamic dependency in dynamic lib")
+			t.Errorf("libbar not present as dynamic dependency in dynamic lib: %s", dyn.Module().Name())
 		}
 		if android.InList("libbar", dyn.Module().(*Module).Properties.AndroidMkRlibs) {
-			t.Errorf("libbar present as rlib dependency in dynamic lib")
+			t.Errorf("libbar present as rlib dependency in dynamic lib: %s", dyn.Module().Name())
 		}
 		if !android.InList("librlib_only", dyn.Module().(*Module).Properties.AndroidMkRlibs) {
-			t.Errorf("librlib_only should be selected by rustlibs as an rlib.")
+			t.Errorf("librlib_only should be selected by rustlibs as an rlib: %s.", dyn.Module().Name())
 		}
 	}
 }
@@ -375,6 +395,7 @@
 
 	libbarShared := ctx.ModuleForTests("libbar", "android_arm64_armv8-a_shared").Module().(*Module)
 	libbarStatic := ctx.ModuleForTests("libbar", "android_arm64_armv8-a_static").Module().(*Module)
+	libbarFFIRlib := ctx.ModuleForTests("libbar", "android_arm64_armv8-a_rlib_rlib-std").Module().(*Module)
 
 	// prefer_rlib works the same for both rust_library and rust_ffi, so a single check is sufficient here.
 	libbarRlibStd := ctx.ModuleForTests("libbar.prefer_rlib", "android_arm64_armv8-a_shared").Module().(*Module)
@@ -398,6 +419,12 @@
 	if !android.InList("libfoo.rlib-std", libbarStatic.Properties.AndroidMkRlibs) {
 		t.Errorf("Device rust_ffi_static does not link dependent rustlib rlib-std variant")
 	}
+	if !android.InList("libstd", libbarFFIRlib.Properties.AndroidMkRlibs) {
+		t.Errorf("Device rust_ffi_rlib does not link libstd as an rlib")
+	}
+	if !android.InList("libfoo.rlib-std", libbarFFIRlib.Properties.AndroidMkRlibs) {
+		t.Errorf("Device rust_ffi_rlib does not link dependent rustlib rlib-std variant")
+	}
 	if !android.InList("libstd", libbarRlibStd.Properties.AndroidMkRlibs) {
 		t.Errorf("rust_ffi with prefer_rlib does not link libstd as an rlib")
 	}
diff --git a/rust/proc_macro.go b/rust/proc_macro.go
index b491449..1ff6637 100644
--- a/rust/proc_macro.go
+++ b/rust/proc_macro.go
@@ -76,6 +76,7 @@
 	srcPath := crateRootPath(ctx, procMacro)
 	ret := TransformSrctoProcMacro(ctx, srcPath, deps, flags, outputFile)
 	procMacro.baseCompiler.unstrippedOutputFile = outputFile
+
 	return ret
 }
 
diff --git a/rust/rust.go b/rust/rust.go
index c2b6151..4edd807 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -158,6 +158,8 @@
 	sourceProvider   SourceProvider
 	subAndroidMkOnce map[SubAndroidMkProvider]bool
 
+	exportedLinkDirs []string
+
 	// Output file to be installed, may be stripped or unstripped.
 	outputFile android.OptionalPath
 
@@ -234,8 +236,8 @@
 
 func (mod *Module) NonCcVariants() bool {
 	if mod.compiler != nil {
-		if _, ok := mod.compiler.(libraryInterface); ok {
-			return false
+		if library, ok := mod.compiler.(libraryInterface); ok {
+			return library.buildRlib() || library.buildDylib()
 		}
 	}
 	panic(fmt.Errorf("NonCcVariants called on non-library module: %q", mod.BaseModuleName()))
@@ -465,6 +467,11 @@
 	linkDirs    []string
 	linkObjects []string
 
+	// exportedLinkDirs are exported linkDirs for direct rlib dependencies to
+	// cc_library_static dependants of rlibs.
+	// Track them separately from linkDirs so superfluous -L flags don't get emitted.
+	exportedLinkDirs []string
+
 	// Used by bindgen modules which call clang
 	depClangFlags         []string
 	depIncludePaths       android.Paths
@@ -477,6 +484,9 @@
 	// Paths to generated source files
 	SrcDeps          android.Paths
 	srcProviderFiles android.Paths
+
+	// Used by Generated Libraries
+	depExportedRlibs []cc.RustRlibDep
 }
 
 type RustLibraries []RustLibrary
@@ -543,6 +553,10 @@
 	return mod.Properties.VndkVersion
 }
 
+func (mod *Module) ExportedCrateLinkDirs() []string {
+	return mod.exportedLinkDirs
+}
+
 func (mod *Module) PreventInstall() bool {
 	return mod.Properties.PreventInstall
 }
@@ -657,15 +671,6 @@
 	return nil
 }
 
-func (mod *Module) IncludeDirs() android.Paths {
-	if mod.compiler != nil {
-		if library, ok := mod.compiler.(*libraryDecorator); ok {
-			return library.includeDirs
-		}
-	}
-	panic(fmt.Errorf("IncludeDirs called on non-library module: %q", mod.BaseModuleName()))
-}
-
 func (mod *Module) SetStatic() {
 	if mod.compiler != nil {
 		if library, ok := mod.compiler.(libraryInterface); ok {
@@ -914,6 +919,10 @@
 	}
 
 	deps := mod.depsToPaths(ctx)
+	// Export linkDirs for CC rust generatedlibs
+	mod.exportedLinkDirs = append(mod.exportedLinkDirs, deps.exportedLinkDirs...)
+	mod.exportedLinkDirs = append(mod.exportedLinkDirs, deps.linkDirs...)
+
 	flags := Flags{
 		Toolchain: toolchain,
 	}
@@ -991,6 +1000,9 @@
 			if ctx.Failed() {
 				return
 			}
+			// Export your own directory as a linkDir
+			mod.exportedLinkDirs = append(mod.exportedLinkDirs, linkPathFromFilePath(mod.OutputFile().Path()))
+
 		}
 
 		ctx.Phony("rust", ctx.RustModule().OutputFile().Path())
@@ -1223,7 +1235,7 @@
 			return
 		}
 
-		if rustDep, ok := dep.(*Module); ok && !rustDep.CcLibraryInterface() {
+		if rustDep, ok := dep.(*Module); ok && !rustDep.Static() && !rustDep.Shared() {
 			//Handle Rust Modules
 			makeLibName := rustMakeLibName(ctx, mod, rustDep, depName+rustDep.Properties.RustSubName)
 
@@ -1249,9 +1261,16 @@
 				mod.Properties.AndroidMkRlibs = append(mod.Properties.AndroidMkRlibs, makeLibName)
 				mod.Properties.SnapshotRlibs = append(mod.Properties.SnapshotRlibs, cc.BaseLibName(depName))
 
+				// rust_ffi rlibs may export include dirs, so collect those here.
+				exportedInfo, _ := android.OtherModuleProvider(ctx, dep, cc.FlagExporterInfoProvider)
+				depPaths.depIncludePaths = append(depPaths.depIncludePaths, exportedInfo.IncludeDirs...)
+				depPaths.exportedLinkDirs = append(depPaths.exportedLinkDirs, linkPathFromFilePath(rustDep.OutputFile().Path()))
+
 			case procMacroDepTag:
 				directProcMacroDeps = append(directProcMacroDeps, rustDep)
 				mod.Properties.AndroidMkProcMacroLibs = append(mod.Properties.AndroidMkProcMacroLibs, makeLibName)
+				// proc_macro link dirs need to be exported, so collect those here.
+				depPaths.exportedLinkDirs = append(depPaths.exportedLinkDirs, linkPathFromFilePath(rustDep.OutputFile().Path()))
 
 			case sourceDepTag:
 				if _, ok := mod.sourceProvider.(*protobufDecorator); ok {
@@ -1281,12 +1300,12 @@
 				directSrcProvidersDeps = append(directSrcProvidersDeps, rustDep)
 			}
 
+			exportedInfo, _ := android.OtherModuleProvider(ctx, dep, FlagExporterInfoProvider)
 			//Append the dependencies exportedDirs, except for proc-macros which target a different arch/OS
 			if depTag != procMacroDepTag {
-				exportedInfo, _ := android.OtherModuleProvider(ctx, dep, FlagExporterInfoProvider)
-				depPaths.linkDirs = append(depPaths.linkDirs, exportedInfo.LinkDirs...)
 				depPaths.depFlags = append(depPaths.depFlags, exportedInfo.Flags...)
 				depPaths.linkObjects = append(depPaths.linkObjects, exportedInfo.LinkObjects...)
+				depPaths.linkDirs = append(depPaths.linkDirs, exportedInfo.LinkDirs...)
 			}
 
 			if depTag == dylibDepTag || depTag == rlibDepTag || depTag == procMacroDepTag {
@@ -1296,6 +1315,7 @@
 					lib.exportLinkDirs(linkDir)
 				}
 			}
+
 			if depTag == sourceDepTag {
 				if _, ok := mod.sourceProvider.(*protobufDecorator); ok && mod.Source() {
 					if _, ok := rustDep.sourceProvider.(*protobufDecorator); ok {
@@ -1560,6 +1580,7 @@
 	}
 
 	rlibDepVariations := commonDepVariations
+	rlibDepVariations = append(rlibDepVariations, blueprint.Variation{Mutator: "link", Variation: ""})
 
 	if lib, ok := mod.compiler.(libraryInterface); !ok || !lib.sysroot() {
 		rlibDepVariations = append(rlibDepVariations,
@@ -1575,6 +1596,8 @@
 
 	// dylibs
 	dylibDepVariations := append(commonDepVariations, blueprint.Variation{Mutator: "rust_libraries", Variation: dylibVariation})
+	dylibDepVariations = append(dylibDepVariations, blueprint.Variation{Mutator: "link", Variation: ""})
+
 	for _, lib := range deps.Dylibs {
 		actx.AddVariationDependencies(dylibDepVariations, dylibDepTag, lib)
 	}
@@ -1594,7 +1617,7 @@
 					// otherwise select the rlib variant.
 					autoDepVariations := append(commonDepVariations,
 						blueprint.Variation{Mutator: "rust_libraries", Variation: autoDep.variation})
-
+					autoDepVariations = append(autoDepVariations, blueprint.Variation{Mutator: "link", Variation: ""})
 					if actx.OtherModuleDependencyVariantExists(autoDepVariations, lib) {
 						actx.AddVariationDependencies(autoDepVariations, autoDep.depTag, lib)
 
@@ -1609,7 +1632,11 @@
 			for _, lib := range deps.Rustlibs {
 				srcProviderVariations := append(commonDepVariations,
 					blueprint.Variation{Mutator: "rust_libraries", Variation: "source"})
+				srcProviderVariations = append(srcProviderVariations, blueprint.Variation{Mutator: "link", Variation: ""})
 
+				// Only add rustlib dependencies if they're source providers themselves.
+				// This is used to track which crate names need to be added to the source generated
+				// in the rust_protobuf mod.rs.
 				if actx.OtherModuleDependencyVariantExists(srcProviderVariations, lib) {
 					actx.AddVariationDependencies(srcProviderVariations, sourceDepTag, lib)
 				}
@@ -1621,7 +1648,7 @@
 	if deps.Stdlibs != nil {
 		if mod.compiler.stdLinkage(ctx) == RlibLinkage {
 			for _, lib := range deps.Stdlibs {
-				actx.AddVariationDependencies(append(commonDepVariations, []blueprint.Variation{{Mutator: "rust_libraries", Variation: "rlib"}}...),
+				actx.AddVariationDependencies(append(commonDepVariations, []blueprint.Variation{{Mutator: "rust_libraries", Variation: "rlib"}, {Mutator: "link", Variation: ""}}...),
 					rlibDepTag, lib)
 			}
 		} else {
diff --git a/rust/rust_test.go b/rust/rust_test.go
index 6d083f6..8b96df8 100644
--- a/rust/rust_test.go
+++ b/rust/rust_test.go
@@ -150,15 +150,11 @@
 			host_supported: true,
 			name: "cc_stubs_dep",
 		}
-		rust_ffi_host_static {
+		cc_library_host_static {
 			name: "libstatic",
-			srcs: ["foo.rs"],
-			crate_name: "static",
 		}
-		rust_ffi_host_static {
+		cc_library_host_static {
 			name: "libwholestatic",
-			srcs: ["foo.rs"],
-			crate_name: "wholestatic",
 		}
 		rust_ffi_host_shared {
 			name: "libshared",
@@ -435,6 +431,105 @@
 	}
 }
 
+func TestRustRlibs(t *testing.T) {
+	ctx := testRust(t, `
+		rust_ffi_rlib {
+			name: "libbar",
+			crate_name: "bar",
+			srcs: ["src/lib.rs"],
+			export_include_dirs: ["bar_includes"]
+		}
+
+		rust_ffi_rlib {
+			name: "libfoo",
+			crate_name: "foo",
+			srcs: ["src/lib.rs"],
+			export_include_dirs: ["foo_includes"]
+		}
+
+		cc_library_shared {
+			name: "libcc_shared",
+			srcs:["foo.c"],
+			static_rlibs: ["libbar"],
+		}
+
+		cc_library_static {
+			name: "libcc_static",
+			srcs:["foo.c"],
+			static_rlibs: ["libfoo"],
+		}
+
+		cc_binary {
+			name: "ccBin",
+			srcs:["foo.c"],
+			static_rlibs: ["libbar"],
+			static_libs: ["libcc_static"],
+		}
+		`)
+
+	libbar := ctx.ModuleForTests("libbar", "android_arm64_armv8-a_rlib_rlib-std").Rule("rustc")
+	libcc_shared_rustc := ctx.ModuleForTests("libcc_shared", "android_arm64_armv8-a_shared").Rule("rustc")
+	libcc_shared_ld := ctx.ModuleForTests("libcc_shared", "android_arm64_armv8-a_shared").Rule("ld")
+	libcc_shared_cc := ctx.ModuleForTests("libcc_shared", "android_arm64_armv8-a_shared").Rule("cc")
+	ccbin_rustc := ctx.ModuleForTests("ccBin", "android_arm64_armv8-a").Rule("rustc")
+	ccbin_ld := ctx.ModuleForTests("ccBin", "android_arm64_armv8-a").Rule("ld")
+	ccbin_cc := ctx.ModuleForTests("ccBin", "android_arm64_armv8-a").Rule("cc")
+
+	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"])
+	}
+
+	// Make sure there's a rustc command, and it's producing a staticlib
+	if !strings.Contains(libcc_shared_rustc.Args["rustcFlags"], "crate-type=staticlib") {
+		t.Errorf("missing crate-type for static variant, expecting %#v, rustcFlags: %#v",
+			"staticlib", libcc_shared_rustc.Args["rustcFlags"])
+	}
+
+	// Make sure the static lib is included in the ld command
+	if !strings.Contains(libcc_shared_ld.Args["libFlags"], "generated_rust_staticlib/liblibcc_shared_rust_staticlib.a") {
+		t.Errorf("missing generated static library in linker step libFlags %#v, libFlags: %#v",
+			"libcc_shared.generated_rust_staticlib.a", libcc_shared_ld.Args["libFlags"])
+	}
+
+	// Make sure the static lib includes are in the cc command
+	if !strings.Contains(libcc_shared_cc.Args["cFlags"], "-Ibar_includes") {
+		t.Errorf("missing rlibs includes, expecting %#v, cFlags: %#v",
+			"-Ibar_includes", libcc_shared_cc.Args["cFlags"])
+	}
+
+	// Make sure there's a rustc command, and it's producing a staticlib
+	if !strings.Contains(ccbin_rustc.Args["rustcFlags"], "crate-type=staticlib") {
+		t.Errorf("missing crate-type for static variant, expecting %#v, rustcFlags: %#v", "staticlib", ccbin_rustc.Args["rustcFlags"])
+	}
+
+	// Make sure the static lib is included in the cc command
+	if !strings.Contains(ccbin_ld.Args["libFlags"], "generated_rust_staticlib/libccBin_rust_staticlib.a") {
+		t.Errorf("missing generated static library in linker step libFlags, expecting %#v, libFlags: %#v",
+			"ccBin.generated_rust_staticlib.a", ccbin_ld.Args["libFlags"])
+	}
+
+	// Make sure the static lib includes are in the ld command
+	if !strings.Contains(ccbin_cc.Args["cFlags"], "-Ibar_includes") {
+		t.Errorf("missing rlibs includes, expecting %#v, cFlags: %#v",
+			"-Ibar_includes", ccbin_cc.Args)
+	}
+
+	// Make sure that direct dependencies and indirect dependencies are
+	// propagating correctly to the generated rlib.
+	if !strings.Contains(ccbin_rustc.Args["libFlags"], "--extern foo=") {
+		t.Errorf("Missing indirect dependency libfoo when writing generated Rust staticlib: %#v", ccbin_rustc.Args["libFlags"])
+	}
+	if !strings.Contains(ccbin_rustc.Args["libFlags"], "--extern bar=") {
+		t.Errorf("Missing direct dependency libbar when writing generated Rust staticlib: %#v", ccbin_rustc.Args["libFlags"])
+	}
+
+	// Test indirect includes propagation
+	if !strings.Contains(ccbin_cc.Args["cFlags"], "-Ifoo_includes") {
+		t.Errorf("missing rlibs includes, expecting %#v, cFlags: %#v",
+			"-Ifoo_includes", ccbin_cc.Args)
+	}
+}
+
 func assertString(t *testing.T, got, expected string) {
 	t.Helper()
 	if got != expected {
diff --git a/rust/testing.go b/rust/testing.go
index 5837dcc..f31c591 100644
--- a/rust/testing.go
+++ b/rust/testing.go
@@ -49,16 +49,28 @@
 func GatherRequiredDepsForTest() string {
 	bp := `
 		rust_prebuilt_library {
-				name: "libstd",
-				crate_name: "std",
-				rlib: {
-					srcs: ["libstd.rlib"],
-				},
-				dylib: {
-					srcs: ["libstd.so"],
-				},
-				host_supported: true,
-				sysroot: true,
+			name: "libstd",
+			crate_name: "std",
+			rlib: {
+				srcs: ["libstd/libstd.rlib"],
+			},
+			dylib: {
+				srcs: ["libstd/libstd.so"],
+			},
+			host_supported: true,
+			sysroot: true,
+		}
+		rust_prebuilt_library {
+			name: "libcore.sysroot",
+			crate_name: "core",
+			rlib: {
+				srcs: ["libcore/libcore.rlib"],
+			},
+			dylib: {
+				srcs: ["libcore/libcore.so"],
+			},
+			host_supported: true,
+			sysroot: true,
 		}
 		//////////////////////////////
 		// Device module requirements
@@ -176,10 +188,12 @@
 	ctx.RegisterModuleType("rust_fuzz_host", RustFuzzHostFactory)
 	ctx.RegisterModuleType("rust_ffi", RustFFIFactory)
 	ctx.RegisterModuleType("rust_ffi_shared", RustFFISharedFactory)
-	ctx.RegisterModuleType("rust_ffi_static", RustFFIStaticFactory)
+	ctx.RegisterModuleType("rust_ffi_rlib", RustFFIRlibFactory)
+	ctx.RegisterModuleType("rust_ffi_static", RustFFIStaticRlibFactory)
 	ctx.RegisterModuleType("rust_ffi_host", RustFFIHostFactory)
 	ctx.RegisterModuleType("rust_ffi_host_shared", RustFFISharedHostFactory)
-	ctx.RegisterModuleType("rust_ffi_host_static", RustFFIStaticHostFactory)
+	ctx.RegisterModuleType("rust_ffi_host_rlib", RustFFIRlibHostFactory)
+	ctx.RegisterModuleType("rust_ffi_host_static", RustFFIStaticRlibHostFactory)
 	ctx.RegisterModuleType("rust_proc_macro", ProcMacroFactory)
 	ctx.RegisterModuleType("rust_protobuf", RustProtobufFactory)
 	ctx.RegisterModuleType("rust_protobuf_host", RustProtobufHostFactory)