Merge "cc bp2build for sysprop_library modules"
diff --git a/android/allowlists/allowlists.go b/android/allowlists/allowlists.go
index ccd4a77..661dcf5 100644
--- a/android/allowlists/allowlists.go
+++ b/android/allowlists/allowlists.go
@@ -134,6 +134,7 @@
 		"external/libjpeg-turbo":                 Bp2BuildDefaultTrueRecursively,
 		"external/libmpeg2":                      Bp2BuildDefaultTrueRecursively,
 		"external/libpng":                        Bp2BuildDefaultTrueRecursively,
+		"external/libvpx":                        Bp2BuildDefaultTrueRecursively,
 		"external/libyuv":                        Bp2BuildDefaultTrueRecursively,
 		"external/lz4/lib":                       Bp2BuildDefaultTrue,
 		"external/lzma/C":                        Bp2BuildDefaultTrueRecursively,
@@ -459,7 +460,6 @@
 		"libactivitymanager_aidl", // TODO(b/207426160): Unsupported use of aidl sources (via Dactivity_manager_procstate_aidl) in a cc_library
 
 		// TODO(b/198619163) module has same name as source
-		"gen-kotlin-build-file.py",
 		"logtagd.rc",
 
 		"libgtest_ndk_c++", "libgtest_main_ndk_c++", // TODO(b/201816222): Requires sdk_version support.
@@ -471,9 +471,7 @@
 		"KernelLibcutilsTest",
 
 		"linker",                 // TODO(b/228316882): cc_binary uses link_crt
-		"libdebuggerd",           // TODO(b/228314770): support product variable-specific header_libs
 		"versioner",              // TODO(b/228313961):  depends on prebuilt shared library libclang-cpp_host as a shared library, which does not supply expected providers for a shared library
-		"libvpx",                 // TODO(b/240756936): Arm neon variant not supported
 		"art_libartbase_headers", // TODO(b/236268577): Header libraries do not support export_shared_libs_headers
 		"apexer_test",            // Requires aapt2
 		"apexer_test_host_tools",
@@ -547,6 +545,7 @@
 		"libartbased-art-gtest",                                      // depends on unconverted modules: libgtest_isolated, libartd, libartd-compiler, libdexfiled, libprofiled
 		"libartd",                                                    // depends on unconverted modules: art_operator_srcs, libcpu_features, libodrstatslog, libelffiled, art_cmdlineparser_headers, cpp-define-generator-definitions, libdexfiled, libnativebridge, libnativeloader, libsigchain, libartbased, libprofiled, cpp-define-generator-asm-support, apex-info-list-tinyxml, libtinyxml2, libnativeloader-headers, heapprofd_client_api
 		"libartd-runtime-gtest",                                      // depends on unconverted modules: libgtest_isolated, libartd-compiler, libdexfiled, libprofiled, libartbased, libartbased-art-gtest
+		"libdebuggerd",                                               // depends on unconverted module: libdexfile
 		"libdebuggerd_handler",                                       // depends on unconverted module libdebuggerd_handler_core
 		"libdebuggerd_handler_core", "libdebuggerd_handler_fallback", // depends on unconverted module libdebuggerd
 		"libdexfiled",                                             // depends on unconverted modules: dexfile_operator_srcs, libartbased, libartpalette
diff --git a/android/arch.go b/android/arch.go
index 1952b17..9bc9d89 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -19,6 +19,7 @@
 	"fmt"
 	"reflect"
 	"runtime"
+	"sort"
 	"strings"
 
 	"android/soong/bazel"
@@ -2084,13 +2085,22 @@
 	// For each arch type (x86, arm64, etc.)
 	for _, arch := range ArchTypeList() {
 		// Arch properties are sometimes sharded (see createArchPropTypeDesc() ).
-		// Iterate over ever shard and extract a struct with the same type as the
+		// Iterate over every shard and extract a struct with the same type as the
 		// input one that contains the data specific to that arch.
 		propertyStructs := make([]reflect.Value, 0)
+		archFeaturePropertyStructs := make(map[string][]reflect.Value, 0)
 		for _, archProperty := range archProperties {
 			archTypeStruct, ok := getArchTypeStruct(ctx, archProperty, arch)
 			if ok {
 				propertyStructs = append(propertyStructs, archTypeStruct)
+
+				// For each feature this arch supports (arm: neon, x86: ssse3, sse4, ...)
+				for _, feature := range archFeatures[arch] {
+					prefix := "arch." + arch.Name + "." + feature
+					if featureProperties, ok := getChildPropertyStruct(ctx, archTypeStruct, feature, prefix); ok {
+						archFeaturePropertyStructs[feature] = append(archFeaturePropertyStructs[feature], featureProperties)
+					}
+				}
 			}
 			multilibStruct, ok := getMultilibStruct(ctx, archProperty, arch)
 			if ok {
@@ -2098,10 +2108,31 @@
 			}
 		}
 
-		// Create a new instance of the requested property set
-		value := reflect.New(reflect.ValueOf(propertySet).Elem().Type()).Interface()
+		archToProp[arch.Name] = mergeStructs(ctx, propertyStructs, propertySet)
 
-		archToProp[arch.Name] = mergeStructs(ctx, propertyStructs, value)
+		// In soong, if multiple features match the current configuration, they're
+		// all used. In bazel, we have to have unambiguous select() statements, so
+		// we can't have two features that are both active in the same select().
+		// One alternative is to split out each feature into a separate select(),
+		// but then it's difficult to support exclude_srcs, which may need to
+		// exclude things from the regular arch select() statement if a certain
+		// feature is active. Instead, keep the features in the same select
+		// statement as the arches, but emit the power set of all possible
+		// combinations of features, so that bazel can match the most precise one.
+		allFeatures := make([]string, 0, len(archFeaturePropertyStructs))
+		for feature := range archFeaturePropertyStructs {
+			allFeatures = append(allFeatures, feature)
+		}
+		for _, features := range bazel.PowerSetWithoutEmptySet(allFeatures) {
+			sort.Strings(features)
+			propsForCurrentFeatureSet := make([]reflect.Value, 0)
+			propsForCurrentFeatureSet = append(propsForCurrentFeatureSet, propertyStructs...)
+			for _, feature := range features {
+				propsForCurrentFeatureSet = append(propsForCurrentFeatureSet, archFeaturePropertyStructs[feature]...)
+			}
+			archToProp[arch.Name+"-"+strings.Join(features, "-")] =
+				mergeStructs(ctx, propsForCurrentFeatureSet, propertySet)
+		}
 	}
 	axisToProps[bazel.ArchConfigurationAxis] = archToProp
 
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 49a5d2a..d6803f6 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -8846,6 +8846,16 @@
 		android.FixtureMergeEnv(map[string]string{
 			"EMMA_INSTRUMENT": "true",
 		}),
