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 symbol
foo' defined in .text.foo section in objs/foo.o(and the section
.text.foois used instead of
.textbecause of
-ffunction-sections` in CFLAGS).
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.
It appears that the problem is probably fixed in gcc 4.3.3 and later, but I've been using 4.3.2. I created a pet example that won't execute, but demonstrates the use of different sections and the resulting link error. It fails to build with Codesourcery's arm-2008q3, but it does build with arm-2009q1 and later. It will take more time for me to update the whole project to use a newer gcc version, so I can't yet say definitively that this fixes my issue, but I strongly suspect it does.
As an aside, I have another workaround as an alternative to the grouping of all ROM-based functions into a -mthumb-calls
-built rom.c: Call everything via function pointer. In the case of this workaround, the cure is worse than the disease:
((void(*)(void*, void*, int))&memcpy+1)(&dest, &src, len);
You need the +1
so that the optimizer doesn't outsmart you, but it's not a problem in my case because bx
-ing to an odd address indicates thumb mode, and all of my code is thumb. However, I don't believe there is a way to make a generic macro to wrap all such function calls, since each pointer will need to be explicitly cast to match return type and argument list - you effectively end up explicitly re-declaring every function you want to call, even library functions that you don't provide.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With