AVR链接错误,“重定位被截断以适应”

8
我正在尝试为ATmega328微控制器编译一些代码,并希望使用Arduino的库和核心。我正在使用CMake。我已经成功编译了核心库、我的代码对象以及Arduino的库。但是在链接时,出现了以下错误:

..."relocation truncated to fit: R_AVR_13_PCREL against symbol"..."avr5/libgcc.a"...

我通过Google发现这是一个常见的错误,但是没有任何解决方案适用于我。唯一无法做到的是在链接器语句的末尾添加“-lm”和“-lc”标志,因为我不知道如何在CMake中实现。

编辑:我也尝试使用Makefile编译,但结果相同,即使在链接语句的末尾加上“-lm”和“-lc”标志。

我把我的Makefile和CMake文件放在这里:


CMakeList.txt 主要的CMake文件

cmake_minimum_required(VERSION 2.6)
Project(IMU)

set(ARDUINO_PROCESSOR atmega328p)
set(ARDUINO_PROCESSOR_FREQ 1600000L)

include(./arduino.cmake)

add_library(ardlib
        libraries/EEPROM/EEPROM.cpp
        libraries/Wire/utility/twi.c
        libraries/Wire/Wire.cpp
        libraries/HMC58X3/HMC58X3
        )

LINK_DIRECTORIES(${IMU_SRC_DIR}/libarduinocore
        ${IMU_SRC_DIR}/libraries/EEPROM
        ${IMU_SRC_DIR}/libraries/Wire
        ${IMU_SRC_DIR}/libraries/HMC58X3
        )

link_libraries(arduinocore ardlib)

include_directories(
        libarduinocore
        libraries/EEPROM
        libraries/Wire
        libraries/Wire/utility
        libraries/HMC58X3
        )

set(C_SRCS
        ADXL345.cpp
        ApplicationRoutines.cpp
        DCM.cpp
        HMC5883L.cpp
        ITG3200.cpp
        matrix.cpp
        output.cpp
        timing.cpp
        vector.cpp
        )

set(C_HDRS
        ADXL345.h
        ApplicationRoutines.h
        DCM.h
        HMC5883L.h
        ITG3200.h
        matrix.h
        output.h
        timing.h
        vector.h
        declarations.h
        )

add_executable(IMU.elf main.cpp ${C_SRCS} ${C_HDRS})

add_subdirectory(libarduinocore)

arduino.cmake。这是由CMakeList.txt导入的:

set(ARDUINO_PROCESSOR atmega328p)
set(ARDUINO_PROCESSOR_FREQ 16000000L)

# This module defines macros intended for use by cross-compiling toolchain files when
# CMake is not able to automatically detect the compiler identification.
include (CMakeForceCompiler)

# Set this for cross compiling.  Otherwise it is set to CMAKE_HOST_SYSTEM_NAME,
# which is the system we are developing on.
set (CMAKE_SYSTEM_NAME Generic)

# It sets CMAKE_<lang>_COMPILER to the given compiler and the cmake internal variable
# CMAKE_<lang>_COMPILER_ID to the given compiler-id. It also bypasses the check for
# working compiler and basic compiler information tests.
SET(CMAKE_C_COMPILER avr-gcc)
SET(CMAKE_CXX_COMPILER avr-g++)
cmake_force_cxx_compiler (avr-g++ CrossAVR)
cmake_force_c_compiler (avr-gcc CrossAVR)

# Appparently we want to use the gnuc99 standard.
#set (CSTANDARD "-std=gnu99")

# Generate .stabs debugging symbols for assembler source lines. This enables avr-gdb to
# trace through assembler source files.
#set (CDEBUG "-gstabs")

# Warn for functions declared or defined without specified argument types.
set (CWARN "-Wall -Wstrict-prototypes")

