cortex-crash 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cortex_crash-1.0.0/LICENSE +21 -0
- cortex_crash-1.0.0/PKG-INFO +43 -0
- cortex_crash-1.0.0/README.md +25 -0
- cortex_crash-1.0.0/cortex_crash/__init__.py +2 -0
- cortex_crash-1.0.0/cortex_crash/cfsr.py +59 -0
- cortex_crash-1.0.0/cortex_crash/cli.py +88 -0
- cortex_crash-1.0.0/cortex_crash/snippet.py +44 -0
- cortex_crash-1.0.0/cortex_crash/unwind.py +111 -0
- cortex_crash-1.0.0/cortex_crash.egg-info/PKG-INFO +43 -0
- cortex_crash-1.0.0/cortex_crash.egg-info/SOURCES.txt +13 -0
- cortex_crash-1.0.0/cortex_crash.egg-info/dependency_links.txt +1 -0
- cortex_crash-1.0.0/cortex_crash.egg-info/entry_points.txt +2 -0
- cortex_crash-1.0.0/cortex_crash.egg-info/top_level.txt +1 -0
- cortex_crash-1.0.0/pyproject.toml +31 -0
- cortex_crash-1.0.0/setup.cfg +4 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Trust & Thread
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cortex-crash
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: All-in-one ARM Cortex-M HardFault triage suite (CFSR decoder, stack unwinder, C handlers)
|
|
5
|
+
Author-email: Trust & Thread <contact@trustandthread.com>
|
|
6
|
+
Project-URL: Homepage, https://www.trustandthread.com
|
|
7
|
+
Project-URL: Bug Tracker, https://github.com/GaneshMba4066/cortex-crash/issues
|
|
8
|
+
Project-URL: Documentation, https://www.trustandthread.com/firmware-checklist/
|
|
9
|
+
Keywords: arm,cortex-m,stm32,hardfault,firmware,debugging,unwind,cfsr
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Classifier: Topic :: Software Development :: Embedded Systems
|
|
14
|
+
Requires-Python: >=3.6
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
License-File: LICENSE
|
|
17
|
+
Dynamic: license-file
|
|
18
|
+
|
|
19
|
+
# cortex-crash
|
|
20
|
+
|
|
21
|
+
All-in-one ARM Cortex-M HardFault triage suite: decode CFSR status registers, unwind hardware-stacked frames with `addr2line`, and generate drop-in C/ASM fault capture stubs.
|
|
22
|
+
|
|
23
|
+
## Installation
|
|
24
|
+
|
|
25
|
+
pip install cortex-crash
|
|
26
|
+
|
|
27
|
+
## Usage
|
|
28
|
+
|
|
29
|
+
# Print drop-in naked C/ASM HardFault handler
|
|
30
|
+
cortex-crash init-c
|
|
31
|
+
|
|
32
|
+
# Decode fault registers
|
|
33
|
+
cortex-crash cfsr 0x00010200 0x20001FF7
|
|
34
|
+
|
|
35
|
+
# Unwind 8-word stack frame and resolve line from ELF
|
|
36
|
+
cortex-crash unwind 0x0 0x1 0x20000000 0x40021000 0x0 0x08001255 0x08001290 0x21000000 app.elf
|
|
37
|
+
|
|
38
|
+
# Unified triage
|
|
39
|
+
cortex-crash parse --cfsr 0x00010200 --bfar 0x20001FF7 --frame 0x0 0x1 0x20000000 0x40021000 0x0 0x08001255 0x08001290 0x21000000
|
|
40
|
+
|
|
41
|
+
## License
|
|
42
|
+
|
|
43
|
+
MIT — Trust & Thread
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# cortex-crash
|
|
2
|
+
|
|
3
|
+
All-in-one ARM Cortex-M HardFault triage suite: decode CFSR status registers, unwind hardware-stacked frames with `addr2line`, and generate drop-in C/ASM fault capture stubs.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
pip install cortex-crash
|
|
8
|
+
|
|
9
|
+
## Usage
|
|
10
|
+
|
|
11
|
+
# Print drop-in naked C/ASM HardFault handler
|
|
12
|
+
cortex-crash init-c
|
|
13
|
+
|
|
14
|
+
# Decode fault registers
|
|
15
|
+
cortex-crash cfsr 0x00010200 0x20001FF7
|
|
16
|
+
|
|
17
|
+
# Unwind 8-word stack frame and resolve line from ELF
|
|
18
|
+
cortex-crash unwind 0x0 0x1 0x20000000 0x40021000 0x0 0x08001255 0x08001290 0x21000000 app.elf
|
|
19
|
+
|
|
20
|
+
# Unified triage
|
|
21
|
+
cortex-crash parse --cfsr 0x00010200 --bfar 0x20001FF7 --frame 0x0 0x1 0x20000000 0x40021000 0x0 0x08001255 0x08001290 0x21000000
|
|
22
|
+
|
|
23
|
+
## License
|
|
24
|
+
|
|
25
|
+
MIT — Trust & Thread
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"""CFSR and fault status register decoder logic."""
|
|
2
|
+
|
|
3
|
+
FAULT_STATUS_BITS = {
|
|
4
|
+
"DIVBYZERO": (1 << 25, "UsageFault", "Divide by zero executed (CCR.DIV_0_TRP enabled)."),
|
|
5
|
+
"UNALIGNED": (1 << 24, "UsageFault", "Unaligned memory access performed (CCR.UNALIGN_TRP enabled)."),
|
|
6
|
+
"NOCP": (1 << 19, "UsageFault", "Attempted coprocessor access without enable (e.g. FPU CPACR)."),
|
|
7
|
+
"INVPC": (1 << 18, "UsageFault", "Invalid PC load on EXC_RETURN or illegal integrity check."),
|
|
8
|
+
"INVSTATE": (1 << 17, "UsageFault", "Attempted execution in invalid EPSR state (e.g. Thumb bit cleared)."),
|
|
9
|
+
"UNDEFINSTR": (1 << 16, "UsageFault", "Undefined instruction encountered."),
|
|
10
|
+
"BFARVALID": (1 << 15, "BusFault", "BFAR address holds the valid faulting location."),
|
|
11
|
+
"LSPERR": (1 << 13, "BusFault", "Bus fault occurred during lazy FP state preservation."),
|
|
12
|
+
"STKERR": (1 << 12, "BusFault", "Bus fault on exception entry stacking."),
|
|
13
|
+
"UNSTKERR": (1 << 11, "BusFault", "Bus fault on exception exit unstacking."),
|
|
14
|
+
"IMPRECISERR": (1 << 10, "BusFault", "Imprecise data bus error (asynchronous)."),
|
|
15
|
+
"PRECISERR": (1 << 9, "BusFault", "Precise data bus error on valid address."),
|
|
16
|
+
"IBUSERR": (1 << 8, "BusFault", "Instruction bus error during prefetch."),
|
|
17
|
+
"MMARVALID": (1 << 7, "MemManage", "MMFAR holds valid address of memory management fault."),
|
|
18
|
+
"MLSPERR": (1 << 5, "MemManage", "MemManage fault during lazy FP preservation."),
|
|
19
|
+
"MSTKERR": (1 << 4, "MemManage", "MemManage fault during exception entry stacking."),
|
|
20
|
+
"MUNSTKERR": (1 << 3, "MemManage", "MemManage fault during exception exit unstacking."),
|
|
21
|
+
"DACCVIOL": (1 << 1, "MemManage", "Data access violation (MPU protection or NULL pointer dereference)."),
|
|
22
|
+
"IACCVIOL": (1 << 0, "MemManage", "Instruction access violation (MPU XN region or unmapped memory)."),
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def classify_address(addr):
|
|
27
|
+
if 0x00000000 <= addr < 0x20000000:
|
|
28
|
+
return "Flash / Code Memory"
|
|
29
|
+
if 0x20000000 <= addr < 0x40000000:
|
|
30
|
+
return "SRAM"
|
|
31
|
+
if 0x40000000 <= addr < 0x60000000:
|
|
32
|
+
return "Peripheral Bus"
|
|
33
|
+
if 0x60000000 <= addr < 0xA0000000:
|
|
34
|
+
return "External RAM / Memory"
|
|
35
|
+
if 0xE0000000 <= addr <= 0xFFFFFFFF:
|
|
36
|
+
return "Private Peripheral Bus (PPB / System Control)"
|
|
37
|
+
return "Reserved / Undefined Region"
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def decode_cfsr(cfsr, bfar=None, mmfar=None):
|
|
41
|
+
lines = [f"CFSR: 0x{cfsr:08X}"]
|
|
42
|
+
active = []
|
|
43
|
+
for bit_name, (mask, fault_type, desc) in FAULT_STATUS_BITS.items():
|
|
44
|
+
if cfsr & mask:
|
|
45
|
+
active.append((fault_type, bit_name, desc))
|
|
46
|
+
|
|
47
|
+
if not active:
|
|
48
|
+
lines.append(" No standard CFSR fault flags set.")
|
|
49
|
+
else:
|
|
50
|
+
for fault_type, bit_name, desc in active:
|
|
51
|
+
lines.append(f" [{fault_type}] {bit_name:<11} : {desc}")
|
|
52
|
+
|
|
53
|
+
if bfar is not None and (cfsr & (1 << 15)):
|
|
54
|
+
lines.append(f"\n BFAR: 0x{bfar:08X} ({classify_address(bfar)})")
|
|
55
|
+
|
|
56
|
+
if mmfar is not None and (cfsr & (1 << 7)):
|
|
57
|
+
lines.append(f"\n MMFAR: 0x{mmfar:08X} ({classify_address(mmfar)})")
|
|
58
|
+
|
|
59
|
+
return lines
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"""Main entry point for unified CLI."""
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import sys
|
|
5
|
+
from .cfsr import decode_cfsr
|
|
6
|
+
from .unwind import decode_frame
|
|
7
|
+
from .snippet import print_snippet
|
|
8
|
+
|
|
9
|
+
VERSION = "1.0.0"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def parse_hex(val):
|
|
13
|
+
return int(val.strip().lower().replace("0x", "").replace(",", ""), 16)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def main():
|
|
17
|
+
parser = argparse.ArgumentParser(
|
|
18
|
+
prog="cortex-crash",
|
|
19
|
+
description="Unified ARM Cortex-M Crash Diagnostics Suite by Trust & Thread"
|
|
20
|
+
)
|
|
21
|
+
parser.add_argument("-v", "--version", action="version", version=f"cortex-crash v{VERSION}")
|
|
22
|
+
|
|
23
|
+
subparsers = parser.add_subparsers(dest="command")
|
|
24
|
+
|
|
25
|
+
# Command: parse
|
|
26
|
+
p_parse = subparsers.add_parser("parse", help="Decode registers and unwound stack frame")
|
|
27
|
+
p_parse.add_argument("--cfsr", type=str, help="CFSR hex value (e.g. 0x00010200)")
|
|
28
|
+
p_parse.add_argument("--bfar", type=str, help="BFAR hex value")
|
|
29
|
+
p_parse.add_argument("--mmfar", type=str, help="MMFAR hex value")
|
|
30
|
+
p_parse.add_argument("--frame", nargs=8, help="8 stacked registers: r0 r1 r2 r3 r12 lr pc xpsr")
|
|
31
|
+
p_parse.add_argument("elf", nargs="?", help="Optional .elf file to resolve symbols")
|
|
32
|
+
|
|
33
|
+
# Command: cfsr
|
|
34
|
+
p_cfsr = subparsers.add_parser("cfsr", help="Quickly decode CFSR hex")
|
|
35
|
+
p_cfsr.add_argument("cfsr", type=str, help="CFSR register hex value")
|
|
36
|
+
p_cfsr.add_argument("bfar", nargs="?", type=str, help="Optional BFAR address")
|
|
37
|
+
|
|
38
|
+
# Command: unwind
|
|
39
|
+
p_unwind = subparsers.add_parser("unwind", help="Quickly unwind stack frame")
|
|
40
|
+
p_unwind.add_argument("regs", nargs=8, help="8 stacked registers: r0 r1 r2 r3 r12 lr pc xpsr")
|
|
41
|
+
p_unwind.add_argument("elf", nargs="?", help="Optional .elf file")
|
|
42
|
+
|
|
43
|
+
# Command: init-c
|
|
44
|
+
subparsers.add_parser("init-c", help="Print drop-in C/ASM HardFault handler source")
|
|
45
|
+
|
|
46
|
+
args = parser.parse_args()
|
|
47
|
+
|
|
48
|
+
if not args.command:
|
|
49
|
+
parser.print_help()
|
|
50
|
+
return 0
|
|
51
|
+
|
|
52
|
+
print("=" * 66)
|
|
53
|
+
print(f" TRUST & THREAD :: CORTEX-M CRASH ANALYZER v{VERSION}")
|
|
54
|
+
print("=" * 66)
|
|
55
|
+
|
|
56
|
+
if args.command == "init-c":
|
|
57
|
+
print_snippet()
|
|
58
|
+
return 0
|
|
59
|
+
|
|
60
|
+
if args.command == "cfsr":
|
|
61
|
+
cfsr_val = parse_hex(args.cfsr)
|
|
62
|
+
bfar_val = parse_hex(args.bfar) if args.bfar else None
|
|
63
|
+
print("\n".join(decode_cfsr(cfsr_val, bfar_val)))
|
|
64
|
+
|
|
65
|
+
elif args.command == "unwind":
|
|
66
|
+
frame_vals = [parse_hex(x) for x in args.regs]
|
|
67
|
+
print("\n".join(decode_frame(frame_vals, args.elf)))
|
|
68
|
+
|
|
69
|
+
elif args.command == "parse":
|
|
70
|
+
if args.cfsr:
|
|
71
|
+
cfsr_val = parse_hex(args.cfsr)
|
|
72
|
+
bfar_val = parse_hex(args.bfar) if args.bfar else None
|
|
73
|
+
mmfar_val = parse_hex(args.mmfar) if args.mmfar else None
|
|
74
|
+
print("\n[Fault Registers]")
|
|
75
|
+
print("\n".join(decode_cfsr(cfsr_val, bfar_val, mmfar_val)))
|
|
76
|
+
if args.frame:
|
|
77
|
+
frame_vals = [parse_hex(x) for x in args.frame]
|
|
78
|
+
print("\n[Stack Frame]")
|
|
79
|
+
print("\n".join(decode_frame(frame_vals, args.elf)))
|
|
80
|
+
|
|
81
|
+
print("\n" + "=" * 66)
|
|
82
|
+
print(" Free Firmware Checklist: https://www.trustandthread.com/firmware-checklist/")
|
|
83
|
+
print("=" * 66)
|
|
84
|
+
return 0
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
if __name__ == "__main__":
|
|
88
|
+
sys.exit(main())
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"""Embedded C/assembly snippet generator."""
|
|
2
|
+
|
|
3
|
+
C_STUB = r"""/* Drop-in HardFault Handler for ARM Cortex-M (Trust & Thread) */
|
|
4
|
+
#include <stdint.h>
|
|
5
|
+
#include <stdio.h>
|
|
6
|
+
|
|
7
|
+
#define SCB_CFSR (*(volatile uint32_t *)0xE000ED28)
|
|
8
|
+
#define SCB_BFAR (*(volatile uint32_t *)0xE000ED38)
|
|
9
|
+
|
|
10
|
+
typedef struct __attribute__((packed)) {
|
|
11
|
+
uint32_t r0, r1, r2, r3, r12, lr, pc, xpsr;
|
|
12
|
+
} StackFrame_t;
|
|
13
|
+
|
|
14
|
+
void HardFault_Handler_C(StackFrame_t *frame, uint32_t lr_val) {
|
|
15
|
+
uint32_t cfsr = SCB_CFSR;
|
|
16
|
+
uint32_t bfar = SCB_BFAR;
|
|
17
|
+
printf("\r\n--- CORTEX-M CRASH DUMP ---\r\n");
|
|
18
|
+
printf("CFSR: 0x%08lX | BFAR: 0x%08lX\r\n", cfsr, bfar);
|
|
19
|
+
printf("STACK: 0x%08lX 0x%08lX 0x%08lX 0x%08lX 0x%08lX 0x%08lX 0x%08lX 0x%08lX\r\n",
|
|
20
|
+
frame->r0, frame->r1, frame->r2, frame->r3,
|
|
21
|
+
frame->r12, frame->lr, frame->pc, frame->xpsr);
|
|
22
|
+
printf("RUN COMMAND:\r\n");
|
|
23
|
+
printf("cortex-crash parse --cfsr 0x%08lX --bfar 0x%08lX --frame 0x%08lX 0x%08lX 0x%08lX 0x%08lX 0x%08lX 0x%08lX 0x%08lX 0x%08lX app.elf\r\n",
|
|
24
|
+
cfsr, bfar, frame->r0, frame->r1, frame->r2, frame->r3,
|
|
25
|
+
frame->r12, frame->lr, frame->pc, frame->xpsr);
|
|
26
|
+
__asm volatile("bkpt #1");
|
|
27
|
+
while (1);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
__attribute__((naked)) void HardFault_Handler(void) {
|
|
31
|
+
__asm volatile(
|
|
32
|
+
"tst lr, #4 \n"
|
|
33
|
+
"ite eq \n"
|
|
34
|
+
"mrseq r0, msp \n"
|
|
35
|
+
"mrsne r0, psp \n"
|
|
36
|
+
"mov r1, lr \n"
|
|
37
|
+
"b HardFault_Handler_C\n"
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def print_snippet():
|
|
44
|
+
print(C_STUB)
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"""Stack unwinder and symbol resolution logic."""
|
|
2
|
+
|
|
3
|
+
import shutil
|
|
4
|
+
import subprocess
|
|
5
|
+
|
|
6
|
+
FRAME_REGS = ["r0", "r1", "r2", "r3", "r12", "lr", "pc", "xpsr"]
|
|
7
|
+
|
|
8
|
+
EXC_RETURN_KIND = {
|
|
9
|
+
0x01: ("Handler mode, MSP, no FP", False),
|
|
10
|
+
0x09: ("Thread mode, MSP, no FP", False),
|
|
11
|
+
0x0D: ("Thread mode, PSP, no FP", False),
|
|
12
|
+
0x11: ("Handler mode, MSP, FP extended frame", True),
|
|
13
|
+
0x19: ("Thread mode, MSP, FP extended frame", True),
|
|
14
|
+
0x1D: ("Thread mode, PSP, FP extended frame", True),
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def pick_addr2line():
|
|
19
|
+
for t in ("arm-none-eabi-addr2line", "addr2line"):
|
|
20
|
+
if shutil.which(t):
|
|
21
|
+
return t
|
|
22
|
+
return None
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def addr2line(tool, elf, addr):
|
|
26
|
+
if not tool or not elf:
|
|
27
|
+
return None
|
|
28
|
+
try:
|
|
29
|
+
r = subprocess.run([tool, "-f", "-C", "-e", elf, f"0x{addr:08X}"],
|
|
30
|
+
capture_output=True, text=True, timeout=10)
|
|
31
|
+
lines = [l.strip() for l in r.stdout.strip().splitlines() if l.strip()]
|
|
32
|
+
if len(lines) >= 2 and "??" not in lines[0]:
|
|
33
|
+
return lines[0], lines[1]
|
|
34
|
+
except Exception:
|
|
35
|
+
pass
|
|
36
|
+
return None
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def decode_xpsr(xpsr):
|
|
40
|
+
lines = []
|
|
41
|
+
ipsr = xpsr & 0x1FF
|
|
42
|
+
lines.append(f"IPSR = {ipsr}")
|
|
43
|
+
if ipsr == 0:
|
|
44
|
+
lines.append(" Active execution mode: Thread Mode")
|
|
45
|
+
elif ipsr == 2:
|
|
46
|
+
lines.append(" Active exception: NMI")
|
|
47
|
+
elif ipsr == 3:
|
|
48
|
+
lines.append(" Active exception: HardFault")
|
|
49
|
+
elif ipsr == 4:
|
|
50
|
+
lines.append(" Active exception: MemManage")
|
|
51
|
+
elif ipsr == 5:
|
|
52
|
+
lines.append(" Active exception: BusFault")
|
|
53
|
+
elif ipsr == 6:
|
|
54
|
+
lines.append(" Active exception: UsageFault")
|
|
55
|
+
elif 16 <= ipsr < 256:
|
|
56
|
+
lines.append(f" Active interrupt: IRQ{ipsr - 16}")
|
|
57
|
+
|
|
58
|
+
if xpsr & (1 << 24):
|
|
59
|
+
lines.append(" Thumb bit set (EPSR.T=1) — valid execution state")
|
|
60
|
+
else:
|
|
61
|
+
lines.append(" [!] Thumb bit CLEAR (EPSR.T=0) — invalid state / corrupt branch target")
|
|
62
|
+
return lines
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def decode_frame(frame, elf=None):
|
|
66
|
+
tool = pick_addr2line()
|
|
67
|
+
lines = []
|
|
68
|
+
lines.append(f" {'Reg':<6} {'Value':<12} Notes")
|
|
69
|
+
lines.append("-" * 40)
|
|
70
|
+
lr_val = frame[5]
|
|
71
|
+
exc_return = (lr_val & 0xFFFFFFF0) == 0xFFFFFFF0
|
|
72
|
+
for name, val in zip(FRAME_REGS, frame):
|
|
73
|
+
if name == "lr" and exc_return:
|
|
74
|
+
note = "(EXC_RETURN)"
|
|
75
|
+
elif name in ("pc", "lr") and (val & 1):
|
|
76
|
+
note = "(Thumb bit set)"
|
|
77
|
+
else:
|
|
78
|
+
note = ""
|
|
79
|
+
lines.append(f" {name:<6} 0x{val:08X} {note}")
|
|
80
|
+
lines.append("-" * 40)
|
|
81
|
+
|
|
82
|
+
lines.append("\n[Execution State]")
|
|
83
|
+
for l in decode_xpsr(frame[7]):
|
|
84
|
+
lines.append(" " + l)
|
|
85
|
+
|
|
86
|
+
if exc_return:
|
|
87
|
+
n = lr_val & 0x1F
|
|
88
|
+
desc, fp = EXC_RETURN_KIND.get(n, (f"Variant 0x{lr_val & 0xFF:X}", not bool(lr_val & (1 << 4))))
|
|
89
|
+
lines.append(f"\n[EXC_RETURN] 0x{lr_val:08X} ({desc})")
|
|
90
|
+
if fp:
|
|
91
|
+
lines.append(" Extended frame: 26 words stacked (includes FP registers).")
|
|
92
|
+
else:
|
|
93
|
+
lines.append(" Basic frame: 8 words stacked.")
|
|
94
|
+
|
|
95
|
+
pc = frame[6] & ~1
|
|
96
|
+
lines.append("\n[Crash Site]")
|
|
97
|
+
lines.append(f" PC: 0x{pc:08X}")
|
|
98
|
+
res_pc = addr2line(tool, elf, pc)
|
|
99
|
+
if res_pc:
|
|
100
|
+
lines.append(f" -> {res_pc[0]} at {res_pc[1]}")
|
|
101
|
+
|
|
102
|
+
if not exc_return:
|
|
103
|
+
lr = lr_val & ~1
|
|
104
|
+
lines.append(f" LR: 0x{lr:08X}")
|
|
105
|
+
res_lr = addr2line(tool, elf, lr)
|
|
106
|
+
if res_lr:
|
|
107
|
+
lines.append(f" -> {res_lr[0]} at {res_lr[1]}")
|
|
108
|
+
|
|
109
|
+
if elf and not tool:
|
|
110
|
+
lines.append("\n [!] arm-none-eabi-addr2line not found in PATH.")
|
|
111
|
+
return lines
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cortex-crash
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: All-in-one ARM Cortex-M HardFault triage suite (CFSR decoder, stack unwinder, C handlers)
|
|
5
|
+
Author-email: Trust & Thread <contact@trustandthread.com>
|
|
6
|
+
Project-URL: Homepage, https://www.trustandthread.com
|
|
7
|
+
Project-URL: Bug Tracker, https://github.com/GaneshMba4066/cortex-crash/issues
|
|
8
|
+
Project-URL: Documentation, https://www.trustandthread.com/firmware-checklist/
|
|
9
|
+
Keywords: arm,cortex-m,stm32,hardfault,firmware,debugging,unwind,cfsr
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Classifier: Topic :: Software Development :: Embedded Systems
|
|
14
|
+
Requires-Python: >=3.6
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
License-File: LICENSE
|
|
17
|
+
Dynamic: license-file
|
|
18
|
+
|
|
19
|
+
# cortex-crash
|
|
20
|
+
|
|
21
|
+
All-in-one ARM Cortex-M HardFault triage suite: decode CFSR status registers, unwind hardware-stacked frames with `addr2line`, and generate drop-in C/ASM fault capture stubs.
|
|
22
|
+
|
|
23
|
+
## Installation
|
|
24
|
+
|
|
25
|
+
pip install cortex-crash
|
|
26
|
+
|
|
27
|
+
## Usage
|
|
28
|
+
|
|
29
|
+
# Print drop-in naked C/ASM HardFault handler
|
|
30
|
+
cortex-crash init-c
|
|
31
|
+
|
|
32
|
+
# Decode fault registers
|
|
33
|
+
cortex-crash cfsr 0x00010200 0x20001FF7
|
|
34
|
+
|
|
35
|
+
# Unwind 8-word stack frame and resolve line from ELF
|
|
36
|
+
cortex-crash unwind 0x0 0x1 0x20000000 0x40021000 0x0 0x08001255 0x08001290 0x21000000 app.elf
|
|
37
|
+
|
|
38
|
+
# Unified triage
|
|
39
|
+
cortex-crash parse --cfsr 0x00010200 --bfar 0x20001FF7 --frame 0x0 0x1 0x20000000 0x40021000 0x0 0x08001255 0x08001290 0x21000000
|
|
40
|
+
|
|
41
|
+
## License
|
|
42
|
+
|
|
43
|
+
MIT — Trust & Thread
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
cortex_crash/__init__.py
|
|
5
|
+
cortex_crash/cfsr.py
|
|
6
|
+
cortex_crash/cli.py
|
|
7
|
+
cortex_crash/snippet.py
|
|
8
|
+
cortex_crash/unwind.py
|
|
9
|
+
cortex_crash.egg-info/PKG-INFO
|
|
10
|
+
cortex_crash.egg-info/SOURCES.txt
|
|
11
|
+
cortex_crash.egg-info/dependency_links.txt
|
|
12
|
+
cortex_crash.egg-info/entry_points.txt
|
|
13
|
+
cortex_crash.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
cortex_crash
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "cortex-crash"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
authors = [
|
|
9
|
+
{ name="Trust & Thread", email="contact@trustandthread.com" },
|
|
10
|
+
]
|
|
11
|
+
description = "All-in-one ARM Cortex-M HardFault triage suite (CFSR decoder, stack unwinder, C handlers)"
|
|
12
|
+
readme = "README.md"
|
|
13
|
+
requires-python = ">=3.6"
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Programming Language :: Python :: 3",
|
|
16
|
+
"License :: OSI Approved :: MIT License",
|
|
17
|
+
"Operating System :: OS Independent",
|
|
18
|
+
"Topic :: Software Development :: Embedded Systems",
|
|
19
|
+
]
|
|
20
|
+
keywords = ["arm", "cortex-m", "stm32", "hardfault", "firmware", "debugging", "unwind", "cfsr"]
|
|
21
|
+
|
|
22
|
+
[tool.setuptools]
|
|
23
|
+
packages = ["cortex_crash"]
|
|
24
|
+
|
|
25
|
+
[project.urls]
|
|
26
|
+
"Homepage" = "https://www.trustandthread.com"
|
|
27
|
+
"Bug Tracker" = "https://github.com/GaneshMba4066/cortex-crash/issues"
|
|
28
|
+
"Documentation" = "https://www.trustandthread.com/firmware-checklist/"
|
|
29
|
+
|
|
30
|
+
[project.scripts]
|
|
31
|
+
cortex-crash = "cortex_crash.cli:main"
|