Over a million developers have joined DZone.

GNU Link Time Optimization Finds Non-Matching Declarations

DZone's Guide to

GNU Link Time Optimization Finds Non-Matching Declarations

Here's a nifty trick for using Link Time Optimization. Not only can it optimize across compilation units, but the linker can flag issues that might not be visible to gcc.

· IoT Zone ·
Free Resource

By default, the GNU compiler (gcc) optimizes each compilation unit (source file) separately. This is effective, but misses the opportunity to optimize across compilation units. Here is where the Link Time Optimization (LTO, option -flto) can help out: With a global view, it can optimize one step further.

The other positive side effect is that the linker can flag possible issues, like the one below, that are not visible to the compiler alone:

type of '__SP_INIT' does not match original declaration [enabled by default]

Warning by LTO

Warning by LTO

Link Time Optimization (-flto)

The Link Time Optimizer can be turned on in the optimization settings of the GNU MCU Eclipse plugins (e.g. in Kinetis Design Studio):

Link Time Optimizer in GNU MCU Eclipse Plugins

Link Time Optimizer in GNU MCU Eclipse Plugins

The same setting can be found inside the MCUXpresso IDE (shown for version 10.2 below):

-Flto Setting in MCUXpresso IDE Linker Settings

-Flto Setting in MCUXpresso IDE Linker Settings

Type Does Not Match Original Declaration

What LTO has found in this case is an issue the compiler was not able to see:

Warning by LTO

Warning by LTO

The warning is for the object named ‘__SP_INIT’, which is flagged for use with different prototypes. Searching the project for the usage of that object shows that LTO is correct with its analysis:

Inconsistent Usage of types

Inconsistent Usage of types

In startup.c, the (linker generated) symbol __SP_INIT is declared as

extern char __SP_INIT[];

While it is used in Vectors.c with:

extern uint32_t __SP_INIT;

Note that in one case it is an array of char, while in the other case it is an unsigned 32bit variable!

That problem would not exist if that external declaration would be in a header file, but this is how the engineers have set up the startup and vector table files.

In Vectors.c __SP_INIT is used to initialize the stack pointer (SP) in the vector table as:

extern uint32_t __SP_INIT;
__attribute__ ((section (".vectortable"))) const tVectorTable __vect_table = { /* Interrupt vector table */
/* ISR address No. Name */
VECTOR_SP_MAIN, /* 0x00 ivINT_Initial_Stack_Pointer */

__SP_INIT is defined in the linker script:

_estack = 0x20000000; /* end of m_data */
__SP_INIT = _estack;

So the linker creates a virtual object and assigns it to the address 0x2000’0000.

It is used in the startup code as below:

extern char __SP_INIT[];
__attribute__((naked)) void __thumb_startup(void)
    int addr = (int)__SP_INIT;
    /* setup the stack before we attempt anything else
    skip stack setup if __SP_INIT is 0
    assume sp is already setup. */
    __asm (
        "mov r0,%0\n\t"
        "cmp r0,#0\n\t"
        "beq skip_sp\n\t"
        "mov sp,r0\n\t"
        "sub sp,#4\n\t"
        "mov r0,#0\n\t"
        "mvn r0,r0\n\t"
        "str r0,[sp,#0]\n\t"
        "add sp,#4\n\t"

So for the vector table, it is just a 32-bit value (memory address), while in the startup code, it is an array of char, and assigning the array name with a cast will take the address of that object and assign it to the local variable ‘addr’.

While this ‘magically’ works, it is not correct, especially using an object as above with different declarations. At least, it is not a clean way to use that symbol.


How can we solve this? One challenge, in this case, is that the ‘Vectors.c’ file is generated by Processor Expert, so I cannot easily change that one. But I can fix the variable and usage in startup.c, which is normal application code. The fix is to match the declaration present in vectors.c:

Fixed __SP_INIT in startup.c

Fixed __SP_INIT in startup.c

Fixed __SP_INIT in startup code

Fixed __SP_INIT in startup code

With this, the linker is happy. And I’m happy too.

LTO and FreeRTOS

As a reminder: If using -flto with FreeRTOS and you want to debug it, make sure you turn on the LTO helpers in the FreeRTOS configuration:

FreeRTOS -Lto Helpers

FreeRTOS -Lto Helpers

The above setting tweaks the FreeRTOS source base and makes sure that symbols needed for Kernel Awareness or symbols used in some assembly routines are not removed or tweaked by LTO.


Link Time Optimization is a cool optimization. The optimization still has room for improvements, but I have found that, with turning it on, it is able to flag hidden issues in code. So -flto is also an extra check for my code.

Happy Finding!


iot ,link time optimization ,gcc ,optimization ,tutorial

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}