# -funsigned-char - Make any unqualfied char type an unsigned char. Without this option,
#   they default to a signed char.
# -funsigned-bitfields - Make any unqualified bitfield type unsigned. By default,
#    they are signed.
# -fpack-struct - Pack all structure members together without holes.
# -fshort-enums - Allocate to an enum type only as many bytes as it needs for the declared
#   range of possible values. Specifically, the enum type will be equivalent to the
#   smallest integer type which has enough room.
set (CTUNING_FLAGS "-ffunction-sections -fdata-sections -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums")

# Optimize for size.   The special option -Os is meant to turn on all -O2 optimizations
# that are not expected to increase code size.
set (COPT "-Os")

SET(CINCS "-I${ArduinoCode_SOURCE_DIR}/libarduinocore")

# Finally the compilation flags are now configured.
set(CMAKE_CXX_FLAGS "-lc -lm -mmcu=${ARDUINO_PROCESSOR} -DF_CPU=${ARDUINO_PROCESSOR_FREQ} ${CTUNING_FLAGS} ${CWARN} ${CSTANDARD} ${CDEBUG} ${COPT} ${CINCS} -lc")
set(CMAKE_C_FLAGS "-lc -lm ${CMAKE_CXX_FLAGS} ${CTUNING_FLAGS} ${CWARN} ${CSTANDARD} ${CDEBUG} ${CINCS} -lc")

# On gentoo, -rdynamic is passed to the compiler. The avr compiler does not recognize this
# option.  Also, we are not building shared libraries.
set(CMAKE_EXE_LINKER_FLAGS "-Wl,--gc-sections")
set(CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS ""

Arduino核心CMake文件。这是一个放置在libarduinocore目录中的CMakeList.txt文件。

include(../arduino.cmake)


add_library (arduinocore
        HardwareSerial.cpp
        pins_arduino.c
        Print.cpp
        Tone.cpp
        WInterrupts.c
        wiring_analog.c
        wiring.c
        wiring_digital.c
        wiring_pulse.c
        wiring_shift.c
        WMath.cpp
        WString.cpp
        )

Makefile

TARGET = IMU
PORT = /dev/ttyACM0
BAUD = 57600
PROGRAMMER = arduino
MCU = atmega328p
F_CPU = 8000000L

CXX_SRCS = ADXL345.cpp \
           ApplicationRoutines.cpp \
           DCM.cpp \
           HMC5883L.cpp \
           ITG3200.cpp \
           matrix.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 \
           matrix.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) -Wall $(FLAGS_TUNNIG) $(FLAGS_OPT)
#ALL_ASFLAGS = -mmcu=$(MCU) -I. -x assembler-with-cpp $(ASFLAGS)

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

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

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

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

.SUFFIXES: .hex .cpp .o .c

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

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


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

1
你能展示一下使用Makefile时得到的输出吗?我之前也遇到了同样的问题,通过在Makefile中为.elf目标行末添加-lm(但不是-lc)来解决它。 - Jens Willy Johannsen
你们确定不是内存不足了吗?328p有32KB的空间,其中一部分被引导加载程序使用。 - Julie in Austin
8个回答

11

说明:

正如错误消息所提示的,问题与代码的 重定位 有关,导致一些 截断 发生。该消息来自链接器,它试图将代码块映射到程序内存中的适当位置。

当代码放置或移动到某个位置(“重定位”)并且从另一个代码块引用此代码块时,通过 JMPCALL(即函数调用),必须将重定位的地址添加到引用它的 JMPCALL 指令中。

AVR 设备支持 两种 跳转 / 调用指令: JMP vs. RJMPCALL vs. RCALLR 变量相对于当前位置进行调用,并且在程序存储器使用和执行时间方面更加高效。但这是有代价的:只能在距离它们在程序内存中的位置不到 +/-4kb 的范围内使用 RJMPRCALL。如果程序内存不超过 8kb,则永远不会出现问题,因为可以从任何位置通过 RCALLRJMP 寻址整个 8kb 范围。

然而,在具有超过 8kb 程序存储器的设备上,对于所有可能的位置这并不总是正确的。因此,如果 链接器(linker)决定可以将要调用的代码放置在 +/-4kb 范围内的话,就没有问题。但是,如果链接器未能将该代码重新定位到该范围内,则不能使用RJMP/RCALL来到达代码的新地址,地址因此被截断(就像在C中执行 uint16_t value = 12345; uint8_t truncatedValue = value; 时一样),从而导致生成的代码出现错误。

