ArmClang/ArmLink LTO removes object with __attribute__((used))

562 Views Asked by At

In a bootloader I have a version string that is not explicitly used, but must be present at a specific location in ROM for access by the application loaded by said bootloader. In a source file version.cpp I have:

// Place version string at end of bootrom less 16 bytes
#define VERSION_STRING "090600.01.00.00"
#define MAX_VERSION_STRING_LENGTH 0x10

const char bootmainVersionString[MAX_VERSION_STRING_LENGTH] __attribute__((used)) = VERSION_STRING ;

And the location is achieved via the scatter file:

LR_VERSION_IROM1 0x08001FF0 0x00000010  
{                         
  VERSION_IROM1 0x08001FF0 0x00000010 
  {
    version.o (+RO)
  }
}

This may seem rather convoluted but previous methods valid in armcc v5 are no longer supported in the LLVM/Clang based v6.

However, while the __attribute__((used)) prevents the unused object being removed normally, the linker removes it when LTO (Link-time Optimisation) is enabled. Since I am trying to keep the bootrom inside 8Kb in this case, LTO is otherwise useful.

I get a linker warning:

.\bootrom.sct(21): warning: L6314W: No section matches pattern version.o(RO).

so VERSION_IROM1 is empty, whereas without LTO enabled it is located as required, but not using adds ~560 bytes to the image size in this case.

Toolchain details:

Toolchain:          MDK-ARM Plus  Version: 5.36.0.0
C Compiler:         ArmClang.exe  V6.16
Linker/Locator:     ArmLink.exe   V6.16

Is there a means of preventing LTO from removing this object file?

I have tried using a volatile qualifier, and including a dummy reference in the code, to no avail.


Further I have tried the linker option --keep=bootmainVersionString, but that does not work either with LTO enabled, and has no effect otherwise that __attribute__((used)) does not achieve.

However I have noticed that the object is present in the link, but not located per the scatter file. In the map file I have:

Without LTO (located as intended):

bootmainVersionString    0x08001ff0   Data   16  version.o(.rodata.bootmainVersionString)

with LTO (located by the linker):


bootmainVersionString    0x0800130c   Data   16  lto-llvm-dbc16f.o(.rodata)
2

There are 2 best solutions below

0
On BEST ANSWER

I have been unable to solve this problem using linker directives - LTO defeating it at every turn. However I have devised a work-around that has the advantage or being tool-chain independent by not relying at all on tool-chain specific linker directives or linker specific script syntax.

My solution is:

  1. Place the required content in a file (in my case it was auto-generated in a pre-build step).
  2. Post build, convert that file to Intel hex using SRecord, e.g:
    srec_cat buildinfo -binary -fill 0x00 0x000 0x100 -o buildinfo.hex -Intel
    
  3. Concatenate that with the toolchain generated hex file at the required offset, e.g.:
    srec_cat final.hex -Intel buildinfo.hex -Intel -offset %BUILD_INFO_ADDR% -o application.hex -Intel
    

The disadvantage of this perhaps, is that when the toolchain compiled/linked object code is loaded by the debugger, the located data is of course not included, and it is possible even to have data present from a previous build if the associated flash page did not need to be erased when the object code is loaded.

2
On

.\bootrom.sct(21): warning: L6314W: No section matches pattern version.o(RO).

You cannot use object name explicitily in the scatter file when LTO is enabled as the linker can only see the bytecode instead of object files.

You could solve the problem by:

  1. use __attribute__((section("version"))) to modify VERSION_STRING
  2. change the scatter file snippet to something like:
LR_VERSION_IROM1 0x08001FF0 0x00000010  
{                         
  VERSION_IROM1 0x08001FF0 0x00000010 
  {
    version (+RO, +FIRST)
  }
}