裸机嵌入式 C++:将闪存写入 ram 时未定义对 memcpy 和 memset 的引用

Posted

技术标签:

【中文标题】裸机嵌入式 C++:将闪存写入 ram 时未定义对 memcpy 和 memset 的引用【英文标题】:bare metal embedded c++: undefined reference to memcpy and memset while writing flash to ram 【发布时间】:2022-01-02 08:57:05 【问题描述】:

正如标题所暗示的那样,我收到一个与未定义 memcpy 和 memset 相关的错误,即使我没有直接使用它(尽管鉴于此错误的性质,我猜它用于链接器和编译过程)

长话短说:我正在将最初存储在 FLASH 中的 .data 和 .bss 部分写入 SRAM 内存,我正在使用指针进行此类操作,如下所示:

#ifdef __cplusplus
extern "C" 
#endif

#include <stdint.h>
#include <string.h>


int main(void);
void Reset_Handler(void);
void Default_Handler(void);


#ifdef __cplusplus

#endif


extern uint32_t _etext;
extern uint32_t _sdata;
extern uint32_t _edata;
extern uint32_t _sbss;
extern uint32_t _ebss;


void Reset_Handler(void)
    
   //copy .data section to SRAM
    uint32_t size = (uint32_t)&_edata - (uint32_t)&_sdata;
    
    uint8_t *pDst = (uint8_t*)&_sdata; //sram
    uint8_t *pSrc = (uint8_t*)&_etext; //source point comes from flash memory that is end of flash 
    
    for(uint32_t i =0 ; i < size ; i++)
    
        *pDst++ = *pSrc++;              //"MEMCPY NOT DEFINED" ERROR TRIGGERS HERE
    
    
    //copy .bss section to SRAM
    size = (uint32_t)&_ebss - (uint32_t)&_sbss;
    
    pDst = (uint8_t*)&_sbss; //sram
    
    for(uint32_t i =0 ; i < size ; i++)
    
        *pDst++ = 0;                 //"MEMSET NOT DEFINED" ERROR TRIGGERS HERE
    
    
    main();


我正在使用 arm 嵌入式 g++ 编译器来构建 .o 文件,然后使用 -nostdlib 选项链接到 .elf ,因为这是嵌入式应用程序,因此不需要/负担得起的标准库。

该过程失败,因为似乎存在对“memcpy”和“memset”函数的隐式调用。我试图包含 (据我所知,这是定义这两个函数的地方)另外,我已经在 extern "C" 中进行了包含,以避免由于名称修改而导致“未定义符号” .

这是 make 文件调用和终端输出:

arm-none-eabi-g++ -c -mcpu=cortex-m4 -mthumb -std=c++11 -O2 -g -fno-exceptions stm32_startup.cpp -o stm32_startup.o

arm-none-eabi-g++ -nostdlib -T stm32_ls.ld -Wl,-Map=final.map    main.o GPIO_PORT.o stm32_startup.o config.o -o final.elf

/usr/bin/../lib/gcc/arm-none-eabi/10.3.1/../../../../arm-none-eabi/bin/ld: stm32_startup.o: in function `Reset_Handler':
/home/inumaki/Development/stm32/Workspace/stm32_startup.cpp:155: undefined reference to `memcpy'

/usr/bin/../lib/gcc/arm-none-eabi/10.3.1/../../../../arm-none-eabi/bin/ld: /home/inumaki/Development/stm32/Workspace/stm32_startup.cpp:165: undefined reference to `memset'
collect2: error: ld returned 1 exit status
make: *** [Makefile:35: final.elf] Error 1


inumaki@dev-Inumaki:~/Development/stm32/Workspace$ make
arm-none-eabi-g++ -c -mcpu=cortex-m4 -mthumb -std=c++11 -O2 -g -fno-exceptions stm32_startup.cpp -o stm32_startup.o

arm-none-eabi-g++ -nostdlib -T stm32_ls.ld -Wl,-Map=final.map    main.o GPIO_PORT.o stm32_startup.o config.o -o final.elf

/usr/bin/../lib/gcc/arm-none-eabi/10.3.1/../../../../arm-none-eabi/bin/ld: stm32_startup.o: in function `Reset_Handler':
/home/inumaki/Development/stm32/Workspace/stm32_startup.cpp:155: undefined reference to `memcpy'
/usr/bin/../lib/gcc/arm-none-eabi/10.3.1/../../../../arm-none-eabi/bin/ld: /home/inumaki/Development/stm32/Workspace/stm32_startup.cpp:165: undefined reference to `memset'
collect2: error: ld returned 1 exit status
make: *** [Makefile:35: final.elf] Error 1

