[002] [RISC-V] RTT与MRS配置C++环境

RISC-V
Contents
RTT配置
配置步骤
使用注意事项
MRS配置
配置步骤
问题与优化

1 RTT配置

IDE:RT-Thread Studio

RT-Thread版本:4.1.0

MCU:CH32V103C8T6

1.1 配置步骤

  1. 点亮c++组件

image-20220809141545008

  1. 链接脚本配置

.text 段中加入:

PROVIDE(__ctors_start__ = .);
KEEP (*(SORT(.init_array.*)))
KEEP (*(.init_array))
PROVIDE(__ctors_end__ = .);
. = ALIGN(4);

因为在../rt-thread/components/cxx_crt_init.c中对C++全局构造函数进行了初始化,链接脚本文件 link.ldsC++ 全局构造函数的代码分配了段,链接时将其所产生的目标文件链接至 __ctors_start____ctors_end__组成的段中。

image-20220809143454415

在链接脚本加入下面段,增加对C++异常的支持:

/* .ARM.exidx is sorted, so has to go in its own output section.  */
__exidx_start = .;
ARM.exidx :
{
*(.ARM.exidx* .gnu.linkonce.armexidx.*)
/* This is used by the startup in order to initialize the .data secion */
_sidata = .;
} >FLASH AT>FLASH
__exidx_end = .;

__exidx_start 分配了 C++ 异常的起始地址, __exidx_end 分配了 C++ 异常的结束地址,当异常产生的时候,就会被分配到指定的段地址中。

最后,在.data加入:

PROVIDE(__dtors_start__ = .);
KEEP(*(SORT(.dtors.*)))
KEEP(*(.dtors))
PROVIDE(__dtors_end__ = .);
. = ALIGN(4);
  1. C++编译/链接器配置

    注:以下配置内容ARM开发板创建工程时会自动配置,无需手动配置。

将C Complier的Preprocessorincludes内容复制到C++ Complier中:

image-20220809151439295

添加C++ Linker中链接脚本的路径:

image-20220809151819122

然后在C++ Linker中勾选:

image-20220809151638551

  1. main.c改为main.cpp(后面的应用程序应都采用cpp格式)

1.2 使用注意事项

由于RT-Thread RTOS多用于嵌入式系统,对于c++应用程序有一些规则:

  1. 不使用异常。(实际测试异常捕获失败,导致相关线程卡死,或与因为内核的缘故?)
  2. 不使用运行时类型信息(RTTI)。
  3. 不鼓励使用模板,它很容易导致代码文本变大。
  4. 不鼓励使用静态类变量。调用它们的构造函数的时间和地点无法精确控制,这使多线程编程成为一场噩梦。
  5. 强烈反对多重继承,因为它会导致不可容忍的混乱。

2 MRS配置

IDE:MounRiver Studio

MCU:CH32V103C8T6

2.1 配置步骤

将创建的C工程转换为C++工程

image-20220812104124669

image-20220812114215014

修改工程配置

  • 将GNU C中的目录复制到GNU C++中

image-20220812114805836

如:

image-20220812114831148

  • 选用C++11标准

image-20220812114638791

  • main.c改为main.cpp(后面的应用程序应都采用cpp格式),对于所有.h文件加入:
#ifdef __cplusplus
       extern "C" {
    
    
#endif

// original code
// ...

#ifdef __cplusplus
        }
#endif

2.2 问题与优化

  1. 引用未定义函数 _read _write _sbrk

使用 printf ,scanf ,malloc 等函数需要实现_read _lseek _isatty _fstat _write _sbrk函数。

可以重新实现_write() 和 _read() ,实现printf串口重定向。

