In embedded development, when you have for instance a vector of interrupt pointers, it's very handy to be able to use weak linking to get default handlers for interrupts you're not interested in.
This works by defining an empty handler (once), then introducing one new properly-named symbol for each interrupt pointer that you need, which is weakly linked to the default handler.
The vector is then filled with these symbols, which will all point at the same actual code, until you decide to implement one of them using the same (proper) name, then your code "overpowers" the weak link, causing a pointer to your code to be installed in the interrupt table.
This is often implemented in some mixture of C and assembly, but using C pseudocode we might have something like:
static void placeholder_isr(void)
{
}
/* Introduce properly-named function pointers, with weak linking.
* NOTE: This syntax is completely fictional as far as I know.
*/
void (*timer1_isr)() = placeholder_isr __attribute("weak linking");
void (*timer2_isr)() = placeholder_isr __attribute("weak linking");
void (*usart1_isr)() = placeholder_isr __attribute("weak linking");
void (*usart2_isr)() = placeholder_isr __attribute("weak linking");
void (*dma1_isr)() = placeholder_isr __attribute("weak linking");
void (*dma1_isr)() = placeholder_isr __attribute("weak linking");
/* Declare the table of interrupt handlers. */
static void (*isr_table)[] = {
timer1_isr,
timer2_isr,
usart1_isr,
usart2_isr,
dma1_isr,
dma2_isr,
} __attribute("isr vector"); /* Attribute to place it where it needs to go. */
Then you can just implement your own function when needed:
void timer1_isr(void)
{
/* Handler ISR from timer1. */
}
without having to change anything else, it "just works". As long as your name is the one that the above "support code" expects, of course.