xref: /openbsd-src/lib/check_sym (revision d7922f91b633bd649d004aed2641da227c6f102b)
159abad11Sguenther#!/bin/ksh
2*d7922f91Stb#  $OpenBSD: check_sym,v 1.14 2024/12/24 18:14:49 tb Exp $
3588fe94fSguenther#
43ea1dcecSguenther# Copyright (c) 2016,2019,2022 Philip Guenther <guenther@openbsd.org>
5588fe94fSguenther#
6588fe94fSguenther# Permission to use, copy, modify, and distribute this software for any
7588fe94fSguenther# purpose with or without fee is hereby granted, provided that the above
8588fe94fSguenther# copyright notice and this permission notice appear in all copies.
9588fe94fSguenther#
10588fe94fSguenther# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11588fe94fSguenther# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12588fe94fSguenther# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13588fe94fSguenther# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14588fe94fSguenther# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15588fe94fSguenther# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16588fe94fSguenther# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17588fe94fSguenther#
1859abad11Sguenther#
1959abad11Sguenther#  check_sym -- compare the symbols and external function references in two
2045cee0b3Sguenther#	versions of a library
2159abad11Sguenther#
2259abad11Sguenther#  SYNOPSIS
2345cee0b3Sguenther#	check_sym [-chkSv] [old [new]]
2459abad11Sguenther#
2559abad11Sguenther#  DESCRIPTION
2659abad11Sguenther#	Library developers need to be aware when they have changed the
2759abad11Sguenther#	ABI of a library.  To assist them, check_sym examines two versions
2859abad11Sguenther#	of a shared library and reports changes to the following:
2959abad11Sguenther#	 * the set of exported symbols and their strengths
3059abad11Sguenther#	 * the set of undefined symbols referenced
3159abad11Sguenther#	 * the set of lazily-resolved functions (PLT)
3259abad11Sguenther#
3359abad11Sguenther#	In each case, additions and removals are reported; for exported
3459abad11Sguenther#	symbols it also reports when a symbol is weakened or strengthened.
3559abad11Sguenther#
3645cee0b3Sguenther#	With the -S option, a similar analysis is done but for the static lib.
3745cee0b3Sguenther#
3859abad11Sguenther#	The shared libraries to compare can be specified on the
3959abad11Sguenther#	command-line.  Otherwise, check_sym expects to be run from the
40b26f28fbSguenther#	source directory of a library with a shlib_version file specifying
4159abad11Sguenther#	the version being built and the new library in the obj subdirectory.
42b26f28fbSguenther#	If the old library to compare against wasn't specified either then
4359abad11Sguenther#	check_sym will take the highest version of that library in the
4459abad11Sguenther#	*current* directory, or the highest version of that library in
4559abad11Sguenther#	/usr/lib if it wasn't present in the current directory.
4659abad11Sguenther#
473ea1dcecSguenther#	By default, check_sym places all its intermediate files in a
48*d7922f91Stb#	temporary directory and removes it on exit.  They contain useful
493ea1dcecSguenther#	details for understanding what changed, so if the -k option is used
503ea1dcecSguenther#	they will instead be placed in /tmp/ and left behind.  If any of
513ea1dcecSguenther#	them cannot be created by the user, the command will fail.  The
523ea1dcecSguenther#	files left behind by the -k option can be cleaned up by invoking
533ea1dcecSguenther#	check_syms with the -c option.
5459abad11Sguenther#
5545cee0b3Sguenther#	The -v option enables verbose output, showing relocation counts.
5659abad11Sguenther#
5759abad11Sguenther#	The *basic* rules of thumb for library versions are: if you
5859abad11Sguenther#	 * stop exporting a symbol, or
59c9fb8097Sguenther#	 * change the size of a data symbol
6059abad11Sguenther#	 * start exporting a symbol that an inter-dependent library needs
6159abad11Sguenther#	then you need to bump the MAJOR version of the library.
6259abad11Sguenther#
6359abad11Sguenther#	Otherwise, if you:
6459abad11Sguenther#	 * start exporting a symbol
6559abad11Sguenther#	then you need to bump the MINOR version of the library.
6659abad11Sguenther#
6759abad11Sguenther#  SEE ALSO
6859abad11Sguenther#	readelf(1), elf(5)
6959abad11Sguenther#
7059abad11Sguenther#  AUTHORS
7159abad11Sguenther#	Philip Guenther <guenther@openbsd.org>
7259abad11Sguenther#
7359abad11Sguenther#  CAVEATS
7459abad11Sguenther#	The elf format is infinitely extendable, but check_sym only
7559abad11Sguenther#	handles a few weirdnesses.  Running it on or against new archs
7659abad11Sguenther#	may result in meaningless results.
7759abad11Sguenther#
7859abad11Sguenther#  BUGS
797ea1ffd3Sguenther#	While the author stills find the intermediate files useful,
807ea1ffd3Sguenther#	most people won't.  By default they should be placed in a
817ea1ffd3Sguenther#	temp directory and removed.
827ea1ffd3Sguenther#
8359abad11Sguenther
8459abad11Sguentherget_lib_name()
8559abad11Sguenther{
8645cee0b3Sguenther	sed -n '/^[ 	]*LIB[ 	]*=/{ s/^[^=]*=[ 	]*\([^ 	]*\).*/\1/p; q;}' "$@"
8759abad11Sguenther}
8859abad11Sguenther
8959abad11Sguentherpick_highest()
9059abad11Sguenther{
9159abad11Sguenther	old=
9259abad11Sguenther	omaj=-1
9359abad11Sguenther	omin=0
9459abad11Sguenther	for i
9559abad11Sguenther	do
9659abad11Sguenther		[[ -f $i ]] || continue
9759abad11Sguenther		maj=${i%.*}; maj=${maj##*.}
9859abad11Sguenther		min=${i##*.}
9959abad11Sguenther		if [[ $maj -gt $omaj || ( $maj -eq $omaj && $min -gt $omin ) ]]
10059abad11Sguenther		then
10159abad11Sguenther			old=$i
10259abad11Sguenther			omaj=$maj
10359abad11Sguenther			omin=$min
10459abad11Sguenther		fi
10559abad11Sguenther	done
10659abad11Sguenther	[[ $old != "" ]]
10759abad11Sguenther}
10859abad11Sguenther
10945cee0b3Sguentherfail() { echo "$*" >&2; exit 1; }
11045cee0b3Sguenther
1117ea1ffd3Sguentherusage()
1127ea1ffd3Sguenther{
11345cee0b3Sguenther	usage="usage: check_sym [-chkSv] [old [new]]"
11445cee0b3Sguenther	[[ $# -eq 0 ]] || fail "check_sym: $*
11545cee0b3Sguenther$usage"
1167ea1ffd3Sguenther	echo "$usage"
1177ea1ffd3Sguenther	exit 0
1187ea1ffd3Sguenther}
1197ea1ffd3Sguenther
12045cee0b3Sguenther
12145cee0b3Sguenther#
12245cee0b3Sguenther#  Output helpers
12345cee0b3Sguenther#
12445cee0b3Sguentherdata_sym_changes()
12545cee0b3Sguenther{
12645cee0b3Sguenther	join "$@" | awk '$2 != $3 { print $1 " " $2 " --> " $3 }'
12745cee0b3Sguenther}
12845cee0b3Sguenther
12945cee0b3Sguentheroutput_if_not_empty()
13045cee0b3Sguenther{
13145cee0b3Sguenther	leader=$1
13245cee0b3Sguenther	shift
13345cee0b3Sguenther	if "$@" | grep -q .
13445cee0b3Sguenther	then
13545cee0b3Sguenther		echo "$leader"
13645cee0b3Sguenther		"$@" | sed 's:^:	:'
13745cee0b3Sguenther		echo
13845cee0b3Sguenther	fi
13945cee0b3Sguenther}
14045cee0b3Sguenther
14145cee0b3Sguenther
14245cee0b3Sguenther#
14345cee0b3Sguenther#  Dynamic library routines
14445cee0b3Sguenther#
14545cee0b3Sguenther
14645cee0b3Sguentherdynamic_collect()
14745cee0b3Sguenther{
14845cee0b3Sguenther	readelf -sW $old | filt_symtab > $odir/Ds1
14945cee0b3Sguenther	readelf -sW $new | filt_symtab > $odir/Ds2
15045cee0b3Sguenther
15145cee0b3Sguenther	readelf -rW $old > $odir/r1
15245cee0b3Sguenther	readelf -rW $new > $odir/r2
15345cee0b3Sguenther
15445cee0b3Sguenther	case $(readelf -h $new | grep '^ *Machine:') in
15545cee0b3Sguenther	*MIPS*)	cpu=mips64
15645cee0b3Sguenther		gotsym1=$(readelf -d $old | awk '$2 ~ /MIPS_GOTSYM/{print $3}')
15745cee0b3Sguenther		gotsym2=$(readelf -d $new | awk '$2 ~ /MIPS_GOTSYM/{print $3}')
15845cee0b3Sguenther		;;
15945cee0b3Sguenther	*HPPA*)	cpu=hppa;;
16045cee0b3Sguenther	*)	cpu=dontcare;;
16145cee0b3Sguenther	esac
16245cee0b3Sguenther}
16345cee0b3Sguenther
16445cee0b3Sguentherjump_slots()
16545cee0b3Sguenther{
16645cee0b3Sguenther	case $cpu in
16745cee0b3Sguenther	hppa)	awk '/IPLT/ && $5 != ""{print $5}' r$1
16845cee0b3Sguenther		;;
16945cee0b3Sguenther	mips64)	# the $((gotsym$1)) converts hex to decimal
17045cee0b3Sguenther		awk -v g=$((gotsym$1)) \
17145cee0b3Sguenther			'/^Symbol table ..symtab/{exit}
17245cee0b3Sguenther			$6 == "PROTECTED" { next }
17345cee0b3Sguenther			$1+0 >= g && $4 == "FUNC" {print $8}' Ds$1
17445cee0b3Sguenther		;;
17545cee0b3Sguenther	*)	awk '/JU*MP_SL/ && $5 != ""{print $5}' r$1
17645cee0b3Sguenther		;;
17745cee0b3Sguenther	esac | sort -o j$1
17845cee0b3Sguenther}
17945cee0b3Sguenther
18045cee0b3Sguentherdynamic_sym()
18145cee0b3Sguenther{
18245cee0b3Sguenther	awk -v s=$1 '/^Symbol table ..symtab/{exit}
18345cee0b3Sguenther		! /^ *[1-9]/   {next}
18445cee0b3Sguenther		$5 == "LOCAL"  {next}
18545cee0b3Sguenther		$7 == "UND"    {print $8     | ("sort -o DU" s); next }
18645cee0b3Sguenther		$5 == "GLOBAL" {print $8     | ("sort -o DS" s) }
18745cee0b3Sguenther		$5 == "WEAK"   {print $8     | ("sort -o DW" s) }
18845cee0b3Sguenther		$4 == "OBJECT" {print $8, $3 | ("sort -o DO" s) }
18945cee0b3Sguenther		{print $8 | ("sort -o D" s)
19045cee0b3Sguenther		 print $4, $5, $6, $8}' Ds$1 | sort -o d$1
19145cee0b3Sguenther}
19245cee0b3Sguenther
19345cee0b3Sguentherstatic_sym()
19445cee0b3Sguenther{
19545cee0b3Sguenther	awk '/^Symbol table ..symtab/{s=1}
19645cee0b3Sguenther	     /LOCAL/{next}
19745cee0b3Sguenther	     s&&/^ *[1-9]/{print $4, $5, $6, $8}' Ds$1 | sort -o s$1
19845cee0b3Sguenther}
19945cee0b3Sguenther
20045cee0b3Sguentherdynamic_analysis()
20145cee0b3Sguenther{
20245cee0b3Sguenther	jump_slots $1
20345cee0b3Sguenther	dynamic_sym $1
20445cee0b3Sguenther	#static_sym $1
20545cee0b3Sguenther	comm -23 j$1 DU$1 >J$1
20645cee0b3Sguenther	return 0
20745cee0b3Sguenther}
20845cee0b3Sguenther
20945cee0b3Sguentherdynamic_output()
21045cee0b3Sguenther{
21145cee0b3Sguenther	if cmp -s d[12] && cmp -s DO[12]
21245cee0b3Sguenther	then
21345cee0b3Sguenther		printf "No dynamic export changes\n"
21445cee0b3Sguenther	else
21545cee0b3Sguenther		printf "Dynamic export changes:\n"
21645cee0b3Sguenther		output_if_not_empty "added:" comm -13 D[12]
21745cee0b3Sguenther		output_if_not_empty "removed:" comm -23 D[12]
21845cee0b3Sguenther		output_if_not_empty "weakened:" comm -12 DS1 DW2
21945cee0b3Sguenther		output_if_not_empty "strengthened:" comm -12 DW1 DS2
22045cee0b3Sguenther		output_if_not_empty "data object sizes changes:" \
22145cee0b3Sguenther						data_sym_changes DO[12]
22245cee0b3Sguenther	fi
22345cee0b3Sguenther	if ! cmp -s DU[12]
22445cee0b3Sguenther	then
22545cee0b3Sguenther		printf "External reference changes:\n"
22645cee0b3Sguenther		output_if_not_empty "added:" comm -13 DU[12]
22745cee0b3Sguenther		output_if_not_empty "removed:" comm -23 DU[12]
22845cee0b3Sguenther	fi
22945cee0b3Sguenther
23045cee0b3Sguenther	if $verbose; then
23145cee0b3Sguenther		printf "\nReloc counts:\nbefore:\n"
23245cee0b3Sguenther		grep ^R r1
23345cee0b3Sguenther		printf "\nafter:\n"
23445cee0b3Sguenther		grep ^R r2
23545cee0b3Sguenther	fi
23645cee0b3Sguenther
23745cee0b3Sguenther	output_if_not_empty "PLT added:" comm -13 J[12]
23845cee0b3Sguenther	output_if_not_empty "PLT removed:" comm -23 J[12]
23945cee0b3Sguenther}
24045cee0b3Sguenther
24145cee0b3Sguenther
24245cee0b3Sguenther#
24345cee0b3Sguenther#  Static library routines
24445cee0b3Sguenther#
24545cee0b3Sguentherstatic_collect()
24645cee0b3Sguenther{
24745cee0b3Sguenther	readelf -sW $old | filt_ret | filt_symtab > $odir/Ss1
24845cee0b3Sguenther	readelf -sW $new | filt_ret | filt_symtab > $odir/Ss2
24945cee0b3Sguenther}
25045cee0b3Sguenther
25145cee0b3Sguentherstatic_analysis()
25245cee0b3Sguenther{
25345cee0b3Sguenther	awk -v s=$1 '!/^ *[1-9]/{next}
25445cee0b3Sguenther		$5 == "LOCAL"  {next}
25545cee0b3Sguenther		$7 == "UND"    {print $8     | ("sort -uo SU" s); next }
25645cee0b3Sguenther		$6 == "HIDDEN" {print $8     | ("sort -uo SH" s) }
25745cee0b3Sguenther		$5 == "GLOBAL" {print $8     | ("sort -o SS" s) }
25845cee0b3Sguenther		$5 == "WEAK"   {print $8     | ("sort -o SW" s) }
25945cee0b3Sguenther		$4 == "OBJECT" {print $8, $3 | ("sort -o SO" s) }
26045cee0b3Sguenther		{print $8 | ("sort -o S" s)
26145cee0b3Sguenther		 print $4, $5, $6, $8}' Ss$1 | sort -o s$1
26245cee0b3Sguenther	grep -v '^_' SH$1 >Sh$1 || :
26345cee0b3Sguenther}
26445cee0b3Sguenther
26545cee0b3Sguentherstatic_output()
26645cee0b3Sguenther{
26745cee0b3Sguenther	output_if_not_empty "hidden but not reserved:" comm -13 Sh[12]
26845cee0b3Sguenther	if cmp -s s[12] && cmp -s SO[12]
26945cee0b3Sguenther	then
27045cee0b3Sguenther		printf "No static export changes\n"
27145cee0b3Sguenther	else
27245cee0b3Sguenther		printf "Static export changes:\n"
27345cee0b3Sguenther		output_if_not_empty "added:" comm -13 S[12]
27445cee0b3Sguenther		output_if_not_empty "removed:" comm -23 S[12]
27545cee0b3Sguenther		output_if_not_empty "weakened:" comm -12 SS1 SW2
27645cee0b3Sguenther		output_if_not_empty "strengthened:" comm -12 SW1 SS2
27745cee0b3Sguenther		output_if_not_empty "data object sizes changes:" \
27845cee0b3Sguenther						data_sym_changes SO[12]
27945cee0b3Sguenther	fi
28045cee0b3Sguenther	if ! cmp -s SU[12]
28145cee0b3Sguenther	then
28245cee0b3Sguenther		printf "External reference changes:\n"
28345cee0b3Sguenther		output_if_not_empty "added:" comm -13 SU[12]
28445cee0b3Sguenther		output_if_not_empty "removed:" comm -23 SU[12]
28545cee0b3Sguenther	fi
28645cee0b3Sguenther}
28745cee0b3Sguenther
28845cee0b3Sguenther
2893ea1dcecSguentherunset odir
29045cee0b3Sguentherfile_list={D{,O,S,s,W,U},J,d,j,r}{1,2}
29145cee0b3Sguentherstatic_file_list={S{,H,h,O,S,U,W},U,s}{1,2}
29259abad11Sguenther
2933ea1dcecSguentherkeep_temp=false
29445cee0b3Sguentherdynamic=true
29545cee0b3Sguentherstatic=false
2967ea1ffd3Sguentherverbose=false
29745cee0b3Sguenther
29845cee0b3Sguentherdo_static() { static=true dynamic=false file_list=$static_file_list; }
29945cee0b3Sguenther
30045cee0b3Sguentherwhile getopts :chkSv opt "$@"
3017ea1ffd3Sguentherdo
3027ea1ffd3Sguenther	case $opt in
3033ea1dcecSguenther	c)	rm -f /tmp/$file_list
3047ea1ffd3Sguenther		exit 0;;
3053ea1dcecSguenther	h)	usage;;
3063ea1dcecSguenther	k)	keep_temp=true;;
30745cee0b3Sguenther	S)	do_static;;
3087ea1ffd3Sguenther	v)	verbose=true;;
3097ea1ffd3Sguenther	\?)	usage "unknown option -- $OPTARG";;
3107ea1ffd3Sguenther	esac
3117ea1ffd3Sguentherdone
3127ea1ffd3Sguenthershift $((OPTIND - 1))
3137ea1ffd3Sguenther[[ $# -gt 2 ]] && usage "too many arguments"
31459abad11Sguenther
31559abad11Sguenther# Old library?
31645cee0b3Sguentherif ! $static && [[ $1 = ?(*/)lib*.so* ]]
31759abad11Sguentherthen
31845cee0b3Sguenther	[[ -f $1 ]] || fail "$1 doesn't exist"
31959abad11Sguenther	old=$1
32059abad11Sguenther	lib=${old##*/}
32159abad11Sguenther	lib=${lib%%.so.*}
32259abad11Sguenther	shift
32345cee0b3Sguentherelif [[ $1 = ?(*/)lib*.a ]]
32445cee0b3Sguentherthen
32545cee0b3Sguenther	# woo hoo, static library mode
32645cee0b3Sguenther	do_static
32745cee0b3Sguenther	if [[ -f $1 ]]
32845cee0b3Sguenther	then
32945cee0b3Sguenther		old=$1
33045cee0b3Sguenther		lib=${old##*/}
33145cee0b3Sguenther	elif [[ $1 = lib*.a && -f /usr/lib/$1 ]]
33245cee0b3Sguenther	then
33345cee0b3Sguenther		old=/usr/lib/$1
33445cee0b3Sguenther		lib=$1
33545cee0b3Sguenther	else
33645cee0b3Sguenther		fail "$1 doesn't exist"
33745cee0b3Sguenther	fi
33845cee0b3Sguenther	lib=${lib%%.a}
33945cee0b3Sguenther	shift
34059abad11Sguentherelse
34159abad11Sguenther	# try determining it from the current directory
34259abad11Sguenther	if [[ -f Makefile ]] && lib=$(get_lib_name Makefile) &&
34359abad11Sguenther	   [[ $lib != "" ]]
34459abad11Sguenther	then
34559abad11Sguenther		lib=lib$lib
34659abad11Sguenther	else
34759abad11Sguenther		lib=libc
34859abad11Sguenther	fi
34959abad11Sguenther
35059abad11Sguenther	# Is there a copy of that lib in the current directory?
35159abad11Sguenther	# If so, use the highest numbered one
35245cee0b3Sguenther	if ! $static &&
35345cee0b3Sguenther	   ! pick_highest $lib.so.* &&
35445cee0b3Sguenther	   ! pick_highest /usr/lib/$lib.so.*
35559abad11Sguenther	then
35645cee0b3Sguenther		fail "unable to find $lib.so.*"
35745cee0b3Sguenther	elif $static
35845cee0b3Sguenther	then
35945cee0b3Sguenther		old=/usr/lib/${lib}.a
36045cee0b3Sguenther		[[ -f $old ]] || fail "$old doesn't exist"
36159abad11Sguenther	fi
36259abad11Sguentherfi
36359abad11Sguenther
36459abad11Sguenther# New library?
36545cee0b3Sguentherif [[ $1 = ?(*/)lib*.so* ]] ||
36645cee0b3Sguenther   { $static && [[ $1 = ?(*/)lib*.a ]]; }
36759abad11Sguentherthen
36859abad11Sguenther	new=$1
36959abad11Sguenther	shift
37045cee0b3Sguentherelif $static
37145cee0b3Sguentherthen
37245cee0b3Sguenther	new=obj/${lib}.a
37359abad11Sguentherelse
37459abad11Sguenther	# Dig info out of the just built library
37559abad11Sguenther	. ./shlib_version
37659abad11Sguenther	new=obj/${lib}.so.${major}.${minor}
37759abad11Sguentherfi
37845cee0b3Sguenther[[ -f $new ]] || fail "$new doesn't exist"
37959abad11Sguenther
38059abad11Sguenther# Filter the output of readelf -s to be easier to parse by removing a
38159abad11Sguenther# field that only appears on some symbols: [<other>: 88]
38259abad11Sguenther# Not really arch-specific, but I've only seen it on alpha
38345cee0b3Sguentherfilt_symtab() { sed 's/\[<other>: [0-9a-f]*\]//'; }
38445cee0b3Sguentherfilt_ret() { egrep -v ' (__retguard_[0-9]+|__llvm_retpoline_[a-z]+[0-9]*)$'; }
38559abad11Sguenther
3863ea1dcecSguentherif $keep_temp
3873ea1dcecSguentherthen
38859abad11Sguenther	# precreate all the files we'll use, but with noclobber set to avoid
38959abad11Sguenther	# symlink attacks
3903ea1dcecSguenther	odir=/tmp
39159abad11Sguenther	files=
3923ea1dcecSguenther	trap 'ret=$?; rm -f $files; exit $ret' 1 2 15 ERR
3933ea1dcecSguentherelse
3943ea1dcecSguenther	trap 'ret=$?; rm -rf "$odir"; exit $ret' 0 1 2 15 ERR
3953ea1dcecSguenther	odir=$(mktemp -dt check_sym.XXXXXXXXXX)
3963ea1dcecSguentherfi
3973ea1dcecSguentherset -C
3983ea1dcecSguentherfor i in $odir/$file_list
39959abad11Sguentherdo
40059abad11Sguenther	rm -f $i
40159abad11Sguenther	3>$i
40259abad11Sguenther	files="$files $i"
40359abad11Sguentherdone
40459abad11Sguentherset +C
40559abad11Sguenther
40659abad11Sguenther
40745cee0b3Sguenther#
40845cee0b3Sguenther#  Collect data
40945cee0b3Sguenther#
41045cee0b3Sguenther$dynamic && dynamic_collect
41145cee0b3Sguenther$static	 && static_collect
41259abad11Sguenther
4133ea1dcecSguenther# Now that we're done accessing $old and $new (which could be
4143ea1dcecSguenther# relative paths), chdir into our work directory, whatever it is
4153ea1dcecSguenthercd $odir
4163ea1dcecSguenther
41745cee0b3Sguenther#
41845cee0b3Sguenther#  Do The Job
41945cee0b3Sguenther#
42059abad11Sguentherfor i in 1 2
42159abad11Sguentherdo
42245cee0b3Sguenther	$dynamic && dynamic_analysis $i
42345cee0b3Sguenther	$static	 && static_analysis $i
42459abad11Sguentherdone
42559abad11Sguenther
42645cee0b3Sguenther{
42759abad11Sguenther	echo "$old --> $new"
42842dc5753Stb	! $dynamic || dynamic_output
42942dc5753Stb	! $static  || static_output
43045cee0b3Sguenther}
43159abad11Sguenther
432