如我所见,唯一的出路是在删除#include 的同时定义我自己的memcpy 和memset 函数实现以避免重新定义问题。虽然我不知道 memcpy 和 memset 是做什么的。

提前致谢。

编辑:

通过删除 -nostdlib,即允许链接器链接标准库,我得到这个错误也与未定义的函数有关。在我看来,它与汇编程序启动文件有关,即使我有一个 .cpp 启动文件。

inumaki@dev-Inumaki:~/Development/stm32/Workspace$ make
arm-none-eabi-g++ -c -mcpu=cortex-m4 -mthumb -std=c++11 -O2 -g -fno-exceptions main.cpp -o main.o

arm-none-eabi-g++ -c -mcpu=cortex-m4 -mthumb -std=c++11 -O2 -g -fno-exceptions GPIO_PORT.cpp -o GPIO_PORT.o

arm-none-eabi-g++ -c -mcpu=cortex-m4 -mthumb -std=c++11 -O2 -g -fno-exceptions stm32_startup.cpp -o stm32_startup.o

arm-none-eabi-g++ -c -mcpu=cortex-m4 -mthumb -std=c++11 -O2 -g -fno-exceptions config.cpp -o config.o

arm-none-eabi-g++ -T stm32_ls.ld -Wl,-Map=final.map main.o GPIO_PORT.o stm32_startup.o config.o -o final.elf

/usr/bin/../lib/gcc/arm-none-eabi/10.3.1/../../../../arm-none-eabi/bin/ld: /usr/bin/../lib/gcc/arm-none-eabi/10.3.1/../../../../arm-none-eabi/lib/crt0.o: in function `_mainCRTStartup':
(.text+0x128): undefined reference to `__bss_start__'

/usr/bin/../lib/gcc/arm-none-eabi/10.3.1/../../../../arm-none-eabi/bin/ld: (.text+0x12c): undefined reference to `__bss_end__'
/usr/bin/../lib/gcc/arm-none-eabi/10.3.1/../../../../arm-none-eabi/bin/ld: /usr/bin/../lib/gcc/arm-none-eabi/10.3.1/../../../../arm-none-eabi/lib/libc.a(lib_a-exit.o): in function `exit':
exit.c:(.text.exit+0x2c): undefined reference to `_exit'
collect2: error: ld returned 1 exit status
make: *** [Makefile:35: final.elf] Error 1

【问题讨论】:

虽然我不知道 memcpy 和 memset 是做什么的。你look them up了吗? 我想知道编译器是不是太聪明了,识别出代码描述的行为,并试图用对memcpymemset的调用替换你的循环,但不知道它们在你的实现中不存在. @PaulSanders 是的,我有点看过他们。我知道他们做什么,但不知道他们是怎么做的。我真正的意思是,如果需要,我不知道如何进行优化。 请注意,-nostdlib 指的是 glibc,而不是 c++ std 库。 gllibc 包括 memcpy、strcpy 等,对于嵌入式应用程序/小型 MCU 来说绝对不会太“昂贵”。 【参考方案1】:

我能够通过将您的代码复制到Compiler Explorer 来重现您的问题。解决方案是在编译该文件时将-ffreestanding 选项添加到GCC。这个网站上有more info about -ffreestanding

或者,您可以尝试删除您在链接期间使用的-nostdlib 选项,允许 GCC 在其标准库中进行链接,前提是它已正确配置以允许这样做。我建议您至少尝试这样做,看看它给您的应用程序增加了多少膨胀。这样做可能没问题,因为 GCC 应该只包含您实际调用的库函数。

【讨论】:

我猜用-ffunction-sections-Wl,-gc-sections 构建也可能有助于减少内存使用?【参考方案2】:

添加-fno-tree-loop-distribute-patterns 以防止gcc 识别memsetmemcpy 等模式并为其生成外部函数调用。这通常是您想要的独立执行环境。请注意,-ffreestanding 不一定 一定会抑制这些函数调用的生成。

或者,您可以链接到您自己的 memcpymemset 实现,正如您所指出的那样 - 但当您让一切正常时,这可能是需要考虑的事情。

【讨论】:

我试过这个没有成功,我得到了完全相同的错误。

以上是关于裸机嵌入式 C++:将闪存写入 ram 时未定义对 memcpy 和 memset 的引用的主要内容,如果未能解决你的问题,请参考以下文章

您如何在内存非常有限的嵌入式系统上处理大数据传输?

在引导阶段将数据从闪存重定位到 RAM

stm32 flash半页写入

将数组写入 .hex 文件中的特定闪存位置

嵌入式 linux/QNX - 如何将闪存完全归零

从 IAR stm32f2/f4 闪存复制函数到 ram 并运行它