Blame kabi.pl

Bernhard M. Wiedemann 6ebc4a
#!/usr/bin/perl
Bernhard M. Wiedemann 6ebc4a
use strict;
Bernhard M. Wiedemann 6ebc4a
use warnings;
Bernhard M. Wiedemann 6ebc4a
Bernhard M. Wiedemann 6ebc4a
use Getopt::Long;
Bernhard M. Wiedemann 6ebc4a
use Data::Dumper;
Bernhard M. Wiedemann 6ebc4a
Bernhard M. Wiedemann 6ebc4a
# ( { sym => regexp, mod => regexp, fail => 0/1 }, ... )
Bernhard M. Wiedemann 6ebc4a
my @rules;
Bernhard M. Wiedemann 6ebc4a
my ($opt_verbose, $opt_rules);
Bernhard M. Wiedemann 6ebc4a
Bernhard M. Wiedemann 9bfe00
# if Module.symvers also lists namespaces (>=5.4)
Bernhard M. Wiedemann 9bfe00
my $use_namespaces;
Bernhard M. Wiedemann 9bfe00
Bernhard M. Wiedemann 6ebc4a
sub load_rules {
Bernhard M. Wiedemann 6ebc4a
	my $file = shift;
Bernhard M. Wiedemann 6ebc4a
	my $errors = 0;
Bernhard M. Wiedemann 6ebc4a
Bernhard M. Wiedemann 6ebc4a
	xopen(my $fh, '<', $file);
Bernhard M. Wiedemann 6ebc4a
	while (<$fh>) {
Bernhard M. Wiedemann 6ebc4a
		chomp;
Bernhard M. Wiedemann 6ebc4a
		s/#.*//;
Bernhard M. Wiedemann 6ebc4a
		next if /^\s*$/;
Bernhard M. Wiedemann 6ebc4a
		my ($pattern, $verdict) = split(/\s+/);
Bernhard M. Wiedemann 6ebc4a
		my $new = {};
Bernhard M. Wiedemann 6ebc4a
		if (uc($verdict) eq "PASS") {
Bernhard M. Wiedemann 6ebc4a
			$new->{fail} = 0;
Bernhard M. Wiedemann 6ebc4a
		} elsif (uc($verdict) eq "FAIL") {
Bernhard M. Wiedemann 6ebc4a
			$new->{fail} = 1;
Bernhard M. Wiedemann 6ebc4a
		} else {
Bernhard M. Wiedemann 6ebc4a
			print STDERR "$file:$.: invalid verdict \"$verdict\", must be either PASS or FAIL.\n";
Bernhard M. Wiedemann 6ebc4a
			$errors++;
Bernhard M. Wiedemann 6ebc4a
			next;
Bernhard M. Wiedemann 6ebc4a
		}
Bernhard M. Wiedemann 6ebc4a
		# simple glob -> regexp conversion
Bernhard M. Wiedemann 6ebc4a
		$pattern =~ s/\*/.*/g;
Bernhard M. Wiedemann 6ebc4a
		$pattern =~ s/\?/./g;
Bernhard M. Wiedemann 6ebc4a
		$pattern =~ s/.*/^$&\$/;
Bernhard M. Wiedemann 9bfe00
Bernhard M. Wiedemann 9bfe00
		# If it matches a module path or vmlinux
Bernhard M. Wiedemann 6ebc4a
		if ($pattern =~ /\/|^vmlinux$/) {
Bernhard M. Wiedemann 6ebc4a
			$new->{mod} = $pattern;
Bernhard M. Wiedemann 9bfe00
		# If it's not a path and the string is all uppercase, assume it's a namespace
Bernhard M. Wiedemann 9bfe00
		} elsif ($use_namespaces &&
Bernhard M. Wiedemann 9bfe00
			$pattern !~ /\// && $pattern eq uc($pattern)) {
Bernhard M. Wiedemann 9bfe00
			$new->{namespace} = $pattern;
Bernhard M. Wiedemann 6ebc4a
		} else {
Bernhard M. Wiedemann 6ebc4a
			$new->{sym} = $pattern;
Bernhard M. Wiedemann 6ebc4a
		}
Bernhard M. Wiedemann 6ebc4a
		push(@rules, $new);
Bernhard M. Wiedemann 6ebc4a
	}
Bernhard M. Wiedemann 6ebc4a
	if ($errors && !@rules) {
Bernhard M. Wiedemann 6ebc4a
		print STDERR "error: only garbage found in $file.\n";
Bernhard M. Wiedemann 6ebc4a
		exit 1;
Bernhard M. Wiedemann 6ebc4a
	}
Bernhard M. Wiedemann 6ebc4a
	close($fh);
Bernhard M. Wiedemann 6ebc4a
}
Bernhard M. Wiedemann 6ebc4a
Bernhard M. Wiedemann 9bfe00
# Return 1 if using new (>=5.4) Module.symvers format with namespaces
Bernhard M. Wiedemann 9bfe00
sub symvers_uses_namespaces {
Bernhard M. Wiedemann 9bfe00
	my $file = shift;
Bernhard M. Wiedemann 9bfe00
	xopen(my $fh, '<', $file);
Bernhard M. Wiedemann 9bfe00
	my $line =  <$fh>;
Bernhard M. Wiedemann 9bfe00
	chomp $line;
Bernhard M. Wiedemann 9bfe00
Bernhard M. Wiedemann afd870
	# The new (>=5.4) Module.symvers format has 4 tabs (5 fields):
Bernhard M. Wiedemann afd870
	#
Bernhard M. Wiedemann afd870
	#    crc\tsymbol\tmodule\texport_type\tnamespace
Bernhard M. Wiedemann afd870
	#
Bernhard M. Wiedemann afd870
	# The older Module.symvers format only has 3 tabs (4 fields):
Bernhard M. Wiedemann afd870
	#
Bernhard M. Wiedemann afd870
	#    crc\tsymbol\tmodule\texport_type
Bernhard M. Wiedemann afd870
Bernhard M. Wiedemann afd870
	my $num_tabs = $line =~ tr/\t//;
Bernhard M. Wiedemann afd870
	if ($num_tabs > 3) {
Bernhard M. Wiedemann 9bfe00
		return 1;
Bernhard M. Wiedemann 9bfe00
	} else {
Bernhard M. Wiedemann 9bfe00
		return 0;
Bernhard M. Wiedemann 9bfe00
	}
Bernhard M. Wiedemann 9bfe00
}
Bernhard M. Wiedemann 9bfe00
Bernhard M. Wiedemann 6ebc4a
sub load_symvers {
Bernhard M. Wiedemann 6ebc4a
	my $file = shift;
Bernhard M. Wiedemann 6ebc4a
	my %res;
Bernhard M. Wiedemann 6ebc4a
	my $errors = 0;
Bernhard M. Wiedemann 9bfe00
	my $new;
Bernhard M. Wiedemann 6ebc4a
Bernhard M. Wiedemann 6ebc4a
	xopen(my $fh, '<', $file);
Bernhard M. Wiedemann 6ebc4a
	while (<$fh>) {
Bernhard M. Wiedemann 9bfe00
		chomp;
Bernhard M. Wiedemann afd870
		my @l = split(/\t/, $_, -1);
Bernhard M. Wiedemann 9bfe00
		if (@l < 4) {
Bernhard M. Wiedemann 6ebc4a
			print STDERR "$file:$.: unknown line\n";
Bernhard M. Wiedemann 6ebc4a
			$errors++;
Bernhard M. Wiedemann 6ebc4a
			next;
Bernhard M. Wiedemann 6ebc4a
		}
Bernhard M. Wiedemann 9bfe00
		if ($use_namespaces) {
Bernhard M. Wiedemann afd870
			$new = { crc => $l[0], mod => $l[2], type => $l[3], namespace => $l[4] };
Bernhard M. Wiedemann 9bfe00
		} else {
Bernhard M. Wiedemann 9bfe00
			$new = { crc => $l[0], mod => $l[2], type => $l[3] };
Bernhard M. Wiedemann 9bfe00
		}
Bernhard M. Wiedemann 6ebc4a
		$res{$l[1]} = $new;
Bernhard M. Wiedemann 6ebc4a
	}
Bernhard M. Wiedemann 6ebc4a
	if (!%res) {
Bernhard M. Wiedemann 6ebc4a
		print STDERR "error: no symvers found in $file.\n";
Bernhard M. Wiedemann 6ebc4a
		exit 1;
Bernhard M. Wiedemann 6ebc4a
	}
Bernhard M. Wiedemann 6ebc4a
	close($fh);
Bernhard M. Wiedemann 6ebc4a
	return %res;
Bernhard M. Wiedemann 6ebc4a
}
Bernhard M. Wiedemann 6ebc4a
Bernhard M. Wiedemann 6ebc4a
# Each bit represents a restriction of the export and adding a restriction
Bernhard M. Wiedemann 6ebc4a
# fails the check
Bernhard M. Wiedemann 6ebc4a
my $type_GPL    = 0x1;
Bernhard M. Wiedemann 6ebc4a
my $type_NOW    = 0x2;
Bernhard M. Wiedemann 6ebc4a
my $type_UNUSED = 0x4;
Bernhard M. Wiedemann 6ebc4a
my %types = (
Bernhard M. Wiedemann 6ebc4a
	EXPORT_SYMBOL            => 0x0,
Bernhard M. Wiedemann 6ebc4a
	EXPORT_SYMBOL_GPL        => $type_GPL | $type_NOW,
Bernhard M. Wiedemann 6ebc4a
	EXPORT_SYMBOL_GPL_FUTURE => $type_GPL,
Bernhard M. Wiedemann 6ebc4a
	EXPORT_UNUSED_SYMBOL     => $type_UNUSED,
Bernhard M. Wiedemann 6ebc4a
	EXPORT_UNUSED_SYMBOL_GPL => $type_UNUSED | $type_GPL | $type_NOW
Bernhard M. Wiedemann 6ebc4a
);
Bernhard M. Wiedemann 6ebc4a
Bernhard M. Wiedemann 6ebc4a
sub type_compatible {
Bernhard M. Wiedemann 6ebc4a
	my ($old, $new) = @_;
Bernhard M. Wiedemann 6ebc4a
Bernhard M. Wiedemann 6ebc4a
	for my $type ($old, $new) {
Bernhard M. Wiedemann 6ebc4a
		if (!exists($types{$type})) {
Bernhard M. Wiedemann 6ebc4a
			print STDERR "error: unrecognized export type $type.\n";
Bernhard M. Wiedemann 6ebc4a
			exit 1;
Bernhard M. Wiedemann 6ebc4a
		}
Bernhard M. Wiedemann 6ebc4a
	}
Bernhard M. Wiedemann 6ebc4a
	# if $new has a bit set that $old does not -> fail
Bernhard M. Wiedemann 6ebc4a
	return !(~$types{$old} & $types{$new});
Bernhard M. Wiedemann 6ebc4a
}
Bernhard M. Wiedemann 6ebc4a
Bernhard M. Wiedemann 6ebc4a
my $kabi_errors = 0;
Bernhard M. Wiedemann 6ebc4a
sub kabi_change {
Bernhard M. Wiedemann 9bfe00
	my ($sym, $symvers, $message) = @_;
Bernhard M. Wiedemann 6ebc4a
	my $fail = 1;
Bernhard M. Wiedemann 6ebc4a
Bernhard M. Wiedemann 6ebc4a
	for my $rule (@rules) {
Bernhard M. Wiedemann 9bfe00
		if ($rule->{mod} && $symvers->{mod} =~ $rule->{mod} ||
Bernhard M. Wiedemann 9bfe00
		    $rule->{sym} && $sym =~ $rule->{sym} ||
Bernhard M. Wiedemann 9bfe00
			($use_namespaces && $rule->{namespace} &&
Bernhard M. Wiedemann 9bfe00
				$symvers->{namespace} =~ $rule->{namespace})) {
Bernhard M. Wiedemann 6ebc4a
			$fail = $rule->{fail};
Bernhard M. Wiedemann 6ebc4a
			last;
Bernhard M. Wiedemann 6ebc4a
		}
Bernhard M. Wiedemann 6ebc4a
	}
Bernhard M. Wiedemann 6ebc4a
	return unless $fail or $opt_verbose;
Bernhard M. Wiedemann 9bfe00
Bernhard M. Wiedemann 9bfe00
	print STDERR "KABI: symbol $sym(mod:$symvers->{mod}";
Bernhard M. Wiedemann 9bfe00
	if ($use_namespaces && $symvers->{namespace}) {
Bernhard M. Wiedemann 9bfe00
		print STDERR " ns:$symvers->{namespace}";
Bernhard M. Wiedemann 9bfe00
	}
Bernhard M. Wiedemann 9bfe00
	print STDERR ") $message";
Bernhard M. Wiedemann 6ebc4a
	if ($fail) {
Bernhard M. Wiedemann 6ebc4a
		$kabi_errors++;
Bernhard M. Wiedemann 6ebc4a
		print STDERR "\n";
Bernhard M. Wiedemann 6ebc4a
	} else {
Bernhard M. Wiedemann 6ebc4a
		print STDERR " (tolerated)\n";
Bernhard M. Wiedemann 6ebc4a
	}
Bernhard M. Wiedemann 6ebc4a
}
Bernhard M. Wiedemann 6ebc4a
Bernhard M. Wiedemann 6ebc4a
sub xopen {
Bernhard M. Wiedemann 6ebc4a
	open($_[0], $_[1], @_[2..$#_]) or die "$_[2]: $!\n";
Bernhard M. Wiedemann 6ebc4a
}
Bernhard M. Wiedemann 6ebc4a
Bernhard M. Wiedemann 6ebc4a
my $res = GetOptions(
Bernhard M. Wiedemann 6ebc4a
	'verbose|v' => \$opt_verbose,
Bernhard M. Wiedemann 6ebc4a
	'rules|r=s' => \$opt_rules,
Bernhard M. Wiedemann 6ebc4a
);
Bernhard M. Wiedemann 6ebc4a
if (!$res || @ARGV != 2) {
Bernhard M. Wiedemann 6ebc4a
	print STDERR "Usage: $0 [--rules <rules file>] Module.symvers.old Module.symvers\n";
Bernhard M. Wiedemann 6ebc4a
	exit 1;
Bernhard M. Wiedemann 6ebc4a
}
Bernhard M. Wiedemann 9bfe00
Bernhard M. Wiedemann 9bfe00
# Determine symvers format
Bernhard M. Wiedemann 9bfe00
$use_namespaces = symvers_uses_namespaces($ARGV[0]);
Bernhard M. Wiedemann 9bfe00
Bernhard M. Wiedemann 6ebc4a
if (defined($opt_rules)) {
Bernhard M. Wiedemann 6ebc4a
	load_rules($opt_rules);
Bernhard M. Wiedemann 6ebc4a
}
Bernhard M. Wiedemann 6ebc4a
my %old = load_symvers($ARGV[0]);
Bernhard M. Wiedemann 6ebc4a
my %new = load_symvers($ARGV[1]);
Bernhard M. Wiedemann 6ebc4a
Bernhard M. Wiedemann 6ebc4a
for my $sym (sort keys(%old)) {
Bernhard M. Wiedemann 6ebc4a
	if (!$new{$sym}) {
Bernhard M. Wiedemann 9bfe00
		kabi_change($sym, $old{$sym}, "lost");
Bernhard M. Wiedemann 6ebc4a
	} elsif ($old{$sym}->{crc} ne $new{$sym}->{crc}) {
Bernhard M. Wiedemann 9bfe00
		kabi_change($sym, $old{$sym}, "changed crc from " .
Bernhard M. Wiedemann 6ebc4a
			"$old{$sym}->{crc} to $new{$sym}->{crc}");
Bernhard M. Wiedemann 6ebc4a
	} elsif (!type_compatible($old{$sym}->{type}, $new{$sym}->{type})) {
Bernhard M. Wiedemann 9bfe00
		kabi_change($sym, $old{$sym}, "changed type from " .
Bernhard M. Wiedemann 6ebc4a
			"$old{$sym}->{type} to $new{$sym}->{type}");
Bernhard M. Wiedemann 6ebc4a
	}
Bernhard M. Wiedemann 6ebc4a
}
Bernhard M. Wiedemann 6ebc4a
if ($kabi_errors) {
Bernhard M. Wiedemann 6ebc4a
	print STDERR "KABI: aborting due to kabi changes.\n";
Bernhard M. Wiedemann 6ebc4a
	exit 1;
Bernhard M. Wiedemann 6ebc4a
}
Bernhard M. Wiedemann 6ebc4a
exit 0;