Long_calls between RAM and ROM sections on bare metal ARM with gcc

armcembeddedgcclinker

I'm working on an ARM7TDMI project using GCC 4.3 and I'm having some difficulity telling the compiler to use long calls in certain cases but not others.

The build process runs arm-eabi-gcc to generate relocatable ELF object files for each .c source file (most relevant CFLAGS include -Os -ffunction-sections -fdata-sections -mthumb -mthumb-interwork), then links them all into an ELF executable (most relevant LDFLAGS include -Wl,--gc-sections -Wl,-static -Wl,-n -nostdlib, and a custom linker script). Then that ELF file is converted to a raw executable with arm-eabi-objcopy -O binary, and a custom bootloader copies it from ROM to RAM (a single SRAM with both code and data) at startup. So everything then executes from RAM, .rodata is present in RAM, and everything proceeds quickly, completely ignoring ROM after boot.

I'm now trying to change that, so that certain select pieces of RO data and the text of select functions can live only in ROM and be accessed during runtime as necessary. I've modified the linker script to know about two new sections ".flashdata" and ".flashtext", both of which should be placed at a fixed address in ROM. I've also sprinkled __attribute__((__section__(".flashdata"))) and __attribute__((__section__(".flashtext"),__long_call__)) throughout the C code as appropriate, and I've rejiggered the build process so that the old objcopy now adds -R .flashdata -R .flashtext, and I do a second objcopy with -j for each of those sections, then I combine the two output files so that the bootloader can do the right thing and the ROM sections appear at the expected memory-mapped location.

This all works fine – I can printf strings tagged into the .flashdata section, and I can call a .flashtext function from code running out of RAM (which knows to use a long call because of the __long_call__ attribute next to the __section__(".flashtext") attribute). That ROM-based function can happily short-call other ROM-based functions, and it can return back to its RAM-based caller.

The problem comes in trying to call from a ROM-based function into a RAM-based one, which must also be a long call. I do not want to use long calls everywhere, so I do not want -mlong_calls in my CFLAGS. If I group all of the functions to live in ROM into a single rom.c, I can build that one file with -mlong-calls and everything works. However, I strongly prefer to avoid that, and keep functions grouped generally by purpose, simply tagging a few here and there as appropriate to run from ROM.

Incidentally, this was not sufficient under gcc 3.4. Using -mlong-calls got the compiler thinking the right thing, but it couldn't follow through because it was only willing to perform long jumps with its helpers _call_via_rX…which all lived in RAM and could only be accessed through a long call. This was fixed in the linker in gcc 4.0, but not backported to anything in the 3.x tree.

So it is wonderful that I can now call back into RAM at all since I'm using gcc 4.3. It would be even better if I could somehow tag the code in the ROM-based functions to force it to use long calls. There is a #pragma long_calls, but it only affects declarations, so I could use it instead of __attribute__((__long_call__)). It sadly does not magically force the compiler to use long calls for all function calls encountered while it is in effect.

Organizationally, it is simply not the right thing to do to group all of the slow-running code into a single file, out of context and separate from other code in its general category. Please tell me there is an option I haven't yet considered. Why isn't -ffunction-sections or just the fact that the code is in different sections (.text versus .flashtext) automatically fixing my problem?

By the way, the error out of the linker when it figures out that the compiler used a short call which didn't leave it enough space to manage the relocation is: relocation truncated to fit: R_ARM_THM_CALL against symbolfoo' defined in .text.foo section in objs/foo.o(and the section.text.foois used instead of.textbecause of-ffunction-sections` in CFLAGS).

Best Answer

I suspect there is no way to do what you want automatically. I do have one suggestion, although it is not exactly what you want.

Declare the RAM functions (in ram_funcs.h, say) like this:

int foo() RAM_CALL;

Then put all your ROM functions into their own files, and start each of those files like this:

#define RAM_CALL __attribute__((__long_call__))
#include "ram_funcs.h"

Other files would use:

#define RAM_CALL
#include "ram_funcs.h"

This is not much better than using -mlong-calls, but at least it puts the logic near the functions themselves instead of in some compilation option. Makes it easier to put a few functions in each .c file, for instance.

Related Topic