| #!/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()) |