diff --git a/debian/rules.d/4-checks.mk b/debian/rules.d/4-checks.mk
index 7eaae529e5c1..68ac5471c6e4 100644
--- a/debian/rules.d/4-checks.mk
+++ b/debian/rules.d/4-checks.mk
@@ -1,8 +1,8 @@
 # Check ABI for package against last release (if not same abinum)
 abi-check-%: $(stampdir)/stamp-install-%
 	@echo Debug: $@
-	@perl -f $(DROOT)/scripts/abi-check "$*" "$(prev_abinum)" "$(abinum)" \
-		"$(prev_abidir)" "$(abidir)" "$(skipabi)"
+	$(DROOT)/scripts/abi-check "$*" \
+		"$(prev_abidir)" "$(abidir)" $(skipabi)
 
 # Check the module list against the last release (always)
 module-check-%: $(stampdir)/stamp-install-%
diff --git a/debian/scripts/abi-check b/debian/scripts/abi-check
index d065f3d698c0..5bc1c935d7e0 100755
--- a/debian/scripts/abi-check
+++ b/debian/scripts/abi-check
@@ -1,210 +1,139 @@
-#!/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;
+#!/usr/bin/python3
+#
+# Check ABI changes
+#
+# To skip the ABI checks, add a file
+#  debian.<foo>/abi/<arch>/ignore.abi
+# or
+#  debian.<foo>/abi/<arch>/<flavor>.ignore.abi
+#
+# To ignore a list of symbols, add the symbols to the file
+#   debian.<foo>/abi/abi.ignore
+#
+
+import os
+import re
+import sys
+
+def decode_symline(line):
+    comps = re.sub(r'\s+', ' ', line).split(' ')
+    if comps[0].startswith('EXPORT_SYMBOL'):
+        stype, sloc, shash, sname = comps
+    else:
+        stype, shash, sname, sloc = comps[1:]
+    return sname, {'type': stype, 'loc': sloc, 'hash': shash}
+
+if len(sys.argv) < 4 or len(sys.argv) > 5:
+    print('Usage: abi-check <flavor> <prev_abidir> <abidir> [<skipabi>]')
+    sys.exit(2)
+
+flavor, prev_abidir, abidir = sys.argv[1:4]  # pylint: disable=W0632
+if len(sys.argv) > 4:
+    skipabi = sys.argv[4].lower() in ['1', 'true', 'yes']
+else:
+    skipabi = False
+
+print('II: Checking ABI for {}...'.format(flavor), end='')
+
+if ((os.path.exists('{}/ignore.abi'.format(prev_abidir)) or
+     os.path.exists('{}/{}.ignore.abi'.format(prev_abidir, flavor)))):
+    print('WW: Explicitly ignoring ABI')
+    print('II: Done')
+    sys.exit(0)
+
+curr_abi = '{}/{}'.format(abidir, flavor)
+prev_abi = '{}/{}'.format(prev_abidir, flavor)
+if not os.path.exists(curr_abi) or not os.path.exists(prev_abi):
+    print('II: Previous or current ABI file missing!')
+    print('    {}'.format(curr_abi))
+    print('    {}'.format(prev_abi))
+    if skipabi:
+        print('WW: Explicitly asked to ignore failures')
+        print('II: Done')
+        sys.exit(0)
+    print('EE: Missing ABI file')
+    sys.exit(1)
+
+print()
+
+symbols = {}
+symbols_ignore = {}
 
 # See if we have any ignores
-my $ignore = 0;
-print "    Reading symbols/modules to ignore...";
-
-for $file ("$prev_abidir/../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);
-}
+print('    Reading symbols to ignore...', end='')
+ignore = 0
+prev_abi_ignore = '{}/../abi.ignore'.format(prev_abidir)
+if os.path.exists(prev_abi_ignore):
+    with open(prev_abi_ignore) as fh:
+        for sym in fh:
+            sym = sym.strip()
+            if sym.startswith('#'):
+                continue
+            symbols_ignore[sym] = 1
+            ignore += 1
+print('read {} symbols.'.format(ignore))
+
+# Read new symbols first
+print('    Reading new symbols...', end='')
+new_count = 0
+with open('{}/{}'.format(abidir, flavor)) as fh:
+    for line in fh:
+        sym, vals = decode_symline(line.strip())
+        symbols[sym] = vals
+        new_count += 1
+print('read {} symbols.'.format(new_count))
+
+# Now the old symbols
+print('    Reading old symbols...', end='')
+old_count = 0
+with open('{}/{}'.format(prev_abidir, flavor)) as fh:
+    for line in fh:
+        sym, vals = decode_symline(line.strip())
+        if sym not in symbols:
+            symbols[sym] = {}
+        symbols[sym]['old'] = vals
+        old_count += 1
+print('read {} symbols.'.format(old_count))
+
+print('II: Checking for ABI changes...')
+changed_loc = 0
+changed_type = 0
+error = False
+for sym, vals in symbols.items():
+    # Ignore new symbols
+    if 'old' not in vals:
+        continue
+
+    # Changes in location don't hurt us, but log it anyway
+    if vals['loc'] != vals['old']['loc']:
+        changed_loc += 1
+        print('    MOVE : {:40} : {} => {}'.format(sym, vals['old']['loc'],
+                                                   vals['loc']))
+
+    # Changes from GPL to non-GPL are bad
+    if ((vals['old']['type'] == 'EXPORT_SYMBOL_GPL' and
+         vals['type'] != 'EXPORT_SYMBOL_GPL')):
+        changed_type += 1
+        if sym in symbols_ignore or vals['loc'] in symbols_ignore:
+            ignored = ' (ignore)'
+        else:
+            ignored = ''
+            error = True
+        print('    TYPE : {:40} : {} => {}{}'.format(sym, vals['old']['type'],
+                                                     vals['type'], ignored))
+
+if changed_loc > 0:
+    print('II: {} symbols changed location'.format(changed_loc))
+
+if changed_type > 0:
+    print('II: {} symbols changed export type'.format(changed_type))
+
+if error:
+    if skipabi:
+        print('WW: Explicitly asked to ignore failures')
+    else:
+        print('EE: Symbol types changed')
+        sys.exit(1)
+
+print('II: Done')
+sys.exit(0)
