I have been trying to create a neatly-organized makefile project template utilizing the ARM mbed library. I have already solved a few of the problems (see this post) related to header file paths. However, now I am having problems with the linker. My goal is to have sources and headers in src, object files in obj, and the final binaries in either debug or release.
Here is the error I am getting...
make
arm-none-eabi-g++ -DTARGET_M4 -DMBED_BUILD_TIMESTAMP=1453683815.81 -DTOOLCHAIN_GCC_ARM -DTOOLCHAIN_GCC -DTARGET_RTOS_M4_M7 -DTARGET_FF_MORPHO -DTARGET_CORTEX_M -D__FPU_PRESENT=1 -DTARGET_FF_ARDUINO -DTARGET_STM32F446RE -DTARGET_NUCLEO_F446RE -D__MBED__=1 -DTARGET_STM -DTARGET_STM32F4 -D__CORTEX_M4 -DARM_MATH_CM4 -std=c++98 -fno-rtti -I lib/ -I lib/mbed/ -I lib/mbed/TARGET_NUCLEO_F446RE/ -I lib/mbed/TARGET_NUCLEO_F446RE/TARGET_STM/ -I lib/mbed/TARGET_NUCLEO_F446RE/TARGET_STM/TARGET_STM32F4/ -I lib/mbed/TARGET_NUCLEO_F446RE/TOOLCHAIN_GCC_ARM/ -I lib/mbed/TARGET_NUCLEO_F446RE/TARGET_STM/TARGET_STM32F4/TARGET_NUCLEO_F446RE/ -o obj/main.o src/main.cc
/usr/lib/gcc/arm-none-eabi/6.1.1/../../../../arm-none-eabi/lib/libc.a(lib_a-exit.o): In function `exit':
exit.c:(.text.exit+0x2c): undefined reference to `_exit'
collect2: error: ld returned 1 exit status
make: *** [makefile:54: obj/main.o] Error 1
This is my makefile. I have denoted where the problem(s) might be, but I am not sure. #Project parameters PROJECT = Nucleo_blink OBJECTS = obj/main.o DEST = debug VPATH = src lib $DEST TARGET = NUCLEO_F446RE
#Compilation options
DEBUG = 1
#Tools
AS = $(GCC_BIN)arm-none-eabi-as
CC = $(GCC_BIN)arm-none-eabi-gcc
CXX = $(GCC_BIN)arm-none-eabi-g++
LD = $(GCC_BIN)arm-none-eabi-gcc
OBJCOPY = $(GCC_BIN)arm-none-eabi-objcopy
OBJDUMP = $(GCC_BIN)arm-none-eabi-objdump
SIZE = $(GCC_BIN)arm-none-eabi-size
include $(TARGET).mk
CFLAGS = $(INCLUDE_PATHS) $(CC_SYMBOLS) $(CPU) -c -g -fno-common -fmessage-length=0 -Wall -Wextra -fno-exceptions -ffunction-sections -fdata-sections -fomit-frame-pointer -MMD -MP
ifeq ($(HARDFP),1)
FLOAT_ABI = hard
else
FLOAT_ABI = softfp
endif
ifeq ($(DEBUG), 1)
CFLAGS += -DDEBUG -O0
else
CFLAGS += -DNDEBUG -Os
endif
#MY PROBLEM MAY BE HERE
LD_FLAGS = $(CPU) -Wl,--gc-sections --specs=nano.specs -Wl,--wrap,main -Wl,-Map=$(PROJECT).map,--cref
#`-u _printf_float -u _scanf_float` after -specs for floating point I/O
LD_SYS_LIBS = -lstdc++ -lsupc++ -lm -lc -lgcc -lnosys
LIBRARIES = -lmbed
.PHONY: all clean lst size
all: $(PROJECT).bin $(PROJECT).hex
clean:
rm -f debug/* obj/* asm/* $(DEPS)
obj/%.o: %.c
$(CC) $(CC_FLAGS) $(CC_SYMBOLS) -std=c99 $(INCLUDE_PATHS) -o $@ $<
obj/%.o: %.cc
$(CXX) $(CC_FLAGS) $(CC_SYMBOLS) -std=c++98 -fno-rtti $(INCLUDE_PATHS) -o $@ $<
obj/%.o: %.cpp
$(CXX) $(CC_FLAGS) $(CC_SYMBOLS) -std=c++98 -fno-rtti $(INCLUDE_PATHS) -o $@ $<
obj/%.o: %.asm
$(CC) $(CPU) -c -x assembler-with-cpp -o asm/$@ $<
#OR HERE
$(PROJECT).elf: $(OBJECTS) $(SYS_OBJECTS)
$(LD) $(LD_FLAGS) -T$(LINKER_SCRIPT) $(LIBRARY_PATHS) -o $(DEST)/$@ $^ $(LIBRARIES) $(LD_SYS_LIBS) $(LIBRARIES) $(LD_SYS_LIBS)
$(PROJECT).bin: $(PROJECT).elf
$(OBJCOPY) -O binary $< $@
$(PROJECT).hex: $(PROJECT).elf
@$(OBJCOPY) -O ihex $< $@
$(PROJECT).lst: $(PROJECT).elf
@$(OBJDUMP) -Sdh $< > $@
lst: $(PROJECT).lst
size: $(PROJECT).elf
$(SIZE) $(PROJECT).elf
DEPS = $(OBJECTS:.o=.d) $(SYS_OBJECTS:.o=.d)
-include $(DEPS)
Before you ask, I have already tried changing --specs=nano.specs
to --specs=nosys.specs
. It does nothing. The strange part is that the linker settings above work fine for the automatically generated mbed makefile.
Here is the working makefile. It compiles without errors...
# This file was automagically generated by mbed.org. For more information,
# see http://mbed.org/handbook/Exporting-to-GCC-ARM-Embedded
GCC_BIN =
PROJECT = Nucleo_blink
OBJECTS = ./source/main.o
SYS_OBJECTS = #Long list of object files
INCLUDE_PATHS = -I. -I./source -I./mbed -I./mbed/TARGET_NUCLEO_F446RE -I./mbed/TARGET_NUCLEO_F446RE/TARGET_STM -I./mbed/TARGET_NUCLEO_F446RE/TARGET_STM/TARGET_STM32F4 -I./mbed/TARGET_NUCLEO_F446RE/TARGET_STM/TARGET_STM32F4/TARGET_NUCLEO_F446RE -I./mbed/TARGET_NUCLEO_F446RE/TOOLCHAIN_GCC_ARM
LIBRARY_PATHS = -L./mbed/TARGET_NUCLEO_F446RE/TOOLCHAIN_GCC_ARM
LIBRARIES = -lmbed
LINKER_SCRIPT = ./mbed/TARGET_NUCLEO_F446RE/TOOLCHAIN_GCC_ARM/STM32F446XE.ld
###############################################################################
AS = $(GCC_BIN)arm-none-eabi-as
CC = $(GCC_BIN)arm-none-eabi-gcc
CPP = $(GCC_BIN)arm-none-eabi-g++
LD = $(GCC_BIN)arm-none-eabi-gcc
OBJCOPY = $(GCC_BIN)arm-none-eabi-objcopy
OBJDUMP = $(GCC_BIN)arm-none-eabi-objdump
SIZE = $(GCC_BIN)arm-none-eabi-size
ifeq ($(HARDFP),1)
FLOAT_ABI = hard
else
FLOAT_ABI = softfp
endif
CPU = -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=$(FLOAT_ABI)
CC_FLAGS = $(CPU) -c -g -fno-common -fmessage-length=0 -Wall -Wextra -fno-exceptions -ffunction-sections -fdata-sections -fomit-frame-pointer -MMD -MP
CC_SYMBOLS = -DTARGET_M4 -DTARGET_FF_ARDUINO -DTOOLCHAIN_GCC_ARM -DTOOLCHAIN_GCC -DTARGET_RTOS_M4_M7 -DTARGET_FF_MORPHO -DTARGET_LIKE_MBED -DTARGET_CORTEX_M -D__FPU_PRESENT=1 -DTARGET_LIKE_CORTEX_M4 -DTARGET_NUCLEO_F446RE -D__MBED__=1 -DTARGET_STM -DMBED_BUILD_TIMESTAMP=1468213384.59 -DTARGET_STM32F446RE -DTARGET_STM32F4 -D__CORTEX_M4 -DARM_MATH_CM4
#My makefile above copies these two lines
LD_FLAGS = $(CPU) -Wl,--gc-sections --specs=nano.specs -u _printf_float -u _scanf_float -Wl,--wrap,main -Wl,-Map=$(PROJECT).map,--cref
LD_SYS_LIBS = -lstdc++ -lsupc++ -lm -lc -lgcc -lnosys
ifeq ($(DEBUG), 1)
CC_FLAGS += -DDEBUG -O0
else
CC_FLAGS += -DNDEBUG -Os
endif
.PHONY: all clean lst size
all: $(PROJECT).bin $(PROJECT).hex size
clean:
rm -f $(PROJECT).bin $(PROJECT).elf $(PROJECT).hex $(PROJECT).map $(PROJECT).lst $(OBJECTS) $(DEPS)
.asm.o:
$(CC) $(CPU) -c -x assembler-with-cpp -o $@ $<
.s.o:
$(CC) $(CPU) -c -x assembler-with-cpp -o $@ $<
.S.o:
$(CC) $(CPU) -c -x assembler-with-cpp -o $@ $<
.c.o:
$(CC) $(CC_FLAGS) $(CC_SYMBOLS) -std=gnu99 $(INCLUDE_PATHS) -o $@ $<
.cpp.o:
$(CPP) $(CC_FLAGS) $(CC_SYMBOLS) -std=gnu++98 -fno-rtti $(INCLUDE_PATHS) -o $@ $<
$(PROJECT).elf: $(OBJECTS) $(SYS_OBJECTS)
$(LD) $(LD_FLAGS) -T$(LINKER_SCRIPT) $(LIBRARY_PATHS) -o $@ $^ -Wl,--start-group $(LIBRARIES) $(LD_SYS_LIBS) -Wl,--end-group
$(PROJECT).bin: $(PROJECT).elf
$(OBJCOPY) -O binary $< $@
$(PROJECT).hex: $(PROJECT).elf
@$(OBJCOPY) -O ihex $< $@
$(PROJECT).lst: $(PROJECT).elf
@$(OBJDUMP) -Sdh $< > $@
lst: $(PROJECT).lst
size: $(PROJECT).elf
$(SIZE) $(PROJECT).elf
DEPS = $(OBJECTS:.o=.d) $(SYS_OBJECTS:.o=.d)
-include $(DEPS)
I think my problem is some sort of path error...
- The _exit
symbol may be defined, but inaccessible by main.o
- There may be some major error in the makefile I'm missing
- Something totally different?
Feel free to comment any suggested changes to this question.
EDIT: All I had to do to fix the error was change CFLAGS
to CCFLAGS
. The answer I marked as the solution explained what was happening, and a potential way to fix it. Although I didn't need to use the suggested solution, the explanation of why it wasn't working is useful, and the information provided by both answers is useful.
Adding --specs=nosys.specs
to your linker options may also solve this issue.
You can see exactly what this does by looking at the nosys.specs file which will be somewhere in your compiler directory.
See also: exit.c:(.text+0x18): undefined reference to `_exit' when using arm-none-eabi-gcc
_exit
is a system call, as well as some other functions you probably will need later. When you compile a binary for (for example) Linux, these calls are serviced by the operating system. In bare-metal embedded project you need to define these functions by yourself. The common way is to create a file called syscalls.c
or something like that and put all needed system calls there. Take a look at example of such file, rapidly found by google: https://github.com/bjornfor/stm32-test/blob/master/STM32L1xx_StdPeriph_Lib_V1.1.1/syscalls.c
As a bonus, if you properly implement _read
and _write
to work with UART, you will get a serial console capable to do formatted IO via printf
and scanf
.
The _exit symbol may be defined, but inaccessible by main.o - There may be some major error in the makefile I'm missing - Something totally different?
This happens when a call to a standard library method indirectly has a dependency on exit()
. Often from automatically generated exception unwind code that's supposed to abort your process on exit, which obviously has different semantics in a bare metal application. The division-by-zero floating point exception is one such example. -fno-exceptions
does not help with this.
You can of course supply a do-nothing exit()
handler but your code would be smaller if the exception unwind code didn't get inserted in the first place.
I discovered by examining the generated assembler that you can replace the exception handlers with do-nothing versions like this in a compilation unit:
extern "C" void __wrap___aeabi_unwind_cpp_pr0() {}
extern "C" void __wrap___aeabi_unwind_cpp_pr1() {}
extern "C" void __wrap___aeabi_unwind_cpp_pr2() {}
and then link those in as replacements at link time with these additional linker arguments.
-Wl,-wrap,__aeabi_unwind_cpp_pr0,
-Wl,-wrap,__aeabi_unwind_cpp_pr1
-Wl,-wrap,__aeabi_unwind_cpp_pr2
Another benefit is that your code should shrink as a result. You may even want to insert your own implementations that reset the MCU.
For me it took both -fno-exceptions
and --specs=nosys.specs
in compiler flags.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With