summaryrefslogtreecommitdiff
path: root/debian/scripts/abi-check
blob: c7a02c5589afe4e052ec9889a091635f3583ee60 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
#!/usr/bin/perl -w

my $flavour = shift;
my $prev_abinum = shift;
my $abinum = shift;
my $prev_abidir = shift;
my $abidir = shift;
my $skipabi = shift;

my $fail_exit = 1;
my $EE = "EE:";
my $errors = 0;
my $abiskip = 0;

my $count;

print "II: Checking ABI for $flavour...\n";

if (-f "$prev_abidir/ignore"
    or -f "$prev_abidir/$flavour.ignore" or "$skipabi" eq "true") {
	print "WW: Explicitly asked to ignore ABI, running in no-fail mode\n";
	$fail_exit = 0;
	$abiskip = 1;
	$EE = "WW:";
}

if ($prev_abinum != $abinum) {
	print "II: Different ABI's, running in no-fail mode\n";
	$fail_exit = 0;
	$EE = "WW:";
}

if (not -f "$abidir/$flavour" or not -f "$prev_abidir/$flavour") {
	print "EE: Previous or current ABI file missing!\n";
	print "    $abidir/$flavour\n" if not -f "$abidir/$flavour";
	print "    $prev_abidir/$flavour\n" if not -f "$prev_abidir/$flavour";

	# Exit if the ABI files are missing, but return status based on whether
	# skip ABI was indicated.
	if ("$abiskip" eq "1") {
		exit(0);
	} else {
		exit(1);
	}
}

my %symbols;
my %symbols_ignore;
my %modules_ignore;
my %module_syms;

# See if we have any ignores
my $ignore = 0;
print "    Reading symbols/modules to ignore...";

for $file ("$prev_abidir/../blacklist", "$prev_abidir/../../perm-blacklist") {
	if (-f $file) {
		open(IGNORE, "< $file") or
			die "Could not open $file";
		while (<IGNORE>) {
			chomp;
			if ($_ =~ m/M: (.*)/) {
				$modules_ignore{$1} = 1;
			} else {
				$symbols_ignore{$_} = 1;
			}
			$ignore++;
		}
		close(IGNORE);
	}
}
print "read $ignore symbols/modules.\n";

sub is_ignored($$) {
	my ($mod, $sym) = @_;

	die "Missing module name in is_ignored()" if not defined($mod);
	die "Missing symbol name in is_ignored()" if not defined($sym);

	if (defined($symbols_ignore{$sym}) or defined($modules_ignore{$mod})) {
		return 1;
	}
	return 0;
}

# Read new syms first
print "    Reading new symbols ($abinum)...";
$count = 0;
open(NEW, "< $abidir/$flavour") or
	die "Could not open $abidir/$flavour";
while (<NEW>) {
	chomp;
	m/^(\S+)\s(.+)\s(0x[0-9a-f]+)\s(.+)$/;
	$symbols{$4}{'type'} = $1;
	$symbols{$4}{'loc'} = $2;
	$symbols{$4}{'hash'} = $3;
	$module_syms{$2} = 0;
	$count++;
}
close(NEW);
print "read $count symbols.\n";

# Now the old symbols, checking for missing ones
print "    Reading old symbols ($prev_abinum)...";
$count = 0;
open(OLD, "< $prev_abidir/$flavour") or
	die "Could not open $prev_abidir/$flavour";
while (<OLD>) {
	chomp;
	m/^(\S+)\s(.+)\s(0x[0-9a-f]+)\s(.+)$/;
	$symbols{$4}{'old_type'} = $1;
	$symbols{$4}{'old_loc'} = $2;
	$symbols{$4}{'old_hash'} = $3;
	$count++;
}
close(OLD);

print "read $count symbols.\n";

print "II: Checking for missing symbols in new ABI...";
$count = 0;
foreach $sym (keys(%symbols)) {
	if (!defined($symbols{$sym}{'type'})) {
		print "\n" if not $count;
		printf("    MISS : %s%s\n", $sym,
			is_ignored($symbols{$sym}{'old_loc'}, $sym) ? " (ignored)" : "");
		$count++ if !is_ignored($symbols{$sym}{'old_loc'}, $sym);
	}
}
print "    " if $count;
print "found $count missing symbols\n";
if ($count) {
	print "$EE Symbols gone missing (what did you do!?!)\n";
	$errors++;
}


print "II: Checking for new symbols in new ABI...";
$count = 0;
foreach $sym (keys(%symbols)) {
	if (!defined($symbols{$sym}{'old_type'})) {
		print "\n" if not $count;
		print "    NEW : $sym\n";
		$count++;
	}
}
print "    " if $count;
print "found $count new symbols\n";
if ($count and $prev_abinum == $abinum) {
	print "WW: Found new symbols within same ABI. Not recommended\n";
}

print "II: Checking for changes to ABI...\n";
$count = 0;
my $moved = 0;
my $changed_type = 0;
my $changed_hash = 0;
foreach $sym (keys(%symbols)) {
	if (!defined($symbols{$sym}{'old_type'}) or
	    !defined($symbols{$sym}{'type'})) {
		next;
	}

	# Changes in location don't hurt us, but log it anyway
	if ($symbols{$sym}{'loc'} ne $symbols{$sym}{'old_loc'}) {
		printf("    MOVE : %-40s : %s => %s\n", $sym, $symbols{$sym}{'old_loc'},
			$symbols{$sym}{'loc'});
		$moved++;
	}

	# Changes to export type are only bad if new type isn't
	# EXPORT_SYMBOL. Changing things to GPL are bad.
	if ($symbols{$sym}{'type'} ne $symbols{$sym}{'old_type'}) {
		printf("    TYPE : %-40s : %s => %s%s\n", $sym, $symbols{$sym}{'old_type'}.
			$symbols{$sym}{'type'}, is_ignored($symbols{$sym}{'loc'}, $sym)
			? " (ignored)" : "");
		$changed_type++ if $symbols{$sym}{'type'} ne "EXPORT_SYMBOL"
			and !is_ignored($symbols{$sym}{'loc'}, $sym);
	}

	# Changes to the hash are always bad
	if ($symbols{$sym}{'hash'} ne $symbols{$sym}{'old_hash'}) {
		printf("    HASH : %-40s : %s => %s%s\n", $sym, $symbols{$sym}{'old_hash'},
			$symbols{$sym}{'hash'}, is_ignored($symbols{$sym}{'loc'}, $sym)
			? " (ignored)" : "");
		$changed_hash++ if !is_ignored($symbols{$sym}{'loc'}, $sym);
		$module_syms{$symbols{$sym}{'loc'}}++;
	}
}

print "WW: $moved symbols changed location\n" if $moved;
print "$EE $changed_type symbols changed export type and weren't ignored\n" if $changed_type;
print "$EE $changed_hash symbols changed hash and weren't ignored\n" if $changed_hash;

$errors++ if $changed_hash or $changed_type;
if ($changed_hash) {
	print "II: Module hash change summary...\n";
	foreach $mod (sort { $module_syms{$b} <=> $module_syms{$a} } keys %module_syms) {
		next if ! $module_syms{$mod};
		printf("    %-40s: %d\n", $mod, $module_syms{$mod});
	}
}

print "II: Done\n";

if ($errors) {
	exit($fail_exit);
} else {
	exit(0);
}