I want to develop for AArch32 bare-metal target (arm-none-eabi).
By default each release installs to its own directory instead of upgrading the previous one. This way several releases can coexist and you can select which one you use for a specific project. One downside to this is that the directory and filename convention is heavy. For practical use, you need to configure an IDE or encapsulate those paths and names in Makefile variables.
### Build environment selection
GCCDIR = "D:/Program Files (x86)/GNU Arm Embedded Toolchain/arm-gnu-toolchain-14.2.rel1-mingw-w64-i686-arm-none-eabi"
BINPFX = $(GCCDIR)/bin/arm-none-eabi-
CC = $(BINPFX)gcc
### Build rules
.PHONY: version
version:
$(CC) --version
$ make "D:/Program Files (x86)/GNU Arm Embedded Toolchain/arm-gnu-toolchain-14.2.rel1-m ingw-w64-i686-arm-none-eabi"/bin/arm-none-eabi-gcc --version arm-none-eabi-gcc.exe (Arm GNU Toolchain 14.2.Rel1 (Build arm-14.52)) 14.2.1 202 41119 Copyright (C) 2024 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
GCCDIR = $(HOME)/Packages/arm-gnu-toolchain-14.2.rel1-x86_64-arm-none-eabiBy selecting the path based on the development environment, there is no need to make changes while switching between OS. gmake has the built-in variable MAKE_HOST that can be tested for this.
### Build environment selection
ifeq (linux, $(findstring linux, $(MAKE_HOST)))
GCCDIR = $(HOME)/Packages/arm-gnu-toolchain-14.2.rel1-x86_64-arm-none-eabi
else
GCCDIR = "D:/Program Files (x86)/GNU Arm Embedded Toolchain/arm-gnu-toolchain-14.2.rel1-mingw-w64-i686-arm-none-eabi"
endif
BINPFX = $(GCCDIR)/bin/arm-none-eabi-
CC = $(BINPFX)gcc
### Build rules
.PHONY: version
version:
$(CC) --version
I use the path prefix $(HOME)/Packages instead of ~/Packages when
defining GCCDIR as some sub-processes called by gmake may
have issues with ~ expansion (in this case ld). This way
gmake will handle the expansion before calling the sub-processes.
$ touch empty.c $ make empty.o "D:/Program Files (x86)/GNU Arm Embedded Toolchain/arm-gnu-toolchain-14.2.rel1-m ingw-w64-i686-arm-none-eabi"/bin/arm-none-eabi-gcc -c -o empty.o empty.cCompilation is succesful and empty.o file is generated.
There are sample link scripts that come with the toolchain, they are located in the subfolder share/gcc-arm-none-eabi/samples/ldscripts. For now I can use the simplest script: mem.ld.
### Build environment selection
ifeq (linux, $(findstring linux, $(MAKE_HOST)))
GCCDIR = $(HOME)/Packages/arm-gnu-toolchain-14.2.rel1-x86_64-arm-none-eabi
else
GCCDIR = "D:/Program Files (x86)/GNU Arm Embedded Toolchain/arm-gnu-toolchain-14.2.rel1-mingw-w64-i686-arm-none-eabi"
endif
BINPFX = $(GCCDIR)/bin/arm-none-eabi-
CC = $(BINPFX)gcc
LD = $(BINPFX)ld
LD_SCRIPT = $(GCCDIR)/share/gcc-arm-none-eabi/samples/ldscripts/mem.ld
### Build rules
.PHONY: version
version:
$(CC) --version
%.elf: %.o
$(LD) -T$(LD_SCRIPT) -o $@ $<
$ make empty.elf "D:/Program Files (x86)/GNU Arm Embedded Toolchain/arm-gnu-toolchain-14.2.rel1-m ingw-w64-i686-arm-none-eabi"/bin/arm-none-eabi-ld -T"D:/Program Files (x86)/GNU Arm Embedded Toolchain/arm-gnu-toolchain-14.2.rel1-mingw-w64-i686-arm-none-eabi" /share/gcc-arm-none-eabi/samples/ldscripts/mem.ld -o empty.elf empty.oLink terminates successfully and creates empty.elf.
### Build environment selection
ifeq (linux, $(findstring linux, $(MAKE_HOST)))
GCCDIR = $(HOME)/Packages/arm-gnu-toolchain-14.2.rel1-x86_64-arm-none-eabi
else
GCCDIR = "D:/Program Files (x86)/GNU Arm Embedded Toolchain/arm-gnu-toolchain-14.2.rel1-mingw-w64-i686-arm-none-eabi"
endif
BINPFX = $(GCCDIR)/bin/arm-none-eabi-
CC = $(BINPFX)gcc
LD = $(BINPFX)ld
OBJCOPY = $(BINPFX)objcopy
LD_SCRIPT = $(GCCDIR)/share/gcc-arm-none-eabi/samples/ldscripts/mem.ld
### Build rules
.PHONY: version
version:
$(CC) --version
%.elf: %.o
$(LD) -T$(LD_SCRIPT) -o $@ $<
%.bin: %.elf
$(OBJCOPY) -O binary $< $@
%.hex: %.elf
$(OBJCOPY) -O ihex $< $@
Now, if I start in a directory that contains only this Makefile and an
empty empty.c file, I can successfully build.
$ make empty.bin empty.hex "D:/Program Files (x86)/GNU Arm Embedded Toolchain/arm-gnu-toolchain-14.2.rel1-m ingw-w64-i686-arm-none-eabi"/bin/arm-none-eabi-gcc -c -o empty.o empty.c "D:/Program Files (x86)/GNU Arm Embedded Toolchain/arm-gnu-toolchain-14.2.rel1-m ingw-w64-i686-arm-none-eabi"/bin/arm-none-eabi-ld -T"D:/Program Files (x86)/GNU Arm Embedded Toolchain/arm-gnu-toolchain-14.2.rel1-mingw-w64-i686-arm-none-eabi" /share/gcc-arm-none-eabi/samples/ldscripts/mem.ld -o empty.elf empty.o "D:/Program Files (x86)/GNU Arm Embedded Toolchain/arm-gnu-toolchain-14.2.rel1-m ingw-w64-i686-arm-none-eabi"/bin/arm-none-eabi-objcopy -O binary empty.elf empty .bin "D:/Program Files (x86)/GNU Arm Embedded Toolchain/arm-gnu-toolchain-14.2.rel1-m ingw-w64-i686-arm-none-eabi"/bin/arm-none-eabi-objcopy -O ihex empty.elf empty.h ex rm empty.o empty.elfNotice that gmake automatically removes the intermediary .o and .elf files on completion.
The generated empty.bin is empty.
### Build environment selection
ifeq (linux, $(findstring linux, $(MAKE_HOST)))
GCCDIR = $(HOME)/Packages/arm-gnu-toolchain-14.2.rel1-x86_64-arm-none-eabi
else
GCCDIR = "D:/Program Files (x86)/GNU Arm Embedded Toolchain/arm-gnu-toolchain-14.2.rel1-mingw-w64-i686-arm-none-eabi"
endif
BINPFX = @$(GCCDIR)/bin/arm-none-eabi-
CC = $(BINPFX)gcc
LD = $(BINPFX)ld
OBJCOPY = $(BINPFX)objcopy
LD_SCRIPT = $(GCCDIR)/share/gcc-arm-none-eabi/samples/ldscripts/mem.ld
### Build rules
.PHONY: clean version
version:
$(CC) --version
clean:
@echo CLEAN
@rm -f *.o *.elf *.bin *.hex
%.elf: %.o
@echo $@
$(LD) -T$(LD_SCRIPT) -o $@ $<
%.bin: %.elf
@echo $@
$(OBJCOPY) -O binary $< $@
%.hex: %.elf
@echo $@
$(OBJCOPY) -O ihex $< $@
$ make clean CLEAN $ make empty.bin empty.hex empty.elf empty.bin empty.hex rm empty.o empty.elf
Next, I will select a micro-controller from the STM32 family and generate a binary file that it can execute.