裸机嵌入式C++使用-nostdlib编译时出现memcpy、memset未定义报错
问题原因
GCC在开启-O2优化时,会自动将连续内存拷贝、批量内存置零的循环逻辑,替换为更高效的memcpy/memset库函数调用,这就是你没有显式调用也触发未定义引用报错的根本原因,和头文件引入方式、名字修饰无关。你加了-nostdlib后没有链接标准库的实现,自然找不到这两个符号。
解决办法
按推荐优先级排序:
方案1:添加裸机编译选项(推荐,无需改代码)
在你的编译参数中添加-ffreestanding 以及对应内置函数禁用选项,直接禁止GCC自动替换内置函数:
你原来的编译指令修改为:
arm-none-eabi-g++ -c -mcpu=cortex-m4 -mthumb -std=c++11 -O2 -g -fno-exceptions -ffreestanding -fno-builtin-memcpy -fno-builtin-memset stm32_startup.cpp -o stm32_startup.o
-ffreestanding是裸机开发的标准编译选项,会告知GCC当前为无标准库的独立运行环境,不会默认假定main函数存在,也不会随意将循环替换为标准库函数调用。
方案2:自行实现memcpy和memset函数
如果不想改编译参数,可以直接在你的启动代码中添加这两个函数的极简实现,注意要加extern "C"避免C++名字修饰:
首先删除#include <string.h>,然后添加如下代码:
extern "C" void* memcpy(void* __restrict dest, const void* __restrict src, size_t n) { uint8_t* d = static_cast<uint8_t*>(dest); const uint8_t* s = static_cast<const uint8_t*>(src); for (size_t i = 0; i < n; ++i) { d[i] = s[i]; } return dest; } extern "C" void* memset(void* dest, int c, size_t n) { uint8_t* d = static_cast<uint8_t*>(dest); const uint8_t val = static_cast<uint8_t>(c); for (size_t i = 0; i < n; ++i) { d[i] = val; } return dest; }
这个实现完全满足你拷贝.data段、清.bss段的需求,不需要额外优化也能正常运行。
方案3:不要移除-nostdlib(不推荐)
你移除-nostdlib后报错是因为标准库默认的crt0启动代码需要你的链接脚本定义__bss_start__、__bss_end__等符号,还需要你实现_exit等系统调用存根,完全没必要。你自己已经实现了Reset_Handler作为入口,用-nostdlib才是裸机开发的正确选择。
额外建议
如果后续使用C++特性还要注意:
- 如果你有全局/静态对象,需要在Reset_Handler调用main之前执行全局构造函数,需要在链接脚本里定义构造函数段的起止符号,然后遍历调用函数指针即可。
- 编译参数建议始终保留
-ffreestanding -fno-rtti -fno-exceptions,避免生成依赖标准库的代码。
内容的提问来源于stack exchange,提问作者Juan_David
相关产品推荐
相关产品推荐

