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?

SyscallTriggered by
_writeprintf(), puts()
_readscanf(), getchar()
_sbrkmalloc(), free() (no warning from libnosys)
_close, _fstat, _lseekexit(), C++ static destructors
_getpid, _killrand(), signal handling
_isattynewlib 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

SituationAction
You use printf() and it worksWrite custom syscalls (Method 1)
No stdio, only GPIO/interruptsWarnings are harmless
STM32CubeMX generated projectCheck linker flags in the Makefile
You want zero warnings15 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.