blob: f59cb7ae9c4f014de3690de8ec57c513c7475963 [file] [log] [blame]
#!/usr/bin/env python3
# Copyright lowRISC contributors.
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0
import argparse
import sys
from typing import Dict
from shared.check import CheckResult
from shared.decode import OTBNProgram, decode_elf
from shared.insn_yaml import Insn
from shared.operand import RegOperandType
def _check_call_stack_insn(insn: Insn, operands: Dict[str, int]) -> bool:
'''Returns false if the instruction uses x1 and is not `jal`/`jalr`.'''
if insn.mnemonic in ['jal', 'jalr']:
# JAL and JALR instructions are allowed to use x1.
return True
# For all gpr operands, check that they are not x1.
for op in insn.operands:
if isinstance(op.op_type,
RegOperandType) and op.op_type.reg_type == 'gpr':
if operands[op.name] == 1:
return False
return True
def check_call_stack(program: OTBNProgram) -> CheckResult:
'''Check that the special register x1 is used safely.
If x1 is used for purposes unrelated to the call stack, it can trigger a
CALL_STACK error. This check errors if x1 is used for any other instruction
than `jal` or `jalr`.
'''
out = CheckResult()
for pc, (insn, operands) in program.insns.items():
if not _check_call_stack_insn(insn, operands):
out.err(
'Potentially dangerous use of the call stack register x1 at '
'PC {:#x}: {}'.format(pc, insn.disassemble(pc, operands)))
out.set_prefix('check_call_stack: ')
return out
def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument('elf', help=('The .elf file to check.'))
parser.add_argument('-v', '--verbose', action='store_true')
args = parser.parse_args()
program = decode_elf(args.elf)
result = check_call_stack(program)
if args.verbose or result.has_errors() or result.has_warnings():
print(result.report())
return 1 if result.has_errors() else 0
if __name__ == "__main__":
sys.exit(main())