1#!/bin/bash
2
3# Copyright (C) 2019 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# Usage:
18#   build/build_abi.sh
19#
20# The following environment variables are considered during execution:
21#
22#   ABI_OUT_TAG
23#     Customize the output file name for the abi dump. If undefined, the tag is
24#     derived from `git describe`.
25#
26#   ABI_DEFINITION
27#     Specify an expected Kernel ABI representation. If defined, this script
28#     will, in addition to extracting the ABI representation from the currently
29#     built kernel, compare the extracted ABI to the expected one. In case of
30#     any significant differences, it will exit with the return code of
31#     diff_abi and optionally (-r) print a report.
32#     ABI_DEFINITION is supposed to be defined relative to $KERNEL_DIR/
33#
34#   KMI_WHITELIST
35#     Define a Kernel Module Interface white list description. If defined, it
36#     will be taken into account when extracting Kernel ABI information from
37#     vmlinux and kernel modules.
38#     KMI_WHITELIST is supposed to be defined relative to $KERNEL_DIR/
39#
40
41export ROOT_DIR=$(readlink -f $(dirname $0)/..)
42
43function show_help {
44    echo "USAGE: $0 [-u|--update] [-n|--nodiff]"
45    echo
46    echo "  -u | --update         Update the abi.xml in the source directory"
47    echo "  -n | --nodiff         Do not generate a ABI report with abidiff"
48    echo "  -r | --print-report   Print ABI report in case of differences"
49}
50
51UPDATE=0
52DIFF=1
53PRINT_REPORT=0
54
55ARGS=()
56for i in "$@"
57do
58case $i in
59    -u|--update)
60    UPDATE=1
61    shift # past argument=value
62    ;;
63    -n|--nodiff)
64    DIFF=0
65    shift # past argument=value
66    ;;
67    -r|--print-report)
68    PRINT_REPORT=1
69    shift # past argument=value
70    ;;
71    -h|--help)
72    show_help
73    exit 0
74    ;;
75    *)
76    ARGS+=("$1")
77    shift
78    ;;
79esac
80done
81
82set -- "${ARGS[@]}"
83
84set -e
85set -a
86
87# if we are using the default OUT_DIR, add a suffix so we are free to wipe it
88# before building to ensure a clean build/analysis. That is the default case.
89if [[ -z "$OUT_DIR" ]]; then
90    export OUT_DIR_SUFFIX="_abi"
91    wipe_out_dir=1
92fi
93
94source "${ROOT_DIR}/build/_setup_env.sh"
95
96# Now actually do the wipe out as above.
97if [[ $wipe_out_dir -eq 1 ]]; then
98    rm -rf "${COMMON_OUT_DIR}"
99fi
100
101# inject CONFIG_DEBUG_INFO=y
102export POST_DEFCONFIG_CMDS="${POST_DEFCONFIG_CMDS} : && update_config_for_abi_dump"
103function update_config_for_abi_dump() {
104    ${KERNEL_DIR}/scripts/config --file ${OUT_DIR}/.config \
105         -e CONFIG_DEBUG_INFO
106    (cd ${OUT_DIR} && \
107     make O=${OUT_DIR} "${TOOL_ARGS[@]}" $archsubarch CROSS_COMPILE=${CROSS_COMPILE} olddefconfig)
108}
109export -f check_defconfig
110export -f update_config_for_abi_dump
111
112function version_greater_than() {
113    test "$(printf '%s\n' "$@" | sort -V | head -n 1)" != "$1";
114}
115
116# ensure that abigail is present in path
117if ! ( hash abidiff 2>/dev/null); then
118    echo "ERROR: libabigail is not found in \$PATH at all!"
119    echo "Have you run build/abi/bootstrap and followed the instructions?"
120    exit 1
121fi
122
123# ensure we have a "new enough" version of abigail present before continuing
124if ! ( version_greater_than "$(abidiff --version | awk '{print $2}')"  \
125			    "1.6.0" ); then
126    echo "ERROR: no suitable libabigail (>= 1.6.0) in \$PATH."
127    echo "Have you run build/abi/bootstrap and followed the instructions?"
128    exit 1
129fi
130
131# For now we require a specific versions of libabigail identified by a commit
132# hash. That is a bit inconvenient, but we do not have another reliable
133# identifier at this time.
134required_abigail_version="1.7.0-$(cat ${ROOT_DIR}/build/abi/bootstrap| grep 'ABIGAIL_VERSION=' | cut -d= -f2)"
135if [[ ! $(abidiff --version) =~ $required_abigail_version ]]; then
136    echo "ERROR: required libabigail version is $required_abigail_version"
137    echo "Have you run build/abi/bootstrap and followed the instructions?"
138    exit 1
139fi
140
141# delegate the actual build to build.sh.
142# suppress possible values of ABI_DEFINITION when invoking build.sh to avoid
143# the generated abi.xml to be copied to <DIST_DIR>/abi.out.
144ABI_DEFINITION= ${ROOT_DIR}/build/build.sh $*
145
146# define a common KMI whitelist flag for the abi tools
147KMI_WHITELIST_FLAG=
148if [ -n "$KMI_WHITELIST" ]; then
149
150    if [ $UPDATE -eq 1 ]; then
151        echo "========================================================"
152        echo " Updating the ABI whitelist"
153        ${ROOT_DIR}/build/abi/extract_symbols       \
154            --whitelist $KERNEL_DIR/$KMI_WHITELIST  \
155            ${DIST_DIR}
156    fi
157
158    KMI_WHITELIST_FLAG="--kmi-whitelist ${DIST_DIR}/abi_whitelist"
159fi
160
161echo "========================================================"
162echo " Creating ABI dump"
163
164# create abi dump
165COMMON_OUT_DIR=$(readlink -m ${OUT_DIR:-${ROOT_DIR}/out/${BRANCH}})
166id=${ABI_OUT_TAG:-$(git -C $KERNEL_DIR describe --dirty --always)}
167abi_out_file=abi-${id}.xml
168${ROOT_DIR}/build/abi/dump_abi                \
169    --linux-tree ${DIST_DIR}                  \
170    --out-file ${DIST_DIR}/${abi_out_file}    \
171    $KMI_WHITELIST_FLAG
172
173# sanitize the abi.xml by removing any occurences of the kernel path
174sed -i "s#${ROOT_DIR}/${KERNEL_DIR}/##g" ${DIST_DIR}/${abi_out_file}
175# now also do that with any left over paths sneaking in
176# (e.g. from the prebuilts)
177sed -i "s#${ROOT_DIR}/##g" ${DIST_DIR}/${abi_out_file}
178
179# Append debug information to abi file
180echo "
181<!--
182     libabigail: $(abidw --version)
183     built with: $CC: $($CC --version | head -n1)
184-->" >> ${DIST_DIR}/${abi_out_file}
185
186ln -sf ${abi_out_file} ${DIST_DIR}/abi.xml
187
188echo "========================================================"
189echo " ABI dump has been created at ${DIST_DIR}/${abi_out_file}"
190
191rc=0
192if [ -n "$ABI_DEFINITION" ]; then
193    if [ $DIFF -eq 1 ]; then
194        echo "========================================================"
195        echo " Comparing ABI against expected definition ($ABI_DEFINITION)"
196        abi_report=${DIST_DIR}/abi.report
197        set +e
198        ${ROOT_DIR}/build/abi/diff_abi --baseline $KERNEL_DIR/$ABI_DEFINITION \
199                                       --new      ${DIST_DIR}/${abi_out_file} \
200                                       --report   ${abi_report}               \
201                                       --short-report ${abi_report}.short     \
202                                       $KMI_WHITELIST_FLAG
203        rc=$?
204        set -e
205        echo "========================================================"
206        echo " ABI report has been created at ${abi_report}"
207
208        if [ $rc -ne 0 ]; then
209            echo " ABI DIFFERENCES HAVE BEEN DETECTED! (RC=$rc)"
210        fi
211
212        if [ $PRINT_REPORT -eq 1 ] && [ $rc -ne 0 ] ; then
213            echo "========================================================"
214            cat ${abi_report}.short
215        fi
216    fi
217    if [ $UPDATE -eq 1 ] ; then
218        echo "========================================================"
219        echo " Updating expected ABI definition ($ABI_DEFINITION)"
220        cp -v ${DIST_DIR}/${abi_out_file} $KERNEL_DIR/$ABI_DEFINITION
221    fi
222fi
223
224exit $rc
225
226