Merge changes from topic "no_staticlinking_to_stubs"
* changes:
Prevent statically linking to a lib providing stable C APIs
Add GetPathString
diff --git a/android/module.go b/android/module.go
index f5cfe66..e431ada 100644
--- a/android/module.go
+++ b/android/module.go
@@ -19,6 +19,7 @@
"os"
"path"
"path/filepath"
+ "regexp"
"strings"
"text/scanner"
@@ -135,6 +136,13 @@
// GetTagPath()[i] is the tag between GetWalkPath()[i] and GetWalkPath()[i+1]
GetTagPath() []blueprint.DependencyTag
+ // GetPathString is supposed to be called in visit function passed in WalkDeps()
+ // and returns a multi-line string showing the modules and dependency tags
+ // among them along the top-down dependency path from a start module to current child module.
+ // skipFirst when set to true, the output doesn't include the start module,
+ // which is already printed when this function is used along with ModuleErrorf().
+ GetPathString(skipFirst bool) string
+
AddMissingDependencies(missingDeps []string)
Target() Target
@@ -1734,6 +1742,41 @@
return b.tagPath
}
+// A regexp for removing boilerplate from BaseDependencyTag from the string representation of
+// a dependency tag.
+var tagCleaner = regexp.MustCompile(`\QBaseDependencyTag:blueprint.BaseDependencyTag{}\E(, )?`)
+
+// PrettyPrintTag returns string representation of the tag, but prefers
+// custom String() method if available.
+func PrettyPrintTag(tag blueprint.DependencyTag) string {
+ // Use tag's custom String() method if available.
+ if stringer, ok := tag.(fmt.Stringer); ok {
+ return stringer.String()
+ }
+
+ // Otherwise, get a default string representation of the tag's struct.
+ tagString := fmt.Sprintf("%#v", tag)
+
+ // Remove the boilerplate from BaseDependencyTag as it adds no value.
+ tagString = tagCleaner.ReplaceAllString(tagString, "")
+ return tagString
+}
+
+func (b *baseModuleContext) GetPathString(skipFirst bool) string {
+ sb := strings.Builder{}
+ tagPath := b.GetTagPath()
+ walkPath := b.GetWalkPath()
+ if !skipFirst {
+ sb.WriteString(walkPath[0].String())
+ }
+ for i, m := range walkPath[1:] {
+ sb.WriteString("\n")
+ sb.WriteString(fmt.Sprintf(" via tag %s\n", PrettyPrintTag(tagPath[i])))
+ sb.WriteString(fmt.Sprintf(" -> %s", m.String()))
+ }
+ return sb.String()
+}
+
func (m *moduleContext) VisitAllModuleVariants(visit func(Module)) {
m.bp.VisitAllModuleVariants(func(module blueprint.Module) {
visit(module.(Module))
diff --git a/apex/apex.go b/apex/apex.go
index 1947980..44aab60 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -18,7 +18,6 @@
"fmt"
"path"
"path/filepath"
- "regexp"
"sort"
"strings"
"sync"
@@ -1811,24 +1810,6 @@
return intVer
}
-// A regexp for removing boilerplate from BaseDependencyTag from the string representation of
-// a dependency tag.
-var tagCleaner = regexp.MustCompile(`\QBaseDependencyTag:blueprint.BaseDependencyTag{}\E(, )?`)
-
-func PrettyPrintTag(tag blueprint.DependencyTag) string {
- // Use tag's custom String() method if available.
- if stringer, ok := tag.(fmt.Stringer); ok {
- return stringer.String()
- }
-
- // Otherwise, get a default string representation of the tag's struct.
- tagString := fmt.Sprintf("%#v", tag)
-
- // Remove the boilerplate from BaseDependencyTag as it adds no value.
- tagString = tagCleaner.ReplaceAllString(tagString, "")
- return tagString
-}
-
// Ensures that the dependencies are marked as available for this APEX
func (a *apexBundle) checkApexAvailability(ctx android.ModuleContext) {
// Let's be practical. Availability for test, host, and the VNDK apex isn't important
@@ -1863,14 +1844,7 @@
if to.AvailableFor(apexName) || whitelistedApexAvailable(apexName, toName) {
return true
}
- message := ""
- tagPath := ctx.GetTagPath()
- // Skip the first module as that will be added at the start of the error message by ctx.ModuleErrorf().
- walkPath := ctx.GetWalkPath()[1:]
- for i, m := range walkPath {
- message = fmt.Sprintf("%s\n via tag %s\n -> %s", message, PrettyPrintTag(tagPath[i]), m.String())
- }
- ctx.ModuleErrorf("%q requires %q that is not available for the APEX. Dependency path:%s", fromName, toName, message)
+ ctx.ModuleErrorf("%q requires %q that is not available for the APEX. Dependency path:%s", fromName, toName, ctx.GetPathString(true))
// Visit this module's dependencies to check and report any issues with their availability.
return true
})
@@ -1886,6 +1860,44 @@
}
}
+// Ensures that a lib providing stub isn't statically linked
+func (a *apexBundle) checkStaticLinkingToStubLibraries(ctx android.ModuleContext) {
+ // Practically, we only care about regular APEXes on the device.
+ if ctx.Host() || a.testApex || a.vndkApex {
+ return
+ }
+
+ a.walkPayloadDeps(ctx, func(ctx android.ModuleContext, from blueprint.Module, to android.ApexModule, externalDep bool) bool {
+ if ccm, ok := to.(*cc.Module); ok {
+ apexName := ctx.ModuleName()
+ fromName := ctx.OtherModuleName(from)
+ toName := ctx.OtherModuleName(to)
+
+ // If `to` is not actually in the same APEX as `from` then it does not need apex_available and neither
+ // do any of its dependencies.
+ if am, ok := from.(android.DepIsInSameApex); ok && !am.DepIsInSameApex(ctx, to) {
+ // As soon as the dependency graph crosses the APEX boundary, don't go further.
+ return false
+ }
+
+ // The dynamic linker and crash_dump tool in the runtime APEX is the only exception to this rule.
+ // It can't make the static dependencies dynamic because it can't
+ // do the dynamic linking for itself.
+ if apexName == "com.android.runtime" && (fromName == "linker" || fromName == "crash_dump") {
+ return false
+ }
+
+ isStubLibraryFromOtherApex := ccm.HasStubsVariants() && !android.DirectlyInApex(apexName, toName)
+ if isStubLibraryFromOtherApex && !externalDep {
+ ctx.ModuleErrorf("%q required by %q is a native library providing stub. "+
+ "It shouldn't be included in this APEX via static linking. Dependency path: %s", to.String(), fromName, ctx.GetPathString(false))
+ }
+
+ }
+ return true
+ })
+}
+
func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) {
buildFlattenedAsDefault := ctx.Config().FlattenApex() && !ctx.Config().UnbundledBuild()
switch a.properties.ApexType {
@@ -1923,6 +1935,7 @@
a.checkApexAvailability(ctx)
a.checkUpdatable(ctx)
+ a.checkStaticLinkingToStubLibraries(ctx)
handleSpecialLibs := !android.Bool(a.properties.Ignore_system_library_special_case)
@@ -2134,7 +2147,7 @@
filesInfo = append(filesInfo, apexFileForPrebuiltEtc(ctx, prebuilt, depName))
}
} else if am.CanHaveApexVariants() && am.IsInstallableToApex() {
- ctx.ModuleErrorf("unexpected tag %s for indirect dependency %q", PrettyPrintTag(depTag), depName)
+ ctx.ModuleErrorf("unexpected tag %s for indirect dependency %q", android.PrettyPrintTag(depTag), depName)
}
}
}
diff --git a/apex/apex_test.go b/apex/apex_test.go
index dc69862..81df02a 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -4774,6 +4774,42 @@
ensureNotContains(t, ldFlags, "mylib/android_arm64_armv8-a_shared_1/mylib.so")
}
+func TestNoStaticLinkingToStubsLib(t *testing.T) {
+ testApexError(t, `.*required by "mylib" is a native library providing stub.*`, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ native_shared_libs: ["mylib"],
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ cc_library {
+ name: "mylib",
+ srcs: ["mylib.cpp"],
+ static_libs: ["otherlib"],
+ system_shared_libs: [],
+ stl: "none",
+ apex_available: [ "myapex" ],
+ }
+
+ cc_library {
+ name: "otherlib",
+ srcs: ["mylib.cpp"],
+ system_shared_libs: [],
+ stl: "none",
+ stubs: {
+ versions: ["1", "2", "3"],
+ },
+ apex_available: [ "myapex" ],
+ }
+ `)
+}
+
func TestMain(m *testing.M) {
run := func() int {
setUp()
diff --git a/cc/cc.go b/cc/cc.go
index 02c4879..4128fa7 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -2862,6 +2862,9 @@
return false
}
}
+ } else if ctx.OtherModuleDependencyTag(dep) == llndkImplDep {
+ // We don't track beyond LLNDK
+ return false
}
return true
}