请注意,对于任何超过 4kb 程序存储器的项目(对于具有>8kb 程序存储器的设备而言),这可能会发生或可能不会发生,因为它取决于所需代码重定位的情况,这基本上可能会随着每个添加或删除的 C 代码行、每个要链接的库以及链接库或其他代码块的顺序(例如,当链接器像"A B C"这样定位代码时,从"A"到"B"的调用可能有效,但当链接器决定像"A C B"那样重新定位时,它可能会失败)而改变。

解决方案:

您必须让编译器知道它需要生成 JMP/CALL指令而不是(更有效的)RJMP/RCALL指令。在 AVR Studio/Atmel Studio 中,可以在项目属性、工具链、AVR/GNU C 编译器、优化中完成此操作。相关选项是“ 在>8k设备上使用rjmp/rcall (有限范围) (-mshort-calls)”,需要将其取消选中,以避免发生上述错误。
正如标签所示,相关的命令行选项是-mshort-calls,需要从gcc命令行参数列表中删除,以在从IDE外部调用gcc时实现相同的效果。

更新:

为避免此错误可能引起的不必要混淆,-mshort-calls已在avr-gcc 4.7中弃用并将在4.8中被移除。来源:GCC 4.8变更

现在用户应该使用-mrelax代替,以生成具有调用优化的二进制文件,其中可能但永远不会产生错误。


3
我遇到了avr-gcc重定位截断错误消息,并花了几天时间解决。简而言之,链接器中似乎存在一个错误。
为了快速修复,请在全局变量区域中添加以下代码。您可能需要尝试几种不同的数组大小。
#include <avr/pgmspace.h>
const char pad[500] PROGMEM = { 0 };

以下是相关情况。闪存内存的前224字节是中断向量表。当发生中断(例如计时器到期或某些接收缓冲区中有一个字节等待)时,该表告诉处理器要执行哪些代码。此示例不使用许多中断,因此未使用的向量将被发送到bad_interrupt()例程。以下是向量表中的几行。

0000 <__vectors>:
   0:    0c 94 08 08   jmp   0x1010  ; 0x1010 <__ctors_end>
   4:    0c 94 12 08   jmp   0x1024  ; 0x1024 <__bad_interrupt>
   8:    0c 94 12 08   jmp   0x1024  ; 0x1024 <__bad_interrupt>
   c:    0c 94 12 08   jmp   0x1024  ; 0x1024 <__bad_interrupt>
  10:    0c 94 12 08   jmp   0x1024  ; 0x1024 <__bad_interrupt>
  14:    0c 94 12 08   jmp   0x1024  ; 0x1024 <__bad_interrupt>
  18:    0c 94 12 08   jmp   0x1024  ; 0x1024 <__bad_interrupt>
  1c:    0c 94 12 08   jmp   0x1024  ; 0x1024 <__bad_interrupt>
  20:    0c 94 12 08   jmp   0x1024  ; 0x1024 <__bad_interrupt>
  24:    ff c7         rjmp  .+4094  ; 0x1024 <__bad_interrupt>
  26:    00 00         nop
  28:    fd c7         rjmp  .+4090  ; 0x1024 <__bad_interrupt>
  2a:    00 00         nop
  2c:    fb c7         rjmp  .+4086  ; 0x1024 <__bad_interrupt>
  2e:    00 00         nop
  30:    f9 c7         rjmp  .+4082  ; 0x1024 <__bad_interrupt>

需要注意以下几点:

  • bad_interrupt例程位于地址0x1024
  • 前几个向量使用直接跳转到0x1024
  • 最后几个向量使用相对跳转到0x1024

同时使用jmp和rjmp是使用了-mrelax编译器标志的结果。除其他外,它告诉编译器在目标足够接近(即+/- 4k)时使用rjmp指令。否则,编译器应该使用jmp指令。这并不是什么坏事,rjmp指令运行速度更快,并且使用2个字节的数据。

