#!/usr/bin/env python3
# SPDX-License-Identifier: GPL-2.0
#
# check-kabi - Red Hat kABI reference checking tool
#
# We use this script to check against reference Module.kabi files.
#
# Author: Jon Masters <jcm@redhat.com>
# Copyright (C) 2007-2009 Red Hat, Inc.
#
# Python 2 is no longer maintained, hence the port to Python 3.
#
# Author: GONG Ruiqi <gongruiqi1@huawei.com>
# Copyright (C) 2026 Huawei Technologies Co., Ltd.
#
# This software may be freely redistributed under the terms of the GNU
# General Public License (GPL).

import getopt
import sys

def load_table(table, filename):
	"""Load a Module.symvers or Module.kabi file."""

	with open(filename, "r") as f:
		for in_line in f:
			if in_line == "\n":
				continue
			checksum, symbol, directory, type, namespace = in_line.split("\t")
			table[symbol] = in_line[0:-1]

def check_kabi(symvers, kabi):
	"""Check Module.kabi and Module.symvers files."""

	fail = False
	warn = False
	changed_symbols = []
	moved_symbols = []

	for symbol, line in kabi.items():
		abi_hash, _, abi_dir, _, _ = line.split("\t")
		if symbol in symvers:
			sym_hash, _, sym_dir, _, _ = symvers[symbol].split("\t")
			if abi_hash != sym_hash:
				fail = True
				changed_symbols.append(symbol)

			if abi_dir != sym_dir:
				warn = True
				moved_symbols.append(symbol)
		else:
			fail = True
			changed_symbols.append(symbol)

	if fail:
		print("*** ERROR - ABI BREAKAGE WAS DETECTED ***")
		print("")
		print("The following symbols have been changed (this will cause an ABI breakage):")
		print("")
		for symbol in changed_symbols:
			print(symbol)
		print("")

	if warn:
		print("*** WARNING - ABI SYMBOLS MOVED ***")
		print("")
		print("The following symbols moved (typically caused by moving a symbol from being")
		print("provided by the kernel vmlinux out to a loadable module):")
		print("")
		for symbol in moved_symbols:
			print(symbol)
		print("")

	# Halt the build, if we got errors and/or warnings.  In either case,
	# double-checking is required to avoid introducing / concealing
	# KABI inconsistencies.
	if fail or warn:
		sys.exit(1)
	sys.exit(0)

def usage():
	print("""
check-kabi-py3: check Module.kabi and Module.symvers files.

	check-kabi-py3 [ -k Module.kabi ] [ -s Module.symvers ]

""")

if __name__ == "__main__":

	symvers_file = ""
	kabi_file = ""

	opts, args = getopt.getopt(sys.argv[1:], "hk:s:")

	for o, v in opts:
		if o == "-s":
			symvers_file = v
		if o == "-h":
			usage()
			sys.exit(0)
		if o == "-k":
			kabi_file = v

	if (symvers_file == "") or (kabi_file == ""):
		usage()
		sys.exit(1)

	symvers = {}
	kabi = {}

	load_table(symvers, symvers_file)
	load_table(kabi, kabi_file)
	check_kabi(symvers, kabi)
