2026-06-08 · STM32 · ARM GCC · Toolchain
gcc-arm-none-eabi 11.3:
"is not implemented and will always fail"
A complete guide to understanding and fixing the infamous linker syscall warnings that appear when upgrading to ARM GCC 11.3+ on bare-metal STM32 projects.
warning: _close is not implemented and will always fail warning: _fstat is not implemented and will always fail warning: _getpid is not implemented and will always fail warning: _isatty is not implemented and will always fail warning: _kill is not implemented and will always fail warning: _lseek is not implemented and will always fail warning: _open is not implemented and will always fail warning: _read is not implemented and will always fail warning: _write is not implemented and will always fail
If you've seen this wall of warnings after updating your ARM GCC toolchain, you're not alone — the Stack Overflow question has been viewed over 34,000 times. This article explains why they appear, what they mean, and how to fix them.
Why Do These Warnings Appear?
The ARM GCC toolchain uses newlib as its C standard library. newlib requires low-level system calls (syscalls) like _write, _read, _sbrk, etc. On an OS these are provided by the kernel. On bare-metal — you provide them (or a stub library does).
The libnosys library provides minimal do-nothing stubs for every syscall. Before version 11.3, these linked silently. Starting with 11.3, ARM GCC developers added explicit warning directives using .gnu.warning.SYMBOL_NAME in the assembler. The linker now prints a warning whenever a syscall stub is actually pulled into your binary.
Does This Break Anything?
No. Your firmware works exactly as before. "Fail" means "returns -1 and does nothing" — well-defined behavior. But the warnings tell you which syscalls your code (or libraries) are actually calling. If _write is called via printf and you expect serial output, a silent stub will leave you debugging blind.
What Triggers These Syscalls?
| Syscall | Triggered by |
|---|---|
_write | printf(), puts() |
_read | scanf(), getchar() |
_sbrk | malloc(), free() (no warning from libnosys) |
_close, _fstat, _lseek | exit(), C++ static destructors |
_getpid, _kill | rand(), signal handling |
_isatty | newlib buffering internals |
How to Fix the Warnings
Method 1: Provide Your Own Syscalls (Recommended)
Write a syscalls.c file. This eliminates libnosys entirely and gives you full control:
#include <sys/stat.h>
#include <errno.h>
int _write(int file, char *ptr, int len) {
for (int i = 0; i < len; i++) {
while (!(USART1->SR & USART_SR_TXE));
USART1->DR = (uint8_t)ptr[i];
}
return len;
}
void *_sbrk(int incr) {
extern char _estack;
static char *heap_end = 0;
char *prev_heap_end;
if (heap_end == 0) heap_end = &_estack;
prev_heap_end = heap_end;
heap_end += incr;
return (void*)prev_heap_end;
}
int _close(int f) { return -1; }
int _fstat(int f, struct stat *st) { st->st_mode = S_IFCHR; return 0; }
int _isatty(int f) { return 1; }
int _lseek(int f, int p, int d) { return 0; }
int _read(int f, char *p, int len) { return 0; }
int _getpid(void) { return 1; }
int _kill(int p, int s) { return -1; }
Then remove --specs=nosys.specs from your linker flags. The warnings disappear, and printf actually outputs to your USART.
Method 2: Accept the Warnings
If your project works fine and you don't care about warning noise, just ignore them. No linker flag can suppress these particular warnings (they come from embedded object symbols, not the linker itself).
Method 3: Remove Unused printf References
Often the warnings appear because of a leftover printf() call. Removing it eliminates the _write reference and most of the warnings. Use direct register writes for debug output instead.
Method 4: Keep Using --specs=nano.specs + --specs=nosys.specs
If you're already linking with these flags, you're doing the right thing. The warnings are expected and harmless. The binary is correct.
Important: The _sbrk Trap
libnosys provides _sbrk without a warning — and it works. But if you switch to custom syscalls (Method 1), you must implement _sbrk correctly. A broken _sbrk will silently corrupt your heap, turning a simple malloc into days of debugging.
Summary
| Situation | Action |
|---|---|
| You use printf() and it works | Write custom syscalls (Method 1) |
| No stdio, only GPIO/interrupts | Warnings are harmless |
| STM32CubeMX generated project | Check linker flags in the Makefile |
| You want zero warnings | 15 lines of syscalls.c (Method 1) |
The ARM GCC 11.3 syscall warnings are a signal, not a problem. They tell you which stubs your code uses, so you can decide whether they should actually do something. For most bare-metal STM32 projects, writing a small syscalls.c takes 10 minutes and makes your build output warning-free.
References
Davide Carrese — Embedded Firmware Engineer · carrese.eu
Comments
Have comments? Send me an email.