How To Debug the GNU LD Linker Configuration File [ARM Cortex-M Development]

Posted by Magnus Unemyr on Jan 11, 2017 9:00:00 AM

One of the most confusing things in embedded development can be the linker configuration files. Their grammar looks like something from the dark ages, and tends to genuinely confuse even seasoned software developers.

This is a bad thing since the linker configuration file is a common source of problems. To understand what the linker did to your code, you can study the MAP file, which is also hard to understand and cumbersome to use, so few developers do it. A build analyzer tool can come to your rescue and make you a more efficient developer!


What is the purpose of the linker configuration file in the first place? To place different code and data areas on suitable memory ranges in the memory map of your hardware design.

There are many reasons for this, including placing the interrupt vector table where the hardware device expects to find it, to put variables in RAM rather than in FLASH, and to put different code areas in the appropriate FLASH sections. Commonly executed code might benefit from being located in faster on-chip FLASH rather than in slower external FLASH devices, for example.

Unfortunately, the linker configuration file format uses very obscure grammar. It is hard to get it right unless you are one of relatively few developers who have spent a lot of time actually learning this stuff thoroughly. Therefore, you might end up with bugs that cannot be found by studying or debugging the source code - as it is not the code that is the problem.

The traditional way of checking how the linker actually placed code and data in the memory layout is to read the MAP file that can optionally be generated by the GNU LD linker. While this strategy certainly works, it is not a very convenient method. The MAP file does not provide a user-friendly presentation of the raw data.

A modern C/C++ IDE such as TrueSTUDIO Pro includes a Build Analyzer that presents the memory layout of the generated FLASH binary in an easy-to-read format. Using a Build Analyzer, it becomes easy to understand how the linker actually placed code and data in memory.

This provides a developer-friendly way of analyzing the result of the GNU LD linker and its configuration file, and to detect any related problems.

Common linker related problems include:

  • Interrupt vector table not located where the hardware expects to find it
  • Variables/code not located at the desired addresses
  • Functions that are not statically called (such as interrupt handlers and RTOS tasks) are removed by linker optimization
  • Linker segments may overlap in memory (should cause link-time warning)
  • Adjacent segments may grow into each other
  • Wrong start-up file being included
  • Wrong object modules are included
  • Not all object modules are included
  • Unused object modules are  included

None of these problems can be found by studying the source code or by debugging the code. The problem can only be found by studying the linker configuration file or by analyzing the memory layout after linking.

To start with, the Build Analyzer of Atollic TrueSTUDIO can display what memory regions are defined in the linker configuration file, as well as their location and size. This is the big picture of the memory map - you find it in the "Memory Regions" tab. If you get the memory regions wrong, you may end up with all sorts of trouble.




The Build Analyzer can quickly give you a snapshot of the memory usage as well, and you can easily see what memory regions have headroom for more code or data, and which doesn't. Color coding highlights the level of memory consumption as well.

You can also get a more detailed view by opening the "Memory details" tab. Here you can see the size of each memory section, as well as their load and run addresses (in some cases, code or data are copied from FLASH to RAM during execution, for example).




There are multiple reasons why you would like copy code from one address and run it from another. The most common ones are:

  • Statically initialized variables get their initial value copied from FLASH to RAM during startup.
  • Code is copied from slow external FLASH memory to fast internal RAM memory during startup.

As each C/C++ function by default has its own unique section in the linker map (this is required for the linker optimizer to remove unused functions), it is easy to see which functions take up the most memory space.




Since you can sort the functions (or more technically correct, the linker sections) by size, the Build Analyzer is a great tool to help identify what C/C++ functions are the largest, and thus are good targets for code-size optimization efforts. A search function further simplify its use.

All-in-all, the Build Analyzer provide ARM Cortex developers with a highly useful, developer-friendly way to analyze the memory map after linking, which in turn helps identify problems that cannot be detected by studying or debugging the source code. Finally, developers have an easy way to understand the obscure linker configuration and MAP files.

The Build Analyzer is just one of the many powerful tools in the Atollic TrueSTUDIO Pro IDE that can simplify your life and improve your project.

Read the superset blog post that covers the entire topic of Cortex-M debugging:

How to use the most powerful debug techniques on ARM Cortex-M devices

You can also get a good overview of Serial Wire Viewer real-time tracing on Cortex-M cores by reading this white paper on the subject:

Read our SWV event and data tracing whitepaper!

Read our free whitepaper to learn more about advanced tools for ARM Cortex development and debugging.

Read our ARM development  whitepaper!


Topics: GNU tools (GCC/GDB), Atollic TrueSTUDIO, Embedded Software Development