You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Cygwin下编译ARM Makefile报ld returned 1 exit status错误

错误根因

collect2.exe: error: ld returned 1 exit status是链接阶段的通用报错,本质是链接器找不到代码中引用的符号实现,日志里的未定义引用对应问题点非常明确:

  • _exit:ARM裸机环境默认没有操作系统提供的系统调用桩,链接newlib标准库时会找不到这个退出函数实现
  • SystemInit:CMSIS标准规定的内核初始化函数,一般由芯片SDK的系统初始化文件、启动文件提供
  • Cy_SysEnableApplCore/Cy_GPIO_Pin_Init/Cy_SysTick_DelayInUs:这三个是Traveo系列官方PDL外设驱动库的接口函数,说明没有把对应驱动源文件加入编译列表,也没有正确链接预编译的PDL库
  • __cm4_vector_base_linker_symbol:这个是Cortex-M4核专属的链接脚本符号,说明要么误用了CM4核的链接脚本/启动文件,要么没打开对应CM0+核的编译宏,导致驱动代码里引用了CM4核的相关逻辑
具体修复步骤
  1. 补全必须的编译源文件
    当前源文件清单漏了裸机开发必须的三类文件,从Traveo官方SDK对应所用芯片型号的目录里找出来,加到Makefile的编译列表里:
    • 对应CM0+核的启动汇编文件(文件名一般为startup_<芯片型号>.s),包含中断向量表、复位入口逻辑
    • 系统初始化源文件(文件名一般为system_<芯片型号>.c),SystemInit函数就在这个文件里实现,负责内核时钟、内存的初始化配置
    • PDL驱动源文件:对应GPIO、SysTick、系统控制模块的c文件,一般存放在PDL库的drivers/src目录下,至少要包含cy_gpio.c、cy_systick.c、cy_syslib.c、cy_sysclk.c
  2. 补全标准库链接配置
    在链接参数LDFLAGS里加入--specs=nano.specs --specs=nosys.specs,其中nosys.specs会自动提供_exit等所有newlib依赖的系统调用空实现,不需要自己手动写桩函数;nano.specs会链接精简版C库,大幅减小固件体积。
  3. 修正链接脚本与编译宏配置
    • 删掉所有CM4核相关的链接配置,找到SDK中对应CM0+核的链接脚本(后缀为.ld),在链接参数中用-T <链接脚本路径>指定
    • 在编译参数CFLAGS中加入CM0+核对应的编译宏,比如-DCORE_M0P、对应芯片型号的宏,关闭双核场景下CM4核的相关代码逻辑,避免驱动代码引用CM4专属符号
    • 确认编译架构参数匹配CM0+核:CFLAGS和LDFLAGS里都要加-mcpu=cortex-m0plus -mthumb,不要误用CM4的-mcpu=cortex-m4参数
  4. 验证编译流程
    Cygwin环境下先确认arm-none-eabi工具链路径已经加入Cygwin的PATH变量,不要混用Windows原生工具链和Cygwin版make,避免路径识别错误。调整完参数后先执行make clean清除旧的编译产物,再执行make,链接阶段如果还有未定义符号,就顺着符号名找对应的源文件/库文件,加到编译链接列表里即可。链接生成elf文件后,用arm-none-eabi-objcopy就能转换出hex、bin格式的固件。
Makefile优化建议

针对ARM裸机开发的场景,Makefile可以做以下调整,大幅降低维护成本:

  • 用通配符自动收集源文件,不要手动逐个列.o文件规则,参考写法:
    # 工具链定义,方便后续切换版本
    TOOLCHAIN_PREFIX ?= arm-none-eabi-
    CC := $(TOOLCHAIN_PREFIX)gcc
    AS := $(TOOLCHAIN_PREFIX)gcc
    LD := $(TOOLCHAIN_PREFIX)gcc
    OBJCOPY := $(TOOLCHAIN_PREFIX)objcopy
    SIZE := $(TOOLCHAIN_PREFIX)size
    
    # 目录定义
    SRC_DIR := src
    DRIVER_DIR := drivers
    BUILD_DIR := build
    TARGET_NAME := firmware
    
    # 自动收集所有需要编译的c、s文件
    SRC_C := $(wildcard $(SRC_DIR)/*.c) $(wildcard $(DRIVER_DIR)/src/*.c)
    SRC_S := startup_<芯片型号>.s
    OBJS := $(patsubst %.c,$(BUILD_DIR)/%.o,$(SRC_C))
    OBJS += $(patsubst %.s,$(BUILD_DIR)/%.o,$(SRC_S))
    
    # 通用编译规则,自动把c/s文件编译到build目录下对应路径
    $(BUILD_DIR)/%.o: %.c
        @mkdir -p $(dir $@)
        $(CC) $(CFLAGS) -c $< -o $@
    
    $(BUILD_DIR)/%.o: %.s
        @mkdir -p $(dir $@)
        $(AS) $(ASFLAGS) -c $< -o $@
    
  • 拆分编译、链接参数,把通用配置归类:
    • CFLAGS中加入头文件路径、警告等级、优化等级、函数数据段分离参数:-Os -Wall -ffunction-sections -fdata-sections -I$(DRIVER_DIR)/include
    • LDFLAGS中加入段回收、map文件生成参数:-Wl,--gc-sections -Wl,-Map=$(BUILD_DIR)/$(TARGET_NAME).map,配合-ffunction-sections -fdata-sections可以自动删掉没被调用的函数,减小固件体积
  • 增加固件生成、大小统计规则:
    .PHONY: all clean
    all: $(BUILD_DIR)/$(TARGET_NAME).elf $(BUILD_DIR)/$(TARGET_NAME).hex $(BUILD_DIR)/$(TARGET_NAME).bin
        $(SIZE) $<
    
    $(BUILD_DIR)/$(TARGET_NAME).elf: $(OBJS)
        $(LD) $(LDFLAGS) $^ -o $@
    
    $(BUILD_DIR)/$(TARGET_NAME).hex: $(BUILD_DIR)/$(TARGET_NAME).elf
        $(OBJCOPY) -O ihex $< $@
    
    $(BUILD_DIR)/$(TARGET_NAME).bin: $(BUILD_DIR)/$(TARGET_NAME).elf
        $(OBJCOPY) -O binary $< $@
    
    clean:
        rm -rf $(BUILD_DIR)
    
  • 不要硬编码文件路径,所有路径、芯片型号、工具链前缀都用变量定义,后续换芯片型号、换目录结构只需要修改变量值即可。

内容的提问来源于stack exchange,提问作者mrm

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 12:25:16