Merge "NDK library: collect NDK headers for ABI monitoring" into main
diff --git a/cc/ndk_headers.go b/cc/ndk_headers.go
index d0ae4a5..1a8e90f 100644
--- a/cc/ndk_headers.go
+++ b/cc/ndk_headers.go
@@ -82,6 +82,7 @@
properties headerProperties
+ srcPaths android.Paths
installPaths android.Paths
licensePath android.Path
}
@@ -125,8 +126,8 @@
m.licensePath = android.PathForModuleSrc(ctx, String(m.properties.License))
- srcFiles := android.PathsForModuleSrcExcludes(ctx, m.properties.Srcs, m.properties.Exclude_srcs)
- for _, header := range srcFiles {
+ m.srcPaths = android.PathsForModuleSrcExcludes(ctx, m.properties.Srcs, m.properties.Exclude_srcs)
+ for _, header := range m.srcPaths {
installDir := getHeaderInstallDir(ctx, header, String(m.properties.From),
String(m.properties.To))
installedPath := ctx.InstallFile(installDir, header.Base(), header)
@@ -193,6 +194,7 @@
properties versionedHeaderProperties
+ srcPaths android.Paths
installPaths android.Paths
licensePath android.Path
}
@@ -211,9 +213,9 @@
fromSrcPath := android.PathForModuleSrc(ctx, String(m.properties.From))
toOutputPath := getCurrentIncludePath(ctx).Join(ctx, String(m.properties.To))
- srcFiles := ctx.GlobFiles(headerGlobPattern(fromSrcPath.String()), nil)
+ m.srcPaths = ctx.GlobFiles(headerGlobPattern(fromSrcPath.String()), nil)
var installPaths []android.WritablePath
- for _, header := range srcFiles {
+ for _, header := range m.srcPaths {
installDir := getHeaderInstallDir(ctx, header, String(m.properties.From), String(m.properties.To))
installPath := installDir.Join(ctx, header.Base())
installPaths = append(installPaths, installPath)
@@ -224,11 +226,11 @@
ctx.ModuleErrorf("glob %q matched zero files", String(m.properties.From))
}
- processHeadersWithVersioner(ctx, fromSrcPath, toOutputPath, srcFiles, installPaths)
+ processHeadersWithVersioner(ctx, fromSrcPath, toOutputPath, m.srcPaths, installPaths)
}
func processHeadersWithVersioner(ctx android.ModuleContext, srcDir, outDir android.Path,
- srcFiles android.Paths, installPaths []android.WritablePath) android.Path {
+ srcPaths android.Paths, installPaths []android.WritablePath) android.Path {
// The versioner depends on a dependencies directory to simplify determining include paths
// when parsing headers. This directory contains architecture specific directories as well
// as a common directory, each of which contains symlinks to the actually directories to
@@ -253,7 +255,7 @@
Rule: versionBionicHeaders,
Description: "versioner preprocess " + srcDir.Rel(),
Output: timestampFile,
- Implicits: append(srcFiles, depsGlob...),
+ Implicits: append(srcPaths, depsGlob...),
ImplicitOutputs: installPaths,
Args: map[string]string{
"depsPath": depsPath.String(),
@@ -317,6 +319,7 @@
properties preprocessedHeadersProperties
+ srcPaths android.Paths
installPaths android.Paths
licensePath android.Path
}
@@ -329,9 +332,9 @@
preprocessor := android.PathForModuleSrc(ctx, String(m.properties.Preprocessor))
m.licensePath = android.PathForModuleSrc(ctx, String(m.properties.License))
- srcFiles := android.PathsForModuleSrcExcludes(ctx, m.properties.Srcs, m.properties.Exclude_srcs)
+ m.srcPaths = android.PathsForModuleSrcExcludes(ctx, m.properties.Srcs, m.properties.Exclude_srcs)
installDir := getCurrentIncludePath(ctx).Join(ctx, String(m.properties.To))
- for _, src := range srcFiles {
+ for _, src := range m.srcPaths {
installPath := installDir.Join(ctx, src.Base())
m.installPaths = append(m.installPaths, installPath)
diff --git a/cc/ndk_library.go b/cc/ndk_library.go
index 9281aeb..b201dd8 100644
--- a/cc/ndk_library.go
+++ b/cc/ndk_library.go
@@ -43,11 +43,17 @@
CommandDeps: []string{"$ndkStubGenerator"},
}, "arch", "apiLevel", "apiMap", "flags")
+ // $headersList should include paths to public headers. All types
+ // that are defined outside of public headers will be excluded from
+ // ABI monitoring.
+ //
+ // STG tool doesn't access content of files listed in $headersList,
+ // so there is no need to add them to dependencies.
stg = pctx.AndroidStaticRule("stg",
blueprint.RuleParams{
- Command: "$stg -S :$symbolList --elf $in -o $out",
+ Command: "$stg -S :$symbolList --file-filter :$headersList --elf $in -o $out",
CommandDeps: []string{"$stg"},
- }, "symbolList")
+ }, "symbolList", "headersList")
stgdiff = pctx.AndroidStaticRule("stgdiff",
blueprint.RuleParams{
@@ -347,14 +353,19 @@
this.abiDumpPath = getNdkAbiDumpInstallBase(ctx).Join(ctx,
this.apiLevel.String(), ctx.Arch().ArchType.String(),
this.libraryName(ctx), "abi.stg")
+ headersList := getNdkABIHeadersFile(ctx)
ctx.Build(pctx, android.BuildParams{
Rule: stg,
Description: fmt.Sprintf("stg %s", implementationLibrary),
Input: implementationLibrary,
- Implicit: symbolList,
- Output: this.abiDumpPath,
+ Implicits: []android.Path{
+ symbolList,
+ headersList,
+ },
+ Output: this.abiDumpPath,
Args: map[string]string{
- "symbolList": symbolList.String(),
+ "symbolList": symbolList.String(),
+ "headersList": headersList.String(),
},
})
}
diff --git a/cc/ndk_sysroot.go b/cc/ndk_sysroot.go
index feb3880..9ec2ae4 100644
--- a/cc/ndk_sysroot.go
+++ b/cc/ndk_sysroot.go
@@ -54,6 +54,7 @@
import (
"android/soong/android"
+ "strings"
)
func init() {
@@ -96,15 +97,56 @@
return android.PathForOutput(ctx, "ndk.timestamp")
}
+// The list of all NDK headers as they are located in the repo.
+// Used for ABI monitoring to track only structures defined in NDK headers.
+func getNdkABIHeadersFile(ctx android.PathContext) android.WritablePath {
+ return android.PathForOutput(ctx, "ndk_abi_headers.txt")
+}
+
func NdkSingleton() android.Singleton {
return &ndkSingleton{}
}
+// Collect all NDK exported headers paths into a file that is used to
+// detect public types that should be ABI monitored.
+//
+// Assume that we have the following code in exported header:
+//
+// typedef struct Context Context;
+// typedef struct Output {
+// ...
+// } Output;
+// void DoSomething(Context* ctx, Output* output);
+//
+// If none of public headers exported to end-users contain definition of
+// "struct Context", then "struct Context" layout and members shouldn't be
+// monitored. However we use DWARF information from a real library, which
+// may have access to the definition of "string Context" from
+// implementation headers, and it will leak to ABI.
+//
+// STG tool doesn't access source and header files, only DWARF information
+// from compiled library. And the DWARF contains file name where a type is
+// defined. So we need a rule to build a list of paths to public headers,
+// so STG can distinguish private types from public and do not monitor
+// private types that are not accessible to library users.
+func writeNdkAbiSrcFilter(ctx android.BuilderContext,
+ headerSrcPaths android.Paths, outputFile android.WritablePath) {
+ var filterBuilder strings.Builder
+ filterBuilder.WriteString("[decl_file_allowlist]\n")
+ for _, headerSrcPath := range headerSrcPaths {
+ filterBuilder.WriteString(headerSrcPath.String())
+ filterBuilder.WriteString("\n")
+ }
+
+ android.WriteFileRule(ctx, outputFile, filterBuilder.String())
+}
+
type ndkSingleton struct{}
func (n *ndkSingleton) GenerateBuildActions(ctx android.SingletonContext) {
var staticLibInstallPaths android.Paths
- var headerPaths android.Paths
+ var headerSrcPaths android.Paths
+ var headerInstallPaths android.Paths
var installPaths android.Paths
var licensePaths android.Paths
ctx.VisitAllModules(func(module android.Module) {
@@ -113,19 +155,22 @@
}
if m, ok := module.(*headerModule); ok {
- headerPaths = append(headerPaths, m.installPaths...)
+ headerSrcPaths = append(headerSrcPaths, m.srcPaths...)
+ headerInstallPaths = append(headerInstallPaths, m.installPaths...)
installPaths = append(installPaths, m.installPaths...)
licensePaths = append(licensePaths, m.licensePath)
}
if m, ok := module.(*versionedHeaderModule); ok {
- headerPaths = append(headerPaths, m.installPaths...)
+ headerSrcPaths = append(headerSrcPaths, m.srcPaths...)
+ headerInstallPaths = append(headerInstallPaths, m.installPaths...)
installPaths = append(installPaths, m.installPaths...)
licensePaths = append(licensePaths, m.licensePath)
}
if m, ok := module.(*preprocessedHeadersModule); ok {
- headerPaths = append(headerPaths, m.installPaths...)
+ headerSrcPaths = append(headerSrcPaths, m.srcPaths...)
+ headerInstallPaths = append(headerInstallPaths, m.installPaths...)
installPaths = append(installPaths, m.installPaths...)
licensePaths = append(licensePaths, m.licensePath)
}
@@ -175,9 +220,11 @@
ctx.Build(pctx, android.BuildParams{
Rule: android.Touch,
Output: getNdkHeadersTimestampFile(ctx),
- Implicits: headerPaths,
+ Implicits: headerInstallPaths,
})
+ writeNdkAbiSrcFilter(ctx, headerSrcPaths, getNdkABIHeadersFile(ctx))
+
fullDepPaths := append(staticLibInstallPaths, getNdkBaseTimestampFile(ctx))
// There's a phony "ndk" rule defined in core/main.mk that depends on this.