IDE:CLion
GCC:arm-none-eabi 14.2rel1
MCU:stm32f407
普通工程使用LTO报错
今天在使用LTO优化程序时,莫名出现许多系统调用缺失的情况,不使用LTO是可以正常编译的
add_compile_options(-flto) add_link_options(-flto )
我确信系统调用均已实现(由STM32CubeMX自动生成的),且均已编译并链接到程序中。
查了一些方法也没有解决,包括在编译器或者链接器中添加--specs=nosys.spece 标志。也并非是链接顺序的问题(如果是make构建系统,那么要确保
syscalls.o
放在标准库(如libc.a
)之前)。# 在CMake中添加链接器规范也不可行 add_link_options(--specs=nano.specs) add_link_options(--specs=nosys.specs)
后来发现是强定义无法覆盖弱符号的问题,LTO可能会因为优化导致符号未被正确覆盖,那么需要强制让编译器将这些符号视为强定义:即在函数前加上下面扩展,让函数看起来仿佛使用过,让它不要把这些符号优化掉。
__attribute__((used))
不过这个扩展慎用,因为是嵌入式开发,资源紧张,未使用的段需要被移除,避免二进制文件膨胀。这一点在链接器中已经通过标志-Wl,-gc-sections(嵌入式默认添加的)来去除,但是这个链接标志并不会去除加上used扩展的函数
使用FreeRTOS报错vTaskSwitchContext未定义
把FreeRTOS引入工程后,也会报错,报错位置是在汇编语句块里
这应该是LTO的全局优化未能识别汇编代码中的隐式函数引用,做法与前面相同,在函数的声明或者定义前面加上used编译器扩展
used确实好用
强制保留符号
既然前面是因为优化把符号去除,那么理论上就可以通过强制保留符号解决前面的问题。比如FreeRTOS这个问题,可以通过添加链接器标志保留函数符号
# 在链接选项中添加 -u 强制引用符号 add_link_options(-Wl,-u,vTaskSwitchContext)
失败的解决方法
在链接脚本里使用KEEP强制保留符号,好像还是没能解决
使用外部声明好像也不能解决(包括使用.global)
警告
成功解决LTO未定义错误后,编译时会警告什么链接阶段未能启用并行而是用什么串行方式
也不是内存的缘故,比较奇怪。如果不把GUI库引入,那么就不会出现这个警告,不过这个警告并不会对优化有什么影响,只是会优化得慢些(风扇也不知道为什么就转起来了)。
syscalls.c
下面是syscalls.c的代码
/** ****************************************************************************** * @file syscalls.c * @author Auto-generated by STM32CubeIDE * @brief STM32CubeIDE Minimal System calls file * * For more information about which c-functions * need which of these lowlevel functions * please consult the Newlib libc-manual ****************************************************************************** * @attention * * Copyright (c) 2020-2024 STMicroelectronics. * All rights reserved. * * This software is licensed under terms that can be found in the LICENSE file * in the root directory of this software component. * If no LICENSE file comes with this software, it is provided AS-IS. * ****************************************************************************** */ /* Includes */ #include <reent.h> #include <sys/stat.h> #include <stdlib.h> #include <errno.h> #include <stdio.h> #include <signal.h> #include <time.h> #include <sys/time.h> #include <sys/times.h> /* Variables */ extern int __io_putchar(int ch) __attribute__((weak)); extern int __io_getchar(void) __attribute__((weak)); char *__env[1] = { 0 }; char **environ = __env; /* Functions */ void initialise_monitor_handles() { } __attribute__((used)) int _getpid(void) { return 1; } __attribute__((used)) int _kill(int pid, int sig) { (void)pid; (void)sig; errno = EINVAL; return -1; } void _exit (int status) { _kill(status, -1); while (1) {} /* Make sure we hang here */ } __attribute__((weak)) int _read(int file, char *ptr, int len) { (void)file; int DataIdx; for (DataIdx = 0; DataIdx < len; DataIdx++) { *ptr++ = __io_getchar(); } return len; } __attribute__((weak)) int _write(int file, char *ptr, int len) { (void)file; int DataIdx; for (DataIdx = 0; DataIdx < len; DataIdx++) { __io_putchar(*ptr++); } return len; } int _close(int file) { (void)file; return -1; } __attribute__((used)) int _fstat(int file, struct stat *st) { (void)file; st->st_mode = S_IFCHR; return 0; } __attribute__((used)) int _isatty(int file) { (void)file; return 1; } int _lseek(int file, int ptr, int dir) { (void)file; (void)ptr; (void)dir; return 0; } int _open(char *path, int flags, ...) { (void)path; (void)flags; /* Pretend like we always fail */ return -1; } int _wait(int *status) { (void)status; errno = ECHILD; return -1; } int _unlink(char *name) { (void)name; errno = ENOENT; return -1; } int _times(struct tms *buf) { (void)buf; return -1; } int _stat(char *file, struct stat *st) { (void)file; st->st_mode = S_IFCHR; return 0; } int _link(char *old, char *new) { (void)old; (void)new; errno = EMLINK; return -1; } int _fork(void) { errno = EAGAIN; return -1; } int _execve(char *name, char **argv, char **env) { (void)name; (void)argv; (void)env; errno = ENOMEM; return -1; }