rust: Rust sanitized snapshots variations

This adds support for correctly handling Rust sanitized snapshots,
ensuring they only have one variation.

The presence of multiple variations were causing build failures
when a rust_fuzz module for host was defined and a snapshot
build was requested.

This also sets -Z link-native-libraries=no on host modules
(in addition to device modules) to avoid emitting extra linkage
flags due to link attributes.

Bug: 282897366
Test: SOONG_SDK_SNAPSHOT_USE_SRCJAR=true m
Change-Id: Idf980c29145f11c530ad635a4eb5b01a1730ac24
diff --git a/cc/androidmk.go b/cc/androidmk.go
index ce35b5c..e0e543f 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -530,9 +530,9 @@
 
 	entries.SubName = ""
 
-	if c.isSanitizerEnabled(cfi) {
+	if c.IsSanitizerEnabled(cfi) {
 		entries.SubName += ".cfi"
-	} else if c.isSanitizerEnabled(Hwasan) {
+	} else if c.IsSanitizerEnabled(Hwasan) {
 		entries.SubName += ".hwasan"
 	}
 
diff --git a/cc/linkable.go b/cc/linkable.go
index 2099399..5b5b856 100644
--- a/cc/linkable.go
+++ b/cc/linkable.go
@@ -95,6 +95,18 @@
 
 	// IsSnapshotPrebuilt returns true if this module is a snapshot prebuilt.
 	IsSnapshotPrebuilt() bool
+
+	// IsSnapshotSanitizer returns true if this snapshot module implements SnapshotSanitizer.
+	IsSnapshotSanitizer() bool
+
+	// IsSnapshotSanitizerAvailable returns true if this snapshot module has a sanitizer source available (cfi, hwasan).
+	IsSnapshotSanitizerAvailable(t SanitizerType) bool
+
+	// SetSnapshotSanitizerVariation sets the sanitizer variation type for this snapshot module.
+	SetSnapshotSanitizerVariation(t SanitizerType, enabled bool)
+
+	// IsSnapshotUnsanitizedVariant returns true if this is the unsanitized snapshot module variant.
+	IsSnapshotUnsanitizedVariant() bool
 }
 
 // LinkableInterface is an interface for a type of module that is linkable in a C++ library.
diff --git a/cc/sanitize.go b/cc/sanitize.go
index 8eea7db..f37b5c7 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -1189,7 +1189,7 @@
 	if sanitizeable, ok := ctx.Module().(Sanitizeable); ok {
 		enabled := sanitizeable.IsSanitizerEnabled(ctx.Config(), s.sanitizer.name())
 		ctx.VisitDirectDeps(func(dep android.Module) {
-			if c, ok := dep.(*Module); ok && c.sanitize.isSanitizerEnabled(s.sanitizer) {
+			if c, ok := dep.(PlatformSanitizeable); ok && c.IsSanitizerEnabled(s.sanitizer) {
 				enabled = true
 			}
 		})
@@ -1243,12 +1243,10 @@
 		}
 	}
 