没有-mrelax,编译器只在向量表中使用jmp指令,问题就不存在了。顺便说一句,对于我们的目的,--relax与-mrelax相同。

问题在于链接器出现了某些堵塞情况。在上面的示例中,当bad_interrupt例程位于地址0x1028时,地址0x24处的向量应该变成jmp,但是由于某种原因,链接器无法执行此操作。相反,它将指令留为带有相对偏移量为+4098的rjmp。由于允许范围为4096,因此偏移量会被截断为+2,这是一个严重的错误。

"pad[500] PROGMEM = {0};"之所以起作用,是因为它将在向量表和bad_interrupt()之间分配一块闪存,并将其远离向量表,以至于链接器甚至不会考虑使用rjmp指令。

在搜索网络时,这似乎是所有解决方案的慢性问题,有时能够起作用。流行的解决方案包括使用更多/更少的PSTR("Hello World")结构和各种-lm -lc选项。我怀疑这些东西只是在改变子程序地址,靠盲目的运气让它们落在有效的位置。

下面是我用来隔离此错误的代码:

//
// rjmp vector table relocation truncation bug
//
// works when the -mrelax option is not used
//
//  avr-gcc -g -Wall -mrelax pad.c -mmcu=atmega2560 -Wl,-Map -o pad.elf
//  avr-objdump -h -S pad.elf > pad.list
//
//  avr-gcc --version -> avr-gcc (GCC) 4.7.2
//

#include <avr/pgmspace.h>

// note, there are other bands of works/fails
//
// 3884 works
// 3886 fails
// 3894 fails
// 3896 works
const char pad[3886] PROGMEM = { 0 };

int main() {

  int   i, j;

  for (i = 0; 1; i++)
    j += pad[i];
}

