armembeddedllvmltolink-time-optimization

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


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)

Solution

  • 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.