+		// need to mock jacocoagent here to satisfy dependency added for
+		// instrumented libraries at build time
+		android.FixtureAddFile("jacocoagent/Android.bp", []byte(`
+			java_library {
+				name: "jacocoagent",
+				srcs: ["Test.java"],
+				system_modules: "none",
+				sdk_version: "none",
+			}
+		`)),
 	).RunTest(t)
 
 	// Make sure jacoco ran on both mylib and mybootclasspathlib
diff --git a/bazel/configurability.go b/bazel/configurability.go
index d9b0a12..e1cdd4a 100644
--- a/bazel/configurability.go
+++ b/bazel/configurability.go
@@ -16,6 +16,8 @@
 
 import (
 	"fmt"
+	"math"
+	"sort"
 	"strings"
 )
 
@@ -69,6 +71,71 @@
 	AndroidAndNonApex = "android-non_apex"
 )
 
+func PowerSetWithoutEmptySet[T any](items []T) [][]T {
+	resultSize := int(math.Pow(2, float64(len(items))))
+	powerSet := make([][]T, 0, resultSize-1)
+	for i := 1; i < resultSize; i++ {
+		combination := make([]T, 0)
+		for j := 0; j < len(items); j++ {
+			if (i>>j)%2 == 1 {
+				combination = append(combination, items[j])
+			}
+		}
+		powerSet = append(powerSet, combination)
+	}
+	return powerSet
+}
+
+func createPlatformArchMap() map[string]string {
+	// Copy of archFeatures from android/arch_list.go because the bazel
+	// package can't access the android package
+	archFeatures := map[string][]string{
+		"arm": {
+			"neon",
+		},
+		"arm64": {
+			"dotprod",
+		},
+		"x86": {
+			"ssse3",
+			"sse4",
+			"sse4_1",
+			"sse4_2",
+			"aes_ni",
+			"avx",
+			"avx2",
+			"avx512",
+			"popcnt",
+			"movbe",
+		},
+		"x86_64": {
+			"ssse3",
+			"sse4",
+			"sse4_1",
+			"sse4_2",
+			"aes_ni",
+			"avx",
+			"avx2",
+			"avx512",
+			"popcnt",
+		},
+	}
+	result := make(map[string]string)
+	for arch, allFeatures := range archFeatures {
+		result[arch] = "//build/bazel/platforms/arch:" + arch
+		// Sometimes we want to select on multiple features being active, so
+		// add the power set of all possible features to the map. More details
+		// in android.ModuleBase.GetArchVariantProperties
+		for _, features := range PowerSetWithoutEmptySet(allFeatures) {
+			sort.Strings(features)
+			archFeaturesName := arch + "-" + strings.Join(features, "-")
+			result[archFeaturesName] = "//build/bazel/platforms/arch/variants:" + archFeaturesName
+		}
+	}
+	result[ConditionsDefaultConfigKey] = ConditionsDefaultSelectKey
+	return result
+}
+
 var (
 	// These are the list of OSes and architectures with a Bazel config_setting
 	// and constraint value equivalent. These exist in arch.go, but the android
@@ -77,13 +144,7 @@
 
 	// A map of architectures to the Bazel label of the constraint_value
 	// for the @platforms//cpu:cpu constraint_setting
-	platformArchMap = map[string]string{
-		archArm:                    "//build/bazel/platforms/arch:arm",
-		archArm64:                  "//build/bazel/platforms/arch:arm64",
-		archX86:                    "//build/bazel/platforms/arch:x86",
-		archX86_64:                 "//build/bazel/platforms/arch:x86_64",
-		ConditionsDefaultConfigKey: ConditionsDefaultSelectKey, // The default condition of as arch select map.
-	}
+	platformArchMap = createPlatformArchMap()
 
 	// A map of target operating systems to the Bazel label of the
 	// constraint_value for the @platforms//os:os constraint_setting
diff --git a/bazel/properties.go b/bazel/properties.go
index 13e36b5..d82fa64 100644
--- a/bazel/properties.go
+++ b/bazel/properties.go
@@ -725,7 +725,7 @@
 	case noConfig:
 		return lla.Value
 	case arch, os, osArch, productVariables, osAndInApex:
-		return (lla.ConfigurableValues[axis][config])
+		return lla.ConfigurableValues[axis][config]
 	default:
 		panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
 	}
diff --git a/bp2build/cc_library_conversion_test.go b/bp2build/cc_library_conversion_test.go
index 45f15bf..5117c62 100644
--- a/bp2build/cc_library_conversion_test.go
+++ b/bp2build/cc_library_conversion_test.go
@@ -1199,6 +1199,40 @@
 	)
 }
 