image-20220812113640387

  1. C++支持函数重载,所以生成的目标代码的名字和C会有些不同,对于中断服务函数改名后,就与中断向量表中命名不一致,导致程序无法正常跳转到中断。因此存放诸如TIM1_UP_IRQHandler ISR函数的文件不能改为CPP格式,只能用C文件。若需要调用CPP函数,则需要用extern "C"强制成C语言的名字规则,详见STM32 C/C++ (二)混合编程 函数相互调用

  2. 在C++中,全局变量和静态变量的构造函数需要在main函数执行前执行,这些构造函数的地址会放在init_array表中,因此需要调用这些函数的代码对变量进行初始化,否则构造函数中全局变量默认初始化为0。(未初始化的变量放在BSS段被清零)

  • 方法一

需勾选--specs=nano.specs

参考RTT的配置(在gcc中ctor_list里存放的即为全局对象的构造函数的指针):

__attribute__((weak)) int cplusplus_system_init(void)
{
    
    
    typedef void(*pfunc)();
    extern pfunc __ctors_start__[];
    extern pfunc __ctors_end__[];
    pfunc *p;

    for (p = __ctors_start__; p < __ctors_end__; p++)
        (*p)();

    return 0;
}

并在.h文件中进行声明extern void cplusplus_system_init(void);

最后在startup中时钟初始化前调用:

jal  cplusplus_system_init
jal  SystemInit
    la t0, main
	csrw mepc, t0
	mret
  • 方法二

注意:需要删除startup文件中.global _start语句,因为编译器已经为我们定义了程序总入口。

__libc_init_array函数的底层实现,不同的编译器有不同实现方式:

使用libc自带的__libc_init_array初始化函数,使用前需去掉makefile中的编译选项-nostartfiles

image-20220815164238465

同样在startup中时钟初始化前调用:

  jal  __libc_init_array
  jal  SystemInit
      ...

However,「方法二」最终测试并没有成功调用构造函数的初始化列表,具体原因暂时不知…

  1. C++ new和delete的实现

rt-thread实现(rt-thread\components\libc\cplusplus\cxx_crt.cpp):

void *operator new(size_t size)
{
    
    
    return rt_malloc(size);
}

void *operator new[](size_t size)
{
    
    
    return rt_malloc(size);
}

void operator delete(void *ptr)
{
    
    
    rt_free(ptr);
}

void operator delete[](void *ptr)
{
    
    
    return rt_free(ptr);
}

void __cxa_pure_virtual(void)
{
    
    
    rt_kprintf("Illegal to call a pure virtual function.\n");
}

IDE优化

  1. 显示FLASH及RAM的使用占比情况(实际上是为arm-none-eabi-ld.exe工具添加--print-memory-usage命令):

    image-20220812155841825

image-20220812155803001


References

Appendixes

RISC-V Embedded GCC/bin 所有编译工具帮助命令查看:

riscv-none-embed-addr2line.exe --help
riscv-none-embed-ar.exe --help
riscv-none-embed-as.exe --help
riscv-none-embed-c++.exe --help
riscv-none-embed-c++filt.exe --help
riscv-none-embed-cpp.exe --help
riscv-none-embed-elfedit.exe --help
riscv-none-embed-g++.exe --help
riscv-none-embed-gcc.exe --help
riscv-none-embed-gcc-8.2.0.exe --help
riscv-none-embed-gcc-ar.exe --help
riscv-none-embed-gcc-nm.exe --help
riscv-none-embed-gcc-ranlib.exe --help
riscv-none-embed-gcov.exe --help
riscv-none-embed-gcov-dump.exe --help
riscv-none-embed-gcov-tool.exe --help
riscv-none-embed-gdb.exe --help
riscv-none-embed-gdb-py.exe --help
riscv-none-embed-gprof.exe --help
riscv-none-embed-ld.bfd.exe --help
riscv-none-embed-ld.exe --help
riscv-none-embed-nm.exe --help
riscv-none-embed-objcopy.exe --help
riscv-none-embed-objdump.exe --help
riscv-none-embed-ranlib.exe --help
riscv-none-embed-readelf.exe --help
riscv-none-embed-size.exe --help
riscv-none-embed-strings.exe --help
riscv-none-embed-strip.exe --help

END

猜你喜欢

转载自blog.csdn.net/kouxi1/article/details/126836930