• [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);
    
    • 1
    • 2
    • 3
    • 4
    • 5

    因为在../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 = .;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

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

    最后,在.data加入:

    PROVIDE(__dtors_start__ = .);
    KEEP(*(SORT(.dtors.*)))
    KEEP(*(.dtors))
    PROVIDE(__dtors_end__ = .);
    . = ALIGN(4);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

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

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

    jal  cplusplus_system_init
    jal  SystemInit
        la t0, main
    	csrw mepc, t0
    	mret
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 方法二

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

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

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

    image-20220815164238465

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

      jal  __libc_init_array
      jal  SystemInit
          ...
    
    • 1
    • 2
    • 3

    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");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    END

  • 相关阅读:
    Android11适配
    知识库网站如何搭建?需要注意这五个要点!
    动态SQL+分页
    【java】java 类型安全 与 unchecked warning
    MySQL索引
    【Java 进阶篇】MySQL 事务详解
    对Mysql中redo log、undo log、binlog深入理解
    本地引入 Axios 报错
    springAOP讲解
    linux内核网络收包过程(一)
  • 原文地址:https://blog.csdn.net/kouxi1/article/details/126836930