diff options
| author | 2023-08-23 10:06:35 +0000 | |
|---|---|---|
| committer | 2023-09-13 15:39:58 +0000 | |
| commit | 262ed1a347533e894dabae96c3aff0ca3bcadfb2 (patch) | |
| tree | cfb4cb9ae14c71a513efae647048a7114e4215c7 | |
| parent | 4d730a8f142b67767617440f350bbc9322617645 (diff) | |
NDK library: collect NDK headers for ABI monitoring
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.
Bug: 156513478
Test: development/tools/ndk/update_ndk_abi.sh with enabled canDumpAbi
Change-Id: I9fa41e73450a41379638debb3dc56f421e0fb870
Signed-off-by: Aleksei Vetrov <vvvvvv@google.com>
| -rw-r--r-- | cc/ndk_headers.go | 21 | ||||
| -rw-r--r-- | cc/ndk_library.go | 21 | ||||
| -rw-r--r-- | cc/ndk_sysroot.go | 57 |
3 files changed, 80 insertions, 19 deletions
diff --git a/cc/ndk_headers.go b/cc/ndk_headers.go index d0ae4a56d..1a8e90fd9 100644 --- a/cc/ndk_headers.go +++ b/cc/ndk_headers.go @@ -82,6 +82,7 @@ type headerModule struct { properties headerProperties + srcPaths android.Paths installPaths android.Paths licensePath android.Path } @@ -125,8 +126,8 @@ func (m *headerModule) GenerateAndroidBuildActions(ctx android.ModuleContext) { 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 @@ type versionedHeaderModule struct { properties versionedHeaderProperties + srcPaths android.Paths installPaths android.Paths licensePath android.Path } @@ -211,9 +213,9 @@ func (m *versionedHeaderModule) GenerateAndroidBuildActions(ctx android.ModuleCo 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 @@ func (m *versionedHeaderModule) GenerateAndroidBuildActions(ctx android.ModuleCo 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 @@ func processHeadersWithVersioner(ctx android.ModuleContext, srcDir, outDir andro 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 @@ type preprocessedHeadersModule struct { properties preprocessedHeadersProperties + srcPaths android.Paths installPaths android.Paths licensePath android.Path } @@ -329,9 +332,9 @@ func (m *preprocessedHeadersModule) GenerateAndroidBuildActions(ctx android.Modu 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 9281aebb5..b201dd88a 100644 --- a/cc/ndk_library.go +++ b/cc/ndk_library.go @@ -43,11 +43,17 @@ var ( 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 @@ func (this *stubDecorator) dumpAbi(ctx ModuleContext, symbolList android.Path) { 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 feb388037..9ec2ae4ea 100644 --- a/cc/ndk_sysroot.go +++ b/cc/ndk_sysroot.go @@ -54,6 +54,7 @@ package cc import ( "android/soong/android" + "strings" ) func init() { @@ -96,15 +97,56 @@ func getNdkFullTimestampFile(ctx android.PathContext) android.WritablePath { 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 @@ func (n *ndkSingleton) GenerateBuildActions(ctx android.SingletonContext) { } 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 @@ func (n *ndkSingleton) GenerateBuildActions(ctx android.SingletonContext) { 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. |