在RELEASE版本中快速定位DATA ABORT的方法

本文介绍了一种利用DATAABORT消息中的PC值和MAP文件快速定位内存泄漏的方法,并通过案例详细展示了定位过程。同时,作者还分享了该方法存在的局限性。

07年10月11日补充:注意,该方法只能定位显性泄漏,定位到的C语句一定产生泄漏了,但可能这个位置是”理论上”不会出问题的代码.那么这是由于同进程内其他代码泄漏而影响了进程的堆区或栈区(隐性泄漏,这个地方不会产生data abort exception),然后被定位出来的代码才被动地显性泄露.产生data abort. 在篇末我给出代码实例来说明这种情况.

-----------------------
首先在DEBUG版本中定位DATA ABORT的方法,地球人应该都知道了吧,我就不废话了。PlatformBuilder或VS2005、EVC这类IDE工具会在DEBUG模式下自动停在出错的那句,情况就很显然了。

RELEASE版本下的泄漏就要稍微麻烦一点,如何快速定位呢?


案例一:用EVC编译的应用程序泄漏

首先我做了一个内存泄漏的程序walzer_leak.exe,里面做了一个泄漏的函数
void DoLeak()

    int* p = (int*)0x81000000;
    *p = 10;
}

编译的时候注意先在project settings里的Link页里勾选“Generate mapfile”,会生成一个map文件。如下图


把编译出来的RELEASE版本可执行文件放到CE5平台下运行。出错的时候串口打印了一句

Data Abort: Thread=83ad1d38 Proc=820266d0 'walzer_leak.exe'
AKY=00000021 PC=00011008(walzer_leak.exe+0x00001008) RA=00011030(walzer_leak.exe+0x00001030) BVA=81000000 FSR=0000000d

这句是系统自动输出的。我们得到了一个关键的信息:PC指针。和PC指针在walzer_leak.exe中的偏移量。然后打开编译时生成的walzer_leak.map文件
,文件不长,我贴一下

************************************************************
Walzer_leak

 Timestamp is 46fcbb17 (Fri Sep 28 16:28:07 2007)

 Preferred load address is 00010000

 Start         Length     Name                   Class
 0001:00000000 00000258H .text                   CODE
 0002:00000000 00000014H .xdata                  DATA
 0002:00000014 00000014H .idata$2                DATA
 0002:00000028 00000014H .idata$3                DATA
 0002:0000003c 00000010H .idata$4                DATA
 0002:0000004c 0000000cH .idata$6                DATA
 0002:00000058 00000000H .edata                  DATA
 0003:00000000 00000010H .idata$5                DATA
 0003:00000010 00000004H .CRT$XCA                DATA
 0003:00000014 00000004H .CRT$XCZ                DATA
 0003:00000018 00000004H .CRT$XIA                DATA
 0003:0000001c 00000004H .CRT$XIZ                DATA
 0003:00000020 00000004H .CRT$XPA                DATA
 0003:00000024 00000004H .CRT$XPZ                DATA
 0003:00000028 00000004H .CRT$XTA                DATA
 0003:0000002c 00000004H .CRT$XTZ                DATA
 0003:00000030 00000009H .bss                    DATA
 0004:00000000 00000038H .pdata                  DATA
 0005:00000000 00000010H .rsrc$01                DATA
 0005:00000010 00000000H .rsrc$02                DATA

  Address         Publics by Value              Rva+Base     Lib:Object

 0001:00000000       ?DoLeak@@YAXXZ             00011000 f   Walzer_leak.obj
 0001:00000010       WinMain                    00011010 f   Walzer_leak.obj
 0001:0000002c       WinMainCRTStartup          0001102c f   corelibc:pegwmain.obj
 0001:000000a0       _cinit                     000110a0 f   corelibc:crt0dat.obj
 0001:00000210       exit                       00011210 f   corelibc:crt0dat.obj
 0001:00000228       _XcptFilter                00011228 f   coredll:COREDLL.dll
 0001:00000238       __C_specific_handler       00011238 f   coredll:COREDLL.dll
 0001:00000248       LocalFree                  00011248 f   coredll:COREDLL.dll
 0002:00000014       __IMPORT_DESCRIPTOR_COREDLL 00012014     coredll:COREDLL.dll
 0002:00000028       __NULL_IMPORT_DESCRIPTOR   00012028     coredll:COREDLL.dll
 0003:00000000       __imp___C_specific_handler 00013000     coredll:COREDLL.dll
 0003:00000004       __imp_LocalFree            00013004     coredll:COREDLL.dll
 0003:00000008       __imp__XcptFilter          00013008     coredll:COREDLL.dll
 0003:0000000c       /177COREDLL_NULL_THUNK_DATA 0001300c     coredll:COREDLL.dll
 0003:00000010       __xc_a                     00013010     corelibc:crt0init.obj
 0003:00000014       __xc_z                     00013014     corelibc:crt0init.obj
 0003:00000018       __xi_a                     00013018     corelibc:crt0init.obj
 0003:0000001c       __xi_z                     0001301c     corelibc:crt0init.obj
 0003:00000020       __xp_a                     00013020     corelibc:crt0init.obj
 0003:00000024       __xp_z                     00013024     corelibc:crt0init.obj
 0003:00000028       __xt_a                     00013028     corelibc:crt0init.obj
 0003:0000002c       __xt_z                     0001302c     corelibc:crt0init.obj
 0003:00000030       __onexitend                00013030     <common>
 0003:00000034       __onexitbegin              00013034     <common>
 0003:00000038       _exitflag                  00013038     <common>

 entry point at        0001:0000002c

 Static symbols

 0001:0000010c       doexit                     0001110c f   corelibc:crt0dat.obj
