AVR linker error, "relocation truncated to fit"

Explanation:

As the error message suggests, the issue has to do with the relocation (of code) which causes some truncation to occur. The message comes from the linker which is trying to map pieces of code to appropriate locations in the program memory.

When code is placed or moved to some location ("relocation") and this code is referred to from another piece of code, via JMP or CALL (i.e. a function call), the relocated address has to be added to the JMP or CALL instruction referring to it.

The AVR devices support two kinds of jump/call instructions: JMP vs. RJMP, and CALL vs. RCALL. The R variants make calls relative to the current location and are more efficient both in usage of program memory and execution time. This comes at a cost though: RJMP and RCALL can only be used for addresses in the range of +/-4kb from their location in program memory. This is never a problem on devices with no more than 8kb of program memory because the whole 8kb range can be addressed from any location via RCALL or RJMP.

On devices with more than 8kb of program memory, however, this is not true for all possible locations. Therefore, if the linker decides it can put the code to be called within the +/-4kb range from the RJMP/RCALL there will be no problem, but if the linker fails to (re-)locate the code to be within that range, RJMP/RCALL cannot be used to reach the code's new address, the address is thus truncated (just like when doing uint16_t value = 12345; uint8_t truncatedValue = value; in C) and the generated code breaks.

Note that this may or may not happen for any given project exceeding 4kb of program memory (on devices with >8kb of program memory) at some point, because it depends on the relocation of code needed, which may basically change with every new line of C code added or removed, with every library added to be linked in, or even the order in which the libraries or other pieces of code are linked in (e.g. calling from "A" to "B" may work when the linker locates the code like "A B C" but fail when the linker decides to relocate like "A C B").

Solution:

You have to let the compiler know that it needs to generate JMP/CALL instructions instead of the (more efficient) RJMP/RCALL instructions. In AVR Studio/Atmel Studio this can be done in the project's properties, toolchain, AVR/GNU C compiler, optimization. The relevant option is "Use rjmp/rcall (limited range) on >8k devices (-mshort-calls)" which needs to be unchecked to prevent the named error.
As the label indicates, the relevant command line option is -mshort-calls which needs to be removed from the gcc command line parameter list to achieve the same when invoking gcc from outside of the IDE.

Update:

To avoid the unnecessary confusion this error may cause -mshort-calls was deprecated in avr-gcc 4.7 and will be removed from 4.8. Source: GCC 4.8 Changes.

Users should now use -mrelax instead to generate binaries that have the call optimizations where possible but will never produce the error.


