Merge "Add Android.bp prebuilt ELF check error fixer" am: f60ad911d4
Original change: https://android-review.googlesource.com/c/platform/development/+/1253430 Change-Id: I3d8d543a0947c5606cd4f1e4445f6a34f8248b7f
This commit is contained in:
28
vndk/tools/elfcheck/Android.bp
Normal file
28
vndk/tools/elfcheck/Android.bp
Normal file
@@ -0,0 +1,28 @@
|
||||
// Copyright (C) 2020 The Android Open Source Project
|
||||
//
|
||||
// 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.
|
||||
|
||||
blueprint_go_binary {
|
||||
name: "bpflatten",
|
||||
srcs: [
|
||||
"bpflatten/main.go",
|
||||
],
|
||||
deps: [
|
||||
"blueprint-parser",
|
||||
],
|
||||
}
|
||||
|
||||
sh_binary_host {
|
||||
name: "fix_android_bp_prebuilt",
|
||||
src: "fix_android_bp_prebuilt.sh",
|
||||
}
|
||||
229
vndk/tools/elfcheck/bpflatten/main.go
Normal file
229
vndk/tools/elfcheck/bpflatten/main.go
Normal file
@@ -0,0 +1,229 @@
|
||||
// Copyright (C) 2020 The Android Open Source Project
|
||||
//
|
||||
// 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 main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/google/blueprint/parser"
|
||||
)
|
||||
|
||||
type FlatModule struct {
|
||||
Type string
|
||||
Name string
|
||||
PropertyMap map[string]interface{}
|
||||
}
|
||||
|
||||
func expandScalarTypeExpression(value parser.Expression) (scalar interface{}, isScalar bool) {
|
||||
if s, ok := value.(*parser.Bool); ok {
|
||||
return s.Value, true
|
||||
} else if s, ok := value.(*parser.String); ok {
|
||||
return s.Value, true
|
||||
} else if s, ok := value.(*parser.Int64); ok {
|
||||
return s.Value, true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func populatePropertyMap(propMap map[string]interface{}, prefix string, m *parser.Map) {
|
||||
for _, prop := range m.Properties {
|
||||
name := prop.Name
|
||||
if prefix != "" {
|
||||
name = prefix + "." + name
|
||||
}
|
||||
value := prop.Value.Eval()
|
||||
if s, isScalar := expandScalarTypeExpression(value); isScalar {
|
||||
propMap[name] = s
|
||||
} else if list, ok := value.(*parser.List); ok {
|
||||
var l []interface{}
|
||||
for _, v := range list.Values {
|
||||
if s, isScalar := expandScalarTypeExpression(v.Eval()); isScalar {
|
||||
l = append(l, s)
|
||||
}
|
||||
}
|
||||
propMap[name] = l
|
||||
} else if mm, ok := value.(*parser.Map); ok {
|
||||
populatePropertyMap(propMap, name, mm)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var anonymousModuleCount int
|
||||
|
||||
func flattenModule(module *parser.Module) (flattened FlatModule) {
|
||||
flattened.Type = module.Type
|
||||
if prop, found := module.GetProperty("name"); found {
|
||||
if value, ok := prop.Value.Eval().(*parser.String); ok {
|
||||
flattened.Name = value.Value
|
||||
}
|
||||
} else {
|
||||
flattened.Name = fmt.Sprintf("anonymous@<%d>", anonymousModuleCount)
|
||||
anonymousModuleCount++
|
||||
}
|
||||
flattened.PropertyMap = make(map[string]interface{})
|
||||
populatePropertyMap(flattened.PropertyMap, "", &module.Map)
|
||||
return flattened
|
||||
}
|
||||
|
||||
func processFile(filename string, in io.Reader) ([]FlatModule, error) {
|
||||
if in == nil {
|
||||
if file, err := os.Open(filename); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
defer file.Close()
|
||||
in = file
|
||||
}
|
||||
}
|
||||
|
||||
ast, errs := parser.ParseAndEval(filename, in, &parser.Scope{})
|
||||
if len(errs) > 0 {
|
||||
for _, err := range errs {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
}
|
||||
return nil, fmt.Errorf("%d parsing errors", len(errs))
|
||||
}
|
||||
|
||||
var modules []FlatModule
|
||||
for _, def := range ast.Defs {
|
||||
if module, ok := def.(*parser.Module); ok {
|
||||
modules = append(modules, flattenModule(module))
|
||||
}
|
||||
}
|
||||
return modules, nil
|
||||
}
|
||||
|
||||
func quoteBashString(s string) string {
|
||||
return strings.ReplaceAll(s, "$", "\\$")
|
||||
}
|
||||
|
||||
func printBash(flatModules []FlatModule, w io.Writer) {
|
||||
var moduleNameList []string
|
||||
if len(flatModules) == 0 {
|
||||
// Early bail out if we have nothing to output
|
||||
return
|
||||
}
|
||||
fmt.Fprintf(w, "declare -a MODULE_NAMES\n")
|
||||
fmt.Fprintf(w, "declare -A MODULE_TYPE_DICT\n")
|
||||
fmt.Fprintf(w, "declare -A MODULE_PROP_KEYS_DICT\n")
|
||||
fmt.Fprintf(w, "declare -A MODULE_PROP_VALUES_DICT\n")
|
||||
fmt.Fprintf(w, "\n")
|
||||
for _, module := range flatModules {
|
||||
name := quoteBashString(module.Name)
|
||||
moduleNameList = append(moduleNameList, name)
|
||||
var modulePropKeys []string
|
||||
for k := range module.PropertyMap {
|
||||
modulePropKeys = append(modulePropKeys, k)
|
||||
}
|
||||
fmt.Fprintf(w, "MODULE_TYPE_DICT[%q]=%q\n", name, quoteBashString(module.Type))
|
||||
fmt.Fprintf(w, "MODULE_PROP_KEYS_DICT[%q]=%q\n", name,
|
||||
quoteBashString(strings.Join(modulePropKeys, " ")))
|
||||
for k, v := range module.PropertyMap {
|
||||
var propValue string
|
||||
if vl, ok := v.([]interface{}); ok {
|
||||
var list []string
|
||||
for _, s := range vl {
|
||||
list = append(list, fmt.Sprintf("%v", s))
|
||||
}
|
||||
propValue = fmt.Sprintf("%s", strings.Join(list, " "))
|
||||
} else {
|
||||
propValue = fmt.Sprintf("%v", v)
|
||||
}
|
||||
key := name + ":" + quoteBashString(k)
|
||||
fmt.Fprintf(w, "MODULE_PROP_VALUES_DICT[%q]=%q\n", key, quoteBashString(propValue))
|
||||
}
|
||||
fmt.Fprintf(w, "\n")
|
||||
}
|
||||
fmt.Fprintf(w, "MODULE_NAMES=(\n")
|
||||
for _, name := range moduleNameList {
|
||||
fmt.Fprintf(w, " %q\n", name)
|
||||
}
|
||||
fmt.Fprintf(w, ")\n")
|
||||
}
|
||||
|
||||
var (
|
||||
outputBashFlag = flag.Bool("bash", false, "Output in bash format")
|
||||
outputJsonFlag = flag.Bool("json", false, "Output in json format (this is the default)")
|
||||
helpFlag = flag.Bool("help", false, "Display this message and exit")
|
||||
exitCode = 0
|
||||
)
|
||||
|
||||
func init() {
|
||||
flag.Usage = usage
|
||||
}
|
||||
|
||||
func usage() {
|
||||
fmt.Fprintf(os.Stderr, "Usage: %s [OPTION]... [FILE]...\n", os.Args[0])
|
||||
fmt.Fprintf(os.Stderr, "Flatten Android.bp to python friendly json text.\n")
|
||||
fmt.Fprintf(os.Stderr, "If no file list is specified, read from standard input.\n")
|
||||
fmt.Fprintf(os.Stderr, "\n")
|
||||
flag.PrintDefaults()
|
||||
}
|
||||
|
||||
func main() {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error: %v\n", err)
|
||||
exitCode = 1
|
||||
}
|
||||
os.Exit(exitCode)
|
||||
}()
|
||||
|
||||
flag.Parse()
|
||||
|
||||
if *helpFlag {
|
||||
usage()
|
||||
return
|
||||
}
|
||||
|
||||
flatModules := []FlatModule{}
|
||||
|
||||
if flag.NArg() == 0 {
|
||||
if modules, err := processFile("<stdin>", os.Stdin); err != nil {
|
||||
panic(err)
|
||||
} else {
|
||||
flatModules = append(flatModules, modules...)
|
||||
}
|
||||
}
|
||||
|
||||
for _, pathname := range flag.Args() {
|
||||
switch fileInfo, err := os.Stat(pathname); {
|
||||
case err != nil:
|
||||
panic(err)
|
||||
case fileInfo.IsDir():
|
||||
panic(fmt.Errorf("%q is a directory", pathname))
|
||||
default:
|
||||
if modules, err := processFile(pathname, nil); err != nil {
|
||||
panic(err)
|
||||
} else {
|
||||
flatModules = append(flatModules, modules...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if *outputBashFlag {
|
||||
printBash(flatModules, os.Stdout)
|
||||
} else {
|
||||
if b, err := json.MarshalIndent(flatModules, "", " "); err != nil {
|
||||
panic(err)
|
||||
} else {
|
||||
fmt.Printf("%s\n", b)
|
||||
}
|
||||
}
|
||||
}
|
||||
190
vndk/tools/elfcheck/fix_android_bp_prebuilt.sh
Executable file
190
vndk/tools/elfcheck/fix_android_bp_prebuilt.sh
Executable file
@@ -0,0 +1,190 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Copyright (C) 2020 The Android Open Source Project
|
||||
#
|
||||
# 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.
|
||||
|
||||
# This program fixes prebuilt ELF check errors by updating the "shared_libs"
|
||||
# fields in Android.bp.
|
||||
#
|
||||
# Example:
|
||||
# $ source build/envsetup.sh
|
||||
# $ m fix_android_bp_prebuilt bpflatten bpmodify
|
||||
# $ fix_android_bp_prebuilt --in-place path_to_problematic_android_bp
|
||||
|
||||
set -e
|
||||
|
||||
function usage() {
|
||||
cat <<EOF
|
||||
Usage:
|
||||
$0 [OPTION]... FILE
|
||||
|
||||
Options:
|
||||
--in-place
|
||||
Edit file in place (overwrites source file)
|
||||
--diff
|
||||
Show diffs
|
||||
-h, --help, --usage
|
||||
Display this message and exit
|
||||
EOF
|
||||
}
|
||||
|
||||
function exit_handler() {
|
||||
readonly EXIT_CODE="$?"
|
||||
# Cleanup any temporary files
|
||||
rm -rf "$TEMP_DIR"
|
||||
exit "$EXIT_CODE"
|
||||
}
|
||||
|
||||
trap exit_handler EXIT
|
||||
|
||||
function trim_space() {
|
||||
echo "$1" | sed -E 's/^[[:space:]]+//;s/[[:space:]]+$//'
|
||||
}
|
||||
|
||||
function get_prop() {
|
||||
echo "${MODULE_PROP_VALUES_DICT[${1}:${2}]}"
|
||||
}
|
||||
|
||||
function rewrite_prop() {
|
||||
local ORIGINAL_VALUE=$(trim_space "$(get_prop "$1" "$2")")
|
||||
if [[ -n "$ORIGINAL_VALUE" ]]; then
|
||||
bpmodify -m "$1" -property "$2" -r "$ORIGINAL_VALUE" -w "$TEMP_ANDROID_BP"
|
||||
fi
|
||||
if [[ -n "$3" ]]; then
|
||||
bpmodify -m "$1" -property "$2" -a "$3" -w "$TEMP_ANDROID_BP"
|
||||
fi
|
||||
}
|
||||
|
||||
function get_dt_needed() {
|
||||
local DYNAMIC_TABLE=$($READELF -d "${ANDROID_BP_DIR}/$1")
|
||||
if [[ "$?" -ne 0 ]]; then
|
||||
return 1
|
||||
fi
|
||||
echo "$DYNAMIC_TABLE" |
|
||||
sed -n -E 's/^[[:space:]]*0x[[:xdigit:]]+[[:space:]]+\(NEEDED\).*\[(.+)\.so\].*$/\1/p' |
|
||||
xargs
|
||||
}
|
||||
|
||||
function unique() {
|
||||
echo "$1" | xargs -n1 | sort | uniq | xargs
|
||||
}
|
||||
|
||||
|
||||
while [[ "$1" =~ ^- ]]; do
|
||||
case "$1" in
|
||||
-h | --help | --usage)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
--in-place)
|
||||
EDIT_IN_PLACE=1
|
||||
;;
|
||||
--diff)
|
||||
SHOW_DIFF=1
|
||||
;;
|
||||
-x)
|
||||
set -x
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
*)
|
||||
echo >&2 "Unexpected flag: $1"
|
||||
usage >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
if ! [[ -f "$1" ]]; then
|
||||
echo >&2 "No such file: '$1'"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -e "$(command -v llvm-readelf)" ]]; then
|
||||
READELF="llvm-readelf"
|
||||
elif [[ -e "$(command -v readelf)" ]]; then
|
||||
READELF="readelf -W"
|
||||
else
|
||||
echo >&2 'Cannot find readelf in $PATH, please run:'
|
||||
echo >&2 '$ source build/envsetup.sh'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! [[ -e "$(command -v bpflatten)" && -e "$(command -v bpmodify)" ]]; then
|
||||
echo >&2 'Cannot find bpflatten and bpmodify in $PATH, please run:'
|
||||
echo >&2 '$ source build/envsetup.sh'
|
||||
echo >&2 '$ m blueprint_tools'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
readonly EDIT_IN_PLACE
|
||||
readonly SHOW_DIFF
|
||||
readonly READELF
|
||||
readonly ANDROID_BP="$1"
|
||||
readonly ANDROID_BP_DIR=$(dirname "$ANDROID_BP")
|
||||
readonly TEMP_DIR=$(mktemp -d)
|
||||
readonly TEMP_ANDROID_BP="${TEMP_DIR}/Android.bp"
|
||||
|
||||
cp -L "$ANDROID_BP" "$TEMP_ANDROID_BP"
|
||||
|
||||
# This subshell and `eval` must be on separate lines, so that eval would not
|
||||
# shadow the subshell's exit code.
|
||||
# In other words, if `bpflatten` fails, we mustn't eval its output.
|
||||
FLATTEN_COMMAND=$(bpflatten --bash "$ANDROID_BP")
|
||||
eval "$FLATTEN_COMMAND"
|
||||
|
||||
for MODULE_NAME in "${MODULE_NAMES[@]}" ; do
|
||||
MODULE_TYPE="${MODULE_TYPE_DICT[${MODULE_NAME}]}"
|
||||
if ! [[ "$MODULE_TYPE" =~ ^(.+_)?prebuilt(_.+)?$ ]]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
SRCS=$(get_prop "$MODULE_NAME" "srcs")
|
||||
SHARED_LIBS=$(get_prop "$MODULE_NAME" "shared_libs")
|
||||
if [[ -n "${SRCS}" ]]; then
|
||||
DT_NEEDED=$(get_dt_needed "$SRCS")
|
||||
if [[ $(unique "$DT_NEEDED") != $(unique "$SHARED_LIBS") ]]; then
|
||||
rewrite_prop "$MODULE_NAME" "shared_libs" "$DT_NEEDED"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Handle different arch / target variants...
|
||||
for PROP in ${MODULE_PROP_KEYS_DICT[${MODULE_NAME}]} ; do
|
||||
if ! [[ "$PROP" =~ \.srcs$ ]]; then
|
||||
continue
|
||||
fi
|
||||
SRCS=$(get_prop "$MODULE_NAME" "$PROP")
|
||||
DT_NEEDED=$(get_dt_needed "$SRCS")
|
||||
SHARED_LIBS_PROP="${PROP%.srcs}.shared_libs"
|
||||
VARIANT_SHARED_LIBS="${SHARED_LIBS} $(get_prop "$MODULE_NAME" "$SHARED_LIBS_PROP")"
|
||||
if [[ $(unique "$DT_NEEDED") != $(unique "$VARIANT_SHARED_LIBS") ]]; then
|
||||
rewrite_prop "$MODULE_NAME" "$SHARED_LIBS_PROP" "$DT_NEEDED"
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
if [[ -n "$SHOW_DIFF" ]]; then
|
||||
diff -u "$ANDROID_BP" "$TEMP_ANDROID_BP" || true
|
||||
fi
|
||||
|
||||
if [[ -n "$EDIT_IN_PLACE" ]]; then
|
||||
cp "$TEMP_ANDROID_BP" "$ANDROID_BP"
|
||||
fi
|
||||
|
||||
if [[ -z "${SHOW_DIFF}${EDIT_IN_PLACE}" ]]; then
|
||||
cat "$TEMP_ANDROID_BP"
|
||||
fi
|
||||
Reference in New Issue
Block a user