1#!/usr/bin/env bash 2# SPDX-License-Identifier: BSD-3-Clause 3# Copyright (C) 2019 Intel Corporation 4# All rights reserved. 5# 6shopt -s extglob 7git_repo_abi="https://github.com/spdk/spdk-abi.git" 8 9function get_spdk_abi() { 10 local dest=$1 11 mkdir -p $dest 12 if [[ -d $SPDK_ABI_DIR ]]; then 13 echo "spdk-abi found at $SPDK_ABI_DIR" 14 cp -r "$SPDK_ABI_DIR"/* "$dest/" 15 else 16 # In case that someone run test manually and did not set existing 17 # spdk-abi directory via SPDK_ABI_DIR 18 echo "spdk-abi has not been found${SPDK_ABI_DIR:+ at $SPDK_ABI_DIR}, cloning" 19 git clone $git_repo_abi "$dest" 20 fi 21} 22 23function get_release_branch() { 24 tag=$(git describe --tags --abbrev=0 --exclude=LTS --exclude=*-pre $1) 25 branch="${tag:0:6}.x" 26 echo "$branch" 27} 28 29if [ "$(uname -s)" = "FreeBSD" ]; then 30 echo "Not testing for shared object dependencies on FreeBSD." 31 exit 0 32fi 33 34rootdir=$(readlink -f $(dirname $0)/../..) 35 36if [[ ! -f $1 ]]; then 37 echo "ERROR: SPDK test configuration not specified" 38 exit 1 39fi 40 41source $1 42source "$rootdir/test/common/autotest_common.sh" 43 44libdir="$rootdir/build/lib" 45libdeps_file="$rootdir/mk/spdk.lib_deps.mk" 46suppression_file="$HOME/abigail_suppressions.ini" 47 48function check_header_filenames() { 49 local dups_found=0 50 51 xtrace_disable 52 53 include_headers=$(git ls-files -- $rootdir/include/spdk $rootdir/include/spdk_internal | xargs -n 1 basename) 54 dups= 55 for file in $include_headers; do 56 if [[ $(git ls-files "$rootdir/lib/**/$file" "$rootdir/module/**/$file" --error-unmatch 2> /dev/null) ]]; then 57 dups+=" $file" 58 dups_found=1 59 fi 60 done 61 62 xtrace_restore 63 64 if ((dups_found == 1)); then 65 echo "Private header file(s) found with same name as public header file." 66 echo "This is not allowed since it can confuse abidiff when determining if" 67 echo "data structure changes impact ABI." 68 echo $dups 69 return 1 70 fi 71} 72 73function confirm_abi_deps() { 74 local processed_so=0 75 local abidiff_output 76 local release 77 local source_abi_dir="$rootdir/test/make/abi" 78 79 release=$(get_release_branch) 80 81 get_spdk_abi "$source_abi_dir" 82 echo "* Running ${FUNCNAME[0]} against the latest (${release%.*}) release" >&2 83 84 if ! hash abidiff; then 85 echo "Unable to check ABI compatibility. Please install abidiff." 86 return 1 87 fi 88 89 cat << EOF > ${suppression_file} 90[suppress_type] 91 name = spdk_nvme_power_state 92[suppress_type] 93 name = spdk_nvme_ctrlr_data 94[suppress_type] 95 name = spdk_nvme_cdata_oacs 96[suppress_type] 97 name = spdk_nvme_cdata_nvmf_specific 98[suppress_type] 99 name = spdk_nvme_cmd 100[suppress_type] 101 name = spdk_bs_opts 102[suppress_type] 103 name = spdk_app_opts 104# To be removed, comes from nvme_internal.h 105[suppress_type] 106 name = spdk_nvme_qpair 107# The 4 types below are related to changes in __bdev_io_internal_fields 108[suppress_type] 109 name = spdk_ftl_dev 110[suppress_type] 111 name = ftl_io 112[suppress_type] 113 name = __bdev_io_internal_fields 114 soname_regexp = libspdk_ftl\\.so\\.* 115[suppress_type] 116 name = spdk_bdev_io 117 soname_regexp = libspdk_ftl\\.so\\.* 118EOF 119 120 for object in "$libdir"/libspdk_*.so; do 121 abidiff_output=0 122 123 so_file=$(basename $object) 124 if [ ! -f "$source_abi_dir/$release/$so_file" ]; then 125 echo "No corresponding object for $so_file in canonical directory. Skipping." 126 continue 127 fi 128 129 cmd_args=('abidiff' 130 $source_abi_dir/$release/$so_file "$libdir/$so_file" 131 '--leaf-changes-only' '--suppressions' $suppression_file) 132 133 if ! output=$("${cmd_args[@]}" --stat); then 134 # remove any filtered out variables. 135 output=$(sed "s/ [()][^)]*[)]//g" <<< "$output") 136 137 IFS="." read -r _ _ new_so_maj new_so_min < <(readlink "$libdir/$so_file") 138 IFS="." read -r _ _ old_so_maj old_so_min < <(readlink "$source_abi_dir/$release/$so_file") 139 140 found_abi_change=false 141 so_name_changed=no 142 143 if [[ $output == *"ELF SONAME changed"* ]]; then 144 so_name_changed=yes 145 fi 146 147 changed_leaf_types=0 148 if [[ $output =~ "leaf types summary: "([0-9]+) ]]; then 149 changed_leaf_types=${BASH_REMATCH[1]} 150 fi 151 152 removed_functions=0 changed_functions=0 added_functions=0 153 if [[ $output =~ "functions summary: "([0-9]+)" Removed, "([0-9]+)" Changed, "([0-9]+)" Added" ]]; then 154 removed_functions=${BASH_REMATCH[1]} changed_functions=${BASH_REMATCH[2]} added_functions=${BASH_REMATCH[3]} 155 fi 156 157 removed_vars=0 changed_vars=0 added_vars=0 158 if [[ $output =~ "variables summary: "([0-9]+)" Removed, "([0-9]+)" Changed, "([0-9]+)" Added" ]]; then 159 removed_vars=${BASH_REMATCH[1]} changed_vars=${BASH_REMATCH[2]} added_vars=${BASH_REMATCH[3]} 160 fi 161 162 if ((changed_leaf_types != 0)); then 163 if ((new_so_maj == old_so_maj)); then 164 abidiff_output=1 165 touch $fail_file 166 echo "Please update the major SO version for $so_file. A header accessible type has been modified since last release." 167 fi 168 found_abi_change=true 169 fi 170 171 if ((removed_functions != 0)) || ((removed_vars != 0)); then 172 if ((new_so_maj == old_so_maj)); then 173 abidiff_output=1 174 touch $fail_file 175 echo "Please update the major SO version for $so_file. API functions or variables have been removed since last release." 176 fi 177 found_abi_change=true 178 fi 179 180 if ((changed_functions != 0)) || ((changed_vars != 0)); then 181 if ((new_so_maj == old_so_maj)); then 182 abidiff_output=1 183 touch $fail_file 184 echo "Please update the major SO version for $so_file. API functions or variables have been changed since last release." 185 fi 186 found_abi_change=true 187 fi 188 189 if ((added_functions != 0)) || ((added_vars != 0)); then 190 if ((new_so_min == old_so_min && new_so_maj == old_so_maj)) && ! $found_abi_change; then 191 abidiff_output=1 192 touch $fail_file 193 echo "Please update the minor SO version for $so_file. API functions or variables have been added since last release." 194 fi 195 found_abi_change=true 196 fi 197 198 if [[ $so_name_changed == yes ]]; then 199 # All SO major versions are intentionally increased after LTS to allow SO minor changes during the supported period. 200 if [[ "$release" == "$(get_release_branch LTS)" ]]; then 201 found_abi_change=true 202 fi 203 if ! $found_abi_change; then 204 echo "SO name for $so_file changed without a change to abi. please revert that change." 205 touch $fail_file 206 fi 207 208 if ((new_so_maj != old_so_maj && new_so_min != 0)); then 209 echo "SO major version for $so_file was bumped. Please reset the minor version to 0." 210 touch $fail_file 211 fi 212 213 if ((new_so_min > old_so_min + 1)); then 214 echo "SO minor version for $so_file was incremented more than once. Please revert minor version to $((old_so_min + 1))." 215 touch $fail_file 216 fi 217 218 if ((new_so_maj > old_so_maj + 1)); then 219 echo "SO major version for $so_file was incremented more than once. Please revert major version to $((old_so_maj + 1))." 220 touch $fail_file 221 fi 222 fi 223 224 if ((abidiff_output == 1)); then 225 "${cmd_args[@]}" --impacted-interfaces || : 226 fi 227 fi 228 processed_so=$((processed_so + 1)) 229 done 230 rm -f $suppression_file 231 echo "Processed $processed_so objects." 232 rm -rf "$source_abi_dir" 233} 234 235function get_lib_shortname() { 236 local lib=${1##*/} 237 echo "${lib//@(libspdk_|.so)/}" 238} 239 240function import_libs_deps_mk() { 241 local var_mk val_mk dep_mk fun_mk 242 while read -r var_mk _ val_mk; do 243 if [[ $var_mk == "#"* || ! $var_mk =~ (DEPDIRS-|_DEPS|_LIBS) ]]; then 244 continue 245 fi 246 var_mk=${var_mk#*-} 247 for dep_mk in $val_mk; do 248 fun_mk=${dep_mk//@('$('|')')/} 249 if [[ $fun_mk != "$dep_mk" ]]; then 250 eval "${fun_mk}() { echo \$$fun_mk ; }" 251 # Ignore any event_* dependencies. Those are based on the subsystem configuration and not readelf. 252 elif ((IGNORED_LIBS["$dep_mk"] == 1)) || [[ $dep_mk =~ event_ ]]; then 253 continue 254 fi 255 eval "$var_mk=\${$var_mk:+\$$var_mk }$dep_mk" 256 done 257 done < "$libdeps_file" 258} 259 260function confirm_deps() { 261 local lib=$1 deplib lib_shortname 262 263 lib_shortname=$(get_lib_shortname "$lib") 264 lib_make_deps=(${!lib_shortname}) 265 266 symbols=($(readelf -s --wide "$lib" | grep -E "NOTYPE.*GLOBAL.*UND" | awk '{print $8}' | sort -u)) 267 symbols_regx=$( 268 IFS="|" 269 echo "(${symbols[*]})" 270 ) 271 272 if ((${#symbols[@]} > 0)); then 273 for deplib in "$libdir/"libspdk_!("$lib_shortname").so; do 274 readelf -s --wide "$deplib" | grep -m1 -qE "DEFAULT\s+[0-9]+\s$symbols_regx$" || continue 275 found_symbol_lib=$(get_lib_shortname "$deplib") 276 # Ignore the env_dpdk readelf dependency. We don't want people explicitly linking against it. 277 if [[ $found_symbol_lib != *env_dpdk* ]]; then 278 dep_names+=("$found_symbol_lib") 279 fi 280 done 281 fi 282 283 diff=$(echo "${dep_names[@]}" "${lib_make_deps[@]}" | tr ' ' '\n' | sort | uniq -u) 284 if [ "$diff" != "" ]; then 285 touch $fail_file 286 echo "there was a dependency mismatch in the library $lib_shortname" 287 echo "The makefile (spdk.lib_deps.mk) lists: '${lib_make_deps[*]}'" 288 echo "readelf outputs : '${dep_names[*]}'" 289 echo "---------------------------------------------------------------------" 290 fi 291} 292 293function confirm_makefile_deps() { 294 echo "---------------------------------------------------------------------" 295 # Exclude libspdk_env_dpdk.so from the library list. We don't link against this one so that 296 # users can define their own environment abstraction. However we do want to still check it 297 # for dependencies to avoid printing out a bunch of confusing symbols under the missing 298 # symbols section. 299 SPDK_LIBS=("$libdir/"libspdk_!(env_dpdk).so) 300 301 declare -A IGNORED_LIBS=() 302 if grep -q 'CONFIG_RDMA?=n' $rootdir/mk/config.mk; then 303 IGNORED_LIBS["rdma"]=1 304 fi 305 306 ( 307 import_libs_deps_mk 308 for lib in "${SPDK_LIBS[@]}"; do confirm_deps "$lib" & done 309 wait 310 ) 311} 312 313run_test "check_header_filenames" check_header_filenames 314 315config_params=$(get_config_params) 316if [ "$SPDK_TEST_OCF" -eq 1 ]; then 317 config_params="$config_params --with-ocf=$rootdir/ocf.a" 318fi 319 320if [[ -f $rootdir/mk/config.mk ]]; then 321 $MAKE $MAKEFLAGS clean 322fi 323 324$rootdir/configure $config_params --with-shared 325# By setting SPDK_NO_LIB_DEPS=1, we ensure that we won't create any link dependencies. 326# Then we can be sure we get a valid accounting of the symbol dependencies we have. 327SPDK_NO_LIB_DEPS=1 $MAKE $MAKEFLAGS 328 329xtrace_disable 330 331fail_file=$output_dir/check_so_deps_fail 332 333rm -f $fail_file 334 335run_test "confirm_abi_deps" confirm_abi_deps 336 337run_test "confirm_makefile_deps" confirm_makefile_deps 338 339$MAKE $MAKEFLAGS clean 340 341if [ -f $fail_file ]; then 342 rm -f $fail_file 343 echo "shared object test failed" 344 exit 1 345fi 346 347xtrace_restore 348