#!/bin/bash # Tool to do kABI checks. # (c) Kurt Garloff , GNU GPL, 11/2005 # $Id$ # # This tool looks at the generated symvers and compares it to the # reference file (if existent). It prints warnings for changed symbol # versions. # # Return value: # 0 -- no changes # 1 -- usage/input error # 2 -- internal error # 4 -- only additions # >= 8 -- removed or changed symbols (see below) # # Severity classification: # - 8 -- 15: if it's not found in a list (commonsyms or usedsyms) # The score depends on the source; symbols in vmlinux are more # likely to be used by anyone. # - 16 -- 23: symbol is found in the list usedsyms # - 24 -- 31: symbol is found in the list commonsyms severities=" # unimportant ---. .--- important # v v drivers/base/* 13 drivers/char/ipmi/* 10 drivers/char/tpm/tpm 9 drivers/hwmon/* 10 drivers/i2c/i2c-core 9 drivers/md/* 13 drivers/message/fusion/* 6 drivers/pci/* 12 drivers/pci/hotplug/pci_hotplug 10 drivers/scsi/libata 12 drivers/scsi/scsi* 12 drivers/scsi/*/scsi_transport_* 12 drivers/scsi/libiscsi* 12 drivers/ide/ide-core 11 drivers/usb/core/usbcore 10 drivers/usb/serial/usbserial 9 fs/dmapi/dmapi 11 fs/fat/fat 11 fs/jbd/jbd 11 net/ipv4/netfilter/ip_tables 9 vmlinux 15 " # Turning off UTF-8 processing provides a major speedup. export LC_ALL=C echo "${0##*/} $@" unset quiet verbose if [ "$1" = "-q" ]; then shift quiet=1 fi if [ "$1" = "-v" ]; then shift verbose=1 fi if [ $# -lt 2 -o $# -gt 4 ]; then echo "Usage: ${0##*/} [-q] [-v] reference symvers [commonsyms [usedsyms]]" >&2 exit 1 fi for file in "$@"; do [ -r "$file" ] && continue echo "Cannot read from '$file'" >&2 exit 1 done declare_symbol_severity() { declare severity=$1 while [ $# -ge 2 ]; do if ! eval "severity_of_${2//[^a-zA-Z0-9_]/_}=$severity"; then echo "Internal error" >&2 exit 2 fi shift done } consistency_check() { declare_symbol_severity 16 consistency_check_foo check_modified_symbols >/dev/null <<-EOF consistency_check_foo -0x12345678 consistency/check/foo +0x98765432 consistency/check/foo EOF if [ $? -ne 31 ]; then echo "Internal error" >&2 exit 2 fi } #set -x eval ' severity() { case $2 in '"$( ( echo "$severities" echo "consistency/check/* 15" # For the consistency test ) \ | sed -e '/^#/d' -e '/^$/d' \ | while read glob severity; do echo " ($glob) _rc=$severity ;;" done )"' (*) _rc=8 ;; esac # Is a particular severity defined for this symbol? declare severity=severity_of_$1 if [ -n "${!severity}" ]; then ((_rc += ${!severity})) fi return $_rc }' #set +x grab_symvers_from_rpm() {( # (Run in subshell to make trap work.) tmpdir=$(mktemp -t -d ${0##*/}.XXXXXX) trap "cd /; rm -rf $tmpdir" EXIT cd $tmpdir rpm2cpio "$file" \ | cpio -dim --quiet './boot/symvers-*.gz' set -- boot/symvers-*.gz if ! [ -e "$1" ]; then echo "Failed to extract symvers-*.gz from $file" >&2 exit 1 fi zcat "$1" )} grab_symvers() { declare tag=$1 file=$2 pwd tmpdir case "$(file -b - <"$file")" in gzip*) zcat "$file" ;; RPM*) grab_symvers_from_rpm "$file" ;; *) cat "$file" ;; esac \ | sed -e "/^#/d" -e "s/^/$tag/" \ | sort -k 2 } filter_out_identical_symbols() { # This expression works no matter how many columns the files have. grep -v -P '^\S+ -(\S+)( \S+)+ \+\1( \S+)+$' } check_modified_symbols() { declare -i RC=0 _rc declare ignored while read symbol tail; do # Split in half no matter how many columns the files have. set -- $tail ; half=$(($#/2+1)) version1=$1 ; version2=${!half} ; shift source1=$1 ; source2=${!half} ; shift case "$version1$version2" in -\#*) continue ;; -*+* | -*) ignored= case "$version1" in *=\>*) if [ "${version1#*=>}" = "${version2#+}" ]; then version1="${version1%=>*}" ignored="; ignored" fi ;; esac severity $symbol $source1 && continue _rc=$? if [ -z "$quiet" ]; then echo -n "Warning: $source1: $symbol(${version1#-}) " if [ -n "$version2" ]; then echo -n "changed to $symbol(${version2#+})" [ "$source1" != "$source2" ] && echo -n " and moved to $source2" else echo -n "removed" fi echo " (badness ${_rc}$ignored)" fi [ -n "$ignored" ] && _rc=0 ;; *) if [ -n "$verbose" ]; then echo " new symbol $symbol: ${version1#+}" fi _rc=4 ;; esac if [ ${_rc} -gt $RC ]; then RC=${_rc}; fi done return $RC } sort_by_badness() { sed -e 's/.*(badness \([0-9]\+\)).*/\1 &/' -e 't' -e 's/^/0 /' \ | sort -n -r \ | sed -e 's/^[0-9]* //' } consistency_check [ -n "$4" ] && declare_symbol_severity 8 $(< $4) [ -n "$3" ] && declare_symbol_severity 16 $(< $3) join -j 2 -a 1 -a 2 <(grab_symvers - $1) <(grab_symvers + $2) \ | filter_out_identical_symbols \ | check_modified_symbols \ | sort_by_badness RC=${PIPESTATUS[2]} echo "kABI verdict: $RC" exit $RC