1#!/usr/bin/env bash 2 3if [ "$(uname -s)" = "FreeBSD" ]; then 4 echo "Not testing for shared object dependencies on FreeBSD." 5 exit 0 6fi 7 8rootdir=$(readlink -f $(dirname $0)/../..) 9 10if [[ ! -f $1 ]]; then 11 echo "ERROR: SPDK test configuration not specified" 12 exit 1 13fi 14 15source $1 16source "$rootdir/test/common/autotest_common.sh" 17 18libdir="$rootdir/build/lib" 19libdeps_file="$rootdir/mk/spdk.lib_deps.mk" 20source_abi_dir="$HOME/spdk_20_04/build/lib" 21suppression_file="$HOME/abigail_suppressions.ini" 22 23function confirm_abi_deps() { 24 local processed_so=0 25 26 if ! hash abidiff; then 27 echo "Unable to check ABI compatibility. Please install abidiff." 28 return 1 29 fi 30 31 if [ ! -d $source_abi_dir ]; then 32 echo "No source ABI available, failing this test." 33 return 1 34 fi 35 36 cat << EOF > ${suppression_file} 37[suppress_variable] 38 name = SPDK_LOG_IDXD 39[suppress_variable] 40 name = SPDK_LOG_IOAT 41[suppress_variable] 42 name = SPDK_LOG_JSON_UTIL 43[suppress_variable] 44 name = SPDK_LOG_RPC 45[suppress_variable] 46 name = SPDK_LOG_RPC_CLIENT 47[suppress_function] 48 name = spdk_jsonrpc_server_handle_request 49[suppress_function] 50 name = spdk_jsonrpc_server_handle_error 51[suppress_function] 52 name = spdk_jsonrpc_server_send_response 53[suppress_function] 54 name = spdk_jsonrpc_parse_request 55[suppress_function] 56 name = spdk_jsonrpc_free_request 57[suppress_function] 58 name = spdk_jsonrpc_parse_response 59[suppress_variable] 60 name = SPDK_LOG_LOG_RPC 61[suppress_variable] 62 name = SPDK_LOG_LOG 63[suppress_variable] 64 name = SPDK_LOG_LVOL 65[suppress_variable] 66 name = SPDK_LOG_NBD 67[suppress_function] 68 name = spdk_nbd_disk_find_by_nbd_path 69[suppress_function] 70 name = spdk_nbd_disk_first 71[suppress_function] 72 name = spdk_nbd_disk_next 73[suppress_function] 74 name = spdk_nbd_disk_get_nbd_path 75[suppress_function] 76 name = spdk_nbd_disk_get_bdev_name 77[suppress_variable] 78 name = SPDK_LOG_NET 79[suppress_function] 80 name = spdk_interface_net_interface_add_ip_address 81[suppress_function] 82 name = spdk_interface_net_interface_delete_ip_address 83[suppress_function] 84 name = spdk_interface_get_list 85[suppress_function] 86 name = spdk_get_uevent 87[suppress_function] 88 name = spdk_uevent_connect 89[suppress_function] 90 name = spdk_nvme_ctrlr_get_current_process 91[suppress_function] 92 name = spdk_nvme_ctrlr_get_process 93[suppress_function] 94 name = spdk_nvme_get_ctrlr_by_trid_unsafe 95[suppress_function] 96 name = spdk_nvme_io_msg_process 97[suppress_function] 98 name = spdk_nvme_wait_for_completion 99[suppress_function] 100 name = spdk_nvme_wait_for_completion_robust_lock 101[suppress_function] 102 name = spdk_nvme_wait_for_completion_timeout 103[suppress_variable] 104 name = SPDK_LOG_NVME 105[suppress_variable] 106 name = SPDK_LOG_OPAL 107[suppress_variable] 108 name = spdk_opal_method 109[suppress_variable] 110 name = spdk_opal_uid 111[suppress_variable] 112 name = SPDK_LOG_REDUCE 113[suppress_variable] 114 name = SPDK_LOG_THREAD 115[suppress_variable] 116 name = SPDK_LOG_TRACE 117[suppress_function] 118 name = spdk_crc32_table_init 119[suppress_function] 120 name = spdk_crc32_update 121[suppress_variable] 122 name = SPDK_LOG_VIRTIO_DEV 123[suppress_variable] 124 name = SPDK_LOG_VIRTIO_PCI 125[suppress_variable] 126 name = SPDK_LOG_VIRTIO_USER 127[suppress_variable] 128 name = SPDK_LOG_VMD 129[suppress_variable] 130 name = SPDK_LOG_ACCEL_IDXD 131[suppress_variable] 132 name = SPDK_LOG_ACCEL_IOAT 133[suppress_variable] 134 name = SPDK_LOG_AIO 135[suppress_variable] 136 name = SPDK_LOG_VBDEV_COMPRESS 137[suppress_variable] 138 name = SPDK_LOG_CRYPTO 139[suppress_variable] 140 name = SPDK_LOG_VBDEV_DELAY 141[suppress_function] 142 name = spdk_vbdev_error_create 143[suppress_function] 144 name = spdk_vbdev_error_delete 145[suppress_function] 146 name = spdk_vbdev_error_inject_error 147[suppress_variable] 148 name = SPDK_LOG_BDEV_FTL 149[suppress_variable] 150 name = SPDK_LOG_GPT_PARSE 151[suppress_variable] 152 name = SPDK_LOG_VBDEV_GPT 153[suppress_function] 154 name = spdk_gpt_parse_mbr 155[suppress_function] 156 name = spdk_gpt_parse_partition_table 157[suppress_variable] 158 name = SPDK_LOG_ISCSI_INIT 159[suppress_variable] 160 name = SPDK_LOG_LVOL_RPC 161[suppress_variable] 162 name = SPDK_LOG_VBDEV_LVOL 163[suppress_variable] 164 name = SPDK_LOG_BDEV_MALLOC 165[suppress_variable] 166 name = SPDK_LOG_BDEV_NULL 167[suppress_variable] 168 name = SPDK_LOG_BDEV_NVME 169[suppress_function] 170 name = spdk_bdev_nvme_create 171[suppress_function] 172 name = spdk_bdev_nvme_delete 173[suppress_function] 174 name = spdk_bdev_nvme_get_ctrlr 175[suppress_function] 176 name = spdk_bdev_nvme_get_io_qpair 177[suppress_function] 178 name = spdk_bdev_nvme_get_opts 179[suppress_function] 180 name = spdk_bdev_nvme_set_hotplug 181[suppress_function] 182 name = spdk_bdev_nvme_set_opts 183[suppress_function] 184 name = spdk_vbdev_opal_create 185[suppress_function] 186 name = spdk_vbdev_opal_destruct 187[suppress_function] 188 name = spdk_vbdev_opal_enable_new_user 189[suppress_function] 190 name = spdk_vbdev_opal_get_info_from_bdev 191[suppress_function] 192 name = spdk_vbdev_opal_set_lock_state 193[suppress_variable] 194 name = SPDK_LOG_BDEV_OCSSD 195[suppress_variable] 196 name = SPDK_LOG_VBDEV_OPAL 197[suppress_variable] 198 name = SPDK_LOG_OCFCTX 199[suppress_variable] 200 name = SPDK_LOG_VBDEV_PASSTHRU 201[suppress_variable] 202 name = SPDK_LOG_BDEV_PMEM 203[suppress_function] 204 name = spdk_create_pmem_disk 205[suppress_function] 206 name = spdk_delete_pmem_disk 207[suppress_variable] 208 name = SPDK_LOG_BDEV_RAID 209[suppress_variable] 210 name = SPDK_LOG_BDEV_RAID0 211[suppress_variable] 212 name = SPDK_LOG_BDEV_RAID5 213[suppress_variable] 214 name = SPDK_LOG_RAID_RPC 215[suppress_variable] 216 name = SPDK_LOG_BDEV_RBD 217[suppress_function] 218 name = spdk_bdev_rbd_create 219[suppress_function] 220 name = spdk_bdev_rbd_delete 221[suppress_function] 222 name = spdk_bdev_rbd_dup_config 223[suppress_function] 224 name = spdk_bdev_rbd_free_config 225[suppress_function] 226 name = spdk_bdev_rbd_resize 227[suppress_variable] 228 name = SPDK_LOG_VBDEV_SPLIT 229[suppress_function] 230 name = spdk_vbdev_split_destruct 231[suppress_function] 232 name = spdk_vbdev_split_get_part_base 233[suppress_variable] 234 name = SPDK_LOG_URING 235[suppress_variable] 236 name = SPDK_LOG_VIRTIO 237[suppress_variable] 238 name = SPDK_LOG_VIRTIO_BLK 239[suppress_variable] 240 name = SPDK_LOG_VBDEV_ZONE_BLOCK 241[suppress_function] 242 name = spdk_vbdev_zone_block_create 243[suppress_function] 244 name = spdk_vbdev_zone_block_delete 245[suppress_variable] 246 name = SPDK_LOG_BLOBFS_BDEV 247[suppress_variable] 248 name = SPDK_LOG_BLOBFS_BDEV_RPC 249[suppress_function] 250 name = spdk_blobfs_fuse_send_request 251[suppress_function] 252 name = spdk_blobfs_fuse_start 253[suppress_function] 254 name = spdk_blobfs_fuse_stop 255[suppress_variable] 256 name = SPDK_LOG_APP_RPC 257[suppress_function] 258 name = spdk_nvmf_parse_conf 259[suppress_variable] 260 name = SPDK_LOG_VHOST 261[suppress_variable] 262 name = SPDK_LOG_VHOST_BLK 263[suppress_variable] 264 name = SPDK_LOG_VHOST_BLK_DATA 265[suppress_variable] 266 name = SPDK_LOG_VHOST_RING 267[suppress_variable] 268 name = SPDK_LOG_VHOST_RPC 269[suppress_variable] 270 name = SPDK_LOG_VHOST_SCSI 271[suppress_variable] 272 name = SPDK_LOG_VHOST_SCSI_DATA 273[suppress_variable] 274 name = SPDK_LOG_VHOST_SCSI_QUEUE 275[suppress_variable] 276 name = spdk_vhost_scsi_device_backend 277[suppress_type] 278 name = spdk_net_impl 279[suppress_type] 280 name = spdk_lvol 281EOF 282 283 for object in "$libdir"/libspdk_*.so; do 284 so_file=$(basename $object) 285 if [ ! -f "$source_abi_dir/$so_file" ]; then 286 echo "No corresponding object for $so_file in canonical directory. Skipping." 287 continue 288 fi 289 290 if ! output=$(abidiff "$source_abi_dir/$so_file" "$libdir/$so_file" --headers-dir1 "$source_abi_dir/../../include/" --headers-dir2 "$rootdir/include" --leaf-changes-only --suppressions $suppression_file --stat); then 291 # remove any filtered out variables. 292 output=$(sed "s/ [()][^)]*[)]//g" <<< "$output") 293 294 IFS="." read -r _ _ new_so_maj new_so_min < <(readlink "$libdir/$so_file") 295 IFS="." read -r _ _ old_so_maj old_so_min < <(readlink "$source_abi_dir/$so_file") 296 297 found_abi_change=false 298 so_name_changed=no 299 300 if [[ $output == *"ELF SONAME changed"* ]]; then 301 so_name_changed=yes 302 fi 303 304 changed_leaf_types=0 305 if [[ $output =~ "leaf types summary: "([0-9]+) ]]; then 306 changed_leaf_types=${BASH_REMATCH[1]} 307 fi 308 309 removed_functions=0 changed_functions=0 added_functions=0 310 if [[ $output =~ "functions summary: "([0-9]+)" Removed, "([0-9]+)" Changed, "([0-9]+)" Added" ]]; then 311 removed_functions=${BASH_REMATCH[1]} changed_functions=${BASH_REMATCH[2]} added_functions=${BASH_REMATCH[3]} 312 fi 313 314 removed_vars=0 changed_vars=0 added_vars=0 315 if [[ $output =~ "variables summary: "([0-9]+)" Removed, "([0-9]+)" Changed, "([0-9]+)" Added" ]]; then 316 removed_vars=${BASH_REMATCH[1]} changed_vars=${BASH_REMATCH[2]} added_vars=${BASH_REMATCH[3]} 317 fi 318 319 if ((changed_leaf_types != 0)); then 320 if ((new_so_maj == old_so_maj)); then 321 touch $fail_file 322 echo "Please update the major SO version for $so_file. A header accesible type has been modified since last release." 323 fi 324 found_abi_change=true 325 fi 326 327 if ((removed_functions != 0)) || ((removed_vars != 0)); then 328 if ((new_so_maj == old_so_maj)); then 329 touch $fail_file 330 echo "Please update the major SO version for $so_file. API functions or variables have been removed since last release." 331 fi 332 found_abi_change=true 333 fi 334 335 if ((changed_functions != 0)) || ((changed_vars != 0)); then 336 if ((new_so_maj == old_so_maj)); then 337 touch $fail_file 338 echo "Please update the major SO version for $so_file. API functions or variables have been changed since last release." 339 fi 340 found_abi_change=true 341 fi 342 343 if ((added_functions != 0)) || ((added_vars != 0)); then 344 if ((new_so_min == old_so_min && new_so_maj == old_so_maj)) && ! $found_abi_change; then 345 touch $fail_file 346 echo "Please update the minor SO version for $so_file. API functions or variables have been added since last release." 347 fi 348 found_abi_change=true 349 fi 350 351 if [[ $so_name_changed == yes ]]; then 352 if ! $found_abi_change; then 353 # Unfortunately, libspdk_idxd made it into 20.04 without an SO suffix. TODO:: remove after 20.07 354 if [ "$so_file" != "libspdk_idxd.so" ] && [ "$so_file" != "libspdk_accel_idxd.so" ]; then 355 echo "SO name for $so_file changed without a change to abi. please revert that change." 356 touch $fail_file 357 fi 358 fi 359 360 if ((new_so_maj != old_so_maj && new_so_min != 0)); then 361 echo "SO major version for $so_file was bumped. Please reset the minor version to 0." 362 touch $fail_file 363 fi 364 365 expected_new_so_min=$((old_so_min + 1)) 366 if ((new_so_min > old_so_min && expected_new_so_min != new_so_min)); then 367 echo "SO minor version for $so_file was incremented more than once. Please revert minor version to $expected_new_so_min." 368 touch $fail_file 369 fi 370 fi 371 372 continue 373 fi 374 processed_so=$((processed_so + 1)) 375 done 376 rm -f $suppression_file 377 echo "Processed $processed_so objects." 378} 379 380# This function is needed to properly evaluate the Make variables into actual dependencies. 381function replace_defined_variables() { 382 local arr=("$@") 383 local bad_values=() 384 local good_values=() 385 local new_values 386 for dep in "${arr[@]}"; do 387 if [[ $dep == *'$'* ]]; then 388 raw_dep=${dep/$\(/} 389 raw_dep=${raw_dep/\)/ } 390 bad_values+=("$raw_dep") 391 else 392 good_values+=("$dep") 393 fi 394 done 395 for dep in "${bad_values[@]}"; do 396 dep_def_arr=($(grep -v "#" $libdeps_file | grep "${dep}" | cut -d "=" -f 2 | xargs)) 397 new_values=($(replace_defined_variables "${dep_def_arr[@]}")) 398 good_values=("${good_values[@]}" "${new_values[@]}") 399 done 400 echo ${good_values[*]} 401} 402 403function confirm_deps() { 404 lib=$1 405 missing_syms=() 406 dep_names=() 407 found_symbol_lib="" 408 409 #keep the space here to differentiate bdev and bdev_* 410 lib_shortname=$(basename $lib | sed 's,libspdk_,,g' | sed 's,\.so, ,g') 411 lib_make_deps=($(grep "DEPDIRS-${lib_shortname}" $libdeps_file | cut -d "=" -f 2 | xargs)) 412 lib_make_deps=($(replace_defined_variables "${lib_make_deps[@]}")) 413 414 for ign_dep in "${IGNORED_LIBS[@]}"; do 415 for i in "${!lib_make_deps[@]}"; do 416 if [[ ${lib_make_deps[i]} == "$ign_dep" ]]; then 417 unset 'lib_make_deps[i]' 418 fi 419 done 420 done 421 422 symbols=$(readelf -s $lib | grep -E "NOTYPE.*GLOBAL.*UND" | awk '{print $8}' | sort | uniq) 423 for symbol in $symbols; do 424 for deplib in $DEP_LIBS; do 425 if [ "$deplib" == "$lib" ]; then 426 continue 427 fi 428 found_symbol=$(readelf -s $deplib | grep -E "DEFAULT\s+[0-9]+\s$symbol$") || true 429 if [ "$found_symbol" != "" ]; then 430 found_symbol_lib=$(basename $deplib | sed 's,libspdk_,,g' | sed 's,\.so,,g') 431 break 432 fi 433 done 434 if [ "$found_symbol" == "" ]; then 435 missing_syms+=("$symbol") 436 else 437 dep_names+=("$found_symbol_lib") 438 fi 439 done 440 IFS=$'\n' 441 # Ignore any event_* dependencies. Those are based on the subsystem configuration and not readelf. 442 lib_make_deps=($(printf "%s\n" "${lib_make_deps[@]}" | sort | grep -v "event_")) 443 # Ignore the env_dpdk readelf dependency. We don't want people explicitly linking against it. 444 dep_names=($(printf "%s\n" "${dep_names[@]}" | sort | uniq | grep -v "env_dpdk")) 445 unset IFS 446 diff=$(echo "${dep_names[@]}" "${lib_make_deps[@]}" | tr ' ' '\n' | sort | uniq -u) 447 if [ "$diff" != "" ]; then 448 touch $fail_file 449 echo "there was a dependency mismatch in the library $lib_shortname" 450 echo "The makefile lists: '${lib_make_deps[*]}'" 451 echo "readelf outputs : '${dep_names[*]}'" 452 echo "---------------------------------------------------------------------" 453 fi 454} 455 456# By removing the spdk.lib_deps.mk file from spdk.lib.mk, we ensure that we won't 457# create any link dependencies. Then we can be sure we get a valid accounting of the 458# symbol dependencies we have. 459sed -i -e 's,include $(SPDK_ROOT_DIR)/mk/spdk.lib_deps.mk,,g' "$rootdir/mk/spdk.lib.mk" 460 461source ~/autorun-spdk.conf 462config_params=$(get_config_params) 463if [ "$SPDK_TEST_OCF" -eq 1 ]; then 464 config_params="$config_params --with-ocf=$rootdir/build/ocf.a" 465fi 466 467$MAKE $MAKEFLAGS clean 468./configure $config_params --with-shared 469$MAKE $MAKEFLAGS 470 471xtrace_disable 472 473fail_file=$output_dir/check_so_deps_fail 474 475rm -f $fail_file 476 477run_test "confirm_abi_deps" confirm_abi_deps 478 479echo "---------------------------------------------------------------------" 480# Exclude libspdk_env_dpdk.so from the library list. We don't link against this one so that 481# users can define their own environment abstraction. However we do want to still check it 482# for dependencies to avoid printing out a bunch of confusing symbols under the missing 483# symbols section. 484SPDK_LIBS=$(ls -1 $libdir/libspdk_*.so | grep -v libspdk_env_dpdk.so) 485DEP_LIBS=$(ls -1 $libdir/libspdk_*.so) 486 487IGNORED_LIBS=() 488if grep -q 'CONFIG_VHOST_INTERNAL_LIB?=n' $rootdir/mk/config.mk; then 489 IGNORED_LIBS+=("rte_vhost") 490fi 491 492( 493 for lib in $SPDK_LIBS; do confirm_deps $lib & done 494 wait 495) 496 497$MAKE $MAKEFLAGS clean 498git checkout "$rootdir/mk/spdk.lib.mk" 499 500if [ -f $fail_file ]; then 501 rm -f $fail_file 502 echo "shared object test failed" 503 exit 1 504fi 505 506xtrace_restore 507