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