+func TestCcLibraryProductVariablesHeaderLibs(t *testing.T) {
+	runCcLibraryTestCase(t, Bp2buildTestCase{
+		ModuleTypeUnderTest:        "cc_library",
+		ModuleTypeUnderTestFactory: cc.LibraryFactory,
+		Filesystem:                 map[string]string{},
+		Blueprint: soongCcLibraryStaticPreamble + `
+cc_library {
+    name: "foo_static",
+    srcs: ["common.c"],
+    product_variables: {
+        malloc_not_svelte: {
+            header_libs: ["malloc_not_svelte_header_lib"],
+        },
+    },
+    include_build_directory: false,
+}
+
+cc_library {
+    name: "malloc_not_svelte_header_lib",
+    bazel_module: { bp2build_available: false },
+}
+`,
+		ExpectedBazelTargets: makeCcLibraryTargets("foo_static", AttrNameToString{
+			"implementation_deps": `select({
+        "//build/bazel/product_variables:malloc_not_svelte": [":malloc_not_svelte_header_lib"],
+        "//conditions:default": [],
+    })`,
+			"srcs_c":                 `["common.c"]`,
+			"target_compatible_with": `["//build/bazel/platforms/os:android"]`,
+		}),
+	},
+	)
+}
+
 func TestCCLibraryNoCrtTrue(t *testing.T) {
 	runCcLibraryTestCase(t, Bp2buildTestCase{
 		Description:                "cc_library - nocrt: true emits attribute",
diff --git a/bp2build/cc_library_static_conversion_test.go b/bp2build/cc_library_static_conversion_test.go
index 0e47ee8..0637ba2 100644
--- a/bp2build/cc_library_static_conversion_test.go
+++ b/bp2build/cc_library_static_conversion_test.go
@@ -1419,13 +1419,7 @@
 }
 `,
 		ExpectedBazelTargets: []string{
-			MakeBazelTarget("cc_library_static", "all", AttrNameToString{
-				"implementation_dynamic_deps": `select({
-        "//build/bazel/platforms/os:android": [],
-        "//build/bazel/platforms/os:linux_bionic": [],
-        "//conditions:default": [":libc"],
-    })`,
-			}),
+			MakeBazelTarget("cc_library_static", "all", AttrNameToString{}),
 			MakeBazelTarget("cc_library_static", "keep_for_empty_system_shared_libs", AttrNameToString{
 				"implementation_dynamic_deps": `[":libc"]`,
 				"system_dynamic_deps":         `[]`,
diff --git a/bp2build/java_library_conversion_test.go b/bp2build/java_library_conversion_test.go
index 4a4da18..74e2dbd 100644
--- a/bp2build/java_library_conversion_test.go
+++ b/bp2build/java_library_conversion_test.go
@@ -534,3 +534,118 @@
 		ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
 	})
 }
+
+func TestConvertArmNeonVariant(t *testing.T) {
+	t.Helper()
+	RunBp2BuildTestCase(t, func(ctx android.RegistrationContext) {}, Bp2buildTestCase{
+		Description:                "Android Library - simple arch feature",
+		ModuleTypeUnderTest:        "android_library",
+		ModuleTypeUnderTestFactory: java.AndroidLibraryFactory,
+		Blueprint: simpleModuleDoNotConvertBp2build("android_library", "static_lib_dep") + `
+android_library {
+	name: "TestLib",
+	manifest: "manifest/AndroidManifest.xml",
+	srcs: ["lib.java"],
+	arch: {
+		arm: {
+			neon: {
+				srcs: ["arm_neon.java"],
+			},
+		},
+	},
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget(
+				"android_library",
+				"TestLib",
+				AttrNameToString{
+					"srcs": `["lib.java"] + select({
+        "//build/bazel/platforms/arch/variants:arm-neon": ["arm_neon.java"],
+        "//conditions:default": [],
+    })`,
+					"manifest":       `"manifest/AndroidManifest.xml"`,
+					"resource_files": `[]`,
+				}),
+		}})
+}
+
+func TestConvertMultipleArchFeatures(t *testing.T) {
+	t.Helper()
+	RunBp2BuildTestCase(t, func(ctx android.RegistrationContext) {}, Bp2buildTestCase{
+		Description:                "Android Library - multiple arch features",
+		ModuleTypeUnderTest:        "android_library",
+		ModuleTypeUnderTestFactory: java.AndroidLibraryFactory,
+		Blueprint: simpleModuleDoNotConvertBp2build("android_library", "static_lib_dep") + `
+android_library {
+	name: "TestLib",
+	manifest: "manifest/AndroidManifest.xml",
+	srcs: ["lib.java"],
+	arch: {
+		x86: {
+			ssse3: {
+				srcs: ["ssse3.java"],
+			},
+			sse4_1: {
+				srcs: ["sse4_1.java"],
+			},
+		},
+	},
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget(
+				"android_library",
+				"TestLib",
+				AttrNameToString{
+					"srcs": `["lib.java"] + select({
+        "//build/bazel/platforms/arch/variants:x86-sse4_1": ["sse4_1.java"],
+        "//build/bazel/platforms/arch/variants:x86-sse4_1-ssse3": [
+            "sse4_1.java",
+            "ssse3.java",
+        ],
+        "//build/bazel/platforms/arch/variants:x86-ssse3": ["ssse3.java"],
+        "//conditions:default": [],
+    })`,
+					"manifest":       `"manifest/AndroidManifest.xml"`,
+					"resource_files": `[]`,
+				}),
+		}})
+}
+
+func TestConvertExcludeSrcsArchFeature(t *testing.T) {
+	t.Helper()
+	RunBp2BuildTestCase(t, func(ctx android.RegistrationContext) {}, Bp2buildTestCase{
+		Description:                "Android Library - exclude_srcs with arch feature",
+		ModuleTypeUnderTest:        "android_library",
+		ModuleTypeUnderTestFactory: java.AndroidLibraryFactory,
+		Blueprint: simpleModuleDoNotConvertBp2build("android_library", "static_lib_dep") + `
+android_library {
+	name: "TestLib",
+	manifest: "manifest/AndroidManifest.xml",
+	srcs: ["lib.java"],
+	arch: {
+		arm: {
+			srcs: ["arm_non_neon.java"],
+			neon: {
+				exclude_srcs: ["arm_non_neon.java"],
+			},
+		},
+	},
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget(
+				"android_library",
+				"TestLib",
+				AttrNameToString{
+					"srcs": `["lib.java"] + select({
+        "//build/bazel/platforms/arch/variants:arm-neon": [],
+        "//build/bazel/platforms/arch:arm": ["arm_non_neon.java"],
+        "//conditions:default": [],
+    })`,
+					"manifest":       `"manifest/AndroidManifest.xml"`,
+					"resource_files": `[]`,
+				}),
+		}})
+}
diff --git a/bp2build/prebuilt_etc_conversion_test.go b/bp2build/prebuilt_etc_conversion_test.go
index 6738b4b..b1e70dc 100644
--- a/bp2build/prebuilt_etc_conversion_test.go
+++ b/bp2build/prebuilt_etc_conversion_test.go
@@ -181,3 +181,87 @@
 				"dir":         `"etc"`,
 			})}})
 }
