Understanding Weak Symbols

If a developer closely looks at framework and library files, they may come across functions that have __weak in front of them. The weak symbol is a special linker symbol that denotes a function that can be overridden during link time. In today’s post, we will take quick look at weakly linked functions.

Weak symbols are very useful in libraries and framework code because they provide a default function that can be used to successfully compile the code base if a user does not provide their own function. For example, if you were to examine the code generated from STM32CubeIDE for an STM32 microcontroller, you would find in the processor start-up code something that looks like the following:

  .weak      TIM1_CC_IRQHandler

  .thumb_set TIM1_CC_IRQHandler,Default_Handler

This code is telling the linker to assign the Default_Handler to TIM1_CC_IRQHandler if a developer does not provide a TIM1_CC_IRQHandler function themselves. In fact, if you look through the whole start-up code, you will find similar code for every possible interrupt handler. This allows the code to create a default handler for the developer without requiring the developer to assign a default handler to all interrupts explicitly.

It’s very easy for a developer to override these weak symbols. For example, if I wanted to provide my own custom TIM1_CC_IRQHandler to my application, all I need to do is provide a TIM1_CC_IRQHandler function like the following:

void TIM1_CC_IRQHandler(void)
{
    HAL_TIM_IRQHandler(&htim1);
}

Notice that this function does not have the __weak symbol, it is therefore by default strong and will replace any existing functions of the same name that are defined as weak! This is different behavior compared to if a developer were to define to strong functions with the same name that then results in a linker error.

It’s important to note that the C standards do not mention anything about weak and strong symbols. This is a practical technique that is provided by compilers to simplify code management in a code base. This means that the use of weak symbols is generally not portable friendly and may require rework if you plan to port the code or use multiple compilers.

Weak symbols have several different uses. First, they are often used to define interrupt handlers as we have already seen. Second, they can be used to define callback functions. Callback functions are often used in interrupts or event driven applications to define behaviors that may change from application to application or that may be customized based on configuration. Finally, they can be used for any framework, library or driver function where a developer wants the user with an option to customize their application.

When looking through a code base, all the __weak symbols can leave a developer scratching their head. The good news is that weak symbols are not a complicated topic, and point developers to areas of the code where function replacement and customization are possible.

Share >