不错的答案。看起来这个 Bug 已经在 2012 年修复了(https://sourceware.org/bugzilla/show_bug.cgi?id=13410)。 - Edgar Bonet

3
我已经解决了问题,我重构了代码(删除了几乎所有全局变量),并在makefile中添加了“-lc -lm -lc”标志。我认为问题出在代码结构上,由于对Arduino代码样式的不良适应而产生过多的全局变量(所有源文件都粘贴到同一个文件中)。我在此处放置了makefile,希望对某些人有用:
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/$@

3

我花了几个小时来解决这个问题,最终得以解决。对我而言,问题出在必须将avr libm.a包含在链接器命令中,而我正在使用与libc.a库分离的Math.h库,并且没有被正确链接。

尝试修改链接器命令,使其像这样,在命令开头添加-lc -lm,在结尾处添加-lc:

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

我的参考资料: http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1290294587

1
我通过在我的makefile底部覆盖规则来解决了这个问题:
# (...)
include /usr/share/arduino/Arduino.mk

LDFLAGS += -lc -lm

$(TARGET_ELF):  $(LOCAL_OBJS) $(CORE_LIB) $(OTHER_OBJS)
    $(CC) $(LDFLAGS) -o $@ $(LOCAL_OBJS) $(CORE_LIB) $(OTHER_OBJS)

1

经过长时间的努力,我通过在SET(CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "-lm -lc")中添加-lm -lc来消除了重定位错误,因此它们将由CMke在link.txt中追加到末尾。

我的CMakeLists.txt
# generated by cmkoder 
PROJECT (ermote1.cmk)
CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
SET(CMAKE_SYSTEM_NAME Generic)
SET(OUT_BINARY_FILE ermote1)

SET(ENV{CROSS_COMPILE} avr-)
SET(ENV{ARCH} arm)
SET(ENV{TC_BASE} /home/arduino-1.0.5)
SET(CROSS_COMPILE avr-)
SET(ARCH arm)
SET(TC_BASE /home/arduino-1.0.5)
SET(TC_SRC_PATH ${TC_BASE}/libraries/)

SET(CMAKE_CXX_COMPILER ${CROSS_COMPILE}c++)
SET(CMAKE_C_COMPILER ${CROSS_COMPILE}gcc)
SET(TC_LIB_LINKER ${CROSS_COMPILE}ar)
SET(TC_GDB ${CROSS_COMPILE}gdb)
SET(HW_MMCU "-mmcu=atmega328p")
SET(HW_DF_CPU "-DF_CPU=16000000L")
SET(HW_VARIANT "eightanaloginputs")
SET (TC_DEFINES "-DARDUINO=105 -DUSB_VID=null  -DUSB_PID=null")
SET(CMAKE_CXX_FLAGS "-g -Os -Wall -fno-exceptions -ffunction-sections  -fdata-sections -lm -Wl,-gc-sections ${HW_MMCU} ${HW_DF_CPU} -lc  ${TC_DEFINES}")

SET(CMAKE_C_FLAGS  "${CMAKE_CXX_FLAGS}")

SET(CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "-lm -lc")

SET(CMAKE_FIND_ROOT_PATH  ${TC_BASE}/hardware/tools/avr/bin ${TC_BASE}/hardware/tools ${TC_BASE}/hardware/tools/avr/lib/avr/lib)

SET(CMAKE_LIBRARY_PATH  "")

INCLUDE_DIRECTORIES (${TC_BASE}/hardware/arduino/cores/arduino/ 
                     ${TC_BASE}/hardware/arduino/variants/${HW_VARIANT}/ 
                     ${TC_BASE}/libs/ 
                     ${TC_BASE}/libraries/ 
                     ${TC_SRC_PATH}SoftwareSerial 
                     ${TC_SRC_PATH}Wire 
                     ${TC_SRC_PATH}Wire/utility 
                     ${TC_SRC_PATH}Ermote 
                     ${TC_SRC_PATH}Adafruit_BMP085)

SET(TC_SOURCES ${TC_BASE}/hardware/arduino/cores/arduino/HardwareSerial.cpp 
               ${TC_BASE}/hardware/arduino/cores/arduino/Print.cpp 
               ${TC_BASE}/hardware/arduino/cores/arduino/WInterrupts.c 
               ${TC_BASE}/hardware/arduino/cores/arduino/wiring_analog.c 
               ${TC_BASE}/hardware/arduino/cores/arduino/wiring.c 
               ${TC_BASE}/hardware/arduino/cores/arduino/wiring_digital.c 
               ${TC_BASE}/hardware/arduino/cores/arduino/wiring_pulse.c 
               ${TC_BASE}/hardware/arduino/cores/arduino/wiring_shift.c 
               ${TC_BASE}/hardware/arduino/cores/arduino/WMath.cpp 
               ${TC_BASE}/hardware/arduino/cores/arduino/WString.cpp 
               ${TC_BASE}/hardware/arduino/cores/arduino/main.cpp 
               ${TC_BASE}/hardware/arduino/cores/arduino/new.cpp)


SET(SDK_SRCS ${TC_SRC_PATH}/SoftwareSerial/SoftwareSerial.cpp 
             ${TC_SRC_PATH}/Wire/Wire.cpp 
             ${TC_SRC_PATH}/Wire/utility/twi.c 
             ${TC_SRC_PATH}/Adafruit_BMP085/Adafruit_BMP085.cpp)

SET(PRJ_SRCS start.cpp)


FIND_PROGRAM(AVROBJCOPY "avr-objcopy")
FIND_PROGRAM(AVRDUDE "avrdude")
FIND_PROGRAM(AVRSIZE "avr-size")

if(AVROBJCOPY)
    add_custom_target(hex)
    add_dependencies(hex ${OUT_BINARY_FILE})

    add_custom_command(TARGET hex POST_BUILD
        COMMAND ${AVROBJCOPY} -O ihex -R .eeprom  ./${OUT_BINARY_FILE} ./${OUT_BINARY_FILE}.hex
    )
    add_custom_target(elf)
    add_dependencies(elf ${OUT_BINARY_FILE})

    add_custom_command(TARGET elf POST_BUILD
        COMMAND ${AVROBJCOPY} -O ihex -R .eeprom  ./${OUT_BINARY_FILE} ./${OUT_BINARY_FILE}.elf
    )
    add_custom_target(eep)
    add_dependencies(eep ${OUT_BINARY_FILE})

    add_custom_command(TARGET eep POST_BUILD
        COMMAND ${AVROBJCOPY} -O ihex -j .eeprom --set-section-flags=.eeprom=alloc,load --no-change-warnings --change-section-lma .eeprom=0  ./${OUT_BINARY_FILE} ./${OUT_BINARY_FILE}.eep
    )

endif()

ADD_EXECUTABLE ( ${OUT_BINARY_FILE} ${TC_SOURCES} ${SDK_SRCS} ${PRJ_SRCS})
的内容会像这样。
avr-c++   -g -Os -Wall -fno-exceptions -ffunction-sections  -fdata- 
sections -lm -Wl,-gc-sections -mmcu=atmega328p -DF_CPU=16000000L -lc  
-DARDUINO=105 -DUSB_VID=null  -DUSB_PID=null    <all the 
CMakeFiles/....cpp.o >  -o ermote1 -lm -lc  

0

我在使用MPLAB X IDE和AVR-Toolchain编译ATtiny861的一个简短的C程序时遇到了同样的问题。我还在项目属性的链接器菜单中使用了参数-lc -lm,并发现它可以在不进行编译器优化的情况下使用-O0正常工作。

稍后我想,没有优化肯定不是理想的选择,于是我尝试了另一种方式,使用了-O3,结果很好!没有重定位错误!

也许这对进一步的研究有所帮助。我的知识并不深入,所以我希望不会再次遇到这个错误。


0

在最新的avr-gcc中不再需要添加-lm

像其他答案中提出的那样添加-lm不是最新版本avr-gcc(v4.7.2 于2012年发布及更新版本)的正确解决方案。

直到v4.6版本,avr工具链发行版存在一个问题,即libgcc和avr-libc都提供了浮点实现。虽然avr-libc的实现更加高效,但它假设浮点仿真函数彼此靠近,以便RCALL/RCALL可以在闪存大小大于8 KiB的设备上到达其他浮点函数。然而,并没有任何保证这样的浮点函数会彼此靠近,因此avr-libc做出了错误的假设,可能导致RJMP/RCALL无法到达它们的目标(编码为重定位R_AVR_13_PCREL)。

问题变得更加严重,因为从libgcc链接了低效的浮点代码(实际上并不需要,因为这些函数也由avr-libc提供)。这在v4.7.2中得到了修复:自那以后,相应的函数已从libgcc中排除,并且-lm已自动添加到链接器选项中,请参见PR54461。例如,使用avr-gcc simple.c -Wl,-v -mmcu=atmega8进行编译和链接时,您可以看到avr-gcc传递给链接器的选项如下:
<path>/ld ... --start-group -lgcc -lm -lc -latmega8 --end-group

这意味着-lm已经放置在正确的位置,您可以从libm和libc引用libgcc函数,反之亦然。

您的libgcc是否带有应由avr-libc提供的浮点函数可以通过以下命令检测:

avr-objdump -d <INSTALL>/lib/gcc/avr/<VERSION>/libgcc.a | grep __addsf3

如果找不到__addsf3,那么一切都很好。如果找到了,您可能需要尝试使用-lm进行调整。<INSTALL>是您的工具链分发位置,<VERSION>是您的GCC版本。

-mshort-calls在v8+中不是优化选项

建议添加-mshort-calls已经过时。今天,-mshort-calls不是优化选项,请参见v8发布说明

• 支持一个新的命令行选项-mshort-calls。此选项用于avrxmega3变体的多库选择。它不是优化选项。请勿手动设置。

它的效果是使代码大小更小,从而降低了重定位被截断的概率。可以通过-mrelax来实现预期效果,或者如果您的版本还不支持该选项,则可以使用-Wl,--relax


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接