I've solved the problem, i've restructured code (I've deleted almost all globals variables) and i've added '-lc -lm -lc' flags to makefile. I suppose the problem was the code structure, too many global variables due to bad adaptation from an arduino code style (All source files are pasted into in the same file) I put the makefile here, I hope it is useful to someone:

TARGET = IMU
PORT = /dev/ttyUSB0
BAUD_P = 57600
BAUD_T = 9600
PROGRAMMER = arduino
MCU = atmega328p
F_CPU = 8000000L

CXX_SRCS = ADXL345.cpp \
       ApplicationRoutines.cpp \
       DCM.cpp \
       HMC5883L.cpp \
       ITG3200.cpp \
       output.cpp \
       timing.cpp \
       vector.cpp

CXX_OBJ = $(CXX_SRCS:.cpp=.o)

CXX_HDRS = ADXL345.h \
       ApplicationRoutines.h \
       DCM.h \
       declarations.h \
       HMC5883L.h \
       ITG3200.h \
       output.h \
       timing.h \
       vector.h

CORE_DIR = libarduinocore

CORE_CXX_SRCS = $(CORE_DIR)/HardwareSerial.cpp \
        $(CORE_DIR)/Print.cpp \
        $(CORE_DIR)/Tone.cpp \
        $(CORE_DIR)/WMath.cpp \
        $(CORE_DIR)/WString.cpp

CORE_CXX_OBJ = $(CORE_CXX_SRCS:.cpp=.o)

CORE_CC_SRCS = $(CORE_DIR)/pins_arduino.c \
           $(CORE_DIR)/WInterrupts.c \
           $(CORE_DIR)/wiring_analog.c \
           $(CORE_DIR)/wiring.c \
           $(CORE_DIR)/wiring_digital.c \
           $(CORE_DIR)/wiring_pulse.c \
           $(CORE_DIR)/wiring_shift.c

CORE_CC_OBJ = $(CORE_CC_SRCS:.c=.o)

CORE_HDRS = $(CORE_DIR)/binary.h \
        $(CORE_DIR)/HardwareSerial.h \
        $(CORE_DIR)/pins_arduino.h \
        $(CORE_DIR)/Print.h \
        $(CORE_DIR)/Stream.h \
        $(CORE_DIR)/WCharacter.h \
        $(CORE_DIR)/WConstants.h \
        $(CORE_DIR)/wiring.h \
        $(CORE_DIR)/wiring_private.h \
        $(CORE_DIR)/WProgram.h \
        $(CORE_DIR)/WString.h

ARD_LIB_DIR = libraries

ARD_LIB_CXX_SRCS = $(ARD_LIB_DIR)/EEPROM/EEPROM.cpp \
           $(ARD_LIB_DIR)/Wire/Wire.cpp \
           $(ARD_LIB_DIR)/HMC58X3/HMC58X3.cpp
ARD_LIB_CC_SRCS = $(ARD_LIB_DIR)/Wire/utility/twi.c

ARD_LIB_CXX_OBJ = $(ARD_LIB_CXX_SRCS:.cpp=.o)
ARD_LIB_CC_OBJ = $(ARD_LIB_CC_SRCS:.c=.o)

CC = avr-gcc
CXX = avr-g++
OBJCOPY = avr-objcopy
OBJDUMP = avr-objdump
AR  = avr-ar
SIZE = avr-size
NM = avr-nm
AVRDUDE = avrdude

ARD_LIB_INC = -I$(ARD_LIB_DIR) -I$(ARD_LIB_DIR)/EEPROM -I$(ARD_LIB_DIR)/Wire -I$(ARD_LIB_DIR)/HMC58X3 -I$(ARD_LIB_DIR)/Wire/utility

#FLAGS_WARN = -Wall -Wstrict-prototypes
#FLAGS_TUNING = -ffunction-sections -fdata-sections -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums
FLAGS_OPT = -Os

ALL_INC = -I. $(ARD_LIB_INC) -I$(CORE_DIR)
OBJS = $(CXX_OBJ) $(CORE_CXX_OBJ) $(CORE_CC_OBJ) $(ARD_LIB_CC_OBJ) $(ARD_LIB_CXX_OBJ)
ALL_OBJS := $(addprefix build/, $(notdir $(OBJS)))
ALL_CFLAGS = -mmcu=$(MCU) -DF_CPU=$(F_CPU) $(ALL_INC) $(FLAGS_WARN) $(FLAGS_TUNNIG) $(FLAGS_OPT)
ALL_CXXFLAGS = -mmcu=$(MCU) -DF_CPU=$(F_CPU) $(ALL_INC) $(FLAGS_TUNNIG) $(FLAGS_OPT) #-Wall
#ALL_ASFLAGS = -mmcu=$(MCU) -I. -x assembler-with-cpp $(ASFLAGS)
END_FLAGS = -lc -lm -lc

all : $(TARGET).hex

$(TARGET).hex : $(TARGET).out
    avr-objcopy -O ihex -R .eeprom $(TARGET).out $(TARGET).hex

$(TARGET).out : $(OBJS)
    $(CXX) $(ALL_CXXFLAGS) main.cpp $(ALL_OBJS) -o $(TARGET).out $(END_FLAGS)

upload : $(TARGET).hex
    avrdude -c$(PROGRAMMER) -p$(MCU) -b$(BAUD_P) -P$(PORT) -U flash:w:$(TARGET).hex

serialmon :
    picocom -b$(BAUD_T) $(PORT)

.SUFFIXES: .elf .hex .eep .lss .sym .cpp .o .c .s .S

# Define all listing files.
#LST = $(ASRC:.S=.lst) $(CXXSRC:.cpp=.lst) $(SRC:.c=.lst)

# Compile: create object files from C++ source files.
.cpp.o:
    $(CXX) -c $(ALL_CXXFLAGS) $< -o $(addprefix build/, $(notdir $@)) $(END_FLAGS)

# Compile: create object files from C source files.
.c.o:
    $(CC) -c $(ALL_CFLAGS) $< -o $(addprefix build/, $(notdir $@)) $(END_FLAGS)


# Compile: create assembler files from C source files.
#.c.s:
#   $(CC) -S $(ALL_CFLAGS) $< -o build/$@ -lm


# Assemble: create object files from assembler source files.
#.S.o:
#   $(CC) -c $(ALL_ASFLAGS) $< -o build/$@

I've been working on this problem for the past few hours, and finally solved it. For me it had to do with the fact that the avr libm.a must be included in the linker command, and I was using the Math.h library, which is separate from the libc.a library, and wasn't being linked correctly.

Try modifying the linker command to look like this by adding -lc -lm at the beginning of the command and -lc at the end:

${CMD}  -lc -lm ${FLAGS} ${OUTPUT_FLAG}${OUTPUT_PREFIX}${OUTPUT}  ${INPUTS}  -lc

My reference: http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1290294587