AVR Linker: out of range error

IC-ATMEGA168A-PU_LRGThis article describes two common reasons for the out-of-range AVR linker error message and provides solutions. Although this article is specific to the AVR core, the same problems occur on most processor types.

I’m currently preparing a course, teaching students about assembly language. I chose to use Arduino as development platform which is equipped with AVR-type microcontrollers.

If you write native assembler code mixing functions and constant data you will most probably hit the following warning message of the linker (avr-ld):

warning: internal error: out of range error

Usually, this kind of error indicates that a subroutine call or jump is too far. For example the AVR instruction RCALL (relative call) has a range of ±2 Kbytes. Thus, if between the desired subroutine and the RCALL instruction itself are more than 2 Kbytes of code, the call is impossible with this instruction and the linker will output the error message of above. The solution is to use the instruction CALL (absolute call) instead.

But this error message could indicate something else as well, namely that the code is not aligned properly. Due to the internal architecture of the AVR core, all instructions have to be aligned to even byte addresses. If you insert data into the code segment such as e.g. constant strings, it could happen that the instructions behind the data are aligned to odd addresses thus being not executable. Consider the following code snippet:

   ldi r30,lo8(mystr)
   ldi r31,hi8(mystr)
   rcall strfunc
   ; ... some code ...

mystr:
.string "Hello World!"

strfunc:
   push r29
   push r28
   ;....

There is a constant string in the code between the first part and the subroutine strfunc. The string has a length of 13 bytes (the string itself plus a terminating 0), hence, the instructions of strfunc will start at an odd byte address and the linker will throw the error at the RCALL statement.

The solution is to insert an additional padding byte so that strfunc starts at an even byte address again. The best way to do that is to use the assembler directive .balign 2 directly before strfunc.