CodeWarrior中ARM GCC与EWL库配置实战:Kinetis项目构建与迁移指南

发布时间:2026/6/23 20:21:19
CodeWarrior中ARM GCC与EWL库配置实战:Kinetis项目构建与迁移指南 1. 项目概述为什么要在CodeWarrior里折腾GCC和EWL搞嵌入式开发尤其是用Freescale现在叫NXPKinetis系列MCU的朋友对CodeWarrior这个IDE肯定不陌生。早年它自带的编译器用起来挺顺手但随着开源生态的崛起和项目维护的长期性考虑很多团队开始把目光投向GCC。原因很简单免费、开源、社区活跃、跨平台友好而且避免了商业编译器的授权锁问题。但真要把一个用惯了CodeWarrior内置工具链的老项目或者想在新项目里直接用GCC你会发现这中间有一道不小的鸿沟——怎么配置那些针对嵌入式优化过的库去哪找EWL又是什么这就是我们今天要啃的硬骨头。简单说就是在CodeWarrior for Microcontrollers V10.x这个经典环境里把ARM GCC这套开源工具链用起来并且搞清楚它自带的那个嵌入式专用库EWL怎么玩。这不仅仅是换个编译器那么简单它涉及到整个构建属性配置、库文件替换、启动代码调整甚至调试流程的适配。我经历过从原装工具链迁移到GCC的全过程踩过不少坑也总结出了一套能让项目平稳跑起来的配置心法。如果你正在为Kinetis项目寻找更开放、更可控的构建方案或者手里有老项目需要迁移到GCC环境那这篇指南就是为你准备的。2. ARM GCC构建属性深度解析从图形界面到命令行本质在CodeWarrior里用GCC大部分配置工作都在项目属性的“Tool Settings”里完成。别看是图形化界面背后对应的都是实打实的GCC命令行参数。理解这两者的映射关系是你能否灵活配置的关键。2.1 编译器核心面板与命令行的映射打开项目属性找到ARM Ltd Windows GCC C Compiler你会看到几个子面板。第一个就是编译器主设置。Command 与 All options这里显示的是编译器可执行文件路径默认arm-none-eabi-gcc和最终生成的完整命令行。我建议你养成习惯每次修改重要设置后都看一眼“All options”这个框。它能帮你验证IDE生成的命令是否符合预期也是你从IDE配置向纯命令行构建比如用Makefile或CI/CD迁移时的参考蓝图。Expert settings 与 Command line pattern这是高级玩家区域。Command line pattern定义了IDE如何组装最终命令默认模式是${ARMSourceryDir}/${COMMAND} ${INPUTS} ${FLAGS} ${OUTPUT_FLAG}${OUTPUT_PREFIX}${OUTPUT}。除非你有非常特殊的构建需求比如要插入自定义的预处理工具否则不建议动这里。Expert settings则允许你直接注入额外的命令行参数绕过图形界面的限制。比如你想启用某个实验性的优化标志而IDE面板上没有就可以在这里加。实操心得在团队协作中我强烈建议把最终的、稳定的“All options”内容记录下来作为项目构建文档的一部分。这样当新成员加入或环境重建时能快速核对构建命令是否一致避免因IDE配置界面理解差异导致的构建结果不同。2.2 预处理器配置控制宏与头文件搜索Preprocessor面板管理着代码编译前的第一步处理。-nostdinc(Do not search system directories)这个选项要慎用。勾选后编译器将不再搜索标准的系统头文件目录如#include stdio.h会找不到。什么情况下用当你的项目使用了一套完全自定义的、与GCC自带库不兼容的C库时比如某些极度追求尺寸的裸机环境为了避免链接时混入不兼容的标准库头文件定义可以打开它。但对于大多数使用EWL或newlib的项目不要勾选。-E(Preprocess only)勾选后编译器只进行预处理输出.i文件不进行编译、汇编和链接。这主要用于调试复杂的宏展开或者检查头文件包含是否正确。日常构建不要开。-D与-U(Defined/Undefined symbols)这是最常用的功能之一。-D用来定义宏比如-DDEBUG1或-DUSE_FEATURE_X。在IDE里你只需要在输入框里写DEBUG1或USE_FEATURE_XIDE会自动在前面加上-D。-U则用于取消一个宏的定义优先级高于-D。配置多个条件编译开关时这里的清晰管理至关重要。注意事项定义有值的宏如DEBUG1时等号两边不要有空格。IDE通常会帮你处理但手动检查命令输出时要注意。另外用于条件编译的宏最好在项目的主要头文件中用#ifdef/#ifndef再做一次保护性声明避免因编译选项遗漏导致未定义行为。2.3 目录与头文件搜索路径管理Directories面板只有一个核心选项Include paths (-I)。这个选项决定了编译器在寻找#include文件时的搜索目录。它的行为规则需要明确对于#include header.h这种形式编译器先搜索用户通过-I指定的路径即你在这里配置的然后才搜索系统标准路径。对于#include header.h这种形式编译器只搜索系统标准路径。那么什么时候需要手动添加-I路径第三方库当你把某个传感器驱动库、通信协议栈的源代码放在项目目录下比如/libs/就需要将其头文件所在路径如../libs/DriverLib/include添加进来。项目自定义目录结构如果你的项目不是所有源文件和头文件都混在一起而是有清晰的/src,/inc分离就需要将/inc路径加入。不同版本的设备头文件如果你同时维护针对同一MCU不同型号如KL25和KL26的项目可能会将芯片特定的头文件放在不同子目录这时也需要指定。在CodeWarrior的GCC项目模板中通常已经自动添加了EWL库、CMSIS以及对应Kinetis芯片支持包的头文件路径形如${MCUToolsBaseDir}/ARM_GCC_Support/ewl/EWL_C/include。除非你移动了这些文件否则不要随意删除。2.4 优化等级与策略选择在尺寸、速度与调试间权衡Optimization面板是影响最终二进制文件性能和大小的关键。GCC的优化等级从-O0到-O3以及-Os各有侧重。-O0(None)默认的调试等级。不进行任何优化代码执行顺序与源代码完全一致变量全部保存在内存中。这是调试阶段的首选因为你可以单步跟踪每一行代码查看所有变量的实时值。代价是生成的代码最臃肿运行最慢。-O1(Optimize)开始进行一些不影响调试的优化比如删除未使用的代码、将常量表达式提前计算等。代码尺寸和执行速度会有改善但大部分调试信息仍然可用。适合在开发中期进行初步性能测试。-O2(Optimize more)启用几乎所有安全的优化包括指令调度、循环优化等。代码性能显著提升但调试会变得困难因为变量可能被优化到寄存器代码顺序可能重排。这是发布版本最常用的等级在速度和大小间取得较好平衡。-O3(Optimize most)在-O2基础上进行更激进的优化如函数内联、循环展开等。可能会大幅增加代码尺寸速度提升则因代码特性而异有时甚至可能因缓存问题变慢。需要针对具体性能瓶颈进行测试后决定是否使用。-Os(Optimize for size)嵌入式开发的明星选项。其优化目标是减小代码体积而不是提升速度。它会禁用那些通常会导致代码变大的优化如部分循环展开。对于Flash空间紧张的Kinetis M0/M0内核芯片如KL系列-Os往往是必选项。其他关键选项-fpack-struct让编译器不对结构体进行内存对齐填充。这能节省每一字节内存但访问未对齐的成员可能导致硬件异常在Cortex-M0上或性能损失在Cortex-M4上。除非你明确知道所有结构体访问都已是字节对齐的或者通过__packed属性单独控制否则不要全局启用此选项。-ffunction-sections和-fdata-sections这两个选项通常与链接器的--gc-sections配合使用称为“链接时垃圾回收”。它们让每个函数和数据都有自己的独立段section链接器可以删除最终未被引用的函数和数据从而显著减少二进制大小。对于嵌入式项目强烈建议同时开启这三者。避坑指南优化等级切换是发布前必须测试的环节。从-O0切换到-O2或-Os后一定要进行全面的功能测试和边界条件测试。有些在未优化时隐藏的bug如未初始化的变量、依赖特定执行顺序的代码会在优化后暴露出来。建议在版本控制中为调试和发布配置不同的优化等级。2.5 警告与错误处理将隐患扼杀在编译期Warnings面板用于控制编译器的严格程度。我的原则是在开发初期尽可能严苛。-Wall与-Wextra务必同时开启。-Wall启用一组常见警告并非“所有”-Wextra则提供更多有用的警告。它们能帮你发现很多潜在问题比如未使用的参数、有符号无符号比较、遗漏的break等。-Werror在持续集成和团队协作中强烈建议开启。它把所有的警告当作错误处理编译不通过。这能强制团队保持代码清洁避免警告堆积如山最后无人理会。本地开发时可以根据情况暂时关闭以快速验证想法但提交前必须确保在-Werror下编译通过。-pedantic与-pedantic-errors这两个选项要求代码严格遵循ISO C标准禁止使用GNU扩展。对于需要高度可移植性的代码可以考虑但嵌入式开发中经常会用到一些编译器扩展如__attribute__((interrupt))开启后反而麻烦通常不推荐。-fsyntax-only与-fno-common-fsyntax-only只检查语法不生成代码用于快速语法验证。-fno-common会影响未初始化全局变量的处理方式使其行为更符合C标准有助于发现一些链接阶段的重复定义问题建议开启。2.6 杂项设置语言标准与细节控制Miscellaneous面板汇集了一些其他重要设置。Language Standard选择C语言标准。对于新项目建议选择ISO C99或ISO C99 with GNU Extensions。C99标准引入了//注释、inline关键字、变量声明不必在作用域开头等特性写起来更现代。如果维护老代码可能需要选择ISO C90。-funsigned-char/-fsigned-char这是嵌入式开发中一个至关重要的选项。它决定了char类型默认是有符号还是无符号。C标准对此未定义由编译器决定。ARM GCC默认是signed char。如果你的代码逻辑依赖于char的无符号特性比如处理8位ADC采样值或者移植的代码有此假设就必须明确指定。不一致会导致比较、移位运算出现严重错误。-fmessage-length0这个默认存在的选项让错误信息可以不受限制地换行方便阅读完整的错误提示。Verbose (-v)勾选后构建输出窗口会显示IDE调用的每一个详细命令。在排查“为什么我的选项没生效”这类问题时这是最直接的诊断工具。3. EWL库全攻略为Kinetis量身定制的轻量级运行时EWL全称Embedded Warrior Library是CodeWarrior GCC工具链为ARM Cortex-M微控制器尤其是Kinetis提供的默认C/C运行时库。它的设计目标很明确在保证基本功能的前提下极致地减少内存占用。3.1 EWL I/O模式解析与选择EWL最核心的特性是提供了可选的I/O支持模式让你只为实际用到的功能付出代码空间代价。创建项目时在“Language and Build Tools Options”页面就需要做出选择C语言模式ewl(UART模式)标准输入输出重定向到UART串口。这是最常用的模式你需要自己实现底层的__read_console和__write_console函数通常由板级支持包提供将printf/scanf映射到具体的UART外设。ewl_hosted(调试器控制台模式)输入输出通过调试器如J-Link, OpenSDA的ITMInstrumentation Trace Macrocell或Semihosting功能实现。你可以在IDE的调试控制台直接看到printf输出无需额外接线。方便但有性能开销且调试器必须支持。ewl_noio(无I/O模式)不包含任何标准输入输出支持。printf、scanf等函数不可用或为空。如果你的应用是纯控制逻辑不需要任何打印调试信息选这个可以节省大量Flash和RAM。c9x系列模式在以上三种模式基础上增加了对宽字符wchar_t的支持。除非你的项目涉及国际化如多语言UI否则基本用不到。C模式命名规则类似ewl_c,ewl_c_hosted等除了包含C运行时还包含了C标准库如new,delete,iostream等的支持。注意启用C会显著增加代码体积。如何选择产品开发、需要现场日志选ewl(UART模式)连接一个串口到日志收集器。前期调试、快速验证选ewl_hosted利用调试器输出最便捷。资源极端紧张、功能确定无需打印选ewl_noio。项目使用C选择对应的C模式。3.2 格式化器配置按需裁剪精确控制体积EWL更进一步允许你选择printf/scanf家族函数支持的格式化器类型这是其节省空间的精髓所在。在项目属性的Librarian面板或编译器设置的特定位置可以找到Print Formats和Scan Formats下拉框。可选配置int仅支持整数%d,%u,%x等和字符串%s格式化。不支持浮点数%f和长整型%lld。int_FP支持整数、字符串和浮点数。int_LL支持整数包括long long即%lld和字符串。int_FP_LL支持全部整数、长整型、浮点数、字符串但不支持宽字符。选择策略评估需求你的应用代码里到底用了哪些printf/scanf格式用grep或IDE的搜索功能在整个项目里搜一下%f、%lf、%lld等。量化节省从一个配置编译后切换到另一个更精简的配置对比生成的.map文件或直接看二进制大小。通常去掉浮点支持能节省数KB到十几KB的Flash空间对于只有128KB Flash的KL25Z来说非常可观。默认与安全如果不确定或者项目处于早期频繁改动期可以先选int_FP_LL功能全。在功能稳定后再根据实际使用的格式化符尝试降级到int或int_LL进行回归测试。实操心得我曾在一个电池供电的传感器节点项目里通过将格式化器从int_FP改为int并移除所有调试用的printf最终节省了约8KB的Flash空间使得程序得以放入更便宜的、Flash更小的芯片中直接降低了BOM成本。永远不要为你用不到的功能付费代码空间也是成本。3.3 EWL库的组成与手动编译EWL库文件位于CodeWarrior安装目录下的[CW Install Dir]/MCU/ARM_GCC_Support/ewl/lib。它针对不同的ARM Cortex-M内核和浮点ABI进行了预编译armv6-m对应Cortex-M0/M0内核如Kinetis L系列。armv7e-m对应Cortex-M4内核如Kinetis K系列。armv7e-m软件浮点。armv7e-m/fpu硬件浮点使用硬件FPU ABI需要芯片带FPU且编译器选项指定-mfpufpv4-sp-d16 -mfloat-abihard。armv7e-m/softfp硬件浮点但使用软件浮点ABI兼容性更好性能稍差。armv7e-m/spfp类似softfp但使用-fshort-double编译double被视为float。库组件包括libc.a(C库)、libm.a(数学库)、libc.a(C库)、libuart.a(UART模式支持)、libhosted.a(主机模式支持)等。什么时候需要手动重新编译EWL修改了EWL源码位于ewl/目录下比如定制了malloc的实现。需要调整编译选项比如改变优化等级(-Os)、调整结构体打包策略等希望库文件与应用程序的编译选项更匹配。GCC工具链版本升级为了获得更好的兼容性和性能。编译方法有两种从IDE编译导入ewl目录下的.project文件像普通项目一样Clean和Build即可。这是最简单的方法。从命令行编译打开命令行进入ewl目录设置ARM_TOOLS指向GCC工具链、PLATFORM、VENDOR环境变量然后运行make。你可以通过make TARGET/armv6-m/ libc这样的命令单独编译某个库。3.4 实战为UART模式配置控制台输出选择ewl(UART)模式后你需要提供底层驱动让printf知道数据该往哪个UART口发送。CodeWarrior提供了一些参考实现。以TWR-KL25Z128板卡为例添加板级支持文件从CWInstallDir\MCU\ARM_GCC_Support\UART\TWR-KL25Z128目录下将ConsoleIO.c,ConsoleIO.h,UART0_PDD.h,PDD_Types.h复制到你的项目相应文件夹中。这些文件实现了基于UART0的__read_console和__write_console函数。修改主程序在main.c中包含ConsoleIO.h并在初始化阶段调用ConsoleIO_Init()。这个函数会配置UART的波特率通常是38400、引脚等。连接与测试编译下载后用串口工具如Putty、Tera Term连接板载调试器虚拟的COM口或外部UART引脚设置对应的波特率就能看到printf输出的信息了。关键点解析ConsoleIO.c中的__write_console函数是EWL库的“钩子”。当你的代码调用printf时最终会调用到这个函数。你需要在这个函数里将待发送的字符缓冲区通过芯片的UART外设发送出去。参考实现已经为你做好了但如果你的硬件设计使用了不同的UART端口比如UART1你就需要修改这个文件将UART0相关的寄存器操作改为UART1的。4. 迁移指南将遗留项目从原装工具链切换到GCC如果你手头有一个使用CodeWarrior原装ARM编译器非GCC的老项目想迁移到GCC以享受开源工具链的好处这个过程需要系统性的操作。4.1 迁移核心步骤拆解迁移不是简单地换个编译器它涉及项目配置、链接脚本、启动文件、系统初始化代码等一系列文件的替换和修改。核心思路是用一个全新的、能正常编译的GCC项目作为“模板”或“参考”逐步替换老项目的核心组件。步骤一更换工具链配置文件这是最关键的一步告诉IDE从此使用GCC。创建一个新的、空的GCC项目在向导中务必选择GCC作为构建工具。然后将老项目根目录下的隐藏文件.cproject用新GCC项目的.cproject文件替换。这个文件包含了构建器、编译器、链接器等所有工具链的设置。替换后在IDE中刷新项目F5。步骤二验证与调整构建设置打开项目属性检查C/C Build Tool Chain Editor确认当前工具链已变为ARM Ltd. Windows GCC。然后进入Settings切换到[All configurations]视图逐一核对以下关键配置将老项目中的自定义设置移植过来预处理器定义将原项目ARM Compiler下的Preprocessor中定义的符号-D完整地复制到ARM Ltd Windows GCC C Compiler的Preprocessor面板中。头文件包含路径将原项目的头文件路径-I复制到GCC编译器和汇编器的Directories或Preprocessor面板。链接库将原项目链接的库文件.a或.lib及其路径添加到ARM Ltd Windows GCC C Linker的Libraries面板。注意库文件名在GCC中通常是-l加库名如-lm表示链接libm.a路径用-L指定。步骤三替换链接脚本链接脚本Linker Command File控制着代码和数据在内存中的布局。原装编译器使用.lcf格式而GCC使用.ldLinker Script格式。两者语法不同不能混用。将老项目Project_Settings/Linker_Files目录下的所有.lcf文件删除从新建的GCC项目中复制对应的.ld文件过来。这些.ld文件已经为Kinetis的内存映射Flash/RAM起始地址、大小和GCC的段命名约定如.text,.data,.bss做好了配置。步骤四更新启动文件启动文件负责在main()函数之前初始化C运行环境。GCC和原装编译器的启动流程和符号命名有差异。需要从新GCC项目的Project_Settings/Startup_Code目录下复制以下三个文件到老项目中__arm_start.c包含__thumb_startup等启动函数。__arm_end.c可能包含一些结束处理代码。runtime_configuration.hEWL库的运行时配置头文件。步骤五修改系统初始化文件老项目的kinetis_sysinit.c文件通常需要修改以适配GCC。主要修改点包括添加弱中断向量定义GCC的启动文件期望所有中断向量都有定义即使是未使用的。你需要为所有中断处理程序添加__attribute__((weak))别名指向一个统一的Default_Handler。这可以防止因未定义中断向量而导致的链接错误。// 示例在kinetis_sysinit.c中添加 __attribute__((weak)) void DMA0_IRQHandler(void); __attribute__((weak)) void DMA1_IRQHandler(void); // ... 其他中断向量 void DMA0_IRQHandler(void) __attribute__((weak, alias(Default_Handler))); void DMA1_IRQHandler(void) __attribute__((weak, alias(Default_Handler)));声明外部堆栈指针GCC的链接脚本通常会定义一个_estack符号表示堆栈顶部地址。需要在kinetis_sysinit.c中声明它extern unsigned long _estack;。调整中断向量表中断向量表需要与启动文件中定义的弱符号对齐。通常需要将向量表中原来的函数名替换为上面定义的弱符号名。提供默认中断处理函数实现一个Default_Handler函数通常里面放一个断点指令__asm(bkpt)或死循环便于调试未处理的中断。移除编译器特定杂注删除原装编译器特有的#pragma指令如#pragma define_section等。4.2 调试配置修复完成上述步骤并成功编译后尝试调试可能会失败提示“指定的应用程序文件不存在”。这是因为调试配置还指向着旧编译器生成的输出文件如.elf或.axf文件格式和路径可能不同。在项目上右键选择Run As-Debug Configurations...。在左侧找到你的项目对应的调试配置例如hello_world_MK60N512VMD100_INTERNAL_FLASH_PnE。在Main标签页下检查C/C Application路径。它应该指向GCC编译生成的新.elf文件通常位于项目Debug或Release目录下。点击Browse...重新选择正确的文件。同时检查Debugger标签页确保调试器配置如接口、速度与你的硬件匹配。4.3 迁移后的验证清单完成迁移后不要急于进行功能测试先按以下清单进行基础验证编译通过确保0错误0警告在-Werror开启的情况下。链接通过生成的.elf或.hex文件大小合理没有出现“未定义的引用”错误。内存映射检查查看链接生成的.map文件确认.text(代码)、.data(已初始化数据)、.bss(未初始化数据)等段都正确地放入了Flash和RAM的指定区域没有溢出。启动测试下载程序后能否运行到main()函数的开头可以在main()第一条语句设断点验证。基础外设测试一个最简单的功能比如点亮一个LEDGPIO操作这能验证时钟系统、引脚配置基本正常。中断功能如果项目用到中断测试一个简单的中断如SysTick定时器中断是否能正常触发和响应。库函数调用测试一个标准库函数如memcpy或printf如果使能了确保EWL库链接正确。避坑指南迁移过程中最常见的错误是“未定义的引用”。这通常是因为某个源文件没有被包含在构建中。检查Makefile或.cproject中的文件列表。某个函数或变量的声明与定义不一致C vs C链接方式extern C问题。链接库缺失或路径错误。仔细检查-L和-l参数。 解决这类问题要善于使用.map文件查找符号定义位置以及使用arm-none-eabi-nm工具查看库文件或目标文件导出了哪些符号。5. 高级主题从EWL切换到NewlibEWL虽好但它是CodeWarrior/GCC工具链特有的。如果你的项目未来可能需要迁移到其他IDE如Eclipse GNU ARM Embedded Toolchain或者使用更标准的构建系统那么依赖EWL可能会成为障碍。这时可以考虑切换到更通用的newlibC库。5.1 切换的必要性与挑战Newlib是面向嵌入式系统的另一个流行的C标准库实现被广泛用于各种GNU工具链中。切换的好处是更好的可移植性和社区支持。但挑战在于newlib的初始化和底层IO实现与EWL不同需要修改启动代码和链接选项。5.2 切换步骤详解切换的核心是让链接器找到newlib的库文件并提供一个适配的启动流程。禁用EWL自动配置在项目属性的Librarian面板取消勾选Enable automatic library configurations。这阻止IDE自动添加EWL的链接参数。移除EWL头文件路径在ARM Ltd Windows GCC C Compiler Directories中删除所有指向EWL头文件的路径如.../ewl/EWL_C/include。移除EWL库搜索路径在ARM Ltd Windows GCC C Linker Libraries中清空Library search path (-L)里指向EWL库的路径。添加newlib链接标志在ARM Ltd Windows GCC C Linker Miscellaneous的Linker flags中添加-lc -lm -lgcc -lrdimon。这些分别链接newlib的C库、数学库、GCC运行时支持库和半主机库用于调试输出。指定specs文件在Other flags文本框中添加-specsrdimon.specs。这个specs文件告诉链接器使用半主机semihosting版本的newlib初始化代码。如果你最终不需要半主机调试输出可以后续研究使用nano.specs或nosys.specs以进一步减小体积。移除EWL启动文件从项目的Project_Settings/Startup_Code目录中删除__arm_start.c、__arm_end.c和runtime_configuration.h。提供新的启动包装器这是最关键的一步。Newlib期望的入口点是_start但Kinetis芯片需要先进行一些硬件初始化。你需要创建一个新的启动文件如startup.S或.c在其中初始化堆栈指针SP。调用__init_hardware这是Kinetis SDK或原有代码中的硬件初始化函数。如果需要将.data段从Flash复制到RAM对于从Flash运行的程序。最后跳转到newlib的_start函数由它完成C运行时初始化并调用main()。提供的汇编代码startup.S正是做了这些事情。你需要将这个文件添加到项目中并确保链接器脚本中的入口点ENTRY指向你定义的包装函数如__thumb_startup。5.3 切换后的测试与调试切换到newlib后首次编译可能会遇到更多未定义引用错误因为newlib的实现细节与EWL有差异。你需要确保实现了必要的系统调用_write,_read,_sbrk等如果你需要文件IO或malloc的话。对于简单的printf到串口通常只需要实现_write。链接了正确的启动文件crt0.o等这些通常由-specs文件自动处理。堆栈和堆的地址在链接脚本中正确定义_sbrk实现能正确管理堆内存。个人建议除非你有强烈的可移植性需求或者遇到了EWL库的特定限制否则在CodeWarrior环境下优先使用EWL。它更轻量与工具链集成更好配置更简单。将EWL项目迁移到纯GCCnewlib环境可以作为一个独立的后续步骤来规划。