diff --git a/.gitignore b/.gitignore index 8774c22..5265a51 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ /target /testcases -/output \ No newline at end of file +/output +*.pyc +*.zip \ No newline at end of file diff --git a/tests/std.c b/tests/std.c new file mode 100644 index 0000000..b2da05c --- /dev/null +++ b/tests/std.c @@ -0,0 +1,121 @@ +/// +/// @file std.c +/// @brief 外部或内置函数实现 +/// @author zenglj (zenglj@live.com) +/// @version 1.0 +/// @date 2024-09-29 +/// +/// @copyright Copyright (c) 2024 +/// +/// @par 修改日志: +/// +///
Date Version Author Description +///
2024-09-29 1.0 zenglj 新做 +///
+/// +#include +#include + +int getint() +{ + int d; + + scanf("%d", &d); + + return d; +} + +int getch() +{ + char d; + + scanf("%c", &d); + + return d; +} + +int getarray(int a[]) +{ + int n, i; + + // 获取元素个数 + scanf("%d",&n); + + // 获取元素内容 + for(i = 0; i < n; ++i) { + scanf("%d",&a[i]); + } + + return n; +} + +void putint(int k) +{ + printf("%d", k); +} + +void putch(int c) +{ + printf("%c", (char)c); +} + +void putarray(int n, int * d) +{ + int k; + + // 输出元素个数 + printf("%d:", n); + + // 输出元素内容,空格分割 + for(k = 0; k < n; k ++) { + printf(" %d", d[k]); + } + + // 输出换行符 + printf("\n"); +} + +void putstr(char * str) +{ + printf("%s", str); +} + +float getfloat() +{ + float n; + scanf("%a", &n); + return n; +} + +int getfarray(float a[]) +{ + int n; + scanf("%d", &n); + for (int i = 0; i < n; i++) { + scanf("%a", &a[i]); + } + return n; +} + +void putfloat(float a) +{ + printf("%a", a); +} + +void putfarray(int n, float a[]) +{ + printf("%d:", n); + for (int i = 0; i < n; i++) { + printf(" %a", a[i]); + } + printf("\n"); +} + +void putf(char a[], ...) +{ + va_list args; + va_start(args, a); + vfprintf(stdout, a, args); + va_end(args); +} + diff --git a/tools/run_arm_asm.py b/tools/run_arm_asm.py new file mode 100755 index 0000000..540bc28 --- /dev/null +++ b/tools/run_arm_asm.py @@ -0,0 +1,177 @@ +#!/usr/bin/env python3 +"""Compile an ARM assembly file and run it with qemu-arm-static.""" + +from __future__ import annotations + +import argparse +import shutil +import subprocess +import sys +import tempfile +from pathlib import Path + + +def parse_args() -> argparse.Namespace: + if "--" in sys.argv: + separator_index = sys.argv.index("--") + tool_args = sys.argv[1:separator_index] + program_args = sys.argv[separator_index + 1 :] + else: + tool_args = sys.argv[1:] + program_args = [] + + parser = argparse.ArgumentParser( + description=( + "Compile an ARM assembly file with arm-linux-gnueabihf-gcc, " + "run it with qemu-arm-static, then print stdout and $?." + ) + ) + parser.add_argument("asm_file", type=Path, help="path to the assembly file") + parser.add_argument( + "--cc", + default="arm-linux-gnueabihf-gcc", + help="cross compiler to use (default: arm-linux-gnueabihf-gcc)", + ) + parser.add_argument( + "--qemu", + default="qemu-arm-static", + help="ARM qemu runner to use (default: qemu-arm-static)", + ) + parser.add_argument( + "-o", + "--output", + type=Path, + help="output executable path; defaults to an auto-cleaned temporary file", + ) + parser.add_argument( + "-g", + "--gdb", + action="store_true", + help="compile with -g and run qemu in gdb stub mode", + ) + parser.add_argument( + "--gdb-port", + default="1234", + metavar="PORT", + help="qemu gdb stub port used with -g/--gdb (default: 1234)", + ) + parser.add_argument( + "--gdb-command", + help=( + "gdb command shown in debug instructions " + "(default: first available of arm-linux-gnueabihf-gdb, gdb-multiarch, gdb)" + ), + ) + parser.add_argument( + "--no-static", + action="store_true", + help="do not pass -static to the compiler", + ) + parser.add_argument( + "--gcc-arg", + action="append", + default=[], + help="extra argument passed to gcc; repeat for multiple args", + ) + parser.add_argument( + "--std-c", + type=Path, + default=Path("tests/std.c"), + help="C runtime source compiled with the assembly file (default: tests/std.c)", + ) + parser.add_argument( + "--no-std-c", + action="store_true", + help="do not compile tests/std.c with the assembly file", + ) + args = parser.parse_args(tool_args) + args.program_args = program_args + return args + + +def require_command(command: str) -> None: + if shutil.which(command) is None: + print(f"error: command not found: {command}", file=sys.stderr) + sys.exit(127) + + +def run_command(command: list[str]) -> subprocess.CompletedProcess[bytes]: + try: + return subprocess.run(command, capture_output=True, check=False) + except OSError as err: + print(f"error: failed to run {command[0]}: {err}", file=sys.stderr) + sys.exit(127) + + +def choose_gdb_command(requested_command: str | None) -> str: + if requested_command: + return requested_command + + for command in ("arm-linux-gnueabihf-gdb", "gdb-multiarch", "gdb"): + if shutil.which(command) is not None: + return command + + return "gdb-multiarch" + + +def main() -> int: + args = parse_args() + asm_file = args.asm_file + if not asm_file.is_file(): + print(f"error: assembly file not found: {asm_file}", file=sys.stderr) + return 2 + + std_c = args.std_c + if not args.no_std_c and not std_c.is_file(): + print(f"error: std C file not found: {std_c}", file=sys.stderr) + return 2 + + require_command(args.cc) + require_command(args.qemu) + + with tempfile.TemporaryDirectory(prefix="run-arm-asm-") as temp_dir: + output = args.output or Path(temp_dir) / asm_file.with_suffix("").name + + compile_cmd = [args.cc] + if not args.no_static: + compile_cmd.append("-static") + if args.gdb: + compile_cmd.append("-g") + compile_cmd.extend(args.gcc_arg) + compile_cmd.append(str(asm_file)) + if not args.no_std_c: + compile_cmd.append(str(std_c)) + compile_cmd.extend(["-o", str(output)]) + + compile_result = run_command(compile_cmd) + sys.stdout.buffer.write(compile_result.stdout) + sys.stderr.buffer.write(compile_result.stderr) + if compile_result.returncode != 0: + return compile_result.returncode + + qemu_cmd = [args.qemu] + if args.gdb: + qemu_cmd.extend(["-g", args.gdb_port]) + resolved_output = output.resolve() + gdb_command = choose_gdb_command(args.gdb_command) + print(f"executable: {resolved_output}", file=sys.stderr, flush=True) + print( + f"gdb: {gdb_command} {resolved_output} " + f"-ex 'target remote :{args.gdb_port}'", + file=sys.stderr, + flush=True, + ) + print("gdb: use 'si' or 'c'; do not use 'run'", file=sys.stderr, flush=True) + qemu_cmd.extend([str(output), *args.program_args]) + + run_result = run_command(qemu_cmd) + sys.stdout.buffer.write(run_result.stdout) + sys.stderr.buffer.write(run_result.stderr) + if run_result.stdout and not run_result.stdout.endswith(b"\n"): + print() + print(f"$? = {run_result.returncode}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main())