+
+func TestFilenameAsProperty(t *testing.T) {
+	runPrebuiltEtcTestCase(t, Bp2buildTestCase{
+		Description: "prebuilt_etc - filename is specified as a property ",
+		Filesystem:  map[string]string{},
+		Blueprint: `
+prebuilt_etc {
+    name: "foo",
+    src: "fooSrc",
+    filename: "fooFileName",
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("prebuilt_file", "foo", AttrNameToString{
+				"filename": `"fooFileName"`,
+				"src":      `"fooSrc"`,
+				"dir":      `"etc"`,
+			})}})
+}
+
+func TestFileNameFromSrc(t *testing.T) {
+	runPrebuiltEtcTestCase(t, Bp2buildTestCase{
+		Description: "prebuilt_etc - filename_from_src is true  ",
+		Filesystem:  map[string]string{},
+		Blueprint: `
+prebuilt_etc {
+    name: "foo",
+    filename_from_src: true,
+    src: "fooSrc",
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("prebuilt_file", "foo", AttrNameToString{
+				"filename": `"fooSrc"`,
+				"src":      `"fooSrc"`,
+				"dir":      `"etc"`,
+			})}})
+}
+
+func TestFileNameFromSrcMultipleSrcs(t *testing.T) {
+	runPrebuiltEtcTestCase(t, Bp2buildTestCase{
+		Description: "prebuilt_etc - filename_from_src is true but there are multiple srcs",
+		Filesystem:  map[string]string{},
+		Blueprint: `
+prebuilt_etc {
+    name: "foo",
+    filename_from_src: true,
+		arch: {
+        arm: {
+            src: "barSrc",
+        },
+        arm64: {
+            src: "bazSrc",
+        },
+	  }
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("prebuilt_file", "foo", AttrNameToString{
+				"filename_from_src": `True`,
+				"dir":               `"etc"`,
+				"src": `select({
+        "//build/bazel/platforms/arch:arm": "barSrc",
+        "//build/bazel/platforms/arch:arm64": "bazSrc",
+        "//conditions:default": None,
+    })`,
+			})}})
+}
+
+func TestFilenameFromModuleName(t *testing.T) {
+	runPrebuiltEtcTestCase(t, Bp2buildTestCase{
+		Description: "prebuilt_etc - neither filename nor filename_from_src are specified ",
+		Filesystem:  map[string]string{},
+		Blueprint: `
+prebuilt_etc {
+    name: "foo",
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("prebuilt_file", "foo", AttrNameToString{
+				"filename": `"foo"`,
+				"dir":      `"etc"`,
+			})}})
+}
diff --git a/cc/bp2build.go b/cc/bp2build.go
index 77aaec1..c1a3bef 100644
--- a/cc/bp2build.go
+++ b/cc/bp2build.go
@@ -1026,11 +1026,17 @@
 		depResolutionFunc func(ctx android.BazelConversionPathContext, modules, excludes []string) bazel.LabelList
 	}
 
+	// an intermediate attribute that holds Header_libs info, and will be appended to
+	// implementationDeps at the end, to solve the confliction that both header_libs
+	// and static_libs use implementationDeps.
+	var headerDeps bazel.LabelListAttribute
+
 	productVarToDepFields := map[string]productVarDep{
 		// product variables do not support exclude_shared_libs
 		"Shared_libs":       {attribute: &la.implementationDynamicDeps, depResolutionFunc: bazelLabelForSharedDepsExcludes},
 		"Static_libs":       {"Exclude_static_libs", &la.implementationDeps, bazelLabelForStaticDepsExcludes},
 		"Whole_static_libs": {"Exclude_static_libs", &la.wholeArchiveDeps, bazelLabelForWholeDepsExcludes},
+		"Header_libs":       {attribute: &headerDeps, depResolutionFunc: bazelLabelForHeaderDepsExcludes},
 	}
 
 	for name, dep := range productVarToDepFields {
@@ -1072,14 +1078,18 @@
 			)
 		}
 	}
+	la.implementationDeps.Append(headerDeps)
 }
 
 func (la *linkerAttributes) finalize(ctx android.BazelConversionPathContext) {
 	// if system dynamic deps have the default value, any use of a system dynamic library used will
 	// result in duplicate library errors for bionic OSes. Here, we explicitly exclude those libraries
-	// from bionic OSes.
+	// from bionic OSes and the no config case as these libraries only build for bionic OSes.
 	if la.systemDynamicDeps.IsNil() && len(la.usedSystemDynamicDepAsDynamicDep) > 0 {
 		toRemove := bazelLabelForSharedDeps(ctx, android.SortedStringKeys(la.usedSystemDynamicDepAsDynamicDep))
+		la.dynamicDeps.Exclude(bazel.NoConfigAxis, "", toRemove)
+		la.implementationDynamicDeps.Exclude(bazel.NoConfigAxis, "", toRemove)
+		la.implementationDynamicDeps.Exclude(bazel.NoConfigAxis, "", toRemove)
 		la.dynamicDeps.Exclude(bazel.OsConfigurationAxis, "android", toRemove)
 		la.dynamicDeps.Exclude(bazel.OsConfigurationAxis, "linux_bionic", toRemove)
 		la.implementationDynamicDeps.Exclude(bazel.OsConfigurationAxis, "android", toRemove)
@@ -1200,6 +1210,12 @@
 	return bazelLabelForSharedDeps(ctx, modules)
 }
 
+func bazelLabelForHeaderDepsExcludes(ctx android.BazelConversionPathContext, modules, excludes []string) bazel.LabelList {
+	// This is only used when product_variable header_libs is processed, to follow
+	// the pattern of depResolutionFunc
+	return android.BazelLabelForModuleDepsExcludesWithFn(ctx, modules, excludes, bazelLabelForSharedModule)
+}
+
 func bazelLabelForSharedDepsExcludes(ctx android.BazelConversionPathContext, modules, excludes []string) bazel.LabelList {
 	return android.BazelLabelForModuleDepsExcludesWithFn(ctx, modules, excludes, bazelLabelForSharedModule)
 }
diff --git a/cc/cc.go b/cc/cc.go
index 0ed9bb6..3129160 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -485,6 +485,7 @@
 	static() bool
 	staticBinary() bool
 	testBinary() bool
+	testLibrary() bool
 	header() bool
 	binary() bool
 	object() bool
@@ -1486,6 +1487,10 @@
 	return ctx.mod.testBinary()
 }
 
+func (ctx *moduleContextImpl) testLibrary() bool {
+	return ctx.mod.testLibrary()
+}
+
 func (ctx *moduleContextImpl) header() bool {
 	return ctx.mod.Header()
 }
