加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
stack_tracer.py 34.90 KB
一键复制 编辑 原始数据 按行查看 历史
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
from __future__ import print_function
import sys
import os
import struct
import argparse
import re
from elftools.elf.elffile import ELFFile
from elftools.dwarf.descriptions import describe_form_class
from capstone import Cs, CS_ARCH_ARM, CS_MODE_THUMB
import subprocess
# 常量定义
REGISTERS = [
'R0', 'R1', 'R2', 'R3', # 参数传递和临时值
'R4', 'R5', 'R6', 'R7', # 局部变量
'R8', 'R9', 'R10', 'R11', # 局部变量
'R12', # IP(Inter-Procedure call)
'SP', # R13 - 栈指针
'LR', # R14 - 链接寄存器
'PC' # R15 - 程序计数器
]
SPECIAL_REGISTERS = [
'xPSR', # 程序状态寄存器(包含APSR/IPSR/EPSR)
'CFBP', # 控制寄存器组合(CONTROL/FAULTMASK/BASEPRI/PRIMASK)
'MSP', # 主栈指针
'PSP', # 进程栈指针
]
FAULT_REGISTERS = [
'CFSR', # 可配置故障状态寄存器
'HFSR', # 硬故障状态寄存器
'DFSR', # 调试故障状态寄存器
'MMFAR', # 存储器管理故障地址寄存器
'BFAR', # 总线故障地址寄存器
'AFSR' # 辅助故障状态寄存器
]
class StackTracer(object):
def __init__(self, axf_path, ram_dump_path, reg_dump_path=None, map_path=None):
# 基础配置
self.registers = {} # 寄存器值
self.memory_regions = {} # 内存区域信息
self.section_layout = {} # 段布局
self.map_symbols = {} # MAP文件中的符号信息
self.symbols = {} # AXF文件中的符号信息
# 加载AXF文件
self.AXF = axf_path
self._axf = open(axf_path, 'rb')
self.elf = ELFFile(self._axf)
# 加载RAM dump
with open(ram_dump_path, 'rb') as f:
self.ram_data = f.read()
# 加载寄存器值
if reg_dump_path:
self.load_registers(reg_dump_path)
# 加载MAP文件
if map_path:
self.load_map_file(map_path)
# 初始化反汇编引擎
self.cs = Cs(CS_ARCH_ARM, CS_MODE_THUMB)
self.cs.detail = True
# 加载符号
self.load_symbols()
def __del__(self):
"""清理资源"""
if hasattr(self, '_axf'):
self._axf.close()
def load_registers_from_text(self, text):
"""从文本解析寄存器值"""
register_patterns = {
'PC': r'PC\s*=\s*([0-9A-Fa-f]{8})',
'LR': r'R14\(LR\)\s*=\s*([0-9A-Fa-f]{8})',
'SP': r'SP\(R13\)\s*=\s*([0-9A-Fa-f]{8})',
'MSP': r'MSP\s*=\s*([0-9A-Fa-f]{8})',
'PSP': r'PSP\s*=\s*([0-9A-Fa-f]{8})',
'XPSR': r'XPSR\s*=\s*([0-9A-Fa-f]{8})',
'CFBP': r'CFBP\s*=\s*([0-9A-Fa-f]{8})',
'CFSR': r'CFSR\s*=\s*([0-9A-Fa-f]{8})',
'HFSR': r'HFSR\s*=\s*([0-9A-Fa-f]{8})',
'MMFAR': r'MMFAR\s*=\s*([0-9A-Fa-f]{8})',
'BFAR': r'BFAR\s*=\s*([0-9A-Fa-f]{8})'
}
# 添加R0-R12的模式
for i in range(13):
register_patterns['R%d' % i] = r'R%d\s*=\s*([0-9A-Fa-f]{8})' % i
for reg, pattern in register_patterns.items():
match = re.search(pattern, text)
if match:
self.registers[reg] = int(match.group(1), 16)
def load_registers(self, reg_path):
"""加载寄存器文件"""
try:
with open(reg_path, 'r') as f:
content = f.read()
self.load_registers_from_text(content)
self.parse_special_registers()
except Exception as e:
print("Warning: Failed to load registers:", str(e))
def parse_special_registers(self):
"""解析特殊寄存器的位域"""
if 'XPSR' in self.registers:
xpsr = self.registers['XPSR']
self.registers.update({
'IPSR': xpsr & 0x1FF, # 异常号
'EPSR': (xpsr >> 16) & 0xFF, # 执行状态
'APSR_N': (xpsr >> 31) & 1, # 负标志
'APSR_Z': (xpsr >> 30) & 1, # 零标志
'APSR_C': (xpsr >> 29) & 1, # 进位标志
'APSR_V': (xpsr >> 28) & 1, # 溢出标志
'APSR_Q': (xpsr >> 27) & 1 # 饱和标志
})
if 'CFBP' in self.registers:
cfbp = self.registers['CFBP']
self.registers.update({
'CONTROL': cfbp & 0xFF,
'FAULTMASK': (cfbp >> 8) & 0xFF,
'BASEPRI': (cfbp >> 16) & 0xFF,
'PRIMASK': (cfbp >> 24) & 0xFF
})
def print_registers(self):
"""打印寄存器状态"""
print("\n=== Register State ===")
# 打印通用寄存器
for i in range(0, len(REGISTERS), 4):
regs = REGISTERS[i:i+4]
values = []
for reg in regs:
val = self.registers.get(reg, 0)
values.append("{:3}: 0x{:08X}".format(reg, val))
print(" ".join(values))
# 打印特殊寄存器
print("\n=== Special Registers ===")
if 'XPSR' in self.registers:
xpsr = self.registers['XPSR']
print("xPSR : 0x{:08X}".format(xpsr))
print(" IPSR: {} ({})".format(
self.registers.get('IPSR', 0),
"HardFault" if self.registers.get('IPSR', 0) == 3 else "Unknown"
))
print(" APSR: N={} Z={} C={} V={} Q={}".format(
self.registers.get('APSR_N', 0),
self.registers.get('APSR_Z', 0),
self.registers.get('APSR_C', 0),
self.registers.get('APSR_V', 0),
self.registers.get('APSR_Q', 0)
))
if 'CFBP' in self.registers:
print("\nControl Registers:")
print(" CONTROL : 0x{:02X}".format(self.registers.get('CONTROL', 0)))
print(" PRIMASK : 0x{:02X}".format(self.registers.get('PRIMASK', 0)))
print(" BASEPRI : 0x{:02X}".format(self.registers.get('BASEPRI', 0)))
print(" FAULTMASK: 0x{:02X}".format(self.registers.get('FAULTMASK', 0)))
def load_map_file(self, map_path):
"""解析MAP文件"""
current_section = None
try:
with open(map_path, 'r') as f:
lines = f.readlines()
for line in lines:
line = line.strip()
# 解析内存区域
if 'Memory Configuration' in line:
current_section = 'memory'
continue
elif 'Linker script and memory map' in line:
current_section = 'layout'
continue
# 跳过空行
if not line:
continue
if current_section == 'memory':
# 解析内存区域行: "RAM 0x20000000 0x00020000"
parts = line.split()
if len(parts) >= 3 and not line.startswith('.'):
name = parts[0]
try:
start = int(parts[1], 16)
size = int(parts[2], 16)
self.memory_regions[name] = {
'start': start,
'size': size,
'end': start + size
}
except ValueError:
continue
elif current_section == 'layout':
# 解析section和符号定义
if line.startswith('.'):
# 段定义行: ".text 0x08000000"
parts = line.split()
if len(parts) >= 2:
try:
addr = int(parts[-1], 16)
name = ' '.join(parts[:-1])
self.section_layout[name] = {
'address': addr,
'name': name
}
except ValueError:
continue
elif line.startswith('0x'):
# 符号定义行: "0x08000000 Reset_Handler"
parts = line.split()
if len(parts) >= 2:
try:
addr = int(parts[0], 16)
name = ' '.join(parts[1:])
# 过滤掉文件路径等非符号内容
if not name.endswith(('.o', '.a', '/')):
self.map_symbols[name] = {
'address': addr,
'name': name,
'size': 0 # 大小需要后续处理
}
except ValueError:
continue
except Exception as e:
print("Warning: Failed to parse MAP file:", str(e))
# 计算符号大小
self._calculate_symbol_sizes()
def _calculate_symbol_sizes(self):
"""计算符号大小"""
# 按地址排序所有符号
sorted_symbols = sorted(
self.map_symbols.items(),
key=lambda x: x[1]['address']
)
# 计算每个符号的大小(使用下一个符号的地址)
for i in range(len(sorted_symbols) - 1):
curr_sym = sorted_symbols[i][1]
next_sym = sorted_symbols[i + 1][1]
curr_sym['size'] = next_sym['address'] - curr_sym['address']
# 为最后一个符号设置一个默认大小
if sorted_symbols:
last_sym = sorted_symbols[-1][1]
last_sym['size'] = 0x100 # 默认大小
def find_symbol_by_address(self, addr):
"""查找地址对应的符号"""
# 首先在ELF符号表中查找
sym = self._find_function_in_axf(addr)
if sym:
return sym
# 如果没找到,在MAP文件符号中查找
return self._find_function_in_map(addr)
def _find_function_in_map(self, addr):
"""从MAP文件符号中查找函数"""
# 查找最近的符号
closest_sym = None
for sym_name, sym_info in self.map_symbols.items():
sym_addr = sym_info['address']
sym_size = sym_info['size']
if sym_addr <= addr < (sym_addr + sym_size):
if not closest_sym or sym_addr > closest_sym[1]['address']:
closest_sym = (sym_name, sym_info)
if closest_sym:
# 转换为统一的格式
return {
'name': closest_sym[0],
'addr': closest_sym[1]['address'],
'size': closest_sym[1]['size']
}
return None
def load_symbols(self):
"""加载符号表"""
symtab = self.elf.get_section_by_name('.symtab')
if symtab:
for sym in symtab.iter_symbols():
if sym['st_info']['type'] == 'STT_FUNC':
# 只保存函数符号
self.symbols[sym['st_value']] = {
'name': sym.name,
'addr': sym['st_value'],
'size': sym['st_size']
}
def load_line_numbers(self):
"""加载行号信息"""
if not self.debug_info:
return
for CU in self.debug_info.iter_CUs():
lineprog = self.debug_info.line_program_for_CU(CU)
if not lineprog:
continue
for entry in lineprog.get_entries():
state = entry.state
if state is None:
continue
if state.file and state.line:
file_name = lineprog['file_entry'][state.file - 1].name
if not isinstance(file_name, str):
file_name = file_name.decode('utf-8')
# 保存地址到源码的映射
self.line_map[state.address] = {
'file': file_name,
'line': state.line,
'column': state.column if hasattr(state, 'column') else 0,
'is_stmt': state.is_stmt if hasattr(state, 'is_stmt') else False
}
def get_source_location(self, addr):
"""获取源代码位置"""
if not self.line_map:
return None
# 查找最接近但不超过addr的地址
closest_addr = None
for map_addr in self.line_map:
if map_addr <= addr:
if closest_addr is None or map_addr > closest_addr:
closest_addr = map_addr
if closest_addr:
return self.line_map[closest_addr]
return None
def find_function(self, addr):
"""查找函数信息"""
# 首先在AXF符号表中查找
func = self._find_function_in_axf(addr)
if func:
return func
# 如果没找到,在MAP文件符号中查找
return self._find_function_in_map(addr)
def _find_function_in_axf(self, addr):
"""从AXF符号表中查找函数"""
for base_addr, func in self.symbols.items():
if base_addr <= addr < (base_addr + func['size']):
return func
return None
def load_symbols(self):
"""加载符号表"""
symtab = self.elf.get_section_by_name('.symtab')
if symtab:
for sym in symtab.iter_symbols():
if sym['st_info']['type'] == 'STT_FUNC':
# 只保存函数符号
self.symbols[sym['st_value']] = {
'name': sym.name,
'addr': sym['st_value'],
'size': sym['st_size']
}
def read_memory(self, addr, size):
"""读取内存数据"""
ram_base = 0x20000000 # RAM起始地址
if ram_base <= addr < ram_base + len(self.ram_data):
offset = addr - ram_base
return self.ram_data[offset:offset+size]
return None
def get_code_section(self):
"""获取代码段section"""
# 首先尝试获取.text段
section = self.elf.get_section_by_name('.text')
if section is not None:
return section
# 如果没有.text段,查找具有可执行权限的段
for section in self.elf.iter_sections():
if section['sh_flags'] & 0x4: # SHF_EXECINSTR
return section
return None
def analyze_exception_frame(self, sp):
"""分析异常栈帧"""
stack = self.read_memory(sp, 32)
if not stack:
return None
try:
return {
'r0': struct.unpack('<I', stack[0:4])[0],
'r1': struct.unpack('<I', stack[4:8])[0],
'r2': struct.unpack('<I', stack[8:12])[0],
'r3': struct.unpack('<I', stack[12:16])[0],
'r12': struct.unpack('<I', stack[16:20])[0],
'lr': struct.unpack('<I', stack[20:24])[0],
'pc': struct.unpack('<I', stack[24:28])[0],
'psr': struct.unpack('<I', stack[28:32])[0]
}
except:
return None
def unwind_stack(self, sp, max_depth=10):
"""展开调用栈"""
frames = []
current_sp = sp
code_region = None
# 获取代码区域范围
for name, region in self.memory_regions.items():
if name.upper() in ['FLASH', 'ROM', 'CODE']:
code_region = region
break
# 如果找不到代码区域,使用默认范围
if not code_region:
code_region = {
'start': 0x08000000, # 典型的Flash起始地址
'end': 0x08100000 # 假设1MB大小
}
depth = 0
prev_sp = 0
while depth < max_depth:
# 检查SP有效性
if not current_sp or current_sp % 4 != 0: # ARM要求SP四字节对齐
break
if current_sp == prev_sp: # 防止死循环
break
prev_sp = current_sp
# 读取4字节作为可能的返回地址
stack_data = self.read_memory(current_sp, 4)
if not stack_data:
break
# 解析可能的返回地址
try:
ret_addr = struct.unpack('<I', stack_data)[0]
# 检查是否是有效的代码地址
if code_region['start'] <= ret_addr <= code_region['end']:
# 对于返回地址,需要考虑指令长度
# Thumb指令是2或4字节,所以我们往前看几个地址
possible_addrs = [ret_addr - 1, ret_addr - 2, ret_addr - 3, ret_addr - 4]
for addr in possible_addrs:
func = self.find_symbol_by_address(addr)
if func:
# 确认这是一个调用指令的返回地址
code_section = self.get_code_section()
if code_section:
try:
# 检查返回地址前的指令
offset = addr - code_section['sh_addr']
if 0 <= offset < code_section['sh_size']:
code_bytes = code_section.data()[max(0, offset-4):offset]
for insn in self.cs.disasm(code_bytes, addr-len(code_bytes)):
# 检查是否是调用指令
if insn.mnemonic in ['bl', 'blx', 'b']:
frames.append({
'addr': ret_addr,
'func': func,
'sp': current_sp
})
depth += 1
break
except:
pass
break
except:
pass
# 移动到下一个可能的栈帧位置
# 1. 首先尝试通过帧指针(R7或R11)
# 2. 如果失败,则按4字节移动
fp_data = self.read_memory(current_sp + 4, 4) # 尝试读取下一个值作为帧指针
if fp_data:
try:
next_fp = struct.unpack('<I', fp_data)[0]
if (next_fp > current_sp and # 帧指针应该增长
next_fp % 4 == 0 and # 必须4字节对齐
next_fp < current_sp + 0x1000): # 不能增长太多
current_sp = next_fp
continue
except:
pass
current_sp += 4
return frames
def disassemble_context(self, addr, size=32):
"""反汇编出错位置的上下文"""
code_section = self.get_code_section()
if not code_section:
print("No code section found in ELF file")
return
try:
offset = addr - code_section['sh_addr']
if offset < 0 or offset >= code_section['sh_size']:
print("Address 0x{:08X} is outside of code section".format(addr))
return
# 往前取一些指令
start_offset = max(0, offset - size//2)
code_bytes = code_section.data()[start_offset:offset + size//2]
start_addr = code_section['sh_addr'] + start_offset
print("\nDisassembly context around 0x{:08X}:".format(addr))
print("-" * 60)
for insn in self.cs.disasm(code_bytes, start_addr):
prefix = "=>" if insn.address == addr else " "
print("{} 0x{:08X}: {:<8} {}".format(
prefix, insn.address, insn.mnemonic, insn.op_str))
print("-" * 60)
except Exception as e:
print("Disassembly failed:", str(e))
def parse_fault_registers(self):
"""分析故障寄存器以确定异常原因"""
# 首先检查是否是HardFault
if self.registers.get('IPSR', 0) == 3: # HardFault
fault_causes = ["Hard Fault Exception"]
# 检查HFSR
hfsr = self.registers.get('HFSR', 0)
if hfsr:
if hfsr & (1 << 30):
fault_causes.append("Forced Hard Fault - Other configurable fault escalated to HardFault")
if hfsr & (1 << 31):
fault_causes.append("Vector Table Hard Fault")
# 检查CFSR
cfsr = self.registers.get('CFSR', 0)
if cfsr:
mmfsr = cfsr & 0xFF
bfsr = (cfsr >> 8) & 0xFF
ufsr = (cfsr >> 16) & 0xFFFF
# Memory Management Faults
if mmfsr:
if mmfsr & (1 << 0): fault_causes.append("Instruction access violation")
if mmfsr & (1 << 1): fault_causes.append("Data access violation")
if mmfsr & (1 << 3): fault_causes.append("Unstacking error")
if mmfsr & (1 << 4): fault_causes.append("Stacking error")
if mmfsr & (1 << 7) and 'MMFAR' in self.registers:
fault_causes.append("Memory Management Fault at address 0x{:08X}".format(
self.registers['MMFAR']))
# Bus Faults
if bfsr:
if bfsr & (1 << 0): fault_causes.append("Instruction bus error")
if bfsr & (1 << 1): fault_causes.append("Precise data bus error")
if bfsr & (1 << 2): fault_causes.append("Imprecise data bus error")
if bfsr & (1 << 7) and 'BFAR' in self.registers:
fault_causes.append("Bus Fault at address 0x{:08X}".format(
self.registers['BFAR']))
# Usage Faults
if ufsr:
if ufsr & (1 << 0): fault_causes.append("Undefined instruction")
if ufsr & (1 << 1): fault_causes.append("Invalid state")
if ufsr & (1 << 2): fault_causes.append("Invalid PC load")
if ufsr & (1 << 3): fault_causes.append("No coprocessor")
if ufsr & (1 << 8): fault_causes.append("Unaligned access")
if ufsr & (1 << 9): fault_causes.append("Divide by zero")
# 检查死循环
pc = self.registers.get('PC', 0)
# 获取当前指令
code_section = self.get_code_section()
if code_section:
try:
offset = pc - code_section['sh_addr']
code_bytes = code_section.data()[offset:offset+2]
insts = list(self.cs.disasm(code_bytes, pc))
if insts:
inst = insts[0]
if inst.mnemonic == 'b': # 分支指令
target = int(inst.op_str.strip('#'), 16)
if target == pc: # 跳转到自身
fault_causes.append("Infinite loop detected at PC=0x{:08X}".format(pc))
except:
pass
return fault_causes
return []
def analyze_hardfault_location(self):
"""分析 hardfault 发生的具体位置"""
# 1. 获取异常发生时的 PC 值
pc = self.registers.get('PC', 0)
lr = self.registers.get('LR', 0)
sp = self.registers.get('SP', 0)
# 2. 分析异常类型
fault_type = "Unknown Fault"
if 'IPSR' in self.registers:
ipsr = self.registers['IPSR']
if ipsr == 3:
fault_type = "Hard Fault"
elif ipsr == 4:
fault_type = "Memory Management Fault"
elif ipsr == 5:
fault_type = "Bus Fault"
elif ipsr == 6:
fault_type = "Usage Fault"
print("\n=== Fault Location Analysis ===")
print("Fault Type: %s" % fault_type)
# 3. 分析异常栈帧中的上下文
exc_frame = self.analyze_exception_frame(sp)
if exc_frame:
fault_pc = exc_frame['pc']
fault_lr = exc_frame['lr']
print("Fault PC: 0x{0:08X}".format(fault_pc))
print("Fault LR: 0x{0:08X}".format(fault_lr))
# 检查是否是从异常返回时发生的故障
if fault_lr & 0xF == 0xF:
print("Fault occurred during exception return")
# 获取异常发生时的实际代码位置
actual_pc = fault_pc if fault_pc != 0xFFFFFFFD else fault_lr & ~1
else:
actual_pc = pc
# 4. 获取源代码位置
if hasattr(self, 'debug_info') and self.debug_info:
location = self.get_source_location(actual_pc)
if location:
print("\nFault occurred at:")
print(" File: %s" % location['file'])
print(" Line: %d" % location['line'])
# 尝试读取源代码行
try:
with open(location['file'], 'r') as f:
lines = f.readlines()
fault_line = lines[location['line'] - 1].strip()
print(" Code: %s" % fault_line)
except:
pass
# 5. 获取功能上下文
func = self.find_symbol_by_address(actual_pc)
if func:
print("\nFault occurred in function: %s" % func['name'])
offset = actual_pc - func['addr']
print("Offset from function start: +0x%X bytes" % offset)
# 6. 反汇编故障位置的代码上下文
print("\nDisassembly context:")
self.disassemble_context(actual_pc)
# 7. 分析故障原因
cfsr = self.registers.get('CFSR', 0)
if cfsr:
self._analyze_fault_causes(cfsr)
# 8. 检查访问违例地址
if self.registers.get('CFSR', 0) & 0x80: # MMFAR valid
mmfar = self.registers.get('MMFAR', 0)
if mmfar:
print("\nInvalid memory access at address: 0x{0:08X}".format(mmfar))
self._analyze_memory_region(mmfar)
if self.registers.get('CFSR', 0) & 0x8000: # BFAR valid
bfar = self.registers.get('BFAR', 0)
if bfar:
print("Bus fault at address: 0x{0:08X}".format(bfar))
self._analyze_memory_region(bfar)
def _analyze_fault_causes(self, cfsr):
"""详细分析故障原因"""
print("\nFault analysis:")
# 解析 Memory Management Fault
mmfsr = cfsr & 0xFF
if mmfsr:
print("Memory Management Fault:")
if mmfsr & (1 << 0): print(" - Instruction access violation")
if mmfsr & (1 << 1): print(" - Data access violation")
if mmfsr & (1 << 2): print(" - During exception return")
if mmfsr & (1 << 3): print(" - During unstack")
if mmfsr & (1 << 4): print(" - During stack")
# 解析 Bus Fault
bfsr = (cfsr >> 8) & 0xFF
if bfsr:
print("Bus Fault:")
if bfsr & (1 << 0): print(" - Instruction bus error")
if bfsr & (1 << 1): print(" - Precise data bus error")
if bfsr & (1 << 2): print(" - Imprecise data bus error")
if bfsr & (1 << 3): print(" - During exception return")
if bfsr & (1 << 4): print(" - During unstack")
if bfsr & (1 << 5): print(" - During stack")
# 解析 Usage Fault
ufsr = (cfsr >> 16) & 0xFFFF
if ufsr:
print("Usage Fault:")
if ufsr & (1 << 0): print(" - Undefined instruction")
if ufsr & (1 << 1): print(" - Invalid state")
if ufsr & (1 << 2): print(" - Invalid PC load")
if ufsr & (1 << 3): print(" - No coprocessor")
if ufsr & (1 << 8): print(" - Unaligned access")
if ufsr & (1 << 9): print(" - Divide by zero")
def _analyze_memory_region(self, addr):
"""分析内存访问区域"""
# 检查该地址属于哪个内存区域
for name, region in self.memory_regions.items():
if region['start'] <= addr < region['end']:
print("Address belongs to %s region" % name)
offset = addr - region['start']
print("Offset from region start: +0x%X" % offset)
return
# 如果找不到对应区域,检查是否访问了未映射区域
print("Address is in unmapped memory region")
# 检查是否接近某个有效区域
closest_region = None
min_distance = float('inf')
for name, region in self.memory_regions.items():
dist_start = abs(addr - region['start'])
dist_end = abs(addr - region['end'])
min_dist = min(dist_start, dist_end)
if min_dist < min_distance:
min_distance = min_dist
closest_region = (name, region)
if closest_region:
print("Closest valid region is %s, distance: 0x%X bytes" %
(closest_region[0], min_distance))
def call_addr2line(self, elf_file, address):
# 获取当前 Python 脚本所在目录
script_dir = os.path.dirname(os.path.realpath(__file__))
addr2line_path = os.path.join(script_dir, 'addr2line') # 假设 addr2line 和 Python 脚本在同一目录下
# 构建命令行参数
# command = ['addr2line', '-e', elf_file, address]
command = [addr2line_path, '-e', elf_file, address]
creationflags = 0
try:
# 调用 addr2line 并获取输出
result = subprocess.run(command, capture_output=True, text=True, check=True, encoding='utf-8', creationflags = creationflags)
if result.stdout: # 确保有输出
print("Address info:", result.stdout.strip(), "\n")
else:
print("\nNo output received from addr2line.")
except subprocess.CalledProcessError as e:
print(f"Error while running addr2line: {e}")
except FileNotFoundError:
print("addr2line not found. Make sure it's installed and in your PATH.")
except UnicodeDecodeError as e:
print(f"UnicodeDecodeError: {e} - Ensure the output encoding is handled correctly.")
def analyze(self):
"""分析栈回溯"""
try:
# 1. 打印寄存器状态
self.print_registers()
# 2. 分析故障位置(新增)
self.analyze_hardfault_location()
# 3. 打印调用栈
pc = self.registers.get('PC', 0)
lr = self.registers.get('LR', 0)
sp = self.registers.get('SP', 0)
print("\n=== Call Stack ===")
current_func = self.find_symbol_by_address(pc)
if current_func:
print("#0 {} (PC=0x{:08X})".format(current_func['name'], pc))
addr = format(pc, '#08X')
self.call_addr2line(self.AXF, addr)
# 分析异常栈帧
exc_frame = self.analyze_exception_frame(sp)
if exc_frame:
# 使用异常栈帧中保存的PC
fault_pc = exc_frame['pc']
fault_func = self.find_symbol_by_address(fault_pc)
if fault_func:
print("#1 {} (PC=0x{:08X})".format(fault_func['name'], fault_pc))
addr = format(fault_pc, '#08X')
self.call_addr2line(self.AXF, addr)
# 从各种可能的SP开始展开栈
sp_candidates = [
sp, # 当前SP
exc_frame['r1'] if exc_frame else 0, # R1可能保存了原始SP
self.registers.get('R7', 0), # ARM一般用R7作为帧指针
self.registers.get('R11', 0) # 有时也用R11作为帧指针
]
frames_seen = set() # 用于去重
frame_num = 2
for start_sp in sp_candidates:
if not start_sp:
continue
frames = self.unwind_stack(start_sp)
for frame in frames:
# 去重并打印
frame_key = (frame['func']['name'], frame['addr'])
if frame_key not in frames_seen:
frames_seen.add(frame_key)
print("#{} {} (return to 0x{:08X})".format(
frame_num, frame['func']['name'], frame['addr']))
addr = format(frame['addr'], '#08X')
self.call_addr2line(self.AXF, addr)
frame_num += 1
except Exception as e:
print("Error during analysis: {}".format(str(e)))
import traceback
traceback.print_exc()
def main():
parser = argparse.ArgumentParser(description='ARM Stack Trace Tool')
parser.add_argument('axf_file', help='path to AXF file')
parser.add_argument('ram_dump', help='path to RAM dump file')
parser.add_argument('--reg-dump', help='path to register dump file')
parser.add_argument('--map-file', help='path to MAP file')
args = parser.parse_args()
try:
tracer = StackTracer(args.axf_file, args.ram_dump,
args.reg_dump, args.map_file)
tracer.analyze()
except Exception as e:
print("Error:", str(e))
import traceback
traceback.print_exc()
if __name__ == '__main__':
main()
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化