************************************************************

OK,首先,里面有一句“Preferred load address is 00010000”,这意味着DATA ABORT那句的PC=00011008(walzer_leak.exe+0x00001008) 我们必须把括

号里的0x1008加上这个load address的偏移量,得到0x11008(注意不能直接用PC,一会儿再给个案例就知道了),然后我们在函数偏移列表里看Rva+Base

这栏,找到0x11008落在了DoLeak函数的地址范围里,所以是DoLeak函数泄漏了。


案例二:在OAL层做了一个泄漏的函数,用Platform Builder进行RELEASE版编译并LINK到NK.exe里,然后应用程序Call_Leak.exe调用该函数导致泄漏

步骤类似,只是Platform Builder默认就会在RELEASE目录下生成.map文件。应用程序去调用泄漏的OAL函数时,出现

Data Abort: Thread=822fc000 Proc=820267c0 'Call_Leak.exe'
AKY=00000041 PC=8023e3c8(NK.EXE+0x0003e3c8) RA=8023e1d4(NK.EXE+0x0003e1d4)
BVA=8e000000 FSR=00000005

注意我们这次是NK.EXE里制造泄漏,所以PC指针不是在0x00011008这样的Slot 0低地址了,而是在0x80000000以上的KERNEL区域了。
nk.map很长,我选择关键段落来贴

************************************************************
 kern

 Timestamp is 46fca696 (Fri Sep 28 15:00:38 2007)

 Preferred load address is 00010000

 ...
 0001:0003d2c8       OALIoCtlHal_GetDeviceId    0004e2c8 f   oal_ioctl:deviceid.obj
 0001:0003d398       OALIoCtlHal_DoLeak    0004e398 f   oal_ioctl:leaktest.obj
 0001:0003d438       OALIoCtlHal_DdkCall        0004e438 f   oal_io:ioctl.obj
 ...
************************************************************

所以 NK.EXE + 0x0003e3c8 = 0x10000(Preferred load address) + 0x0003e3c8 = 0x4e3c8, 落在了OALIoCtlHal_DoLeak函数里


结论:原理上很简单,就是利用DATA ABORT消息中的PC值,配合MAP文件可以快速定位到泄漏的函数。定位到之后,嘿嘿,谁LEAK谁请客咯。

-------------------------------
10月11日补充:

用上文的方法,不但网友kevin没有成功定位到泄漏的原因(见后面回帖,他定位到微软USBFN的MDD层代码),我这项目组里的人也没抓到原因(定位到PRIVATE下LoadLibrary函数相关的代码)。昨晚我想了下,这个方法的确有漏洞,早上我做了个试验,建立个DialogBox,主处理函数如下

static DWORD* g_pTest = NULL;

int CALLBACK LeakProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch(uMsg)
    {
    case WM_INITDIALOG:
        g_pTest = (DWORD*)malloc(1);
        break;
    case WM_COMMAND:
        switch(wParam)
        {
        case ID_LEAK:
            *((int*)0x1A013860) = 0x1C000000;
            break;
        case ID_CLOSE:
            if(g_pTest)
            {
                *g_pTest = 1;
            }
            EndDialog(hDlg, 0);
            break;
        }
        break;
     }
     return FALSE;
}

