{"id":29826805,"url":"https://github.com/choaib-elmadi/bare-metal-programming","last_synced_at":"2025-07-29T05:32:58.012Z","repository":{"id":291185416,"uuid":"972844626","full_name":"Choaib-ELMADI/bare-metal-programming","owner":"Choaib-ELMADI","description":"A hands-on exploration of bare metal programming with the STM32 Nucleo-F446RE. Learn how embedded systems work from the ground up using C, GCC toolchains, Makefiles, and more.","archived":false,"fork":false,"pushed_at":"2025-05-03T17:29:34.000Z","size":9450,"stargazers_count":19,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-05-13T14:23:31.182Z","etag":null,"topics":["arm","arm-cortex-m4","assembly","bare-metal","bare-metal-programming","build-tool","cprogramming","cprograms","embedded-systems","gcc","gcc-complier","low-level","low-level-programming","makefile","microcontroller","nucleo-board","stm32","stm32f4"],"latest_commit_sha":null,"homepage":"","language":"Assembly","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Choaib-ELMADI.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-04-25T19:00:46.000Z","updated_at":"2025-05-09T11:28:37.000Z","dependencies_parsed_at":"2025-05-03T00:28:44.843Z","dependency_job_id":null,"html_url":"https://github.com/Choaib-ELMADI/bare-metal-programming","commit_stats":null,"previous_names":["choaib-elmadi/bare-metal-programming"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/Choaib-ELMADI/bare-metal-programming","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Choaib-ELMADI%2Fbare-metal-programming","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Choaib-ELMADI%2Fbare-metal-programming/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Choaib-ELMADI%2Fbare-metal-programming/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Choaib-ELMADI%2Fbare-metal-programming/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Choaib-ELMADI","download_url":"https://codeload.github.com/Choaib-ELMADI/bare-metal-programming/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Choaib-ELMADI%2Fbare-metal-programming/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":267633670,"owners_count":24118777,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-07-29T02:00:12.549Z","response_time":2574,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["arm","arm-cortex-m4","assembly","bare-metal","bare-metal-programming","build-tool","cprogramming","cprograms","embedded-systems","gcc","gcc-complier","low-level","low-level-programming","makefile","microcontroller","nucleo-board","stm32","stm32f4"],"created_at":"2025-07-29T05:32:56.886Z","updated_at":"2025-07-29T05:32:58.002Z","avatar_url":"https://github.com/Choaib-ELMADI.png","language":"Assembly","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Choaib ELMADI - Bare Metal Programming](https://img.shields.io/badge/Choaib_ELMADI-Bare_Metal_Programming-8800dd)](https://elmadichoaib.vercel.app) ![Status - Learning](https://img.shields.io/badge/Status-Learning-2bd729) ![Platform - STM32](https://img.shields.io/badge/Platform-STM32-f7d620) [![Credit - Fastbit Embedded Brain Academy](https://img.shields.io/badge/Credit-Fastbit_Embedded_Brain_Academy-3b8af2)](https://www.youtube.com/@FastbitEmbeddedBrainAcademy)\n\n# Bare Metal Programming\n\n`Bare metal programming` refers to writing software that runs directly on the hardware of a microcontroller without the support of an operating system or IDE. It involves manually managing hardware resources, such as memory, peripherals, and interrupts. This approach allows for more control over the hardware and is often used in embedded systems where performance and efficiency are critical.\n\nIn this project, I am using the **STM32 Nucleo-F446RE** development board. Throughout the examples and experiments, I will work with this board to write, build, and flash programs directly into its microcontroller.\n\nThe repository is structured with two main folders:\n\n- `Src/`: Contains small learning examples and experiments created while exploring bare metal concepts step by step.\n- `Main/`: Contains the actual project files.\n\nThe main project goal is to **blink the onboard LED LD2** on the STM32 Nucleo board using bare metal programming, without relying on any high-level libraries or HAL code. This involves directly controlling the GPIO registers of the STM32F446RE microcontroller.\n\n## C Programs Compilation Process\n\nWhen we write a C program for a microcontroller, it goes through several steps before it can actually run on the hardware. This process includes compiling the code, assembling it, linking it into an executable, and finally flashing it onto the microcontroller.\n\n\u003cdiv align=\"center\"\u003e\n\n```mermaid\ngraph LR\n    subgraph Coding...[💻 Coding...]\n        User_Application[\"User Application\u003cbr\u003e(.c, .h files)\"]\n    end\n\n    subgraph Building...[⚒️ Building...]\n        Compiler --\u003e Assembler[\"Assembler\u003cbr\u003e(.o files)\"]\n        Assembler --\u003e Linker[\"Linker\u003cbr\u003e(.bin, .exe, .elf files)\"]\n    end\n\n    subgraph Flashing...[📂 Flashing...]\n        Programmer_and_Debugger[Programmer and\u003cbr\u003eDebugger]\n        Linker --\u003e Programmer_and_Debugger\n    end\n\n    subgraph Running...[🏃 Running...]\n        Programmer_and_Debugger --\u003e Flash[uC Flash\u003cbr\u003eMemory]\n    end\n\n    User_Application --\u003e Compiler[\"Compiler\u003cbr\u003e(.s, .asm files)\"]\n\n    class Coding... coding\n    class User_Application user_app;\n    class Building... building;\n    class Compiler compiler;\n    class Assembler assembler;\n    class Linker linker;\n    class Flashing... flashing;\n    class Programmer_and_Debugger p_and_d;\n    class Running... running;\n    class Flash flash;\n\n    classDef coding fill:#3D90D710,stroke:#3D90D7,stroke-width:2px,color:#3D90D7;\n    classDef user_app fill:#FE774310,stroke:#FE7743,stroke-width:2px,color:#FE7743;\n    classDef building fill:#3A59D110,stroke:#3A59D1,stroke-width:2px,color:#3A59D1;\n    classDef compiler fill:#BE598510,stroke:#BE5985,stroke-width:2px,color:#BE5985;\n    classDef assembler fill:#5F8B4C10,stroke:#5F8B4C,stroke-width:2px,color:#5F8B4C;\n    classDef linker fill:#A89C2910,stroke:#A89C29,stroke-width:2px,color:#A89C29;\n    classDef flashing fill:#81E7AF10,stroke:#81E7AF,stroke-width:2px,color:#81E7AF;\n    classDef p_and_d fill:#FE774910,stroke:#FE7749,stroke-width:2px,color:#FE7749;\n    classDef running fill:#F5C45E10,stroke:#F5C45E,stroke-width:2px,color:#F5C45E;\n    classDef flash fill:#E5202010,stroke:#E52020,stroke-width:2px,color:#E52020;\n\n    linkStyle 4 stroke:#FE7743,stroke-width:2px;\n    linkStyle 0 stroke:#BE5985,stroke-width:2px;\n    linkStyle 1 stroke:#5F8B4C,stroke-width:2px;\n    linkStyle 2 stroke:#A89C29,stroke-width:2px;\n    linkStyle 3 stroke:#FE7749,stroke-width:2px;\n```\n\n\u003c/div\u003e\n\nHere's a quick overview of the main stages in the build process and what each one does.\n\n\u003cdiv align=\"center\"\u003e\n\n| Preprocessor                           | Compiler                 | Assembler                           | Linker                     |\n| -------------------------------------- | ------------------------ | ----------------------------------- | -------------------------- |\n| - Remove comments                      | - Generate assembly code | - Generate relocatable object files | - Combine object files     |\n| - Replace macros                       |                          |                                     | - Link libraries           |\n| - Include header files                 |                          |                                     | - Produce final executable |\n| - Produce translation unit (`.i file`) |                          |                                     |                            |\n\n\u003c/div\u003e\n\n## Cross Compilation and Toolchains\n\n**Cross-compilation** is a process in which the cross-toolchain runs on the host machine (your PC) and creates executables that run on different machine (ARM).\n\n**Cross-toolchain** is a collection of binaries which allows you to compile, assemble and link your applications. It contains binaries to debug the application on the target and analyze executables:\n\n- Disassemble executables\n- Dissect different sections of an executable\n- Extract symbol and size information\n- Convert executables to other formats (bin, ihex, ...)\n- Provide C standard libraries\n\nThe toolchain we will use is GCC (**GNU Compiler Collection**), a free and open-source tool for ARM embedded processors.\n\n### Download the GCC Toolchain\n\nIf you have installed STM32CubeIDE, the GCC toolchain is already installed with it.\n\nIf not, you can download it manually from this link: [ARM GNU Toolchain Downloads](https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads).\n\nI am using a Windows machine, so I installed the `arm-none-eabi` version.\n\n\u003cdiv align=\"center\"\u003e\n\n![ARM GCC and GDB Versions](./Images/arm-none-eabi-gcc-gdb-versions.png)\n\n\u003c/div\u003e\n\n### Important Cross Toolchain Binaries\n\n- Compiler, Assembler, Linker `⟶` arm-none-eabi-gcc\n- Assembler \u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp; `⟶` arm-none-eabi-as\n- Linker \u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp; `⟶` arm-none-eabi-ld\n- ELF File Analyzers \u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp; `⟶` arm-none-eabi-objdump, arm-none-eabi-readelf, arm-none-eabi-nm\n- Format Converter \u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp; `⟶` arm-none-eabi-objcopy\n\n### Basic Compilation Commands\n\nThe following command compiles and assembles the `main.c` file without linking, and outputs a relocatable object file `main.o`:\n\n```bash\narm-none-eabi-gcc -c main.c -o main.o\n```\n\nThis next command does the same thing but specifies the target ARM processor (**cortex-m4**) and tells the compiler to generate Thumb instruction set code:\n\n```bash\narm-none-eabi-gcc -c -mcpu=cortex-m4 -mthumb main.c -o main.o\n```\n\nThis command generates only the assembly code from `main.c`, without assembling it into an object file:\n\n```bash\narm-none-eabi-gcc -S -mcpu=cortex-m4 -mthumb main.c -o main.s\n```\n\n### Automating Compilation with a Makefile\n\nInstead of typing long commands every time, you can automate the process using a simple `Makefile`:\n\n```Makefile\nCC=arm-none-eabi-gcc\nMACH=cortex-m4\nCFLAGS= -c -mcpu=$(MACH) -mthumb -std=gnu11 -O0\n\n# TARGET: DEPENDENCY\nmain.o: main.c\n\t$(CC) $(CFLAGS) $^ -o $@\n\n# $^ --\u003e dependencies (main.c)\n# $@ --\u003e target (main.o)\n```\n\nNow, just typing:\n\n```bash\nmake\n```\n\nWill automatically compile `main.c` into `main.o` based on the rules you defined!\n\n\u003e Note: This is just the initial Makefile with one basic rule. More useful commands (called targets) will be added as we progress through the project. [Check Final Makefile Here!](#final-makefile)\n\nIf you want to dive deeper into build systems like Makefiles, CMake, and how projects are organized and automated, check out this repository: [Getting Started with CMake](https://github.com/Choaib-ELMADI/getting-started-with-cmake)\n\n## Analyzing Build Output Files\n\nWhen we compile our C programs, different types of files are generated during the build process. These files serve different purposes and understanding them is very important in bare metal programming.\n\n### Relocatable Object Files\n\nAfter compiling a C file (without linking), the compiler generates a `.o` file. This is a relocatable object file in the ELF format (**Executable and Linkable Format**) that contains different sections:\n\n- `.text`: Contains the actual program instructions.\n- `.data`: Contains initialized data.\n- `.bss`: Block Starting Symbol, contains uninitialized data.\n- `.rodata`: Contains read-only data.\n- `.comment`: Metadata added by the compiler.\n- `.ARM.attributes`: Metadata added by the compiler.\n\nThey are called **relocatable** because all sections inside the file are assigned the same starting address (usually 0x0). Similarly, the same sections across multiple object files also share the same base address. During linking, these addresses need to be relocated based on the target microcontroller (or memory map) to avoid address conflicts and data corruption.\n\nTo view the sections in the `main.o` file, run the following command:\n\n```bash\narm-none-eabi-objdump -h main.o\n```\n\nThe output of this command looks like this:\n\n```bash\nmain.o:     file format elf32-littlearm\n\nSections:\nIdx Name            Size      VMA       LMA       File off  Algn\n  0 .text           00000520  00000000  00000000  00000034  2**2\n                    CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE\n  1 .data           00000001  00000000  00000000  00000554  2**0\n                    CONTENTS, ALLOC, LOAD, DATA\n  2 .bss            00000054  00000000  00000000  00000558  2**2\n                    ALLOC\n  3 .rodata         000000c9  00000000  00000000  00000558  2**2\n                    CONTENTS, ALLOC, LOAD, READONLY, DATA\n  4 .comment        00000046  00000000  00000000  00000621  2**0\n                    CONTENTS, READONLY\n  5 .ARM.attributes 0000002e  00000000  00000000  00000667  2**0\n                    CONTENTS, READONLY\n```\n\nTo disassemble the `.text` section and write it to a file, run:\n\n```bash\narm-none-eabi-objdump -d main.o \u003e main_log.txt\n```\n\nFor more commands and options, check the documentation here: [View More Commands](https://gcc.gnu.org/onlinedocs/gcc-13.3.0/gcc/#toc-GCC-Command-Options)\n\n### MCU Startup File\n\nThe **startup file** is an assembly or C file that prepares the microcontroller to run a C program. It mainly does the following tasks:\n\n- Set up the initial stack pointer.\n- Define the interrupt vector table.\n- Provide default handlers for interrupts and exceptions.\n- Call the `Reset_Handler`, which initializes the main memory with data in `.data` and `.bss` sections, and then calls the `main` function.\n\nIn the build output, you will find or create a startup file specific to the microcontroller you are using. For the STM32 Nucleo-F446RE, the startup file is `stm32f446xx_startup.s` or `stm32f446xx_startup.c`.\n\n### Linker Script\n\nA **linker script** is used to define how the program's sections (`.text`, `.data`, and `.bss`) are arranged in memory. Linker scripts are written using the **GNU linker command language** and usually have a `.ld` file extension.\n\nYou must supply the linker script during the linking phase by using the `-T` option with the linker.\n\nLinker scripts use special **symbols**, which are names associated with memory addresses (`_etext`, `_sdata`, `_edata`). These symbols help the linker correctly place and reference data and code. They are part of the **symbol table** created during linking and can also be accessed in your C code using `extern`.\n\nSome important keywords used when writing linker scripts are:\n\n- `ENTRY(symbol)`: Defines the entry point (**Reset_Handler**) of the program.\n- `MEMORY`: Defines the available memory regions (FLASH, RAM, ...) and their sizes.\n- `SECTIONS`: Describes how the program sections should be placed into the memory regions.\n- `\u003e REGION`: Specifies into which memory region a section should be placed.\n- `AT \u003e REGION`: Specifies the load memory address for a section.\n- `KEEP()`: Forces the linker to keep certain sections or symbols.\n- `ALIGN(n)`: Aligns sections or symbols to a specific memory boundary (n bytes).\n- `.`: Refers to the **location counter**, which tracks the current memory address during linking. It is automatically incremented by the size of current section.\n\nTo generate the final executable file, use the following command:\n\n```bash\narm-none-eabi-gcc -nostdlib -T stm32f446xx_ls.ld *.o -o final.elf\n```\n\n### Memory Map File\n\nTo better understand how the linker placed each section and symbol in memory, you can generate a **memory map file** during the linking phase. This file shows details like section sizes, memory addresses, and symbol locations. This is very useful for debugging memory issues or verifying that your linker script works correctly.\n\nTo generate it, use the `-Wl,-Map=final.map` flag with the linker:\n\n```bash\narm-none-eabi-gcc -nostdlib -T stm32f446xx_ls.ld *.o -Wl,-Map=final.map -o final.elf\n```\n\n## Downloading and Debugging Executables\n\nOur goal is to flash the final generated executable into the microcontroller’s internal flash memory and run the program on the development board. We will do this using one method:\n\n- **Using a debug adapter** to connect the target to the host. This is called **in-circuit programming/debugging**.\n\n\u003cdiv align=\"center\"\u003e\n\n```mermaid\ngraph LR\n    subgraph HOST[HOST Machine]\n        OpenOCD[Running\u003cbr\u003eOpenOCD]\n    end\n\n    subgraph DEBUGGER[Debug Adapter]\n        STLINK[Example:\u003cbr\u003eST-LINK/V2]\n    end\n\n    subgraph TARGET\n        FLASH[uC\u003cbr\u003eFLASH]\n    end\n\n    OpenOCD --USB Interface--\u003e STLINK --SWD or JTAG Interface--\u003e FLASH\n\n    class HOST host;\n    class OpenOCD openocd;\n    class DEBUGGER debugger;\n    class STLINK stlink;\n    class TARGET target;\n    class FLASH flash;\n\n    classDef host fill:transparent,stroke:transparent,stroke-width:2px,color:#3D90D7;\n    classDef openocd fill:#FE774310,stroke:#FE7743,stroke-width:2px,color:#FE7743;\n    classDef debugger fill:transparent,stroke:transparent,stroke-width:2px,color:#3D90D7;\n    classDef stlink fill:#BE598510,stroke:#BE5985,stroke-width:2px,color:#BE5985;\n    classDef target fill:transparent,stroke:transparent,stroke-width:2px,color:#3D90D7;\n    classDef flash fill:#81E7AF10,stroke:#81E7AF,stroke-width:2px,color:#81E7AF;\n\n    linkStyle 0 stroke:#FE7743,stroke-width:2px;\n    linkStyle 1 stroke:#BE5985,stroke-width:2px;\n```\n\n\u003c/div\u003e\n\n### OpenOCD (Open On-Chip Debugger)\n\n**OpenOCD** is a free and open-source tool that runs on the host machine. It allows you to flash, debug, and test embedded systems:\n\n- It supports many processors like ARM7, ARM9, Cortex-M, and Intel Quark.\n- It works with different debug adapters such as ST-LINK, J-Link, or others.\n- It uses the **GDB protocol** to allow stepping through code, setting breakpoints, and inspecting variables.\n- It supports **flash programming** for internal and external flash memories on various microcontrollers.\n- It’s widely used with STM32 boards and works well with `arm-none-eabi-gdb`.\n\n### Installing and Running OpenOCD\n\nTo use OpenOCD, you need to install it and make sure it’s available in your system path:\n\n- Download it from the official site: [Download OpenOCD for Windows](https://gnutoolchains.com/arm-eabi/openocd/)\n- After extracting, add the `bin` folder (where `openocd.exe` is located) to your system's **Path** environment variable.\n- Make sure your development board is connected to your PC **before** running the OpenOCD command.\n- To run OpenOCD with your STM32 board, use the following command:\n\n```bash\nopenocd -f board/st_nucleo_f4.cfg\n```\n\n\u003e Note: Make sure to use the correct `.cfg` file for your specific development board.\n\nTo quit OpenOCD, simply press: `Ctrl + C`.\n\n### Flashing and Debugging using GDB\n\nOnce the OpenOCD server is running, follow these steps in a new terminal:\n\n- Start the GDB client:\n\n```bash\narm-none-eabi-gdb\n```\n\n- Connect to the OpenOCD server:\n\n```bash\ntarget remote localhost:3333\n```\n\n- After connecting, always run this to reset and initialize the target:\n\n```bash\nmonitor reset init\n```\n\n- Finally, flash the executable into the microcontroller:\n\n```bash\nmonitor flash write_image erase final.elf\n```\n\nThe program is now flashed to the board. You can start debugging it from GDB.\n\n### Useful GDB Commands\n\nHere are some GDB commands to control and inspect the MCU:\n\n- Reset and halt the MCU immediately:\n\n```bash\nmonitor reset halt\n```\n\n- Resume program execution:\n\n```bash\nmonitor resume\n```\n\n- Reset the MCU without halting:\n\n```bash\nmonitor reset\n```\n\n- Halt program execution:\n\n```bash\nmonitor halt\n```\n\n- Read memory at a specific address:\n\n```bash\nmonitor size address count # size = mdd | mdw | mdh | mdb\n```\n\n- Add a breakpoint at a specific address:\n\n```bash\nmonitor bp address length type # type = hw | sw\n```\n\n- Remove a breakpoint:\n\n```bash\nmonitor rbp address\n```\n\n- Exit GDB:\n\n```bash\nquit # or exit\n```\n\nThese commands allow you to fully control program execution, set breakpoints, read memory, and debug issues. More commands here: [OpenOCD General Commands](https://openocd.org/doc/html/General-Commands.html)\n\n## Integrating C Standard Libraries and Semihosting\n\nTo use C standard library features like `printf`, `malloc`, or `open`, embedded systems need a minimal C library. The GNU ARM toolchain provides two options:\n\n- **newlib**: Full-featured C library implementation.\n- **newlib-nano**: A smaller, lightweight version optimized for embedded systems.\n\nBoth are automatically installed when you install the GNU ARM toolchain.\n\n### System Calls and `syscalls.c`\n\nSince microcontrollers do not have an operating system to provide standard I/O or file management, **newlib** relies on **low-level system call stubs** that you must implement yourself.\n\nThese are typically defined in a file called `syscalls.c`, and include functions like:\n\n- `_write` used for `printf()`\n- `_read` used for `scanf()`\n- `_open`, `_close`, `_lseek`, `_fstat`, `_isatty`, `_sbrk`, ...\n\nBy implementing these functions, you tell newlib how to handle I/O operations (like redirecting output to a UART or debugger console) and memory allocation.\n\n### Enabling Semihosting\n\n**Semihosting** is a technique that allows your embedded program to communicate with your host machine through the debug interface. It enables functions like `printf` to send output directly to your PC (inside the OpenOCD terminal), even if the microcontroller doesn’t have a display. To use semihosting:\n\n- Compile your project with this flag:\n\n```bash\n--specs=rdimon.specs\n```\n\n- After starting the GDB client, enable semihosting with:\n\n```bash\nmonitor arm semihosting enable\n```\n\n- Then reset the MCU to start the program:\n\n```bash\nmonitor reset\n```\n\nNow, any output from functions like `printf()` will appear directly in the OpenOCD terminal.\n\n- To safely disconnect OpenOCD from the target when you're done, use:\n\n```bash\nmonitor shutdown\n```\n\n## Final Makefile\n\nHere is the final `Makefile` used in this project:\n\n```Makefile\nCC = arm-none-eabi-gcc\nMACH = cortex-m4\nCFLAGS = -c -mcpu=$(MACH) -mthumb -mfloat-abi=soft -std=gnu11 -Wall -O0\nCSFLAGS = -S -mcpu=$(MACH) -mthumb -mfloat-abi=soft -std=gnu11 -Wall -O0\n# LDFLAGS = -nostdlib -T stm32f446xx_ls.ld -Wl,-Map=final.map\nLDFLAGS = -mcpu=$(MACH) -mthumb -mfloat-abi=soft --specs=nano.specs -T stm32f446xx_ls.ld -Wl,-Map=final.map\nLDFLAGS_SH = -mcpu=$(MACH) -mthumb -mfloat-abi=soft --specs=rdimon.specs -T stm32f446xx_ls.ld -Wl,-Map=final.map\n\nall: main.s main.o led.o stm32f446xx_startup.o syscalls.o final.elf\n\n# sh = SEMIHOSTING\nsh: main.s main.o led.o stm32f446xx_startup.o final_sh.elf\n\nmain.s: main.c\n\t$(CC) $(CSFLAGS) $^ -o $@\n\nmain.o: main.c\n\t$(CC) $(CFLAGS) $^ -o $@\n\nled.o: led.c\n\t$(CC) $(CFLAGS) $^ -o $@\n\nstm32f446xx_startup.o: stm32f446xx_startup.c\n\t$(CC) $(CFLAGS) $^ -o $@\n\nsyscalls.o: syscalls.c\n\t$(CC) $(CFLAGS) $^ -o $@\n\nfinal.elf: main.o led.o stm32f446xx_startup.o syscalls.o\n\t$(CC) $(LDFLAGS) $^ -o $@\n\nfinal_sh.elf: main.o led.o stm32f446xx_startup.o\n\t$(CC) $(LDFLAGS_SH) $^ -o $@\n\nclean:\n\tclear; rm -r *.s *.o *.elf *.map\n\nload:\n\topenocd -f board/st_nucleo_f4.cfg\n```\n\nHappy learning and happy building!\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchoaib-elmadi%2Fbare-metal-programming","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fchoaib-elmadi%2Fbare-metal-programming","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchoaib-elmadi%2Fbare-metal-programming/lists"}