diff --git a/cc/config/bionic.go b/cc/config/bionic.go
index e87f571..a1e3851 100644
--- a/cc/config/bionic.go
+++ b/cc/config/bionic.go
@@ -15,6 +15,7 @@
 package config
 
 type toolchainBionic struct {
+	toolchainBase
 }
 
 var (
@@ -29,6 +30,12 @@
 
 func (toolchainBionic) DefaultSharedLibraries() []string { return bionicDefaultSharedLibraries }
 
+func (toolchainBionic) ShlibSuffix() string { return ".so" }
+
+func (toolchainBionic) ExecutableSuffix() string { return "" }
+
+func (toolchainBionic) AvailableLibraries() []string { return nil }
+
 func (toolchainBionic) CrtBeginStaticBinary() []string  { return bionicCrtBeginStaticBinary }
 func (toolchainBionic) CrtBeginSharedBinary() []string  { return bionicCrtBeginSharedBinary }
 func (toolchainBionic) CrtBeginSharedLibrary() []string { return bionicCrtBeginSharedLibrary }
diff --git a/cc/config/darwin_host.go b/cc/config/darwin_host.go
index 5e3f7c7..01b1e63 100644
--- a/cc/config/darwin_host.go
+++ b/cc/config/darwin_host.go
@@ -176,6 +176,8 @@
 type toolchainDarwin struct {
 	cFlags, ldFlags string
 	toolchain64Bit
+	toolchainNoCrt
+	toolchainBase
 }
 
 type toolchainDarwinX86 struct {
@@ -254,6 +256,10 @@
 	return ".dylib"
 }
 
+func (t *toolchainDarwin) ExecutableSuffix() string {
+	return ""
+}
+
 func (t *toolchainDarwin) AvailableLibraries() []string {
 	return darwinAvailableLibraries
 }
diff --git a/cc/config/toolchain.go b/cc/config/toolchain.go
index 253bb06..d9eaf53 100644
--- a/cc/config/toolchain.go
+++ b/cc/config/toolchain.go
@@ -140,14 +140,6 @@
 	return ""
 }
 
-func (toolchainBase) ShlibSuffix() string {
-	return ".so"
-}
-
-func (toolchainBase) ExecutableSuffix() string {
-	return ""
-}
-
 func (toolchainBase) Asflags() string {
 	return ""
 }
@@ -160,16 +152,14 @@
 	return ""
 }
 
-func (toolchainBase) AvailableLibraries() []string {
-	return nil
-}
+type toolchainNoCrt struct{}
 
-func (toolchainBase) CrtBeginStaticBinary() []string  { return nil }
-func (toolchainBase) CrtBeginSharedBinary() []string  { return nil }
-func (toolchainBase) CrtBeginSharedLibrary() []string { return nil }
-func (toolchainBase) CrtEndStaticBinary() []string    { return nil }
-func (toolchainBase) CrtEndSharedBinary() []string    { return nil }
-func (toolchainBase) CrtEndSharedLibrary() []string   { return nil }
+func (toolchainNoCrt) CrtBeginStaticBinary() []string  { return nil }
+func (toolchainNoCrt) CrtBeginSharedBinary() []string  { return nil }
+func (toolchainNoCrt) CrtBeginSharedLibrary() []string { return nil }
+func (toolchainNoCrt) CrtEndStaticBinary() []string    { return nil }
+func (toolchainNoCrt) CrtEndSharedBinary() []string    { return nil }
+func (toolchainNoCrt) CrtEndSharedLibrary() []string   { return nil }
 
 func (toolchainBase) DefaultSharedLibraries() []string {
 	return nil
@@ -188,7 +178,6 @@
 }
 
 type toolchain64Bit struct {
-	toolchainBase
 }
 
 func (toolchain64Bit) Is64Bit() bool {
@@ -196,7 +185,6 @@
 }
 
 type toolchain32Bit struct {
-	toolchainBase
 }
 
 func (toolchain32Bit) Is64Bit() bool {
diff --git a/cc/config/x86_linux_host.go b/cc/config/x86_linux_host.go
index 4e8fd77..1b126de 100644
--- a/cc/config/x86_linux_host.go
+++ b/cc/config/x86_linux_host.go
@@ -158,6 +158,7 @@
 }
 
 type toolchainLinux struct {
+	toolchainBase
 	cFlags, ldFlags string
 }
 
@@ -247,9 +248,18 @@
 	return linuxAvailableLibraries
 }
 
+func (toolchainLinux) ShlibSuffix() string {
+	return ".so"
+}
+
+func (toolchainLinux) ExecutableSuffix() string {
+	return ""
+}
+
 // glibc specialization of the linux toolchain
 
 type toolchainGlibc struct {
+	toolchainNoCrt
 }
 
 func (toolchainGlibc) Glibc() bool { return true }
diff --git a/cc/config/x86_windows_host.go b/cc/config/x86_windows_host.go
index 2c83211..a33606f 100644
--- a/cc/config/x86_windows_host.go
+++ b/cc/config/x86_windows_host.go
@@ -153,6 +153,8 @@
 
 type toolchainWindows struct {
 	cFlags, ldFlags string
+	toolchainBase
+	toolchainNoCrt
 }
 
 type toolchainWindowsX86 struct {
diff --git a/cc/lto.go b/cc/lto.go
index 455ff7e..5d2fba0 100644
--- a/cc/lto.go
+++ b/cc/lto.go
@@ -140,12 +140,13 @@
 	lib32 := ctx.Arch().ArchType.Multilib == "lib32"
 	// CFI enables full LTO.
 	cfi := ctx.isCfi()
-	// Performance and binary size are less important for host binaries.
+	// Performance and binary size are less important for host binaries and tests.
 	host := ctx.Host()
+	test := ctx.testBinary() || ctx.testLibrary()
 	// FIXME: ThinLTO for VNDK produces different output.
 	// b/169217596
 	vndk := ctx.isVndk()
-	return GlobalThinLTO(ctx) && !lto.Never() && !lib32 && !cfi && !host && !vndk
+	return GlobalThinLTO(ctx) && !lto.Never() && !lib32 && !cfi && !host && !test && !vndk
 }
 
 func (lto *lto) FullLTO() bool {
diff --git a/cc/sanitize.go b/cc/sanitize.go
index 86472a2..edff059 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -77,6 +77,7 @@
 		"-fno-sanitize-recover=integer,undefined"}
 	hwasanGlobalOptions = []string{"heap_history_size=1023", "stack_history_size=512",
 		"export_memory_stats=0", "max_malloc_fill_size=4096", "malloc_fill_byte=0"}
+	memtagStackCommonFlags = []string{"-march=armv8-a+memtag"}
 )
 
 type SanitizerType int