我运行了几次,每次该进程的BaseAddr = 0x1A000000, 而 &g_pTest = 0x1A013860,在进程的全局变量区;malloc之后g_pTest = 0x00030230在进程的堆区。所以我
(1) 在ID_LEAK按钮按下后,模拟一次内存泄漏,直接对&g_pTest地址上的数值改写,把malloc后的0x00030230改成0x00000000, 注意虽然该处已经泄漏了,但是并没有产生data abort exception.
(2) 然后在ID_CLOSE按钮按下后,装做不知道前面那处泄漏,代码风格严谨地先判断下指针是否为空,然后试图在malloc得到的堆区0x00030230地址上写个值,但是由于g_pTest这个指针已经被改写为指向其他进程空间了,所以在*g_pTest = 1这句产生data abort exception并停下来了。
(3) 所以,按照上文的方法,只能抓到*g_pTest = 1这句泄露,而实际上这句是无辜的,真正的元凶*((int*)0x1A013860) = 0x1C000000这句却没有被抓出来。

结论:这篇文章给出的方法不好用啊。

帮我分析这个代码,是arm a52的cpu,32位: //----------------------------------------------------------------------------- // The confidential and proprietary information contained in this file may // only be used by a person authorised under and to the extent permitted // by a subsisting licensing agreement from ARM Limited or its affiliates. // // (C) COPYRIGHT 2014-2021 ARM Limited or its affiliates. // ALL RIGHTS RESERVED // // This entire notice must be reproduced on all copies of this file // and copies of this file may only be made by a person if such person is // permitted to do so under the terms of a subsisting license agreement // from ARM Limited or its affiliates. // // Release Information : Cortex-R52 Processor MP040-r1p4-00rel0 // //----------------------------------------------------------------------------- .include "shared/bootcode/boot_defs.hs" // Enable UAL syntax .syntax unified .section .text.vectors, "ax", %progbits .global vector_table .type vector_table, %function // Weakly import replacement handlers. This allows a test to redefine // handlers. If it doesn't, the default ones below will be used. .weak bootcode_other .weak undef_handler .weak svc_handler .weak pf_abort_handler .weak data_abort_handler .type data_abort_handler, %function .weak irq_handler .type irq_handler, %function .weak fiq_handler .type print, %function //------------------------------------------------------------------------------ // Vector table //------------------------------------------------------------------------------ vector_table: ldr pc, reset_handler_addr // Reset ldr pc, undef_handler_addr // Undef ldr pc, svc_handler_addr // SVC ldr pc, pf_abort_handler_addr // Prefetch abort ldr pc, data_abort_handler_addr // Data abort nop // Not used ldr pc, irq_handler_addr // IRQ ldr pc, fiq_handler_addr // FIQ //------------------------------------------------------------------------------ // Handler addresses //------------------------------------------------------------------------------ reset_handler_addr: .word def_reset_handler undef_handler_addr: .word def_undef_handler svc_handler_addr: .word def_svc_handler pf_abort_handler_addr: .word def_pf_abort_handler data_abort_handler_addr: .word def_data_abort_handler irq_handler_addr: .word def_irq_handler fiq_handler_addr: .word def_fiq_handler //---------------------------------------------------------------------------- // Handler strings //---------------------------------------------------------------------------- // The default handlers print an error message and terminate the simulation // by writing the EOT character to the tube. The error strings are defined // here. undef_exception_str: .asciz "undefined instruction\n" svc_exception_str: .asciz "SVC\n" pf_abort_exception_str: .asciz "prefetch abort\n" data_abort_exception_str: .asciz "data abort\n" irq_exception_str: .asciz "IRQ\n" fiq_exception_str: .asciz "FIQ\n" // Ensure 4-byte alignment for following code .balign 4 //------------------------------------------------------------------------------ // Default handlers // // The default handlers all contain one branch to a weakly-imported exception // handler label followed by default handler code. This allows tests to // define their own handlers, with fall-back default handlers if they do not. // // When a test defines a replacement handler, it must end with an excpetion // return instruction so that the default handler code is never executed. // // When a test does not define a replacement handler, the branch to the // weakly-imported symbol in the default handler is effectively a NOP and // the default handler code is therefore executed. // // The default handlers all print an "Unexpected exception" error message // and terminate the simulation by writing the EOT character to the tube. //------------------------------------------------------------------------------ def_reset_handler: b bootcode def_undef_handler: b undef_handler ldr r0, =undef_exception_str b unexpected_handler def_svc_handler: b svc_handler ldr r0, =svc_exception_str b unexpected_handler def_pf_abort_handler: b pf_abort_handler ldr r0, =pf_abort_exception_str b unexpected_handler def_data_abort_handler: b data_abort_handler ldr r0, =data_abort_exception_str b unexpected_handler def_irq_handler: b irq_handler ldr r0, =irq_exception_str b unexpected_handler def_fiq_handler: b fiq_handler ldr r0, =fiq_exception_str b unexpected_handler // Generic unexpected handler routine. This prints an error message and // terminates the simulation by writing the EOT character to the tube. // Expects r0 to contain a pointer to a string that is the name of the // exception. As this is a terminal routine, no registers are preserved. unexpected_handler: ldr r1, =TUBE_ADDRESS ldr r2, =unexpected_str // Message bl print mov r2, r0 bl print ldr r2, =fail_str bl print // Write EOT character to terminate the simulation mov r2, #0x4 strb r2, [r1] dsb wfi b . // Print a string to the tube // Expects: r1 -> tube // r2 -> message // Modifies r3 print: ldrb r3, [r2], #1 cmp r3, #0 strbne r3, [r1] bne print bx lr unexpected_str: .asciz "Unexpected exception: " fail_str: .asciz "** TEST FAILED **\n" .balign 4 .end
最新发布
06-25
<think>我们正在分析ARMCortex-R52处理器的异常处理汇编代码,特别是中断处理机制和现场恢复问题。根据提供的引用,Cortex-R52支持硬件强制的软件分离,具有实时系统所需的快速环境切换能力[^3]。同时,它使用GIC(通用中断控制器)管理中断[^1],并包含通用定时器(GenericTimer)等组件[^5]。以下是针对中断处理流程和现场恢复问题的逐步分析:###1.**异常向量表(ExceptionVectorTable)**ARMCortex-R52的异常向量表位于内存特定地址(默认0x00000000或通过VBAR寄存器重定位)。每个异常类型(包括IRQ、FIQ等)占用4字节,存储跳转指令或处理程序地址。例如:-IRQ中断向量位于`VBAR+0x18`-FIQ中断向量位于`VBAR+0x1C`**关键点**:如果VBAR设置错误(例如BootLoader未正确初始化),处理器将无法跳转到正确的中断处理程序[^2][^4]。###2.**中断处理流程**#####步骤1:中断触发-外设通过GIC发送中断请求,GIC根据优先级将最高优先级中断(HPPI)转发给CPU[^1]。-CPU硬件自动执行以下动作:-将`CPSR`保存到`SPSR_<mode>`(例如`SPSR_irq`)-将返回地址(`PC+4`或`PC+8`,取决于异常类型)保存到`LR_<mode>`-切换到对应异常模式(如IRQ模式)-跳转到向量表指定地址#####步骤2:中断服务程序(ISR)入口典型的IRQ处理程序入口汇编代码示例:```assemblyIRQ_Handler:SUBLR,LR,#4;修正返回地址(对于IRQ返回LR-4)STMFDSP!,{R0-R12,LR};手动保存通用寄存器和链接寄存器MRSR0,SPSR;保存SPSR到栈STMFDSP!,{R0};...中断处理逻辑...```**注意**:-必须手动保存`R0-R12`和`LR`(IRQ模式的LR实际是返回地址)。-`SPSR`必须显式保存,否则会被后续操作覆盖。#####步骤3:中断处理-ISR通过读取GIC的寄存器(如`ICC_IAR1`)获取中断ID,并执行对应服务[^1]。-处理完成后,向GIC发送EOI(EndofInterrupt)。#####步骤4:现场恢复与返回恢复现场的逆操作:```assemblyLDMFDSP!,{R0};恢复SPSR到R0MSRSPSR_cxsf,R0;写回SPSRLDMFDSP!,{R0-R12,PC}^;恢复寄存器并从异常返回(^符号同时恢复CPSR)```**关键指令**:`LDMFDSP!,{R0-R12,PC}^`-`^`符号表示同时将`SPSR_irq`复制到`CPSR`,实现处理器状态恢复。-该指令将栈顶数据弹出到`R0-R12`和`PC`,并自动完成模式切换。###3.**现场恢复失败的常见原因**#####(a)堆栈操作不对称-**问题**:`STMFD`(满递减堆栈)与`LDMFD`必须成对出现。若入栈和出栈寄存器数量不一致,将导致堆栈指针错位和恢复错误。-**示例**:```assemblySTMFDSP!,{R0-R3,LR};保存5个寄存器(4字+1字=5字);...处理...LDMFDSP!,{R0-R3,PC}^;恢复5个寄存器→正确```若误写为`LDMFDSP!,{R0-R2,PC}^`(少恢复一个寄存器),则堆栈指针错误偏移,后续返回崩溃。#####(b)SPSR保存/恢复遗漏-**问题**:若忘记保存`SPSR`,在ISR中修改`CPSR`(例如切换模式)将导致原始状态丢失,返回时无法恢复。-**解决**:始终通过`MRS`/`MSR`显式保存和恢复`SPSR`。#####(c)中断嵌套处理不当-**问题**:若ISR执行期间允许更高优先级中断(例如FIQ嵌套IRQ),但未切换堆栈或未保存关键寄存器,嵌套中断会破坏当前现场。-**解决**(参考Cortex-R52特性[^3]):-使用专用堆栈指针(R13)用于每种异常模式。-在嵌套前保存`LR`和`SPSR`到当前模式堆栈。#####(d)定时器中断配置错误-**问题**:通用定时器中断未正确配置(如`ENABLE`或`IMASK`设置错误[^5]),导致中断重复触发或状态混乱,影响现场恢复。-**检查点**:确认定时器控制寄存器:```c//伪代码:配置定时器Timer->CTRL.ENABLE=1;//使能定时器Timer->CTRL.IMASK=0;//允许中断```###4.**调试建议**-**使用JTAG调试器**:1.设置断点在`IRQ_Handler`入口和返回指令处。2.检查中断前后`SP`、`PC`、`CPSR`和关键寄存器值是否一致。-**内存与堆栈检查**:-确保IRQ堆栈区域无溢出(例如通过调试器填充魔数`0xDEADBEEF`并定期检查)。-**GIC诊断**:-读取GIC的`ICC_HPPIR1`寄存器验证中断ID是否正确[^1]。###总结流程图```mermaidgraphTDA[中断触发]-->B[硬件自动保存PC->LR,CPSR->SPSR]B-->C[跳转到向量表]C-->D[ISR入口:保存R0-R12,LR,SPSR]D-->E[处理中断:读GIC,服务请求]E-->F[恢复SPSR,R0-R12,PC^]F-->G[返回被中断点]```###相关问题1.Cortex-R52的VBAR寄存器如何配置?[^2][^4]2.GIC中断优先级如何影响现场恢复?[^1]3.如何诊断ARM中断处理中的堆栈溢出问题?4.Cortex-R52的硬件强制分离对中断处理有何影响?[^3]5.通用定时器中断的使能和状态检查如何实现?[^5]</think>### ARM Cortex-R52 异常处理汇编代码分析与现场恢复问题解析 #### 1. **异常处理流程概览** ARM Cortex-R52 的异常处理分为硬件自动操作和软件手动操作两阶段: - **硬件自动操作**(触发异常时): - 将 `CPSR` 保存到 `SPSR_<mode>`(模式相关备份寄存器) - 将返回地址保存到 `LR_<mode>`(模式相关链接寄存器) - 切换到异常模式(如 IRQ/FIQ) - 跳转到异常向量表对应入口 - **软件手动操作**(在异常处理程序中): - 保存通用寄存器(R0-R12) - 执行中断服务程序 - 恢复通用寄存器 - 使用 `SPSR` 恢复 `CPSR` - 通过 `MOV PC, LR` 或 `LDMFD` 指令返回 #### 2. **关键汇编代码分析** 以 IRQ 处理为例的典型代码结构: ```assembly IRQ_Handler: ; 1. 手动保存现场(硬件仅保存PC/CPSR) STMFD SP!, {R0-R12, LR} ; 保存通用寄存器+LR到堆栈 MRS R0, SPSR ; 读取SPSR到R0 STMFD SP!, {R0} ; 保存SPSR到堆栈 ; 2. 中断服务程序主体 BL read_GIC_acknowledge ; 读取GIC中断ID [^1] ... ; 具体中断处理逻辑 BL write_GIC_eoi ; 发送EOI到GIC [^1] ; 3. 恢复现场 LDMFD SP!, {R0} ; 从堆栈恢复SPSR到R0 MSR SPSR_cxsf, R0 ; 写回SPSR寄存器 LDMFD SP!, {R0-R12, PC}^ ; 恢复寄存器并返回(^表示恢复CPSR) ``` **关键点说明**: - `STMFD/LDMFD`:使用满递减堆栈操作,需确保堆栈指针(SP)在异常模式正确初始化[^2][^4] - `^` 符号:在 `LDMFD` 中表示同时恢复 `CPSR`(从 `SPSR`) - GIC 交互:必须处理中断确认(acknowledge)和结束中断(EOI)[^1] #### 3. **现场恢复失败的常见原因** 结合 Cortex-R52 特性分析: ##### (1) 堆栈操作不对称 - **问题**:保存/恢复的寄存器数量不一致 ```assembly STMFD SP!, {R0-R12, LR} ; 保存14个寄存器 ... LDMFD SP!, {R0-R12, PC}^ ; 恢复13个寄存器(缺少LR)❌ ``` - **影响**:堆栈指针错位导致后续恢复错误 ##### (2) SPSR 处理遗漏 - **问题**:未保存/恢复 `SPSR` ```assembly ; 错误示例:缺少SPSR保存 STMFD SP!, {R0-R12, LR} ; 未保存SPSR ❌ ... LDMFD SP!, {R0-R12, PC}^ ; 恢复时使用错误SPSR ``` - **后果**:`CPSR` 恢复错误(如错误启用/禁用中断) ##### (3) 中断嵌套处理不当 - **问题**:高优先级中断破坏当前现场 ```assembly IRQ_Handler: CPSIE I ; 错误:过早开启中断允许嵌套 ❌ STMFD SP!, ... ; 嵌套中断可能在此处破坏堆栈 ``` - **解决**:使用 GIC 优先级管理或临时屏蔽中断[^1][^3] ##### (4) 定时器中断配置错误 - **问题**:通用定时器状态未正确处理[^5] ```assembly ; 未检查ISTATUS导致虚假中断 LDR R0, =TimerBase LDR R1, [R0, #CTRL_OFFSET] TST R1, #ISTATUS_MASK ; 必须检查中断状态[^5] BEQ spurious_interrupt ``` ##### (5) 硬件强制分离特性影响 - Cortex-R52 的硬件强制分离(hardware-enforced separation)要求: - 安全/非安全域需独立堆栈 - 模式切换时需显式配置 SP_irq/SP_fiq[^3] ```assembly ; 安全域初始化示例 MRC p15, 0, R0, c1, c0, 0 ; 读SCR ORR R0, R0, #NS_BIT ; 设置非安全位 LDR SP_irq, =NS_IRQ_Stack ; 非安全域堆栈 ``` #### 4. 调试建议 1. **检查堆栈平衡**: - 在 ISR 入口/出口插入调试代码验证 SP 值 ```assembly IRQ_Entry: MOV R12, SP ; 记录初始SP ... CMP SP, R12 ; 恢复后验证SP BNE stack_error ``` 2. **验证向量表配置**: - 确认 VBAR(向量表基址寄存器)设置正确 ```assembly LDR R0, =VectorTable MCR p15, 0, R0, c12, c0, 0 ; 写VBAR ``` 3. **使用调试异常**: - 在 BKPT 指令后检查寄存器状态 ```assembly LDMFD SP!, {R0-R12, PC}^ BKPT #0x0000 ; 设置断点检查恢复后状态 ``` #### 5. 关键寄存器总结 | 寄存器 | 作用 | 恢复要求 | |--------|------|----------| | `SPSR` | 保存原始 `CPSR` | 必须通过 `MSR` 恢复 | | `LR_irq` | 保存返回地址 | 需修正偏移量(ARM 架构特定) | | `SP_irq` | 异常模式堆栈指针 | Bootloader 需初始化[^2] | | `ICC_IAR1` (GIC) | 中断确认寄存器 | 必须读取并发送 EOI[^1] | | `CNTP_CTL` (Timer) | 定时器控制 | 需处理 ISTATUS[^5] | > 完整流程参考 Cortex-R52 技术参考手册第9章异常处理[^4]。 ### 相关问题 1. Cortex-R52 的硬件强制分离如何影响中断处理程序编写?[^3] 2. GIC 中断优先级在 Cortex-R52 中如何配置?[^1] 3. ARM 异常返回地址修正规则是什么? 4. 如何验证 Cortex-R52 中断处理程序的实时性能?[^4] 5. 通用定时器中断与普通外设中断处理有何区别?[^5]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值