Solving Linker Error: “cannot move location counter backwards”

Upgrading to a newer GNU toolchain always has its risks. That’s why I always recommend to stay on a given toolchain for production code.

But sometimes one needs to upgrade, or gets a code or project that works in one environment, but not in another. Today I have run into a problem with code read-out projection:

Disabled Automatic Placement of Code Read Protection

Well, the code read-out protection is not the root of the problem, but a good example why problems could occur.

Recently I upgraded to a new GNU toolchain for an LPC800 project. And building that project with the new GNU toolchain, failed with the following linker error message. It was working fine with the previous GNU toolchain.

ld.exe:LPC800-DIP_Blinky_Debug.ld:42 cannot move location counter backwards (from 00000340 to 000002fc)

So what is the problem? The linker tries to allocate an object in a section, but fails because it does not fit into that section. So it tries to ‘move backward’ the allocation, but then fails because there is something in its way at address 0x2FC.

If you face such an error message, I recommend that you look at your linker file and check the section having that address in it. In the case of my project it is this:

    .text : ALIGN(4)
    {
        FILL(0xff)
        __vectors_start__ = ABSOLUTE(.) ;
        KEEP(*(.isr_vector))
        /* Global Section Table */
        . = ALIGN(4) ;
        __section_table_start = .;
        __data_section_table = .;
        LONG(LOADADDR(.data));
        LONG(    ADDR(.data));
        LONG(  SIZEOF(.data));
        __data_section_table_end = .;
        __bss_section_table = .;
        LONG(    ADDR(.bss));
        LONG(  SIZEOF(.bss));
        __bss_section_table_end = .;
        __section_table_end = . ;
        /* End of Global Section Table */

        *(.after_vectors*)

        /* Code Read Protection data */
        . = 0x000002FC ;
        PROVIDE(__CRP_WORD_START__ = .) ;
        KEEP(*(.crp))
        PROVIDE(__CRP_WORD_END__ = .) ;
        ASSERT(!(__CRP_WORD_START__ == __CRP_WORD_END__), "Linker CRP Enabled, but no CRP_WORD provided within application");
        /* End of Code Read Protection */
       *(.text*)
       *(.rodata .rodata.* .constdata .constdata.*)
       . = ALIGN(4);
    } > MFlash32

Notice that section I have marked in bold. It is about a special placement for the code read-out protection field of that LPC microcontroller.

And indeed, turning that option off in the linker ‘solves’ the problem:

disabled code-readout-protection

Disabling that option generates a linker file not having that special placement in it:

Linker File Comparison

So is disabling that option the solution? No, because if you place some arbitrary code bytes on that address, you might lock your part.

The problem is not the block with the .crp, but with the placement earlier on:

*(.after_vectors*)

The real issue is that that the .text section overflowed, because the new gcc generates different code. Causing the code not to fit any more into the section, because the ‘after_vectors’ overflowed into the block at 0x2FC.

So I had to remove a piece of code out of that .after_vectors section assignment in the startup code:

The problem was that the new gcc compiler generates 2 bytes more code for the startup code, causing it not to fit into that tight space before the 0x2FC address.

With the above, my LPC800 application links fine with ‘automatic placement of code readout protection’ turned on :-).

So in case you have the same linker error message and your search engine directs you to this page, you can solve your problem too. Otherwise, you still can post a comment and I’ll try to help.

Happy linking 🙂

6 thoughts on “Solving Linker Error: “cannot move location counter backwards”

  1. Nice find and write-up. I recently had to fight with that CRP word as well.
    The CRP word must be written with a specific pattern to enable the “Code Read Protect Level 2”. The C code does this by writing to a special section defined in the linker. So I had this definition in one of my c files:
    __attribute__ ((used,section(“.crp”))) const uint32_t crp_word = 0x87654321;

    But it turns out that there was already a duplicate definition in startup/startup_lpc845.c:
    __CRP const unsigned int CRP_WORD = CRP_NO_CRP ;
    // note __CRP expands to __attribute__ ((used,section(“.crp”)))

    The symbol names were different (one caps, other lowercase) so there was no error given by the linker, but now there were two words being placed into the section, which should only be 1 word. After I found the error, I noticed that the default linker script does have a ASSERT to check that the is indeed defined, but it fails to detect if it is defined more than once. So I changed the linker to emit a warning if this happens again:
    /* Code Read Protection data */
    . = 0x000002FC ;
    PROVIDE(__CRP_WORD_START__ = .) ;
    KEEP(*(.crp))
    PROVIDE(__CRP_WORD_END__ = .) ;
    PROVIDE(__CRP_SIZE__ = __CRP_WORD_END__ – __CRP_WORD_START__);
    ASSERT((__CRP_SIZE__ == 4), “CRP section wrong size! CRP_WORD must be defined once.”);
    /* End of Code Read Protection */

    Like

  2. Thanks for sharing this article. I’m fighting this problem with an LPC832 after adjusting libraries, semi-hosting, UART, and printf for debugging. This chip doesn’t have lots of flash – 16K – and the SCT/PWM code takes up quite a bit of it. While changing things around to fit it all into flash, and still get some PRINTF output, failing, and then removing it to get back to the 16k limit, I hit this error: – cannot move location counter backwards. Even after I removed the changes I remember making I’m still hitting this link error. I commented out the “__attribute__ ((section(“.after_vectors.init_data”)))” line in the startup_lpc832.c file, but it didn’t help.

    I’m using IDE version v11.8.1, and SDK_2.x_LPC832, version 2.13.0.

    It was building and linking correctly before I added and configured the UART peripheral, and tried to enable console debugging. Something in those changes broke the linker. Thoughts?

    Like

    • I recovered the project directory from two days ago, and it builds fine. Out of curiosity, I did a per-file difference on the two folders, and I see the Debug/startup/startup_lpc932.su file has different sizes here:

      original
      ../startup/startup_lpc832.c:249:6:data_init 8 static ../startup/startup_lpc832.c:258:6:bss_init 0 static

      ../startup/startup_lpc832.c:249:6:data_init 40 static
      ../startup/startup_lpc832.c:258:6:bss_init 24 static
      — ./Debug/startup/startup_lpc832.su

      I think I remember reading one of your articles about this. I’m guessing this is related, but it’s really late for me, and I can’t brain enough to put 2 and 2 together. I’ll check back in the morning.

      Like

    • Nevermind. The recovery got things working again, and I changed the project properties – C/C++ Build – Settings – Optimization to “Optimize Most,” and got back nearly 45% of my code space. After that, I had no issues. *eyeroll* Carry on, and have a nice day.

      Like

      • Yes, optimizations can make a big difference in code space. ‘Optimize most’ could pose challenges with debugging the code, that’s why most of the time I try to keep it at -O1 or -O2.

        Like

What do you think?

This site uses Akismet to reduce spam. Learn how your comment data is processed.