1#!/usr/bin/env bash 2shopt -s extglob 3 4if [ "$(uname -s)" = "FreeBSD" ]; then 5 echo "Not testing for shared object dependencies on FreeBSD." 6 exit 0 7fi 8 9rootdir=$(readlink -f $(dirname $0)/../..) 10 11if [[ ! -f $1 ]]; then 12 echo "ERROR: SPDK test configuration not specified" 13 exit 1 14fi 15 16source $1 17source "$rootdir/test/common/autotest_common.sh" 18 19libdir="$rootdir/build/lib" 20libdeps_file="$rootdir/mk/spdk.lib_deps.mk" 21source_abi_dir="$HOME/spdk_abi_latest/build/lib" 22suppression_file="$HOME/abigail_suppressions.ini" 23 24function confirm_abi_deps() { 25 local processed_so=0 26 local abidiff_output 27 28 if ! hash abidiff; then 29 echo "Unable to check ABI compatibility. Please install abidiff." 30 return 1 31 fi 32 33 if [ ! -d $source_abi_dir ]; then 34 echo "No source ABI available, failing this test." 35 return 1 36 fi 37 38 cat << EOF > ${suppression_file} 39[suppress_type] 40 name = spdk_nvme_ctrlr_data 41[suppress_type] 42 name = spdk_nvme_ns_data 43[suppress_type] 44 name = spdk_nvme_log_page 45[suppress_type] 46 name = spdk_nvme_ctrlr_opts 47[suppress_type] 48 name = spdk_bs_dev 49EOF 50 51 for object in "$libdir"/libspdk_*.so; do 52 abidiff_output=0 53 54 so_file=$(basename $object) 55 if [ ! -f "$source_abi_dir/$so_file" ]; then 56 echo "No corresponding object for $so_file in canonical directory. Skipping." 57 continue 58 fi 59 60 cmd_args=('abidiff' 61 $source_abi_dir/$so_file $libdir/$so_file 62 '--headers-dir1' $source_abi_dir/../../include 63 '--headers-dir2' $rootdir/include 64 '--leaf-changes-only' '--suppressions' $suppression_file) 65 66 if ! output=$("${cmd_args[@]}" --stat); then 67 # remove any filtered out variables. 68 output=$(sed "s/ [()][^)]*[)]//g" <<< "$output") 69 70 IFS="." read -r _ _ new_so_maj new_so_min < <(readlink "$libdir/$so_file") 71 IFS="." read -r _ _ old_so_maj old_so_min < <(readlink "$source_abi_dir/$so_file") 72 73 found_abi_change=false 74 so_name_changed=no 75 76 if [[ $output == *"ELF SONAME changed"* ]]; then 77 so_name_changed=yes 78 fi 79 80 changed_leaf_types=0 81 if [[ $output =~ "leaf types summary: "([0-9]+) ]]; then 82 changed_leaf_types=${BASH_REMATCH[1]} 83 fi 84 85 removed_functions=0 changed_functions=0 added_functions=0 86 if [[ $output =~ "functions summary: "([0-9]+)" Removed, "([0-9]+)" Changed, "([0-9]+)" Added" ]]; then 87 removed_functions=${BASH_REMATCH[1]} changed_functions=${BASH_REMATCH[2]} added_functions=${BASH_REMATCH[3]} 88 fi 89 90 removed_vars=0 changed_vars=0 added_vars=0 91 if [[ $output =~ "variables summary: "([0-9]+)" Removed, "([0-9]+)" Changed, "([0-9]+)" Added" ]]; then 92 removed_vars=${BASH_REMATCH[1]} changed_vars=${BASH_REMATCH[2]} added_vars=${BASH_REMATCH[3]} 93 fi 94 95 if ((changed_leaf_types != 0)); then 96 if ((new_so_maj == old_so_maj)); then 97 abidiff_output=1 98 touch $fail_file 99 echo "Please update the major SO version for $so_file. A header accessible type has been modified since last release." 100 fi 101 found_abi_change=true 102 fi 103 104 if ((removed_functions != 0)) || ((removed_vars != 0)); then 105 if ((new_so_maj == old_so_maj)); then 106 abidiff_output=1 107 touch $fail_file 108 echo "Please update the major SO version for $so_file. API functions or variables have been removed since last release." 109 fi 110 found_abi_change=true 111 fi 112 113 if ((changed_functions != 0)) || ((changed_vars != 0)); then 114 if ((new_so_maj == old_so_maj)); then 115 abidiff_output=1 116 touch $fail_file 117 echo "Please update the major SO version for $so_file. API functions or variables have been changed since last release." 118 fi 119 found_abi_change=true 120 fi 121 122 if ((added_functions != 0)) || ((added_vars != 0)); then 123 if ((new_so_min == old_so_min && new_so_maj == old_so_maj)) && ! $found_abi_change; then 124 abidiff_output=1 125 touch $fail_file 126 echo "Please update the minor SO version for $so_file. API functions or variables have been added since last release." 127 fi 128 found_abi_change=true 129 fi 130 131 if [[ $so_name_changed == yes ]]; then 132 if ! $found_abi_change; then 133 # Unfortunately, libspdk_idxd made it into 20.04 without an SO suffix. TODO:: remove after 20.07 134 if [ "$so_file" != "libspdk_idxd.so" ] && [ "$so_file" != "libspdk_accel_idxd.so" ]; then 135 echo "SO name for $so_file changed without a change to abi. please revert that change." 136 touch $fail_file 137 fi 138 fi 139 140 if ((new_so_maj != old_so_maj && new_so_min != 0)); then 141 echo "SO major version for $so_file was bumped. Please reset the minor version to 0." 142 touch $fail_file 143 fi 144 145 if ((new_so_min > old_so_min + 1)); then 146 echo "SO minor version for $so_file was incremented more than once. Please revert minor version to $((old_so_min + 1))." 147 touch $fail_file 148 fi 149 150 if ((new_so_maj > old_so_maj + 1)); then 151 echo "SO major version for $so_file was incremented more than once. Please revert major version to $((old_so_maj + 1))." 152 touch $fail_file 153 fi 154 fi 155 156 if ((abidiff_output == 1)); then 157 "${cmd_args[@]}" --impacted-interfaces 158 fi 159 160 continue 161 fi 162 processed_so=$((processed_so + 1)) 163 done 164 rm -f $suppression_file 165 echo "Processed $processed_so objects." 166} 167 168function get_lib_shortname() { 169 local lib=${1##*/} 170 echo "${lib//@(libspdk_|.so)/}" 171} 172 173function import_libs_deps_mk() { 174 local var_mk val_mk dep_mk fun_mk 175 while read -r var_mk _ val_mk; do 176 if [[ $var_mk == "#"* || ! $var_mk =~ (DEPDIRS-|_DEPS|_LIBS) ]]; then 177 continue 178 fi 179 var_mk=${var_mk#*-} 180 for dep_mk in $val_mk; do 181 fun_mk=${dep_mk//@('$('|')')/} 182 if [[ $fun_mk != "$dep_mk" ]]; then 183 eval "${fun_mk}() { echo \$$fun_mk ; }" 184 # Ignore any event_* dependencies. Those are based on the subsystem configuration and not readelf. 185 elif ((IGNORED_LIBS["$dep_mk"] == 1)) || [[ $dep_mk =~ event_ ]]; then 186 continue 187 fi 188 eval "$var_mk=\${$var_mk:+\$$var_mk }$dep_mk" 189 done 190 done < "$libdeps_file" 191} 192 193function confirm_deps() { 194 local lib=$1 deplib lib_shortname 195 196 lib_shortname=$(get_lib_shortname "$lib") 197 lib_make_deps=(${!lib_shortname}) 198 199 symbols=($(readelf -s --wide "$lib" | grep -E "NOTYPE.*GLOBAL.*UND" | awk '{print $8}' | sort -u)) 200 symbols_regx=$( 201 IFS="|" 202 echo "(${symbols[*]})" 203 ) 204 205 if ((${#symbols[@]} > 0)); then 206 for deplib in "$libdir/"libspdk_!("$lib_shortname").so; do 207 readelf -s --wide "$deplib" | grep -m1 -qE "DEFAULT\s+[0-9]+\s$symbols_regx$" || continue 208 found_symbol_lib=$(get_lib_shortname "$deplib") 209 # Ignore the env_dpdk readelf dependency. We don't want people explicitly linking against it. 210 if [[ $found_symbol_lib != *env_dpdk* ]]; then 211 dep_names+=("$found_symbol_lib") 212 fi 213 done 214 fi 215 216 diff=$(echo "${dep_names[@]}" "${lib_make_deps[@]}" | tr ' ' '\n' | sort | uniq -u) 217 if [ "$diff" != "" ]; then 218 touch $fail_file 219 echo "there was a dependency mismatch in the library $lib_shortname" 220 echo "The makefile lists: '${lib_make_deps[*]}'" 221 echo "readelf outputs : '${dep_names[*]}'" 222 echo "---------------------------------------------------------------------" 223 fi 224} 225 226source ~/autorun-spdk.conf 227config_params=$(get_config_params) 228if [ "$SPDK_TEST_OCF" -eq 1 ]; then 229 config_params="$config_params --with-ocf=$rootdir/build/ocf.a" 230fi 231 232$MAKE $MAKEFLAGS clean 233./configure $config_params --with-shared 234# By setting SPDK_NO_LIB_DEPS=1, we ensure that we won't create any link dependencies. 235# Then we can be sure we get a valid accounting of the symbol dependencies we have. 236SPDK_NO_LIB_DEPS=1 $MAKE $MAKEFLAGS 237 238xtrace_disable 239 240fail_file=$output_dir/check_so_deps_fail 241 242rm -f $fail_file 243 244run_test "confirm_abi_deps" confirm_abi_deps 245 246echo "---------------------------------------------------------------------" 247# Exclude libspdk_env_dpdk.so from the library list. We don't link against this one so that 248# users can define their own environment abstraction. However we do want to still check it 249# for dependencies to avoid printing out a bunch of confusing symbols under the missing 250# symbols section. 251SPDK_LIBS=("$libdir/"libspdk_!(env_dpdk).so) 252 253declare -A IGNORED_LIBS=() 254if grep -q 'CONFIG_RDMA?=n' $rootdir/mk/config.mk; then 255 IGNORED_LIBS["rdma"]=1 256fi 257 258( 259 import_libs_deps_mk 260 for lib in "${SPDK_LIBS[@]}"; do confirm_deps "$lib" & done 261 wait 262) 263 264$MAKE $MAKEFLAGS clean 265 266if [ -f $fail_file ]; then 267 rm -f $fail_file 268 echo "shared object test failed" 269 exit 1 270fi 271 272xtrace_restore 273