c++ - 在 ARM 裸机引导加载程序上启用 C++ 异常
问题描述
出于学习目的,我试图在 ARM MCU (STM32F407ZE) 上获得完整的 C++ 支持。我正在努力使异常正常工作,因此提出了这个问题:
如何在裸机 ARM 引导加载程序上获取 C++ 异常?
扩展一点问题:
我知道异常,例如退出函数需要展开堆栈。退出函数是开箱即用的,但异常处理却不是,这让我认为编译器正在添加函数的展开,但不能针对异常执行此操作。
所以子问题1是:这个前提正确吗?我真的需要为异常处理实现/集成展开库吗?
在我对展开的肤浅理解中,堆栈中有一个框架,展开“只是”需要在它的每个对象上调用析构函数,最后跳转到给定的catch
.
子问题 2 是:展开库如何执行此任务?使用的策略是什么?(适用于 SO 答案的范围)
在我的搜索中,我发现了很多关于什么是展开的解释,但很少有关于如何让它发挥作用的解释。最接近的是:
GCC arm-none-eabi (Codesourcery) 和 C++ 异常
该项目
1) 第一步,但有一些困难,是通过 JTAG 为 MCU 供电和通信。
这只是上下文信息,请不要仅仅因为这张图片而将问题标记为离题。而是跳到第 2 步。
我知道有可用的测试板,但这是一个学习项目,可以更好地理解幕后的所有“魔法”。所以我得到了一个芯片插座、一个面包板并设置了最小的上电电路:
注意:JTAG 是通过 raspberry-pi 的 GPIO 执行的。
Note2:我使用 OpenOCD 与芯片进行通信。
2)第二步,是做一个最小的软件来闪烁黄色的LED。
使用 arm-none-eabi-g++ 作为编译器和链接器,c++ 代码很简单,但是我对链接器脚本的理解还是有些模糊。
3)启用异常处理(尚未工作)。
为此,请提供以下有用的信息:
- https://wiki.osdev.org/C++_Exception_Support
- https://itanium-cxx-abi.github.io/cxx-abi/exceptions.pdf
- https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html
然而,对于一个简单的异常处理来说,它似乎过于复杂,在开始实现/集成展开库之前,我想确保我朝着正确的方向前进。
我想在 2 周内避免耳朵:“哦,顺便说一下,你只需要将这个“-xx”选项添加到编译器中,它就可以工作了”
主文件
auto reset_handler() noexcept ->void;
auto main() -> int;
int global_variable_test=50;
extern "C"
{
#include "stm32f4xx.h"
#include "stm32f4xx_rcc.h"
#include "stm32f4xx_gpio.h"
void assert_failed(uint8_t* file, uint32_t line){}
void hardFaultHandler( unsigned int * hardFaultArgs);
// vector table
#define SRAM_SIZE 128*1024
#define SRAM_END (SRAM_BASE + SRAM_SIZE)
unsigned long *vector_table[] __attribute__((section(".vector_table"))) =
{
(unsigned long *)SRAM_END, // initial stack pointer
(unsigned long *)reset_handler, // main as Reset_Handler
};
}
auto reset_handler() noexcept -> void
{
// Setup execution
// Call the main function
int ret = main();
// never finish
while(true);
}
class A
{
public:
int b;
auto cppFunc()-> void
{
throw (int)4;
}
};
auto main() -> int
{
// Initializing led GPIO
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG, ENABLE);
GPIO_InitTypeDef GPIO_InitDef;
GPIO_InitDef.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14;
GPIO_InitDef.GPIO_OType = GPIO_OType_PP;
GPIO_InitDef.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitDef.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitDef.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_Init(GPIOG, &GPIO_InitDef);
// Testing normal blinking
int loopNum = 500000;
for (int i=0; i<5; ++i)
{
loopNum = 100000;
GPIO_SetBits(GPIOG, GPIO_Pin_13 | GPIO_Pin_14);
for (int i = 0; i < loopNum; i++) continue; //active waiting!
loopNum = 800000;
GPIO_ResetBits(GPIOG, GPIO_Pin_13 | GPIO_Pin_14);
for (int i=0; i<loopNum; i++) continue; //active waiting!
}
// Try exceptions handling
try
{
A a;
a.cppFunc();
}
catch(...){}
return 0;
}
生成文件
CPP_C = arm-none-eabi-g++
C_C = arm-none-eabi-g++
LD = arm-none-eabi-g++
COPY = arm-none-eabi-objcopy
LKR_SCRIPT = -Tstm32_minimal.ld
INCLUDE = -I. -I./stm32f4xx/CMSIS/Device/ST/STM32F4xx/Include -I./stm32f4xx/CMSIS/Include -I./stm32f4xx/STM32F4xx_StdPeriph_Driver/inc -I./stm32f4xx/Utilities/STM32_EVAL/STM3240_41_G_EVAL -I./stm32f4xx/Utilities/STM32_EVAL/Common
C_FLAGS = -c -fexceptions -fno-common -O0 -g -mcpu=cortex-m4 -mthumb -DSTM32F40XX -DUSE_FULL_ASSERT -DUSE_STDPERIPH_DRIVER $(INCLUDE)
CPP_FLAGS = -std=c++11 -c $(C_FLAGS)
LFLAGS = -specs=nosys.specs -nostartfiles -nostdlib $(LKR_SCRIPT)
CPFLAGS = -Obinary
all: main.bin
main.o: main.cpp
$(CPP_C) $(CPP_FLAGS) -o main.o main.cpp
stm32f4xx_gpio.o: stm32f4xx_gpio.c
$(C_C) $(C_FLAGS) -o stm32f4xx_gpio.o stm32f4xx_gpio.c
stm32f4xx_rcc.o: stm32f4xx_rcc.c
$(C_C) $(C_FLAGS) -o stm32f4xx_rcc.o stm32f4xx_rcc.c
main.elf: main.o stm32f4xx_gpio.o stm32f4xx_rcc.o
$(LD) $(LFLAGS) -o main.elf main.o stm32f4xx_gpio.o stm32f4xx_rcc.o
main.bin: main.elf
$(COPY) $(CPFLAGS) main.elf main.bin
clean:
rm -rf *.o *.elf *.bin
write:
./write_bin.sh main.elf
链接描述文件:stm32_minimal.ld
/* memory layout for an STM32F407 */
MEMORY
{
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 512K
SRAM (rwx) : ORIGIN = 0x20000000, LENGTH = 128K
}
/* output sections */
SECTIONS
{
/* program code into FLASH */
.text :
{
*(.vector_table) /* Vector table */
*(.text) /* Program code */
*(.data)
/**(.eh_frame)*/
} >FLASH
.ARM.exidx : /* Required for unwinding the stack? */
{
__exidx_start = .;
* (.ARM.exidx* .gnu.linkonce.armexidx.*)
__exidx_end = .;
} > FLASH
PROVIDE ( end = . );
}
解决方案
推荐阅读
- jsf - 如何在数据更改时刷新 ViewScoped(Omnifaces) 页面?
- java - 如何将休眠中的查询结果显示到引导程序中的表?
- linux - OIFS=$IFS是什么意思;IFS="|"; 在 bash 脚本中
- .net - 上下文等待随机失败/连续两次调用相同的“等待方法”
- javascript - 如何使用 Jquery 设置 Html 元素中的变量值?
- python - Python烧瓶服务器需要很长时间才能启动
- tfs - TFS 和 Mantis 的集成
- elasticsearch - 我们可以将一个查询的结果用作弹性搜索中另一个查询的输入吗?
- swift - Swift:addSubview 放在前一个视图上
- forms - 来自 Laravel 作曲家的评论表