xref: /aosp_15_r20/build/bazel/scripts/elf_compare.sh (revision 7594170e27e0732bc44b93d1440d87a54b6ffe7c)
1*7594170eSAndroid Build Coastguard Worker#! /bin/bash -eu
2*7594170eSAndroid Build Coastguard Worker
3*7594170eSAndroid Build Coastguard Worker# Compare object files the linker used to build given binary for
4*7594170eSAndroid Build Coastguard Worker# two different configurations.
5*7594170eSAndroid Build Coastguard Worker# As an example, suppose we comnt to compare `adbd` binary that is
6*7594170eSAndroid Build Coastguard Worker# included in `com.android.adbd` APEX. We first build this APEX
7*7594170eSAndroid Build Coastguard Worker# with Soong and rename the build tree to `out.ref`:
8*7594170eSAndroid Build Coastguard Worker#   $ m com.android.adbd && mv out out.ref
9*7594170eSAndroid Build Coastguard Worker# Then we build it again with mixed build and rename the build tree
10*7594170eSAndroid Build Coastguard Worker# to `out.mix`
11*7594170eSAndroid Build Coastguard Worker#   $ m --bazel-mode-staging com.android.adbd && mv out out.mix
12*7594170eSAndroid Build Coastguard Worker# Now we can run this script to compare `adbd` binaries between
13*7594170eSAndroid Build Coastguard Worker# two builds as follows:
14*7594170eSAndroid Build Coastguard Worker#   $ compare_elf.sh adbd out.ref out.mix
15*7594170eSAndroid Build Coastguard Worker# Note that we refer to the first of the two build directories as
16*7594170eSAndroid Build Coastguard Worker# 'reference' and to the second one as 'our'.
17*7594170eSAndroid Build Coastguard Worker#
18*7594170eSAndroid Build Coastguard Worker# There are two ways to specify the binaries to compare:
19*7594170eSAndroid Build Coastguard Worker# * compare_elf.sh REFDIR REFELF OURDIR OURELF
20*7594170eSAndroid Build Coastguard Worker#     Compare REFDIR/**/REFELF (i.e., the file in REFDIR whose path ends
21*7594170eSAndroid Build Coastguard Worker#     with REFELF in REFDIR) to OURDIR/**/OUROELF
22*7594170eSAndroid Build Coastguard Worker# * compare_elf.sh ELF REFDIR OURDIR
23*7594170eSAndroid Build Coastguard Worker#     This is a shortcut:
24*7594170eSAndroid Build Coastguard Worker#     if ELF ends with .so, the same as
25*7594170eSAndroid Build Coastguard Worker#          compare_elf.sh REFDIR ELF OURDIR ELF
26*7594170eSAndroid Build Coastguard Worker#     otherwise the same as
27*7594170eSAndroid Build Coastguard Worker#          compare_elf.sh REFDIR ELF OURDIR ELF.unstripped
28*7594170eSAndroid Build Coastguard Worker#
29*7594170eSAndroid Build Coastguard Worker# Overall, the process is as follows:
30*7594170eSAndroid Build Coastguard Worker#  * For each build, extract the list of the input objects and
31*7594170eSAndroid Build Coastguard Worker#    map each such object's unique configuration-independent key
32*7594170eSAndroid Build Coastguard Worker#  * Compare the maps. For each common key, use `elfdiff` to compare
33*7594170eSAndroid Build Coastguard Worker#    the ELF files
34*7594170eSAndroid Build Coastguard Workerfunction die() { format=$1; shift; printf "$format\n" $@; exit 1; }
35*7594170eSAndroid Build Coastguard Worker
36*7594170eSAndroid Build Coastguard Workercase $# in
37*7594170eSAndroid Build Coastguard Worker  3) declare -r refelf=$1 refdir=$2 ourdir=$3
38*7594170eSAndroid Build Coastguard Worker    [[ ${ourelf:=$refelf} =~ .so$ ]] || ourelf=$ourelf.unstripped ;;
39*7594170eSAndroid Build Coastguard Worker  4) declare -r refdir=$1 refelf=$2 ourdir=$3 ourelf=$4 ;;
40*7594170eSAndroid Build Coastguard Worker  *) die "usage:\n ${0##*/} ELF REFDIR OURDIR\nor\n ${0##*/} REFDIR REFELF OURDIR OURELF" ;;
41*7594170eSAndroid Build Coastguard Workeresac
42*7594170eSAndroid Build Coastguard Worker[[ -d $refdir ]] || die "$refdir is not a build directory"
43*7594170eSAndroid Build Coastguard Worker[[ -d $ourdir ]] || die "$outdir is not a build directory"
44*7594170eSAndroid Build Coastguard Worker
45*7594170eSAndroid Build Coastguard Workerdeclare -r elf_input_files="${0%/*}"/elf_input_files.sh
46*7594170eSAndroid Build Coastguard Worker
47*7594170eSAndroid Build Coastguard Worker# Outputs the script that initialize an associative array with
48*7594170eSAndroid Build Coastguard Worker# given name that maps object keys to their paths inside the tree.
49*7594170eSAndroid Build Coastguard Worker# Ignore prebuilts and .so files.
50*7594170eSAndroid Build Coastguard Worker# Normalize library names as in Bazel they sometimes start with
51*7594170eSAndroid Build Coastguard Worker# `liblib` instead of `lib` and may end with `_bp2build_library_static`
52*7594170eSAndroid Build Coastguard Worker# It's a rather ugly sed script.
53*7594170eSAndroid Build Coastguard Worker# Anyways, the output script looks like this:
54*7594170eSAndroid Build Coastguard Worker#  declare -A <name>=(
55*7594170eSAndroid Build Coastguard Worker#     ["libfoo.a(abc.o)"]="<path>/libfoo(abc.o)"
56*7594170eSAndroid Build Coastguard Worker#     ....
57*7594170eSAndroid Build Coastguard Worker#  )
58*7594170eSAndroid Build Coastguard Workerfunction objects_map() {
59*7594170eSAndroid Build Coastguard Worker    local -r name=$1 out_dir=$2 prefix="${3:-}"
60*7594170eSAndroid Build Coastguard Worker    grep -v -e '^prebuilts/' -e '\.so$' | sed -nr \
61*7594170eSAndroid Build Coastguard Worker     -e "1ideclare -A $name=(" \
62*7594170eSAndroid Build Coastguard Worker     -e "s|^|$prefix|" \
63*7594170eSAndroid Build Coastguard Worker     -e "s|^out/|$out_dir/|" \
64*7594170eSAndroid Build Coastguard Worker     -e '/_bp2build_cc_library_static\.a/s|(.*)/(lib)?(lib[^/]*)(_bp2build_cc_library_static\.a)\((.+)\)$|["\3.a(\5)"]="\1/\2\3\4(\5)"|p' \
65*7594170eSAndroid Build Coastguard Worker     -e '/_bp2build_cc_library_static\.a/!s|(.*)/(lib)?(lib[^/]*)\((.+)\)$|["\3(\4)"]="\1/\2\3(\4)"|p' \
66*7594170eSAndroid Build Coastguard Worker     -e 's|(.*)/([^/]*\.s?o)$|["\2"]="\1/\2"|p' \
67*7594170eSAndroid Build Coastguard Worker     -e '$i)'
68*7594170eSAndroid Build Coastguard Worker}
69*7594170eSAndroid Build Coastguard Worker
70*7594170eSAndroid Build Coastguard Workerdeclare -r reffiles=$(mktemp --suffix=.ref) ourfiles=$(mktemp --suffix=.our)
71*7594170eSAndroid Build Coastguard Workerdeclare -r comparator=$(mktemp /tmp/elfdiff.XXXXXX)
72*7594170eSAndroid Build Coastguard Workertrap 'rm -f $ourfiles $reffiles $comparator' EXIT
73*7594170eSAndroid Build Coastguard Worker
74*7594170eSAndroid Build Coastguard Worker# Initialize `ref_objects` to be objects map for ref build
75*7594170eSAndroid Build Coastguard Worker"$elf_input_files" $refelf $refdir >$reffiles || exit 1
76*7594170eSAndroid Build Coastguard Worker. <(objects_map ref_objects $refdir <$reffiles )
77*7594170eSAndroid Build Coastguard Worker
78*7594170eSAndroid Build Coastguard Worker# Initialize `our_objects` to be objects map for our build
79*7594170eSAndroid Build Coastguard Worker"$elf_input_files" $ourelf $ourdir >$ourfiles || exit 1
80*7594170eSAndroid Build Coastguard Workerdeclare -r bazel_prefix=out/bazel/output/execroot/__main__/
81*7594170eSAndroid Build Coastguard Worker. <(objects_map our_objects $ourdir $bazel_prefix <$ourfiles )
82*7594170eSAndroid Build Coastguard Worker
83*7594170eSAndroid Build Coastguard Worker# Minor re-keying fo `our_objects` (e.g., Soong's `main.o` is
84*7594170eSAndroid Build Coastguard Worker# Bazel's libadbd__internal_root.lo(main.o)
85*7594170eSAndroid Build Coastguard Workerdeclare -Ar equivalences=(
86*7594170eSAndroid Build Coastguard Worker    ["libadbd__internal_root.lo(main.o)"]="main.o"
87*7594170eSAndroid Build Coastguard Worker    ["libadbd__internal_root.lo(libbuildversion.o)"]="libbuildversion.a(libbuildversion.o)"
88*7594170eSAndroid Build Coastguard Worker    ["crtend.o"]="crtend_android.o")
89*7594170eSAndroid Build Coastguard Workerfor k in "${!equivalences[@]}"; do
90*7594170eSAndroid Build Coastguard Worker    if [[ -v "our_objects[$k]" ]]; then
91*7594170eSAndroid Build Coastguard Worker        our_objects["${equivalences[$k]}"]="${our_objects[$k]}"
92*7594170eSAndroid Build Coastguard Worker        unset "our_objects[$k]"
93*7594170eSAndroid Build Coastguard Worker    fi
94*7594170eSAndroid Build Coastguard Workerdone
95*7594170eSAndroid Build Coastguard Worker
96*7594170eSAndroid Build Coastguard Workerdeclare -a missing extra common
97*7594170eSAndroid Build Coastguard Worker# Compare the keys from `ref_objects` and `our_objects` and output the script
98*7594170eSAndroid Build Coastguard Worker# to initialize `missing`, `extra` and `common` arrays to resp. only in
99*7594170eSAndroid Build Coastguard Worker# `ref_objects`, only in `sour_objects`, and common
100*7594170eSAndroid Build Coastguard Workerfunction classify() {
101*7594170eSAndroid Build Coastguard Worker  comm <(printf "%s\n" "${!ref_objects[@]}" | sort) <(printf "%s\n" "${!our_objects[@]}" | sort) \
102*7594170eSAndroid Build Coastguard Worker    | sed -nr '/^\t\t/{s|^\t\t(.*)|common+=("\1")|p;d};/^\t/{s|^\t(.*)|extra+=("\1")|p;d};s|(.*)|missing+=("\1")|p'
103*7594170eSAndroid Build Coastguard Worker}
104*7594170eSAndroid Build Coastguard Worker
105*7594170eSAndroid Build Coastguard Worker. <(classify)
106*7594170eSAndroid Build Coastguard Workerif [[ -v missing ]]; then
107*7594170eSAndroid Build Coastguard Worker    printf "The following input object files are missing:\n"
108*7594170eSAndroid Build Coastguard Worker    for o in "${missing[@]}"; do
109*7594170eSAndroid Build Coastguard Worker        printf "  %s\n" "${ref_objects[$o]}"
110*7594170eSAndroid Build Coastguard Worker    done
111*7594170eSAndroid Build Coastguard Workerfi
112*7594170eSAndroid Build Coastguard Worker
113*7594170eSAndroid Build Coastguard Workerif [[ -v extra ]]; then
114*7594170eSAndroid Build Coastguard Worker    printf "The following input object files are extra:\n"
115*7594170eSAndroid Build Coastguard Worker    for o in "${extra[@]}"; do
116*7594170eSAndroid Build Coastguard Worker        printf "  %s\n" "${our_objects[$o]}"
117*7594170eSAndroid Build Coastguard Worker    done
118*7594170eSAndroid Build Coastguard Workerfi
119*7594170eSAndroid Build Coastguard Worker
120*7594170eSAndroid Build Coastguard Worker# Build the ELF files comparator, it is Go binary.
121*7594170eSAndroid Build Coastguard Workerdeclare -r elfdiff=android/bazel/mkcompare/elfdiff/...
122*7594170eSAndroid Build Coastguard WorkerGOWORK=$PWD/build/bazel/mkcompare/go.work go build -o $comparator $elfdiff || exit 1
123*7594170eSAndroid Build Coastguard Worker
124*7594170eSAndroid Build Coastguard Worker# Output ELF file pairs to compare and feed them the parallel executor.
125*7594170eSAndroid Build Coastguard Workerfor o in "${common[@]}"; do echo "${ref_objects[$o]} ${our_objects[$o]}"; done |\
126*7594170eSAndroid Build Coastguard Worker  parallel --colsep ' ' $comparator {1} {2}
127