1#!/bin/bash
2#
3# Copyright (C) 2020 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#      http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17# This program fixes prebuilt ELF check errors by updating the "shared_libs"
18# fields in Android.bp.
19#
20# Example:
21# $ source build/envsetup.sh
22# $ m fix_android_bp_prebuilt bpflatten bpmodify
23# $ fix_android_bp_prebuilt --in-place path_to_problematic_android_bp
24
25set -e
26
27function usage() {
28  cat <<EOF
29Usage:
30    $0 [OPTION]... FILE
31
32Options:
33    --in-place
34        Edit file in place (overwrites source file)
35    --diff
36        Show diffs
37    -h, --help, --usage
38        Display this message and exit
39EOF
40}
41
42function exit_handler() {
43  readonly EXIT_CODE="$?"
44  # Cleanup any temporary files
45  rm -rf "$TEMP_DIR"
46  exit "$EXIT_CODE"
47}
48
49trap exit_handler EXIT
50
51function trim_space() {
52  echo "$1" | sed -E 's/^[[:space:]]+//;s/[[:space:]]+$//'
53}
54
55function get_prop() {
56  echo "${MODULE_PROP_VALUES_DICT[${1}:${2}]}"
57}
58
59function rewrite_prop() {
60  local ORIGINAL_VALUE=$(trim_space "$(get_prop "$1" "$2")")
61  if [[ -n "$ORIGINAL_VALUE" ]]; then
62    bpmodify -m "$1" -property "$2" -r "$ORIGINAL_VALUE" -w "$TEMP_ANDROID_BP"
63  fi
64  if [[ -n "$3" ]]; then
65    bpmodify -m "$1" -property "$2" -a "$3" -w "$TEMP_ANDROID_BP"
66  fi
67}
68
69function get_dt_needed() {
70  local DYNAMIC_TABLE=$($READELF -d "${ANDROID_BP_DIR}/$1")
71  if [[ "$?" -ne 0 ]]; then
72    return 1
73  fi
74  echo "$DYNAMIC_TABLE" |
75    sed -n -E 's/^[[:space:]]*0x[[:xdigit:]]+[[:space:]]+\(NEEDED\).*\[(.+)\.so\].*$/\1/p' |
76    xargs
77}
78
79function unique() {
80  echo "$1" | xargs -n1 | sort | uniq | xargs
81}
82
83
84while [[ "$1" =~ ^- ]]; do
85  case "$1" in
86    -h | --help | --usage)
87      usage
88      exit 0
89      ;;
90    --in-place)
91      EDIT_IN_PLACE=1
92      ;;
93    --diff)
94      SHOW_DIFF=1
95      ;;
96    -x)
97      set -x
98      ;;
99    --)
100      shift
101      break
102      ;;
103    *)
104      echo >&2 "Unexpected flag: $1"
105      usage >&2
106      exit 1
107      ;;
108  esac
109  shift
110done
111
112if ! [[ -f "$1" ]]; then
113  echo >&2 "No such file: '$1'"
114  exit 1
115fi
116
117if [[ -e "$(command -v llvm-readelf)" ]]; then
118  READELF="llvm-readelf"
119elif [[ -e "$(command -v readelf)" ]]; then
120  READELF="readelf -W"
121else
122  echo >&2 'Cannot find readelf in $PATH, please run:'
123  echo >&2 '$ source build/envsetup.sh'
124  exit 1
125fi
126
127if ! [[ -e "$(command -v bpflatten)" && -e "$(command -v bpmodify)" ]]; then
128  echo >&2 'Cannot find bpflatten and bpmodify in $PATH, please run:'
129  echo >&2 '$ source build/envsetup.sh'
130  echo >&2 '$ m blueprint_tools'
131  exit 1
132fi
133
134readonly EDIT_IN_PLACE
135readonly SHOW_DIFF
136readonly READELF
137readonly ANDROID_BP="$1"
138readonly ANDROID_BP_DIR=$(dirname "$ANDROID_BP")
139readonly TEMP_DIR=$(mktemp -d)
140readonly TEMP_ANDROID_BP="${TEMP_DIR}/Android.bp"
141
142cp -L "$ANDROID_BP" "$TEMP_ANDROID_BP"
143
144# This subshell and `eval` must be on separate lines, so that eval would not
145# shadow the subshell's exit code.
146# In other words, if `bpflatten` fails, we mustn't eval its output.
147FLATTEN_COMMAND=$(bpflatten --bash "$ANDROID_BP")
148eval "$FLATTEN_COMMAND"
149
150for MODULE_NAME in "${MODULE_NAMES[@]}" ; do
151  MODULE_TYPE="${MODULE_TYPE_DICT[${MODULE_NAME}]}"
152  if ! [[ "$MODULE_TYPE" =~ ^(.+_)?prebuilt(_.+)?$ ]]; then
153    continue
154  fi
155
156  SRCS=$(get_prop "$MODULE_NAME" "srcs")
157  SHARED_LIBS=$(get_prop "$MODULE_NAME" "shared_libs")
158  if [[ -n "${SRCS}" ]]; then
159    DT_NEEDED=$(get_dt_needed "$SRCS")
160    if [[ $(unique "$DT_NEEDED") != $(unique "$SHARED_LIBS") ]]; then
161      rewrite_prop "$MODULE_NAME" "shared_libs" "$DT_NEEDED"
162    fi
163  fi
164
165  # Handle different arch / target variants...
166  for PROP in ${MODULE_PROP_KEYS_DICT[${MODULE_NAME}]} ; do
167    if ! [[ "$PROP" =~ \.srcs$ ]]; then
168      continue
169    fi
170    SRCS=$(get_prop "$MODULE_NAME" "$PROP")
171    DT_NEEDED=$(get_dt_needed "$SRCS")
172    SHARED_LIBS_PROP="${PROP%.srcs}.shared_libs"
173    VARIANT_SHARED_LIBS="${SHARED_LIBS} $(get_prop "$MODULE_NAME" "$SHARED_LIBS_PROP")"
174    if [[ $(unique "$DT_NEEDED") != $(unique "$VARIANT_SHARED_LIBS") ]]; then
175      rewrite_prop "$MODULE_NAME" "$SHARED_LIBS_PROP" "$DT_NEEDED"
176    fi
177  done
178done
179
180if [[ -n "$SHOW_DIFF" ]]; then
181  diff -u "$ANDROID_BP" "$TEMP_ANDROID_BP" || true
182fi
183
184if [[ -n "$EDIT_IN_PLACE" ]]; then
185  cp "$TEMP_ANDROID_BP" "$ANDROID_BP"
186fi
187
188if [[ -z "${SHOW_DIFF}${EDIT_IN_PLACE}" ]]; then
189  cat "$TEMP_ANDROID_BP"
190fi
191