Add header-abi-checker for Vndk abi checks.

header-abi-dumper: dumps abi exported by source files for Vndk.
header-abi-linker: links abi dumps produced by header-abi-dumper.
header-abi-diff: compares linked dumps.

Test: mm -j64 showcommands > make_log in bionic/libc.
      This produced linked dumps in out/soong/.intermediates.
      Copied these dumps to
      prebuilts/abi-dumps/ndk/current/arm64/source-based/.
      Changed the abi and re-ran mm -j64 showcommands > make_log
      confirmed that the build reported compatibility breakge without
      actually failing (advisory mode).

Change-Id: Iccad6908fe68a80f47230751671d156893b96ead
This commit is contained in:
Jayant Chowdhary 2017-02-08 13:45:53 -08:00
parent c43ae770c5
commit 3e231fd8bd
8 changed files with 287 additions and 1 deletions

View File

@ -127,6 +127,7 @@ bootstrap_go_package {
"cc/proto.go",
"cc/relocation_packer.go",
"cc/sanitize.go",
"cc/sabi.go",
"cc/stl.go",
"cc/strip.go",
"cc/tidy.go",

View File

@ -576,6 +576,30 @@ type ModuleOutPath struct {
var _ Path = ModuleOutPath{}
// PathForVndkRefDump returns an OptionalPath representing the path of the reference
// abi dump for the given module. This is not guaranteed to be valid.
func PathForVndkRefAbiDump(ctx ModuleContext, version, fileName string, vndkOrNdk, isSourceDump bool) OptionalPath {
archName := ctx.Arch().ArchType.Name
var sourceOrBinaryDir string
var vndkOrNdkDir string
var ext string
if isSourceDump {
ext = ".lsdump"
sourceOrBinaryDir = "source-based"
} else {
ext = ".bdump"
sourceOrBinaryDir = "binary-based"
}
if vndkOrNdk {
vndkOrNdkDir = "vndk"
} else {
vndkOrNdkDir = "ndk"
}
refDumpFileStr := "prebuilts/abi-dumps/" + vndkOrNdkDir + "/" + version + "/" +
archName + "/" + sourceOrBinaryDir + "/" + fileName + ext
return OptionalPathForSource(ctx, "", refDumpFileStr)
}
// PathForModuleOut returns a Path representing the paths... under the module's
// output directory.
func PathForModuleOut(ctx ModuleContext, paths ...string) ModuleOutPath {

View File

@ -141,6 +141,13 @@ func (library *libraryDecorator) AndroidMk(ctx AndroidMkContext, ret *android.An
ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) error {
library.androidMkWriteExportedFlags(w)
fmt.Fprintln(w, "LOCAL_ADDITIONAL_DEPENDENCIES := ")
if library.sAbiOutputFile.Valid() {
fmt.Fprintln(w, "LOCAL_ADDITIONAL_DEPENDENCIES += ", library.sAbiOutputFile.String())
if library.sAbiDiff.Valid() && !library.static() {
fmt.Fprintln(w, "LOCAL_ADDITIONAL_DEPENDENCIES += ", library.sAbiDiff.String())
}
}
fmt.Fprintln(w, "LOCAL_BUILT_MODULE_STEM := $(LOCAL_MODULE)"+outputFile.Ext())

View File

@ -168,6 +168,41 @@ var (
Description: "yasm $out",
},
"asFlags")
_ = pctx.SourcePathVariable("sAbiDumper", "prebuilts/build-tools/${config.HostPrebuiltTag}/bin/header-abi-dumper")
sAbiDump = pctx.AndroidStaticRule("sAbiDump",
blueprint.RuleParams{
Command: "rm -f $out && $sAbiDumper -o ${out} $in $exportDirs -- $cFlags -Wno-packed -isystem ${config.RSIncludePath}",
CommandDeps: []string{"$sAbiDumper"},
Description: "header-abi-dumper $in -o $out $exportDirs",
},
"cFlags", "exportDirs")
_ = pctx.SourcePathVariable("sAbiLinker", "prebuilts/build-tools/${config.HostPrebuiltTag}/bin/header-abi-linker")
sAbiLink = pctx.AndroidStaticRule("sAbiLink",
blueprint.RuleParams{
Command: "$sAbiLinker -o ${out} $symbolFile -arch $arch -api $api $exportedHeaderFlags @${out}.rsp ",
CommandDeps: []string{"$sAbiLinker"},
Description: "header-abi-linker $in -o $out",
Rspfile: "${out}.rsp",
RspfileContent: "${in}",
},
"symbolFile", "arch", "api", "exportedHeaderFlags")
_ = pctx.SourcePathVariable("sAbiDiffer", "prebuilts/build-tools/${config.HostPrebuiltTag}/bin/header-abi-diff")
// The output file is different from what the build system knows about.
// This is done since we have to create a report file even when builds
// fail in this case. Abidiff check turned on in advice-only mode. Builds
// will not fail on abi incompatibilties / extensions.
sAbiDiff = pctx.AndroidStaticRule("sAbiDiff",
blueprint.RuleParams{
Command: "$sAbiDiffer -advice-only -o ${out}s -new $in -old $referenceDump",
CommandDeps: []string{"$sAbiDiffer"},
Description: "header-abi-diff -o ${out} -new $in -old $referenceDump",
},
"referenceDump")
)
func init() {
@ -194,12 +229,14 @@ type builderFlags struct {
yaccFlags string
protoFlags string
tidyFlags string
sAbiFlags string
yasmFlags string
aidlFlags string
toolchain config.Toolchain
clang bool
tidy bool
coverage bool
sAbiDump bool
systemIncludeFlags string
@ -214,6 +251,7 @@ type Objects struct {
objFiles android.Paths
tidyFiles android.Paths
coverageFiles android.Paths
sAbiDumpFiles android.Paths
}
func (a Objects) Copy() Objects {
@ -221,6 +259,7 @@ func (a Objects) Copy() Objects {
objFiles: append(android.Paths{}, a.objFiles...),
tidyFiles: append(android.Paths{}, a.tidyFiles...),
coverageFiles: append(android.Paths{}, a.coverageFiles...),
sAbiDumpFiles: append(android.Paths{}, a.sAbiDumpFiles...),
}
}
@ -229,6 +268,7 @@ func (a Objects) Append(b Objects) Objects {
objFiles: append(a.objFiles, b.objFiles...),
tidyFiles: append(a.tidyFiles, b.tidyFiles...),
coverageFiles: append(a.coverageFiles, b.coverageFiles...),
sAbiDumpFiles: append(a.sAbiDumpFiles, b.sAbiDumpFiles...),
}
}
@ -265,6 +305,10 @@ func TransformSourceToObj(ctx android.ModuleContext, subdir string, srcFiles and
flags.systemIncludeFlags,
flags.asFlags,
}, " ")
var sAbiDumpFiles android.Paths
if flags.sAbiDump && flags.clang {
sAbiDumpFiles = make(android.Paths, 0, len(srcFiles))
}
if flags.clang {
cflags += " ${config.NoOverrideClangGlobalCflags}"
@ -296,6 +340,7 @@ func TransformSourceToObj(ctx android.ModuleContext, subdir string, srcFiles and
var ccCmd string
tidy := flags.tidy && flags.clang
coverage := flags.coverage
dump := flags.sAbiDump && flags.clang
switch srcFile.Ext() {
case ".S", ".s":
@ -303,6 +348,7 @@ func TransformSourceToObj(ctx android.ModuleContext, subdir string, srcFiles and
moduleCflags = asflags
tidy = false
coverage = false
dump = false
case ".c":
ccCmd = "gcc"
moduleCflags = cflags
@ -366,12 +412,29 @@ func TransformSourceToObj(ctx android.ModuleContext, subdir string, srcFiles and
})
}
if dump {
sAbiDumpFile := android.ObjPathWithExt(ctx, subdir, srcFile, "sdump")
sAbiDumpFiles = append(sAbiDumpFiles, sAbiDumpFile)
ctx.ModuleBuild(pctx, android.ModuleBuildParams{
Rule: sAbiDump,
Output: sAbiDumpFile,
Input: srcFile,
Implicit: objFile,
Args: map[string]string{
"cFlags": moduleCflags,
"exportDirs": flags.sAbiFlags,
},
})
}
}
return Objects{
objFiles: objFiles,
tidyFiles: tidyFiles,
coverageFiles: coverageFiles,
sAbiDumpFiles: sAbiDumpFiles,
}
}
@ -554,6 +617,47 @@ func TransformObjToDynamicBinary(ctx android.ModuleContext,
})
}
// Generate a rule to combine .dump sAbi dump files from multiple source files
// into a single .ldump sAbi dump file
func TransformDumpToLinkedDump(ctx android.ModuleContext, sAbiDumps android.Paths,
symbolFile android.OptionalPath, apiLevel, baseName, exportedHeaderFlags string) android.OptionalPath {
outputFile := android.PathForModuleOut(ctx, baseName+".lsdump")
var symbolFileStr string
var linkedDumpDep android.Path
if symbolFile.Valid() {
symbolFileStr = "-v " + symbolFile.Path().String()
linkedDumpDep = symbolFile.Path()
}
ctx.ModuleBuild(pctx, android.ModuleBuildParams{
Rule: sAbiLink,
Output: outputFile,
Inputs: sAbiDumps,
Implicit: linkedDumpDep,
Args: map[string]string{
"symbolFile": symbolFileStr,
"arch": ctx.Arch().ArchType.Name,
"api": apiLevel,
"exportedHeaderFlags": exportedHeaderFlags,
},
})
return android.OptionalPathForPath(outputFile)
}
func SourceAbiDiff(ctx android.ModuleContext, inputDump android.Path, referenceDump android.Path,
baseName string) android.OptionalPath {
outputFile := android.PathForModuleOut(ctx, baseName+".abidiff")
ctx.ModuleBuild(pctx, android.ModuleBuildParams{
Rule: sAbiDiff,
Output: outputFile,
Input: inputDump,
Implicit: referenceDump,
Args: map[string]string{
"referenceDump": referenceDump.String(),
},
})
return android.OptionalPathForPath(outputFile)
}
// Generate a rule for extract a table of contents from a shared library (.so)
func TransformSharedObjectToToc(ctx android.ModuleContext, inputFile android.WritablePath,
outputFile android.WritablePath, flags builderFlags) {

View File

@ -49,6 +49,7 @@ func init() {
ctx.BottomUp("tsan", sanitizerMutator(tsan)).Parallel()
ctx.BottomUp("coverage", coverageLinkingMutator).Parallel()
ctx.TopDown("vndk_deps", sabiDepsMutator)
})
pctx.Import("android/soong/cc/config")
@ -108,6 +109,7 @@ type Flags struct {
LdFlags []string // Flags that apply to linker command lines
libFlags []string // Flags to add libraries early to the link order
TidyFlags []string // Flags that apply to clang-tidy
SAbiFlags []string // Flags that apply to header-abi-dumper
YasmFlags []string // Flags that apply to yasm assembly source files
// Global include flags that apply to C, C++, and assembly source files
@ -118,6 +120,7 @@ type Flags struct {
Clang bool
Tidy bool
Coverage bool
SAbiDump bool
RequiredInstructionSet string
DynamicLinker string
@ -177,6 +180,7 @@ type ModuleContextIntf interface {
sdk() bool
sdkVersion() string
vndk() bool
createVndkSourceAbiDump() bool
selectedStl() string
baseModuleName() string
}
@ -283,6 +287,7 @@ type Module struct {
stl *stl
sanitize *sanitize
coverage *coverage
sabi *sabi
androidMkSharedLibDeps []string
@ -316,6 +321,9 @@ func (c *Module) Init() (blueprint.Module, []interface{}) {
if c.coverage != nil {
props = append(props, c.coverage.props()...)
}
if c.sabi != nil {
props = append(props, c.sabi.props()...)
}
for _, feature := range c.features {
props = append(props, feature.props()...)
}
@ -418,6 +426,12 @@ func (ctx *moduleContextImpl) vndk() bool {
return ctx.mod.vndk()
}
// Create source abi dumps if the module belongs to the list of VndkLibraries.
func (ctx *moduleContextImpl) createVndkSourceAbiDump() bool {
return ctx.ctx.Device() && (inList(ctx.baseModuleName(), config.LLndkLibraries())) ||
(inList(ctx.baseModuleName(), config.VndkLibraries()))
}
func (ctx *moduleContextImpl) selectedStl() string {
if stl := ctx.mod.stl; stl != nil {
return stl.Properties.SelectedStl
@ -444,6 +458,7 @@ func newModule(hod android.HostOrDeviceSupported, multilib android.Multilib) *Mo
module.stl = &stl{}
module.sanitize = &sanitize{}
module.coverage = &coverage{}
module.sabi = &sabi{}
return module
}
@ -492,6 +507,9 @@ func (c *Module) GenerateAndroidBuildActions(actx android.ModuleContext) {
if c.coverage != nil {
flags = c.coverage.flags(ctx, flags)
}
if c.sabi != nil {
flags = c.sabi.flags(ctx, flags)
}
for _, feature := range c.features {
flags = feature.flags(ctx, flags)
}
@ -566,6 +584,9 @@ func (c *Module) begin(ctx BaseModuleContext) {
if c.coverage != nil {
c.coverage.begin(ctx)
}
if c.sabi != nil {
c.sabi.begin(ctx)
}
for _, feature := range c.features {
feature.begin(ctx)
}
@ -596,6 +617,9 @@ func (c *Module) deps(ctx DepsContext) Deps {
if c.coverage != nil {
deps = c.coverage.deps(ctx, deps)
}
if c.sabi != nil {
deps = c.sabi.deps(ctx, deps)
}
for _, feature := range c.features {
deps = feature.deps(ctx, deps)
}
@ -999,9 +1023,12 @@ func (c *Module) depsToPaths(ctx android.ModuleContext) PathDeps {
}
// When combining coverage files for shared libraries and executables, coverage files
// in static libraries act as if they were whole static libraries.
// in static libraries act as if they were whole static libraries. The same goes for
// source based Abi dump files.
depPaths.StaticLibObjs.coverageFiles = append(depPaths.StaticLibObjs.coverageFiles,
staticLib.objs().coverageFiles...)
depPaths.StaticLibObjs.sAbiDumpFiles = append(depPaths.StaticLibObjs.sAbiDumpFiles,
staticLib.objs().sAbiDumpFiles...)
}
if ptr != nil {
@ -1085,6 +1112,7 @@ func DefaultsFactory(props ...interface{}) (blueprint.Module, []interface{}) {
&InstallerProperties{},
&TidyProperties{},
&CoverageProperties{},
&SAbiProperties{},
)
return android.InitDefaultsModule(module, module, props...)

View File

@ -21,6 +21,7 @@ import (
"github.com/google/blueprint/pathtools"
"android/soong/android"
"android/soong/cc/config"
)
type LibraryProperties struct {
@ -220,9 +221,17 @@ type libraryDecorator struct {
sanitize *sanitize
sabi *sabi
// Output archive of gcno coverage information files
coverageOutputFile android.OptionalPath
// linked Source Abi Dump
sAbiOutputFile android.OptionalPath
// Source Abi Diff
sAbiDiff android.OptionalPath
// Decorated interafaces
*baseCompiler
*baseLinker
@ -316,7 +325,20 @@ func (library *libraryDecorator) compile(ctx ModuleContext, flags Flags, deps Pa
}
return Objects{}
}
if ctx.createVndkSourceAbiDump() || (library.sabi.Properties.CreateSAbiDumps && ctx.Device()) {
exportIncludeDirs := android.PathsForModuleSrc(ctx, library.flagExporter.Properties.Export_include_dirs)
var SourceAbiFlags []string
for _, dir := range exportIncludeDirs.Strings() {
SourceAbiFlags = append(SourceAbiFlags, "-I "+dir)
}
flags.SAbiFlags = SourceAbiFlags
total_length := len(library.baseCompiler.Properties.Srcs) + len(deps.GeneratedSources) + len(library.Properties.Shared.Srcs) +
len(library.Properties.Static.Srcs)
if total_length > 0 {
flags.SAbiDump = true
}
}
objs := library.baseCompiler.compile(ctx, flags, deps)
library.reuseObjects = objs
buildFlags := flagsToBuilderFlags(flags)
@ -534,11 +556,45 @@ func (library *libraryDecorator) linkShared(ctx ModuleContext,
objs.coverageFiles = append(objs.coverageFiles, deps.StaticLibObjs.coverageFiles...)
objs.coverageFiles = append(objs.coverageFiles, deps.WholeStaticLibObjs.coverageFiles...)
objs.sAbiDumpFiles = append(objs.sAbiDumpFiles, deps.StaticLibObjs.sAbiDumpFiles...)
objs.sAbiDumpFiles = append(objs.sAbiDumpFiles, deps.WholeStaticLibObjs.sAbiDumpFiles...)
library.coverageOutputFile = TransformCoverageFilesToLib(ctx, objs, builderFlags, library.getLibName(ctx))
library.linkSAbiDumpFiles(ctx, objs, fileName)
return ret
}
func (library *libraryDecorator) linkSAbiDumpFiles(ctx ModuleContext, objs Objects, fileName string) {
//Also take into account object re-use.
if len(objs.sAbiDumpFiles) > 0 && ctx.createVndkSourceAbiDump() {
refSourceDumpFile := android.PathForVndkRefAbiDump(ctx, "current", fileName, vndkVsNdk(ctx), true)
versionScript := android.OptionalPathForModuleSrc(ctx, library.Properties.Version_script)
var symbolFile android.OptionalPath
if versionScript.Valid() {
symbolFile = versionScript
}
exportIncludeDirs := android.PathsForModuleSrc(ctx, library.flagExporter.Properties.Export_include_dirs)
var SourceAbiFlags []string
for _, dir := range exportIncludeDirs.Strings() {
SourceAbiFlags = append(SourceAbiFlags, "-I "+dir)
}
exportedHeaderFlags := strings.Join(SourceAbiFlags, " ")
library.sAbiOutputFile = TransformDumpToLinkedDump(ctx, objs.sAbiDumpFiles, symbolFile, "current", fileName, exportedHeaderFlags)
if refSourceDumpFile.Valid() {
library.sAbiDiff = SourceAbiDiff(ctx, library.sAbiOutputFile.Path(), refSourceDumpFile.Path(), fileName)
}
}
}
func vndkVsNdk(ctx ModuleContext) bool {
if inList(ctx.baseModuleName(), config.LLndkLibraries()) {
return false
}
return true
}
func (library *libraryDecorator) link(ctx ModuleContext,
flags Flags, deps PathDeps, objs Objects) android.Path {
@ -656,6 +712,7 @@ func NewLibrary(hod android.HostOrDeviceSupported) (*Module, *libraryDecorator)
baseLinker: NewBaseLinker(),
baseInstaller: NewBaseInstaller("lib", "lib64", InstallInSystem),
sanitize: module.sanitize,
sabi: module.sabi,
}
module.compiler = library

63
cc/sabi.go Normal file
View File

@ -0,0 +1,63 @@
// Copyright 2017 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cc
import (
"android/soong/android"
"github.com/google/blueprint"
"android/soong/cc/config"
)
type SAbiProperties struct {
CreateSAbiDumps bool `blueprint:"mutated"`
}
type sabi struct {
Properties SAbiProperties
}
func (sabimod *sabi) props() []interface{} {
return []interface{}{&sabimod.Properties}
}
func (sabimod *sabi) begin(ctx BaseModuleContext) {}
func (sabimod *sabi) deps(ctx BaseModuleContext, deps Deps) Deps {
return deps
}
func (sabimod *sabi) flags(ctx ModuleContext, flags Flags) Flags {
return flags
}
func sabiDepsMutator(mctx android.TopDownMutatorContext) {
if c, ok := mctx.Module().(*Module); ok &&
((inList(c.Name(), config.VndkLibraries())) || (inList(c.Name(), config.LLndkLibraries())) ||
(c.sabi != nil && c.sabi.Properties.CreateSAbiDumps)) {
mctx.VisitDirectDeps(func(m blueprint.Module) {
tag := mctx.OtherModuleDependencyTag(m)
switch tag {
case staticDepTag, staticExportDepTag, lateStaticDepTag, wholeStaticDepTag:
cc, _ := m.(*Module)
if cc == nil {
return
}
cc.sabi.Properties.CreateSAbiDumps = true
}
})
}
}

View File

@ -99,11 +99,13 @@ func flagsToBuilderFlags(in Flags) builderFlags {
ldFlags: strings.Join(in.LdFlags, " "),
libFlags: strings.Join(in.libFlags, " "),
tidyFlags: strings.Join(in.TidyFlags, " "),
sAbiFlags: strings.Join(in.SAbiFlags, " "),
yasmFlags: strings.Join(in.YasmFlags, " "),
toolchain: in.Toolchain,
clang: in.Clang,
coverage: in.Coverage,
tidy: in.Tidy,
sAbiDump: in.SAbiDump,
systemIncludeFlags: strings.Join(in.SystemIncludeFlags, " "),