4

AVR g++ has a pointer size of 16 bits. However, my particular chip (the ATMega2560) has 256 KB of RAM. To support this, the compiler automatically generates trampoline sections in the same section of ROM as the current executing code that then contains the extended assembly code to jump into high memory or back. In order for trampolines to be generated, you must take the address of something that sits in high memory.

In my scenario, I have a bootloader that I have written sitting in high memory. The application code needs to be able to call a function in the bootloader. I know the address of this function and need to be able to directly address it by hard-coding the address in my code.

How can I get the compiler/linker to generate the appropriate trampoline for an arbitrary address?

David Pfeffer
  • 38,869
  • 30
  • 127
  • 202
  • Isn't it "forbidden" for application code to execute code from the bootloader-section? Or was it flash-write access only? Because theoretically you could overwrite flash by calling a bootloader-function due to some stack/memory/pointer issue if it where possible. – Rev Oct 10 '17 at 13:01
  • @Rev1.0 Its not just not forbidden, its actually encouraged for my use case. I _am_ in fact calling into the bootloader to then have the bootloader overwrite pieces of flash. – David Pfeffer Oct 10 '17 at 13:04

2 Answers2

2

Compiler and linker will only generate trampoline code when the far address is a symbolic address rather than a literal constant number already in code. something like (assuming the address you want to jump to is 0x20000).

extern void (*farfun)() = 0x20000;

farfun ();

Will definitely not work, it doesn't cause the linker to do anything because the address is already resolved.

You should be able to inject the symbol address in the linker command line like so:

extern void farfun ();

farfun ();

compiling "normally" and linking with

-Wl,--defsym,farfun=0x20000

I think it's clear that you need to make sure yourself that something sensible sits at farfun.

You will most probably also need --relax.

EDIT

Never tried this myself, but maybe:

You could probably try to store the function address in a table in high memory and declare it like this:

extern void (*farfunctable [10])();

(farfunctable [0])();