@@ -89,6 +90,7 @@
 	scs
 	Fuzzer
 	Memtag_heap
+	Memtag_stack
 	cfi // cfi is last to prevent it running before incompatible mutators
 )
 
@@ -100,6 +102,7 @@
 	scs,
 	Fuzzer,
 	Memtag_heap,
+	Memtag_stack,
 	cfi, // cfi is last to prevent it running before incompatible mutators
 }
 
@@ -120,6 +123,8 @@
 		return "scs"
 	case Memtag_heap:
 		return "memtag_heap"
+	case Memtag_stack:
+		return "memtag_stack"
 	case Fuzzer:
 		return "fuzzer"
 	default:
@@ -136,6 +141,8 @@
 		return "hwaddress"
 	case Memtag_heap:
 		return "memtag_heap"
+	case Memtag_stack:
+		return "memtag_stack"
 	case tsan:
 		return "thread"
 	case intOverflow:
@@ -157,7 +164,7 @@
 		sanitizer := &sanitizerSplitMutator{t}
 		ctx.TopDown(t.variationName()+"_markapexes", sanitizer.markSanitizableApexesMutator)
 		ctx.Transition(t.variationName(), sanitizer)
-	case Memtag_heap, intOverflow:
+	case Memtag_heap, Memtag_stack, intOverflow:
 		// do nothing
 	default:
 		panic(fmt.Errorf("unknown SanitizerType %d", t))
@@ -182,6 +189,8 @@
 		return true
 	case Memtag_heap:
 		return true
+	case Memtag_stack:
+		return true
 	default:
 		return false
 	}
@@ -233,6 +242,9 @@
 	// Memory-tagging, only available on arm64
 	// if diag.memtag unset or false, enables async memory tagging
 	Memtag_heap *bool `android:"arch_variant"`
+	// Memory-tagging stack instrumentation, only available on arm64
+	// Adds instrumentation to detect stack buffer overflows and use-after-scope using MTE.
+	Memtag_stack *bool `android:"arch_variant"`
 
 	// A modifier for ASAN and HWASAN for write only instrumentation
 	Writeonly *bool `android:"arch_variant"`
@@ -318,7 +330,7 @@
 		return
 	}
 
-	// cc_test targets default to SYNC MemTag unless explicitly set to ASYNC (via diag: {memtag_heap}).
+	// cc_test targets default to SYNC MemTag unless explicitly set to ASYNC (via diag: {memtag_heap: false}).
 	if ctx.testBinary() {
 		if s.Memtag_heap == nil {
 			s.Memtag_heap = proptools.BoolPtr(true)
@@ -404,6 +416,10 @@
 			}
 		}
 
+		if found, globalSanitizers = removeFromList("memtag_stack", globalSanitizers); found && s.Memtag_stack == nil {
+			s.Memtag_stack = proptools.BoolPtr(true)
+		}
+
 		if len(globalSanitizers) > 0 {
 			ctx.ModuleErrorf("unknown global sanitizer option %s", globalSanitizers[0])
 		}
@@ -470,14 +486,19 @@
 	}
 
 	// Memtag_heap is only implemented on AArch64.
-	if ctx.Arch().ArchType != android.Arm64 || !ctx.toolchain().Bionic() {
+	// Memtag ABI is Android specific for now, so disable for host.
+	if ctx.Arch().ArchType != android.Arm64 || !ctx.toolchain().Bionic() || ctx.Host() {
 		s.Memtag_heap = nil
+		s.Memtag_stack = nil
 	}
 
 	// Also disable CFI if ASAN is enabled.
 	if Bool(s.Address) || Bool(s.Hwaddress) {
 		s.Cfi = nil
 		s.Diag.Cfi = nil
+		// HWASAN and ASAN win against MTE.
+		s.Memtag_heap = nil
+		s.Memtag_stack = nil
 	}
 
 	// Disable sanitizers that depend on the UBSan runtime for windows/darwin builds.
@@ -534,7 +555,7 @@
 
 	if ctx.Os() != android.Windows && (Bool(s.All_undefined) || Bool(s.Undefined) || Bool(s.Address) || Bool(s.Thread) ||
 		Bool(s.Fuzzer) || Bool(s.Safestack) || Bool(s.Cfi) || Bool(s.Integer_overflow) || len(s.Misc_undefined) > 0 ||
-		Bool(s.Scudo) || Bool(s.Hwaddress) || Bool(s.Scs) || Bool(s.Memtag_heap)) {
+		Bool(s.Scudo) || Bool(s.Hwaddress) || Bool(s.Scs) || Bool(s.Memtag_heap) || Bool(s.Memtag_stack)) {
 		sanitize.Properties.SanitizerEnabled = true
 	}
 
@@ -691,6 +712,20 @@
 		}
 	}
 
+	if Bool(sanitize.Properties.Sanitize.Memtag_stack) {
+		flags.Local.CFlags = append(flags.Local.CFlags, memtagStackCommonFlags...)
+		flags.Local.AsFlags = append(flags.Local.AsFlags, memtagStackCommonFlags...)
+		flags.Local.LdFlags = append(flags.Local.LdFlags, memtagStackCommonFlags...)
+	}
+
+	if (Bool(sanitize.Properties.Sanitize.Memtag_heap) || Bool(sanitize.Properties.Sanitize.Memtag_stack)) && ctx.binary() {
+		if Bool(sanitize.Properties.Sanitize.Diag.Memtag_heap) {
+			flags.Local.LdFlags = append(flags.Local.LdFlags, "-fsanitize-memtag-mode=sync")
+		} else {
+			flags.Local.LdFlags = append(flags.Local.LdFlags, "-fsanitize-memtag-mode=async")
+		}
+	}
+
 	if Bool(sanitize.Properties.Sanitize.Integer_overflow) {
 		flags.Local.CFlags = append(flags.Local.CFlags, intOverflowCflags...)
 	}
@@ -804,6 +839,8 @@
 		return sanitize.Properties.Sanitize.Scs
 	case Memtag_heap:
 		return sanitize.Properties.Sanitize.Memtag_heap
+	case Memtag_stack:
+		return sanitize.Properties.Sanitize.Memtag_stack
 	case Fuzzer:
 		return sanitize.Properties.Sanitize.Fuzzer
 	default:
@@ -819,6 +856,7 @@
 		!sanitize.isSanitizerEnabled(cfi) &&
 		!sanitize.isSanitizerEnabled(scs) &&
 		!sanitize.isSanitizerEnabled(Memtag_heap) &&
