4.3.6.3 Function Pointers
The MPLAB XC8 compiler fully supports pointers to functions. These are often used to call one of several function addresses stored in a user-defined C array, which acts like a lookup table.
Function pointers are 2 bytes in size. As the address is word aligned,
such pointers can reach program memory addresses up to 128 KB. If the device you are using
supports more than this amount of program memory and you wish to indirectly access routines
above this address, then you need to use the -mrelax
option (see 3.6.1.11 Relax Option), which maintains the
size of the pointer,but will instruct the linker to have calls reach their final
destination via lookups.
In order to facilitate indirect jump on devices with more than 128 KB of
program memory space, there is a special function register called EIND that serves as most
significant part of the target address when eicall
or
eijmp
instructions are executed. The compiler might also use this
register in order to emulate an indirect call or jump by means of a ret
instruction.
The compiler never sets the EIND register and assumes that it never changes during the startup code or program execution, and this implies that the EIND register is not saved or restored in function or interrupt service routine prologues or epilogues.
To accommodate indirect calls to functions and computed gotos, the linker generates function stubs, or trampolines, that contain direct jumps to the desired addresses. Indirect calls and jumps are made to the stub, which then redirects execution to the desired function or location.
For the stubs to work correctly, the -mrelax
option must
be used. This option ensures that the linker will use a 16-bit function pointer and stub
combination, even though the destination address might be above 128 KB.
The default linker script assumes code requires the EIND register contain
zero. If this is not the case, a customized linker script must be used in order to place
the sections whose name begin with .trampolines
into the segment
appropriate to the value held by the EIND register.
The startup code from the libgcc.a library never sets the EIND register.
It is legitimate for user-specific startup code to set up EIND early, for
example by means of initialization code located in section .init3
. Such
code runs prior to general startup code that initializes RAM and calls constructors, but
after the AVR-LibC startup code that sets the EIND register to a value appropriate for the
location of the vector table.
#include <avr/io.h>
static void
__attribute__((section(".init3"),naked,used,no_instrument_function))
init3_set_eind (void)
{
__asm volatile ("ldi r24,pm_hh8(__trampolines_start)\n\t"
"out %i0,r24" :: "n" (&EIND) : "r24","memory");
}
The __trampolines_start
symbol is defined in the linker
script.
Stubs are generated automatically by the linker, if the following two conditions are met:
- The address of a label is taken by means of the
gs
assembler modifier (short for generate stubs) like so:LDI r24, lo8(gs(func)) LDI r25, hi8(gs(func))
- The final location of that label is in a code segment outside the segment where the stubs are located.
The compiler emits gs
modifiers for code labels in the
following situations:
- When taking the address of a function or code label
- When generating a computed goto
- If the prologue-save function is used (see 3.6.1.3 Call-prologues Option)
- When generating switch/case dispatch tables (these can be inhibited by
specifying the
-fno-jump-tables
option, 3.6.1.10 Jump-tables Option) - C and C++ constructors/destructors called during startup/shutdown
Jumping to absolute addresses is not supported, as shown in the following example:
int main (void)
{
/* Call function at word address 0x2 */
return ((int(*)(void)) 0x2)();
}
Instead, the function has to be called through a symbol
(func_4
in the following example) so that a stub can be set up:
int main (void)
{
extern int func_4 (void);
/* Call function at byte address 0x4 */
return func_4();
}
The project should be linked with
-Wl,--defsym,func_4=0x4
. Alternatively, func_4
can be
defined in the linker script.