aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorColin Cross <ccross@android.com>2016-09-30 17:10:16 -0700
committerColin Cross <ccross@android.com>2016-09-30 21:05:59 -0700
commit26c34ede294735354d75a5f511d9afd39dc8013c (patch)
tree347b7a9d337b27e16eacf541fa92f6f773b0b1e4
parent12013c8fe6d272c665b8b31672d49868a3cd41d3 (diff)
downloadbuild_soong-26c34ede294735354d75a5f511d9afd39dc8013c.tar.gz
build_soong-26c34ede294735354d75a5f511d9afd39dc8013c.tar.bz2
build_soong-26c34ede294735354d75a5f511d9afd39dc8013c.zip
Add support for toc optimization in soong
Skip relinking against shared libraries whose interface hasn't changed. Test: mmma -j frameworks/native/libs/gui Test: touch frameworks/native/libs/gui/BufferItem.cpp Test: mmma -j frameworks/native/libs/gui, see nothing relinks past libgui Bug: 26014946 Change-Id: I4d4b8da6a35c682341ae51869f5c72b51e192053
-rw-r--r--cc/binary.go3
-rw-r--r--cc/builder.go29
-rw-r--r--cc/cc.go43
-rw-r--r--cc/library.go26
-rwxr-xr-xscripts/strip.sh1
-rwxr-xr-xscripts/toc.sh75
6 files changed, 166 insertions, 11 deletions
diff --git a/cc/binary.go b/cc/binary.go
index 8afce094..083cf0d0 100644
--- a/cc/binary.go
+++ b/cc/binary.go
@@ -281,6 +281,9 @@ func (binary *binaryDecorator) link(ctx ModuleContext,
flagsToBuilderFlags(flags), afterPrefixSymbols)
}
+ linkerDeps = append(linkerDeps, deps.SharedLibsDeps...)
+ linkerDeps = append(linkerDeps, deps.LateSharedLibsDeps...)
+
TransformObjToDynamicBinary(ctx, objFiles, sharedLibs, deps.StaticLibs,
deps.LateStaticLibs, deps.WholeStaticLibs, linkerDeps, deps.CrtBegin, deps.CrtEnd, true,
builderFlags, outputFile)
diff --git a/cc/builder.go b/cc/builder.go
index 42a7f48a..b813783d 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -138,6 +138,18 @@ var (
Description: "copy gcc $out",
},
"ccCmd", "cFlags", "libName")
+
+ tocPath = pctx.SourcePathVariable("tocPath", "build/soong/scripts/toc.sh")
+
+ toc = pctx.AndroidStaticRule("toc",
+ blueprint.RuleParams{
+ Depfile: "${out}.d",
+ Deps: blueprint.DepsGCC,
+ Command: "CROSS_COMPILE=$crossCompile $tocPath -i ${in} -o ${out} -d ${out}.d",
+ CommandDeps: []string{"$tocPath"},
+ Restat: true,
+ },
+ "crossCompile")
)
func init() {
@@ -380,7 +392,6 @@ func TransformObjToDynamicBinary(ctx android.ModuleContext,
libFlagsList = append(libFlagsList, lib.String())
}
- deps = append(deps, sharedLibs...)
deps = append(deps, staticLibs...)
deps = append(deps, lateStaticLibs...)
deps = append(deps, wholeStaticLibs...)
@@ -403,6 +414,22 @@ func TransformObjToDynamicBinary(ctx android.ModuleContext,
})
}
+// 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) {
+
+ crossCompile := gccCmd(flags.toolchain, "")
+
+ ctx.ModuleBuild(pctx, android.ModuleBuildParams{
+ Rule: toc,
+ Output: outputFile,
+ Input: inputFile,
+ Args: map[string]string{
+ "crossCompile": crossCompile,
+ },
+ })
+}
+
// Generate a rule for compiling multiple .o files to a .o using ld partial linking
func TransformObjsToObj(ctx android.ModuleContext, objFiles android.Paths,
flags builderFlags, outputFile android.WritablePath) {
diff --git a/cc/cc.go b/cc/cc.go
index 9a198127..75292aa7 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -70,18 +70,25 @@ type Deps struct {
}
type PathDeps struct {
- SharedLibs, LateSharedLibs android.Paths
+ // Paths to .so files
+ SharedLibs, LateSharedLibs android.Paths
+ // Paths to the dependencies to use for .so files (.so.toc files)
+ SharedLibsDeps, LateSharedLibsDeps android.Paths
+ // Paths to .a files
StaticLibs, LateStaticLibs, WholeStaticLibs android.Paths
+ // Paths to .o files
ObjFiles android.Paths
WholeStaticLibObjFiles android.Paths
+ // Paths to generated source files
GeneratedSources android.Paths
GeneratedHeaders android.Paths
Flags, ReexportedFlags []string
ReexportedFlagsDeps android.Paths
+ // Paths to crt*.o files
CrtBegin, CrtEnd android.OptionalPath
}
@@ -815,19 +822,27 @@ func (c *Module) depsToPaths(ctx android.ModuleContext) PathDeps {
checkLinkType(c, cc)
}
+ var ptr *android.Paths
var depPtr *android.Paths
+ linkFile := cc.outputFile
+ depFile := android.OptionalPath{}
+
switch tag {
case ndkStubDepTag, sharedDepTag, sharedExportDepTag:
- depPtr = &depPaths.SharedLibs
+ ptr = &depPaths.SharedLibs
+ depPtr = &depPaths.SharedLibsDeps
+ depFile = cc.linker.(libraryInterface).toc()
case lateSharedDepTag, ndkLateStubDepTag:
- depPtr = &depPaths.LateSharedLibs
+ ptr = &depPaths.LateSharedLibs
+ depPtr = &depPaths.LateSharedLibsDeps
+ depFile = cc.linker.(libraryInterface).toc()
case staticDepTag, staticExportDepTag:
- depPtr = &depPaths.StaticLibs
+ ptr = &depPaths.StaticLibs
case lateStaticDepTag:
- depPtr = &depPaths.LateStaticLibs
+ ptr = &depPaths.LateStaticLibs
case wholeStaticDepTag:
- depPtr = &depPaths.WholeStaticLibs
+ ptr = &depPaths.WholeStaticLibs
staticLib, ok := cc.linker.(libraryInterface)
if !ok || !staticLib.static() {
ctx.ModuleErrorf("module %q not a static library", name)
@@ -844,17 +859,25 @@ func (c *Module) depsToPaths(ctx android.ModuleContext) PathDeps {
depPaths.WholeStaticLibObjFiles =
append(depPaths.WholeStaticLibObjFiles, staticLib.objs()...)
case objDepTag:
- depPtr = &depPaths.ObjFiles
+ ptr = &depPaths.ObjFiles
case crtBeginDepTag:
- depPaths.CrtBegin = cc.outputFile
+ depPaths.CrtBegin = linkFile
case crtEndDepTag:
- depPaths.CrtEnd = cc.outputFile
+ depPaths.CrtEnd = linkFile
default:
panic(fmt.Errorf("unknown dependency tag: %s", tag))
}
+ if ptr != nil {
+ *ptr = append(*ptr, linkFile.Path())
+ }
+
if depPtr != nil {
- *depPtr = append(*depPtr, cc.outputFile.Path())
+ dep := depFile
+ if !dep.Valid() {
+ dep = linkFile
+ }
+ *depPtr = append(*depPtr, dep.Path())
}
})
diff --git a/cc/library.go b/cc/library.go
index 7cc587f5..53c9a580 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -18,6 +18,7 @@ import (
"strings"
"github.com/google/blueprint"
+ "github.com/google/blueprint/pathtools"
"android/soong"
"android/soong/android"
@@ -158,6 +159,8 @@ type libraryDecorator struct {
// For reusing static library objects for shared library
reuseObjFiles android.Paths
+ // table-of-contents file to optimize out relinking when possible
+ tocFile android.OptionalPath
flagExporter
stripper
@@ -269,6 +272,7 @@ type libraryInterface interface {
static() bool
objs() android.Paths
reuseObjs() android.Paths
+ toc() android.OptionalPath
// Returns true if the build options for the module have selected a static or shared build
buildStatic() bool
@@ -434,10 +438,28 @@ func (library *libraryDecorator) linkShared(ctx ModuleContext,
}
}
+ linkerDeps = append(linkerDeps, deps.SharedLibsDeps...)
+ linkerDeps = append(linkerDeps, deps.LateSharedLibsDeps...)
+
TransformObjToDynamicBinary(ctx, objFiles, sharedLibs,
deps.StaticLibs, deps.LateStaticLibs, deps.WholeStaticLibs,
linkerDeps, deps.CrtBegin, deps.CrtEnd, false, builderFlags, outputFile)
+ if ctx.Device() {
+ // For device targets, optimize out relinking against shared
+ // libraries whose interface hasn't changed by depending on
+ // a table of contents file instead of the library itself.
+ // For host targets, the library might be part of a host tool
+ // that is run during the build, use the library directly so
+ // that the timestamp of the binary changes whenever a library
+ // changes and any necessary tools get re-run.
+ tocPath := outputFile.String()
+ tocPath = pathtools.ReplaceExtension(tocPath, flags.Toolchain.ShlibSuffix()[1:]+".toc")
+ tocFile := android.PathForOutput(ctx, tocPath)
+ library.tocFile = android.OptionalPathForPath(tocFile)
+ TransformSharedObjectToToc(ctx, outputFile, tocFile, builderFlags)
+ }
+
return ret
}
@@ -482,6 +504,10 @@ func (library *libraryDecorator) reuseObjs() android.Paths {
return library.reuseObjFiles
}
+func (library *libraryDecorator) toc() android.OptionalPath {
+ return library.tocFile
+}
+
func (library *libraryDecorator) install(ctx ModuleContext, file android.Path) {
if !ctx.static() {
library.baseInstaller.install(ctx, file)
diff --git a/scripts/strip.sh b/scripts/strip.sh
index 5c43028c..82249422 100755
--- a/scripts/strip.sh
+++ b/scripts/strip.sh
@@ -5,6 +5,7 @@
# Environment:
# CROSS_COMPILE: prefix added to readelf, objcopy tools
# Arguments:
+# -i ${file}: input file (required)
# -o ${file}: output file (required)
# -d ${file}: deps file (required)
# --keep-symbols
diff --git a/scripts/toc.sh b/scripts/toc.sh
new file mode 100755
index 00000000..59bf8a3b
--- /dev/null
+++ b/scripts/toc.sh
@@ -0,0 +1,75 @@
+#!/bin/bash -eu
+
+# Script to handle generating a .toc file from a .so file
+# Inputs:
+# Environment:
+# CROSS_COMPILE: prefix added to readelf tool
+# Arguments:
+# -i ${file}: input file (required)
+# -o ${file}: output file (required)
+# -d ${file}: deps file (required)
+
+OPTSTRING=d:i:o:-:
+
+usage() {
+ cat <<EOF
+Usage: toc.sh [options] -i in-file -o out-file -d deps-file
+Options:
+EOF
+ exit 1
+}
+
+do_elf() {
+ ("${CROSS_COMPILE}readelf" -d "${infile}" | grep SONAME || echo "No SONAME for ${infile}") > "${outfile}.tmp"
+ "${CROSS_COMPILE}readelf" --dyn-syms "${infile}" | awk '{$2=""; $3=""; print}' >> "${outfile}.tmp"
+}
+
+do_macho() {
+ otool -l "${infile}" | grep LC_ID_DYLIB -A 5 > "${outfile}.tmp"
+ nm -gP "${infile}" | cut -f1-2 -d" " | grep -v 'U$' >> "${outfile}.tmp"
+}
+
+
+while getopts $OPTSTRING opt; do
+ case "$opt" in
+ d) depsfile="${OPTARG}" ;;
+ i) infile="${OPTARG}" ;;
+ o) outfile="${OPTARG}" ;;
+ -)
+ case "${OPTARG}" in
+ *) echo "Unknown option --${OPTARG}"; usage ;;
+ esac;;
+ ?) usage ;;
+ *) echo "'${opt}' '${OPTARG}'"
+ esac
+done
+
+if [ -z "${infile}" ]; then
+ echo "-i argument is required"
+ usage
+fi
+
+if [ -z "${outfile}" ]; then
+ echo "-o argument is required"
+ usage
+fi
+
+if [ -z "${depsfile}" ]; then
+ echo "-d argument is required"
+ usage
+fi
+
+rm -f "${outfile}.tmp"
+
+cat <<EOF > "${depsfile}"
+${outfile}: \\
+ ${CROSS_COMPILE}readelf \\
+EOF
+
+do_elf
+
+if cmp "${outfile}" "${outfile}.tmp" > /dev/null 2> /dev/null; then
+ rm -f "${outfile}.tmp"
+else
+ mv -f "${outfile}.tmp" "${outfile}"
+fi