Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 06938a0a authored by Jiyong Park's avatar Jiyong Park Committed by Gerrit Code Review
Browse files

Merge changes from topic "no_staticlinking_to_stubs"

* changes:
  Prevent statically linking to a lib providing stable C APIs
  Add GetPathString
parents af6fbcbc 45b90e79
Loading
Loading
Loading
Loading
+43 −0
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ import (
	"os"
	"path"
	"path/filepath"
	"regexp"
	"strings"
	"text/scanner"

@@ -135,6 +136,13 @@ type BaseModuleContext interface {
	// 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 @@ func (b *baseModuleContext) GetTagPath() []blueprint.DependencyTag {
	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))
+41 −28
Original line number Diff line number Diff line
@@ -18,7 +18,6 @@ import (
	"fmt"
	"path"
	"path/filepath"
	"regexp"
	"sort"
	"strings"
	"sync"
@@ -1811,24 +1810,6 @@ func (a *apexBundle) minSdkVersion(ctx android.BaseModuleContext) int {
	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 @@ func (a *apexBundle) checkApexAvailability(ctx android.ModuleContext) {
		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 @@ func (a *apexBundle) checkUpdatable(ctx android.ModuleContext) {
	}
}

// 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 @@ func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) {

	a.checkApexAvailability(ctx)
	a.checkUpdatable(ctx)
	a.checkStaticLinkingToStubLibraries(ctx)

	handleSpecialLibs := !android.Bool(a.properties.Ignore_system_library_special_case)

@@ -2134,7 +2147,7 @@ func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) {
						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)
				}
			}
		}
+36 −0
Original line number Diff line number Diff line
@@ -4774,6 +4774,42 @@ func TestTestFor(t *testing.T) {
	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()
+3 −0
Original line number Diff line number Diff line
@@ -2862,6 +2862,9 @@ func (c *Module) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Modu
				return false
			}
		}
	} else if ctx.OtherModuleDependencyTag(dep) == llndkImplDep {
		// We don't track beyond LLNDK
		return false
	}
	return true
}