+		!sanitize.isSanitizerEnabled(Memtag_stack) &&
 		!sanitize.isSanitizerEnabled(Fuzzer)
 }
 
@@ -850,6 +888,8 @@
 		sanitize.Properties.Sanitize.Scs = bPtr
 	case Memtag_heap:
 		sanitize.Properties.Sanitize.Memtag_heap = bPtr
+	case Memtag_stack:
+		sanitize.Properties.Sanitize.Memtag_stack = bPtr
 	case Fuzzer:
 		sanitize.Properties.Sanitize.Fuzzer = bPtr
 	default:
@@ -1311,23 +1351,11 @@
 		}
 
 		if Bool(c.sanitize.Properties.Sanitize.Memtag_heap) && c.Binary() {
-			noteDep := "note_memtag_heap_async"
-			if Bool(c.sanitize.Properties.Sanitize.Diag.Memtag_heap) {
-				noteDep = "note_memtag_heap_sync"
-			}
-			// If we're using snapshots, redirect to snapshot whenever possible
-			// TODO(b/178470649): clean manual snapshot redirections
-			snapshot := mctx.Provider(SnapshotInfoProvider).(SnapshotInfo)
-			if lib, ok := snapshot.StaticLibs[noteDep]; ok {
-				noteDep = lib
-			}
-			depTag := StaticDepTag(true)
-			variations := append(mctx.Target().Variations(),
-				blueprint.Variation{Mutator: "link", Variation: "static"})
-			if c.Device() {
-				variations = append(variations, c.ImageVariation())
-			}
-			mctx.AddFarVariationDependencies(variations, depTag, noteDep)
+			sanitizers = append(sanitizers, "memtag-heap")
+		}
+
+		if Bool(c.sanitize.Properties.Sanitize.Memtag_stack) {
+			sanitizers = append(sanitizers, "memtag-stack")
 		}
 
 		if Bool(c.sanitize.Properties.Sanitize.Fuzzer) {
diff --git a/cc/sanitize_test.go b/cc/sanitize_test.go
index 5d7e7d8..48ac650 100644
--- a/cc/sanitize_test.go
+++ b/cc/sanitize_test.go
@@ -344,19 +344,13 @@
 
 func checkHasMemtagNote(t *testing.T, m android.TestingModule, expected MemtagNoteType) {
 	t.Helper()
-	note_async := "note_memtag_heap_async"
-	note_sync := "note_memtag_heap_sync"
 
 	found := None
-	implicits := m.Rule("ld").Implicits
-	for _, lib := range implicits {
-		if strings.Contains(lib.Rel(), note_async) {
-			found = Async
-			break
-		} else if strings.Contains(lib.Rel(), note_sync) {
-			found = Sync
-			break
-		}
+	ldFlags := m.Rule("ld").Args["ldFlags"]
+	if strings.Contains(ldFlags, "-fsanitize-memtag-mode=async") {
+		found = Async
+	} else if strings.Contains(ldFlags, "-fsanitize-memtag-mode=sync") {
+		found = Sync
 	}
 
 	if found != expected {
diff --git a/cc/test.go b/cc/test.go
index 3e85e2d..28a0e5e 100644
--- a/cc/test.go
+++ b/cc/test.go
@@ -643,7 +643,6 @@
 
 	Gtest    bool
 	Isolated bool
-	Data     bazel.LabelListAttribute
 }
 
 // testBinaryBp2build is the bp2build converter for cc_test modules. A cc_test's
@@ -659,6 +658,8 @@
 	var testBinaryAttrs testBinaryAttributes
 	testBinaryAttrs.binaryAttributes = binaryBp2buildAttrs(ctx, m)
 
+	var data bazel.LabelListAttribute
+
 	testBinaryProps := m.GetArchVariantProperties(ctx, &TestBinaryProperties{})
 	for axis, configToProps := range testBinaryProps {
 		for config, props := range configToProps {
@@ -668,7 +669,7 @@
 				combinedData.Append(android.BazelLabelForModuleSrc(ctx, p.Data))
 				combinedData.Append(android.BazelLabelForModuleDeps(ctx, p.Data_bins))
 				combinedData.Append(android.BazelLabelForModuleDeps(ctx, p.Data_libs))
-				testBinaryAttrs.Data.SetSelectValue(axis, config, combinedData)
+				data.SetSelectValue(axis, config, combinedData)
 			}
 		}
 	}
@@ -686,6 +687,9 @@
 			Rule_class:        "cc_test",
 			Bzl_load_location: "//build/bazel/rules/cc:cc_test.bzl",
 		},
-		android.CommonAttributes{Name: m.Name()},
+		android.CommonAttributes{
+			Name: m.Name(),
+			Data: data,
+		},
 		&testBinaryAttrs)
 }
diff --git a/etc/prebuilt_etc.go b/etc/prebuilt_etc.go
index 719771f..b2361ce 100644
--- a/etc/prebuilt_etc.go
+++ b/etc/prebuilt_etc.go
@@ -670,10 +670,11 @@
 // For Bazel / bp2build
 
 type bazelPrebuiltFileAttributes struct {
-	Src         bazel.LabelAttribute
-	Filename    string
-	Dir         string
-	Installable bazel.BoolAttribute
+	Src               bazel.LabelAttribute
+	Filename          bazel.LabelAttribute
+	Dir               string
+	Installable       bazel.BoolAttribute
+	Filename_from_src bazel.BoolAttribute
 }
 
 // ConvertWithBp2build performs bp2build conversion of PrebuiltEtc
@@ -694,8 +695,18 @@
 	}
 
 	var filename string
