arm-none-eabi-gcc下Attribute'Address值与实际地址不符的原因咨询
Attribute'Address获取的地址与实际运行地址不符 在arm-none-eabi-gcc(针对Cortex-M0/STM32F03)环境中,我发现通过Attribute'Address获取的地址,编译时插入的值与实际运行时的真实地址存在差异。以Hardfault_Handler为例,相关信息如下:
代码示例
procedure Hardfault_Handler is SP : Address := Get_Stack_Pointer; -- 保存进入Handler后的栈指针 PC : Address := Get_Program_Counter; -- 保存当前程序计数器 PC_Offset : Storage_Offset := PC - Hardfault_Handler'Address; Num_Of_Pushed_Regs : Natural := 0; SP_Calc : Integer_Address := To_Integer (SP); package Thumb_Ins_Pnt is new System.Address_To_Access_Conversions (Push_Instruction); use Thumb_Ins_Pnt; Temp_Ins : Object_Pointer; begin -- 遍历HardfaultHandler开头的程序代码 for I in 0 .. PC_Offset when (I mod 2 = 0) loop Temp_Ins := To_Pointer (Hardfault_Handler'Address + I); -- 判断是否是Push指令? if Temp_Ins.Mask = PUSH_Ins_Mask then -- 统计压入栈的寄存器数量 for Bit of Temp_Ins.Regs when Bit = True loop Num_Of_Pushed_Regs := Num_Of_Pushed_Regs + 1; end loop; -- 调整SP指向Push之前的位置(栈向下增长,所以加偏移) SP_Calc := SP_Calc + 4 * Integer_Address (Num_Of_Pushed_Regs); declare Old_Regs : constant Stacked_Registers with Import, Address => To_Address (SP_Calc + Stacked_Reg_Offset); Old_PC_Content : constant Thumb_Instruction with Import, Address => Old_Regs.PC; begin if Old_PC_Content = Break_Point_Instruction then -- 无调试器连接时触发Hardfault,直接返回 return; end if; end; end if; end loop; Put_Line ("Hard Fault"); -- 待实现的错误处理逻辑 end Hardfault_Handler;
objdump输出
08000754 <m0__startup__hardfault_handler>: procedure Hardfault_Handler is 8000754: b5f8 push {r3, r4, r5, r6, r7, lr} function Get_Stack_Pointer return Address is Result : Address; begin Asm 8000756: 46ec mov ip, sp end Get_Stack_Pointer; function Get_Program_Counter return Address is Result : Address; begin Asm 8000758: 467d mov r5, pc PC_Offset : Storage_Offset := PC - Hardfault_Handler'Address; 800075a: 4e2a ldr r6, [pc, #168] ; (8000804 <m0__startup__hardfault_handler+0xb0>) end "-";
存储的Hardfault_Handler'Address值
8000802: bdf8 pop {r3, r4, r5, r6, r7, pc} 8000804: 08000755 stmdaeq r0, {r0, r2, r4, r6, r8, r9, sl} 8000808: 08002a88 stmdaeq r0, {r3, r7, r9, fp, sp} 800080c: 7fffffff svcvc 0x00ffffff 8000810: 0000beab andeq fp, r0, fp, lsr #29 8000814: 08002a28 stmdaeq r0, {r3, r5, r9, fp, sp} 8000818: 08002ad8 stmdaeq r0, {r3, r4, r6, r7, r9, fp, sp}
向量表信息
向量表代码
Vector_Table : constant Address_Array := (Sram_Stack_Start, Reset_Handler'Address, NMI_Handler'Address, Hardfault_Handler'Address, MemManage_Handler'Address, Bus_Fault_Handler'Address, Usage_Fault_Handler'Address, Reserved, Reserved, Reserved, Reserved, SVCall_Handler'Address, Debug_Handler'Address, Reserved, PendSV_Handler'Address, Systick_Handler'Address, Default_Handler'Address, Default_Handler'Address, Default_Handler'Address, Default_Handler'Address, Default_Handler'Address, Default_Handler'Address, Default_Handler'Address, Default_Handler'Address, Default_Handler'Address, Default_Handler'Address, Default_Handler'Address, Default_Handler'Address, Default_Handler'Address, Default_Handler'Address, Default_Handler'Address, Default_Handler'Address, Default_Handler'Address, Default_Handler'Address, Default_Handler'Address, Default_Handler'Address, Default_Handler'Address, Default_Handler'Address, Default_Handler'Address, Default_Handler'Address); --Default_Handler'Address); pragma Linker_Section (Vector_Table, "_vector_table");
objdump中的向量表内容
08000000 <m0__startup__vector_table>: 8000000: 20001000 andcs r1, r0, r0 8000004: 080005b1 stmdaeq r0, {r0, r4, r5, r7, r8, sl} 8000008: 08000699 stmdaeq r0, {r0, r3, r4, r7, r9, sl} 800000c: 08000755 stmdaeq r0, {r0, r2, r4, r6, r8, r9, sl} 8000010: 080006b1 stmdaeq r0, {r0, r4, r5, r7, r9, sl} 8000014: 080006c9 stmdaeq r0, {r0, r3, r6, r7, r9, sl} 8000018: 080006e1 stmdaeq r0, {r0, r5, r6, r7, r9, sl} ... 080005b0 <Reset_Handler>: ------------------- -- Reset_Handler -- ------------------- procedure Reset_Handler is 80005b0: b570 push {r4, r5, r6, lr} Data_L : Storage_Element with Volatile, Import, External_Name => "__data_size"; Data_Length : constant Storage_Offset := Addr2SO (Data_L'Address); Data_Load_Array : Storage_Array (1 .. Data_Length) with 80005b2: 4c16 ldr r4, [pc, #88] ; (800060c <Reset_Handler+0x5c>) Bss_L : Storage_Element with Volatile, Import, Convention => Asm, External_Name => "__bss_size";
问题
向量表中的地址同样存在差异,但Reset_Handler能正常调用运行。请问该现象的原因是什么?
这是Cortex-M系列处理器的Thumb指令集地址对齐特性导致的:
Thumb指令的最低位标记
Cortex-M0仅支持Thumb指令集,所有函数的实际执行地址都是偶数地址(Thumb指令长度为2或4字节,必须对齐到2字节边界)。但处理器在跳转/调用时,会把地址的最低位(LSB)作为Thumb状态标记:当LSB为1时,说明目标是Thumb指令,处理器会自动清除该位得到真实执行地址。Attribute'Address的行为
在Ada中,Hardfault_Handler'Address返回的是带Thumb标记的地址(即真实执行地址+1)。比如:
- 实际运行地址是
08000754(偶数) Attribute'Address给出的是08000755(最低位为1)
向量表的兼容性
向量表中必须存储带LSB=1的标记地址,这样处理器复位/触发中断时,才能正确识别这是Thumb指令入口。所以Reset_Handler'Address虽然看起来是080005b1,但处理器会自动去掉最低位,跳转到真实地址080005b0执行,因此能正常运行。代码中的修正方法
你的代码直接用Hardfault_Handler'Address计算指令偏移、访问指令内存,相当于用了带标记的地址,会指向指令的第二个字节,自然出现错误。解决方法是在使用地址前手动清除最低位:
-- 获取真实执行地址 Real_Handler_Addr : Address := To_Address(To_Integer(Hardfault_Handler'Address) and 16#FFFFFFFE#);
内容的提问来源于stack exchange,提问作者julius