-	if c, ok := ctx.Module().(*Module); ok {
-		//TODO: When Rust modules have vendor support, enable this path for PlatformSanitizeable
-
+	if c, ok := ctx.Module().(LinkableInterface); ok {
 		// Check if it's a snapshot module supporting sanitizer
-		if ss, ok := c.linker.(snapshotSanitizer); ok {
-			if ss.isSanitizerAvailable(s.sanitizer) {
+		if c.IsSnapshotSanitizer() {
+			if c.IsSnapshotSanitizerAvailable(s.sanitizer) {
 				return []string{"", s.sanitizer.variationName()}
 			} else {
 				return []string{""}
@@ -1280,8 +1278,8 @@
 
 func (s *sanitizerSplitMutator) IncomingTransition(ctx android.IncomingTransitionContext, incomingVariation string) string {
 	if d, ok := ctx.Module().(PlatformSanitizeable); ok {
-		if dm, ok := ctx.Module().(*Module); ok {
-			if ss, ok := dm.linker.(snapshotSanitizer); ok && ss.isSanitizerAvailable(s.sanitizer) {
+		if dm, ok := ctx.Module().(LinkableInterface); ok {
+			if dm.IsSnapshotSanitizerAvailable(s.sanitizer) {
 				return incomingVariation
 			}
 		}
@@ -1396,19 +1394,19 @@
 		if sanitizerVariation {
 			sanitizeable.AddSanitizerDependencies(mctx, s.sanitizer.name())
 		}
-	} else if c, ok := mctx.Module().(*Module); ok {
-		if ss, ok := c.linker.(snapshotSanitizer); ok && ss.isSanitizerAvailable(s.sanitizer) {
-			if !ss.isUnsanitizedVariant() {
+	} else if c, ok := mctx.Module().(LinkableInterface); ok {
+		if c.IsSnapshotSanitizerAvailable(s.sanitizer) {
+			if !c.IsSnapshotUnsanitizedVariant() {
 				// Snapshot sanitizer may have only one variantion.
 				// Skip exporting the module if it already has a sanitizer variation.
 				c.SetPreventInstall()
 				c.SetHideFromMake()
 				return
 			}
-			c.linker.(snapshotSanitizer).setSanitizerVariation(s.sanitizer, sanitizerVariation)
+			c.SetSnapshotSanitizerVariation(s.sanitizer, sanitizerVariation)
 
 			// Export the static lib name to make
-			if c.static() && c.ExportedToMake() {
+			if c.Static() && c.ExportedToMake() {
 				// use BaseModuleName which is the name for Make.
 				if s.sanitizer == cfi {
 					cfiStaticLibs(mctx.Config()).add(c, c.BaseModuleName())
@@ -1420,6 +1418,35 @@
 	}
 }
 
+func (c *Module) IsSnapshotSanitizer() bool {
+	if _, ok := c.linker.(SnapshotSanitizer); ok {
+		return true
+	}
+	return false
+}
+
+func (c *Module) IsSnapshotSanitizerAvailable(t SanitizerType) bool {
+	if ss, ok := c.linker.(SnapshotSanitizer); ok {
+		return ss.IsSanitizerAvailable(t)
+	}
+	return false
+}
+
+func (c *Module) SetSnapshotSanitizerVariation(t SanitizerType, enabled bool) {
+	if ss, ok := c.linker.(SnapshotSanitizer); ok {
+		ss.SetSanitizerVariation(t, enabled)
+	} else {
+		panic(fmt.Errorf("Calling SetSnapshotSanitizerVariation on a non-snapshotLibraryDecorator: %s", c.Name()))
+	}
+}
+
+func (c *Module) IsSnapshotUnsanitizedVariant() bool {
+	if ss, ok := c.linker.(SnapshotSanitizer); ok {
+		return ss.IsUnsanitizedVariant()
+	}
+	return false
+}
+
 func (c *Module) SanitizeNever() bool {
 	return Bool(c.sanitize.Properties.SanitizeMutated.Never)
 }
diff --git a/cc/snapshot_prebuilt.go b/cc/snapshot_prebuilt.go
index bb13310..e29c446 100644
--- a/cc/snapshot_prebuilt.go
+++ b/cc/snapshot_prebuilt.go
@@ -403,11 +403,11 @@
 	Sanitize_minimal_dep *bool `android:"arch_variant"`
 }
 
-type snapshotSanitizer interface {
-	isSanitizerAvailable(t SanitizerType) bool
-	setSanitizerVariation(t SanitizerType, enabled bool)
-	isSanitizerEnabled(t SanitizerType) bool
-	isUnsanitizedVariant() bool
+type SnapshotSanitizer interface {
+	IsSanitizerAvailable(t SanitizerType) bool
+	SetSanitizerVariation(t SanitizerType, enabled bool)
+	IsSanitizerEnabled(t SanitizerType) bool
+	IsUnsanitizedVariant() bool
 }
 
 type snapshotLibraryDecorator struct {
@@ -460,9 +460,9 @@
 		return p.libraryDecorator.link(ctx, flags, deps, objs)
 	}
 
-	if p.isSanitizerEnabled(cfi) {
+	if p.IsSanitizerEnabled(cfi) {
 		p.properties = p.sanitizerProperties.Cfi
-	} else if p.isSanitizerEnabled(Hwasan) {
+	} else if p.IsSanitizerEnabled(Hwasan) {
 		p.properties = p.sanitizerProperties.Hwasan
 	}
 
@@ -526,9 +526,9 @@
 	return false
 }
 
-var _ snapshotSanitizer = (*snapshotLibraryDecorator)(nil)
+var _ SnapshotSanitizer = (*snapshotLibraryDecorator)(nil)
 
-func (p *snapshotLibraryDecorator) isSanitizerAvailable(t SanitizerType) bool {
+func (p *snapshotLibraryDecorator) IsSanitizerAvailable(t SanitizerType) bool {
 	switch t {
 	case cfi:
 		return p.sanitizerProperties.Cfi.Src != nil
@@ -539,23 +539,23 @@
 	}
 }
 
-func (p *snapshotLibraryDecorator) setSanitizerVariation(t SanitizerType, enabled bool) {
-	if !enabled || p.isSanitizerEnabled(t) {
+func (p *snapshotLibraryDecorator) SetSanitizerVariation(t SanitizerType, enabled bool) {
+	if !enabled || p.IsSanitizerEnabled(t) {
 		return
 	}
-	if !p.isUnsanitizedVariant() {
+	if !p.IsUnsanitizedVariant() {
 		panic(fmt.Errorf("snapshot Sanitizer must be one of Cfi or Hwasan but not both"))
 	}
 	p.sanitizerProperties.SanitizerVariation = t
 }
 
-func (p *snapshotLibraryDecorator) isSanitizerEnabled(t SanitizerType) bool {
+func (p *snapshotLibraryDecorator) IsSanitizerEnabled(t SanitizerType) bool {
 	return p.sanitizerProperties.SanitizerVariation == t
 }
 
-func (p *snapshotLibraryDecorator) isUnsanitizedVariant() bool {
-	return !p.isSanitizerEnabled(Asan) &&
-		!p.isSanitizerEnabled(Hwasan)
+func (p *snapshotLibraryDecorator) IsUnsanitizedVariant() bool {
+	return !p.IsSanitizerEnabled(Asan) &&
+		!p.IsSanitizerEnabled(Hwasan)
 }
 
 func snapshotLibraryFactory(image SnapshotImage, moduleSuffix string) (*Module, *snapshotLibraryDecorator) {
diff --git a/rust/config/global.go b/rust/config/global.go
index ca2c9af..4bd495d 100644
--- a/rust/config/global.go
+++ b/rust/config/global.go
@@ -54,11 +54,11 @@
 		"-C symbol-mangling-version=v0",
 		"--color always",
 		"-Zdylib-lto",
+		"-Z link-native-libraries=no",
 	}
 
 	deviceGlobalRustFlags = []string{
 		"-C panic=abort",
-		"-Z link-native-libraries=no",
 		// Generate additional debug info for AutoFDO
 		"-Z debug-info-for-profiling",
 	}
diff --git a/rust/fuzz.go b/rust/fuzz.go
index 235f517..4c04ce8 100644
--- a/rust/fuzz.go
+++ b/rust/fuzz.go
@@ -60,6 +60,25 @@
 	fuzz.binaryDecorator.baseCompiler.dir64 = "fuzz"
 	fuzz.binaryDecorator.baseCompiler.location = InstallInData
 	module.sanitize.SetSanitizer(cc.Fuzzer, true)
+
+	// The fuzzer runtime is not present for darwin or bionic host modules, so disable rust_fuzz modules for these.
+	android.AddLoadHook(module, func(ctx android.LoadHookContext) {
+
+		extraProps := struct {
+			Target struct {
+				Darwin struct {
+					Enabled *bool
+				}
+				Linux_bionic struct {
+					Enabled *bool
+				}
+			}
+		}{}
+		extraProps.Target.Darwin.Enabled = cc.BoolPtr(false)
+		extraProps.Target.Linux_bionic.Enabled = cc.BoolPtr(false)
+		ctx.AppendProperties(&extraProps)
+	})
+
 	module.compiler = fuzz
 	return module, fuzz
 }
diff --git a/rust/snapshot_prebuilt.go b/rust/snapshot_prebuilt.go
index 32d3916..42e3cef 100644
--- a/rust/snapshot_prebuilt.go
+++ b/rust/snapshot_prebuilt.go
@@ -15,6 +15,8 @@
 package rust
 
 import (
+	"fmt"
+
 	"android/soong/android"
 	"android/soong/cc"
 
@@ -26,17 +28,80 @@
 	*libraryDecorator
 	properties          cc.SnapshotLibraryProperties
 	sanitizerProperties struct {
-		CfiEnabled bool `blueprint:"mutated"`
+		SanitizerVariation cc.SanitizerType `blueprint:"mutated"`
 
-		// Library flags for cfi variant.
-		Cfi cc.SnapshotLibraryProperties `android:"arch_variant"`
+		//TODO: Library flags for cfi variant when CFI is supported.
+		//Cfi cc.SnapshotLibraryProperties `android:"arch_variant"`
+
+		// Library flags for hwasan variant.
+		Hwasan cc.SnapshotLibraryProperties `android:"arch_variant"`
 	}
 }
 
+var _ cc.SnapshotSanitizer = (*snapshotLibraryDecorator)(nil)
+
+func (library *snapshotLibraryDecorator) IsSanitizerAvailable(t cc.SanitizerType) bool {
+	switch t {
+	//TODO: When CFI is supported, add a check here as well
+	case cc.Hwasan:
+		return library.sanitizerProperties.Hwasan.Src != nil
+	default:
+		return false
+	}
+}
+
+func (library *snapshotLibraryDecorator) SetSanitizerVariation(t cc.SanitizerType, enabled bool) {
+	if !enabled || library.IsSanitizerEnabled(t) {
+		return
+	}
+	if !library.IsUnsanitizedVariant() {
+		panic(fmt.Errorf("snapshot Sanitizer must be one of Cfi or Hwasan but not both"))
+	}
+	library.sanitizerProperties.SanitizerVariation = t
+}
+
+func (library *snapshotLibraryDecorator) IsSanitizerEnabled(t cc.SanitizerType) bool {
+	return library.sanitizerProperties.SanitizerVariation == t
+}
+
+func (library *snapshotLibraryDecorator) IsUnsanitizedVariant() bool {
+	//TODO: When CFI is supported, add a check here as well
+	return !library.IsSanitizerEnabled(cc.Hwasan)
+}
+
 func init() {
 	registerRustSnapshotModules(android.InitRegistrationContext)
 }
 
+func (mod *Module) IsSnapshotSanitizerAvailable(t cc.SanitizerType) bool {
+	if ss, ok := mod.compiler.(cc.SnapshotSanitizer); ok {
+		return ss.IsSanitizerAvailable(t)
+	}
+	return false
+}
+
+func (mod *Module) SetSnapshotSanitizerVariation(t cc.SanitizerType, enabled bool) {
+	if ss, ok := mod.compiler.(cc.SnapshotSanitizer); ok {
+		ss.SetSanitizerVariation(t, enabled)
+	} else {
+		panic(fmt.Errorf("Calling SetSnapshotSanitizerVariation on a non-snapshotLibraryDecorator: %s", mod.Name()))
+	}
+}
+
+func (mod *Module) IsSnapshotUnsanitizedVariant() bool {
+	if ss, ok := mod.compiler.(cc.SnapshotSanitizer); ok {
+		return ss.IsUnsanitizedVariant()
+	}
+	return false
+}
+
+func (mod *Module) IsSnapshotSanitizer() bool {
+	if _, ok := mod.compiler.(cc.SnapshotSanitizer); ok {
+		return true
+	}
+	return false
+}
+
 func registerRustSnapshotModules(ctx android.RegistrationContext) {
 	cc.VendorSnapshotImageSingleton.RegisterAdditionalModule(ctx,
 		"vendor_snapshot_rlib", VendorSnapshotRlibFactory)
@@ -81,6 +146,9 @@
 
 	library.SetSnapshotAndroidMkSuffix(ctx, variant)
 
+	if library.IsSanitizerEnabled(cc.Hwasan) {
+		library.properties = library.sanitizerProperties.Hwasan
+	}
 	if !library.MatchesWithDevice(ctx.DeviceConfig()) {
 		return buildOutput{}
 	}