-	if module.properties.Filename != nil {
-		filename = *module.properties.Filename
+	var filenameFromSrc bool
+	moduleProps := module.properties
+
+	if moduleProps.Filename != nil && *moduleProps.Filename != "" {
+		filename = *moduleProps.Filename
+	} else if moduleProps.Filename_from_src != nil && *moduleProps.Filename_from_src {
+		if moduleProps.Src != nil {
+			filename = *moduleProps.Src
+		}
+		filenameFromSrc = true
+	} else {
+		filename = ctx.ModuleName()
 	}
 
 	var dir = module.installDirBase
@@ -714,11 +725,16 @@
 
 	attrs := &bazelPrebuiltFileAttributes{
 		Src:         src,
-		Filename:    filename,
 		Dir:         dir,
 		Installable: installable,
 	}
 
+	if filename != "" {
+		attrs.Filename = bazel.LabelAttribute{Value: &bazel.Label{Label: filename}}
+	} else if filenameFromSrc {
+		attrs.Filename_from_src = bazel.BoolAttribute{Value: moduleProps.Filename_from_src}
+	}
+
 	props := bazel.BazelTargetModuleProperties{
 		Rule_class:        "prebuilt_file",
 		Bzl_load_location: "//build/bazel/rules:prebuilt_file.bzl",
diff --git a/java/base.go b/java/base.go
index cf3b3d5..53f0f52 100644
--- a/java/base.go
+++ b/java/base.go
@@ -169,6 +169,8 @@
 		Output_params []string
 	}
 
+	// If true, then jacocoagent is automatically added as a libs dependency so that
+	// r8 will not strip instrumentation classes out of dexed libraries.
 	Instrument bool `blueprint:"mutated"`
 	// If true, then the module supports statically including the jacocoagent
 	// into the library.
@@ -787,6 +789,9 @@
 	} else if j.shouldInstrumentStatic(ctx) {
 		ctx.AddVariationDependencies(nil, staticLibTag, "jacocoagent")
 	}
+	if j.shouldInstrument(ctx) {
+		ctx.AddVariationDependencies(nil, libTag, "jacocoagent")
+	}
 
 	if j.useCompose() {
 		ctx.AddVariationDependencies(ctx.Config().BuildOSCommonTarget.Variations(), kotlinPluginTag,
diff --git a/java/bootclasspath_fragment_test.go b/java/bootclasspath_fragment_test.go
index f95c83f..2bfb255 100644
--- a/java/bootclasspath_fragment_test.go
+++ b/java/bootclasspath_fragment_test.go
@@ -96,10 +96,22 @@
 }
 
 func TestBootclasspathFragment_Coverage(t *testing.T) {
-	prepareForTestWithFrameworkCoverage := android.FixtureMergeEnv(map[string]string{
-		"EMMA_INSTRUMENT":           "true",
-		"EMMA_INSTRUMENT_FRAMEWORK": "true",
-	})
+	prepareForTestWithFrameworkCoverage := android.GroupFixturePreparers(
+		android.FixtureMergeEnv(map[string]string{
+			"EMMA_INSTRUMENT":           "true",
+			"EMMA_INSTRUMENT_FRAMEWORK": "true",
+		}),
+		// need to mock jacocoagent here to satisfy dependency added for
+		// instrumented libraries at build time
+		android.FixtureAddFile("jacocoagent/Android.bp", []byte(`
+			java_library {
+				name: "jacocoagent",
+				srcs: ["Test.java"],
+				system_modules: "none",
+				sdk_version: "none",
+			}
+		`)),
+	)
 
 	prepareWithBp := android.FixtureWithRootAndroidBp(`
 		bootclasspath_fragment {
diff --git a/java/config/config.go b/java/config/config.go
index 3ca9bad..422f860 100644
--- a/java/config/config.go
+++ b/java/config/config.go
@@ -148,7 +148,7 @@
 	pctx.SourcePathVariable("JavaKytheExtractorJar", "prebuilts/build-tools/common/framework/javac_extractor.jar")
 	pctx.SourcePathVariable("Ziptime", "prebuilts/build-tools/${hostPrebuiltTag}/bin/ziptime")
 
-	pctx.HostBinToolVariable("GenKotlinBuildFileCmd", "gen-kotlin-build-file.py")
+	pctx.HostBinToolVariable("GenKotlinBuildFileCmd", "gen-kotlin-build-file")
 
 	pctx.SourcePathVariable("JarArgsCmd", "build/soong/scripts/jar-args.sh")
 	pctx.SourcePathVariable("PackageCheckCmd", "build/soong/scripts/package-check.sh")
diff --git a/scripts/Android.bp b/scripts/Android.bp
index 814bd57..b5b588b 100644
--- a/scripts/Android.bp
+++ b/scripts/Android.bp
@@ -168,7 +168,7 @@
 }
 
 python_binary_host {
-    name: "gen-kotlin-build-file.py",
+    name: "gen-kotlin-build-file",
     main: "gen-kotlin-build-file.py",
     srcs: [
         "gen-kotlin-build-file.py",
diff --git a/symbol_inject/cmd/symbol_inject.go b/symbol_inject/cmd/symbol_inject.go
index 1397b37..89b3619 100644
--- a/symbol_inject/cmd/symbol_inject.go
+++ b/symbol_inject/cmd/symbol_inject.go
@@ -94,4 +94,13 @@
 		os.Remove(*output)
 		os.Exit(5)
 	}
+
+	if file.IsMachoFile {
+		err = symbol_inject.CodeSignMachoFile(*output)
+		if err != nil {
+			fmt.Fprintln(os.Stderr, err.Error())
+			os.Remove(*output)
+			os.Exit(6)
+		}
+	}
 }
diff --git a/symbol_inject/macho.go b/symbol_inject/macho.go
index 6ee3f4f..9946d34 100644
--- a/symbol_inject/macho.go
+++ b/symbol_inject/macho.go
@@ -18,6 +18,7 @@
 	"debug/macho"
 	"fmt"
 	"io"
+	"os/exec"
 	"sort"
 	"strings"
 )
@@ -40,7 +41,7 @@
 		return symbols[i].Value < symbols[j].Value
 	})
 
-	file := &File{}
+	file := &File{IsMachoFile: true}
 
 	for _, section := range machoFile.Sections {
 		file.Sections = append(file.Sections, &Section{
@@ -95,3 +96,8 @@
 
 	return nil
 }
+
+func CodeSignMachoFile(path string) error {
+	cmd := exec.Command("/usr/bin/codesign", "--force", "-s", "-", path)
+	return cmd.Run()
+}
diff --git a/symbol_inject/symbol_inject.go b/symbol_inject/symbol_inject.go
index 2a3d67e..77aff6f 100644
--- a/symbol_inject/symbol_inject.go
+++ b/symbol_inject/symbol_inject.go
@@ -161,9 +161,10 @@
 }
 
 type File struct {
-	r        io.ReaderAt
-	Symbols  []*Symbol
-	Sections []*Section
+	r           io.ReaderAt
+	Symbols     []*Symbol
+	Sections    []*Section
+	IsMachoFile bool
 }
 
 type Symbol struct {