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