and use the very same linker command to resolve the external symbol (now your table at 0x20000 (in the bootloader) needs to look like this:

extern void func1();
extern void func2();

void ((*farfunctab [10])() = {
   func1,
   func2,....
};

I would recommend to put func1() ... func10() in a different module from farfunctab in order to make the linker know it has to generate trampolins.

tofro
  • 5,640
  • 14
  • 31
  • Thank you for this answer. It does directly answer the question, albeit in a way I wasn't expecting. My secondary issue however is that I was planning on putting a dispatch struct (that is, a struct with function pointers to all the various functions). Your solution works well, but requires knowing all of the locations of all of the functions ahead of time. Is there a way to execute a function call to a far address that isn't known at compile time? – David Pfeffer Oct 10 '17 at 13:03
  • Normally, boot loader and application are built as two separate pieces of code - So you need to have *some* fixed address in any case as the linker will not be able to resolve dependencies between two different applications. The problem with AVR-GCC for enhanced AVRs is that it is using 16-bit pointers in a 24-bit environment. You might want to peek into a banking library like https://github.com/ArduSat/atmega2560-xmem or into something like parsing the bootloader map file in your build process for the proper address (and use the procedure as above). – tofro Oct 10 '17 at 13:35
  • My goal was to put the struct with pointers to the functions in a fixed location. That way, it would be a single thing that needed a fixed address rather than every external function. – David Pfeffer Oct 10 '17 at 13:43
  • In this case, you probably need to have a look at inline assembly. You'd be able to store 24- or 32-bit pointers in flash, retrieve them using `pgm_read_word_far`, but not easlily be able to call them directly from C - that would be the job of a small inline assembly piece (`EICALL` instruction). – tofro Oct 10 '17 at 14:15
  • If I wrote stub functions in low-memory with identical signature to the bootloader functions that merely did an EIJMP to the bootloader via inline assembly, that theoretically would preserve the stack and etc. – David Pfeffer Oct 10 '17 at 21:10
  • I have added a proposal to my post - Never tried it myself, and I don't know if the linker's trampoline support can be stretched to tables of function pointers - but *could* do what you want. – tofro Oct 10 '17 at 22:03
  • I would be interested to know whether my proposal in the last paragraph actually works or not. – tofro Oct 11 '17 at 15:54
0

I was planning on putting a dispatch struct (that is, a struct with function pointers to all the various functions). Your solution works well, but requires knowing all of the locations of all of the functions ahead of time. Is there a way to execute a function call to a far address that isn't known at compile time?
[...] My goal was to put the struct with pointers to the functions in a fixed location. That way, it would be a single thing that needed a fixed address rather than every external function.

So you have two applications, let's call them App and Boot, where Boot provides some functionalities that App wants to use. The following problems have to be addressed:

  1. How to get addresses from Boot into App.
  2. How to build a jump table for Boot.
  3. Avoid constructs that will crash when App tries to use code from Boot, like: Using indirect calls or jumps, using static constructors or using static storage in Boot.

App uses Addresses of boot.elf directly

Linking with -Wl,-R,boot.elf

A simple way would be to just link app.elf against boot.elf be means of -Wl,-R,boot.elf. Option -R instructs the linker to use symbol values from the specified file without dragging any code. Problem is that there's no way to specify which symbols to use, for example this might lead to a situation where App uses libgcc functions from Boot.

Defining Symbols by means of -Wl,--defsym,symbol=value

A bit more control over which symbols are being defined can be implemented by following a specific naming convention. Suppose that all symbols from Boot that have "boot" in their name should be "exported", then you could just

> avr-nm -g boot.elf | grep ' T ' | awk '/boot/ { printf("--defsym %s=0x%s\n",$3,$1) }' > syms.opt

This prints global symbol values, and grep filters out symbols in the text section. awk then transforms lines like 00020102 T boot1 to lines like --defsym boot1=0x00020102 which are written to an option file syms.opt. The option file can then be provided to the linker by means of -Wl,@syms.opt.

The advantage of an option file is that it is easier to provide than plain options in a build environment like make: app.elf would depend (amongst others) on syms.opt, which in turn would depend on boot.elf.

Defining Symbols in a Linker Script Snippet

An alternative would be to define the symbols in a linker script augmentation, which you would provide by means of -T syms.ld during link and which would contain

"boot1"=ABSOLUTE(0x00020102);
"boot2"=...
...
INSERT AFTER .text

Defining Symbols in an Assembly Module

Yet another way to define the symbols would be by means of an assembly module which contains definitions like .global boot1 together with boot1 = 0x00020102.

All these approaches have in common that all symbols must be defined, or otherwise the linker will throw an undefined symbol error. This means boot.elf must be available, and it does not matter whether just one symbol is undefined or whether dozends of symbols are undefined.

Let Boot provide a Dispatch Table

The problem with using boot.elf directly, like lined out in the previous section, is that it introduces a direct dependency. This means that if Boot is improved or refactored, then you'll also have to re-compile App each time, even if the interface did not change.

A solution is to let Boot provide a dispatch table whose position and layout are known ahead of time. Only when the interface itself changes, App will have to be rebuilt. Just refactoring Boot will not require to re-build App.

The Assembly Module with the Jump Table

As explained in the "Crash" section below, addresses in a dispatch table (and hence indirect jumps) won't work because EIND has a wrong value. Therefore, let's assume we have a table of JMPs to the desired Boot functions, like in an assembly module boot-table.sx that reads:

;;; Linker description file boot.ld locates input section .boot.table
;;; right after .vectors, hence the address of .boot_table will be
;;; text-section-start + _VECTORS_SIZE, where the latter is
;;; #define'd in <avr/io.h>.

;;; No "x" section flag so that the linker won't relax JMPs to RJMPs.
.section .boot.table,"a",@progbits

.global .boot_table
.type .boot_table,@object
boot_table: 
    jmp boot1
    jmp boot2
.size boot_table, .-boot_table

In this example, we are going to locate the jump table right after .vectors, so that its location is known ahead of time. The respective symbol definitions in App's syms.opt will then read

--defsym boot1=0x20000+vectors_size+0*4
--defsym boot2=0x20000+vectors_size+1*4

provided Boot is located at 0x20000. Symbol vectors_size can be defined in a C/C++ module, here by abusing avr-gcc attribute "address":

#include <avr/io.h>

__attribute__((__address__(_VECTORS_SIZE)))
char vectors_size;

Locating the Jump Table

In order to locate input section .boot.table, we need an own linker description file, which you might already use for Boot anyways. We start with a linker script from avr-gcc installation at ./avr/lib/ldscripts/avr6.xn, copy it to boot.ld, and add the following 2 lines after vectors:

...
  .text   :
  {
    *(.vectors)
    KEEP(*(.vectors))

    *(.boot.table)
    KEEP(*(.boot.table))

    /* For data that needs to reside in the lower 64k of progmem.  */
    *(.progmem.gcc*)
    ...

Auto-Generating Boot's Jump Table Module and the Symbols for App

It's highly advisable to have an interface description used by both App and Boot, say common.h. Moreover, in order to keep Boot's boot-table.sx and App's syms.opt in sync with the interface, it's agood idea to auto-generate these two files from common.h. To that end, assume that common.h reads:

#ifndef COMMON_H
#define COMMON_H

#define EX __attribute__((__used__,__externally_visible__))

EX int boot1 /* @boot_table:0 */ (int);
EX int boot2 /* @boot_table:1 */ (void);

#endif /* COMMON_H */

For the matter of simplicity, let's assume that this is C code or the interfaces are extern "C" so that the symbols in source code match the assembly names, and there's no need to use mangled names. It' easy enough to generate boot-table.sx and syms.opt from common.h using the magic comments. The magic comment follows directly after the symbol, so a regex would retrieve the token left of the magic comment, something like Python:

# ... symbol /* @boot_table:index */...
pat = re.compile (r".*(\b\w+\b)\s*/\* @boot_table:(\d+) \*/.*")

for line in sys.stdin.readlines():
    match = re.match (pat, line)
    if match:
        index = int (match.group(2))
        symbol = match.group(1)

Output template for syms.opt would be something like:

asm_line = "--defsym {symbol}=0x20000+vectors_size+4*{index}\n"

Code that will crash

Using Boot code from App is subject to several restrictions:

Indirect Calls and Jumps

These will crash because the start addresses of App resp. Boot are in different 128KiB segments of flash. When the address of a code symbol is taken, the compiler does this per gs(symbol) which instructs the linker to generate a stub and resolve gs() to that stub in .trampolines if the target address is outside the 128KiB segment where the trampolines are located. An explanation of gs() can be found in this answer, there is however more to it: The startup code will effectively initialize

EIND = __vectors >> 17;

see gcrt1.S, the AVR-LibC bits of start-up code crt<device>.o. The compiler assumes EIND never changes during execution, see EIND and more than 128KiB of Flash in the GCC documentation.

This means code in Boot assumes EIND = 1 but is called with EIND = 0 and hence EICALL resp. EIJMP will target the wrong address. This means common code must avoid indirect calls and jumps, and should be compiled with -fno-jump-tables so that switch/case won't generate such tables.

This also implies that the dispatch table described above won't work if it would just held gs(symbol) entries, because App and Boot will disagree on EIND.

Data in Static Storage

If common Boot code is using data in static storage, the data might collide with App's static storage. One way out is to avoid static storage in respective parts of Boot and pass addresses to, say, some data buffer by means of pointer erguments of respective functions.

One could have completely separate RAM areas; one for Boot and one for App, but that would be a waste of RAM because the applications will never run at the same time.

Static Constructors

Boot's static constructors will be bypassed if App uses code from Boot. This includes:

  • C++ code in Boot that explicitly or implicitly generates such constructors.

  • C/C++ code in Boot that relies on __attribute__((__constructor__)) or code in section .initN which is supposed to run prior to main.

  • Start-up code that initializes static storage, EIND etc., which is also run by locating it in some .initN sections, but will be bypassed if App calls Boot code.

emacs drives me nuts
  • 2,785
  • 13
  • 23