override a weak function A with a function B
Asked Answered
G

3

6

For an embedded device, I have file containing an array with function pointers storing interrupt handlers, define like that (I can't modify it):

typedef void (*const ISRFunction)(void);

__attribute__((weak)) void ISR0(void){ for(;;); }
__attribute__((weak)) void ISR1(void){ for(;;); }
...
__attribute__((weak)) void ISR78(void){ for(;;); }
...

ISRFunction __vector_table[0x79] = 
{
    (ISRFunction)&ISR0,
    (ISRFunction)&ISR1,
    ...
    (ISRFunction)&ISR78,
    ...
}

I have a second file which defines some functions, which I can't modify. This file is like:

void blinkLed(void)
{ ... }

Finally, I have a main source file, with main function and configuration of device. On interrupt 78, I would like to blink led. So I write a strong function ISR78 like that:

void ISR78(void)
{
    blinkLed();
}

I wondered if there was a solution to override weak function ISR78 directly by blinkLed, ie storing address of blinkLed inside __vector_table without modifying it or rename function?


EDIT:

I actually use GNU gcc 4.9.3 and associated linker (GNU ld 2.24.0). I can modify main.c and Makefile associated to project.

Gabriel answered 1/8, 2016 at 9:55 Comment(5)
Some compilers (don't know about yours) support symbol aliases, which might be something to look into.Shaer
Also, you should be very specific about the function types: void func(void) (no arguments) and void func() (unspecfied number of arguments) are different types. Use void always for functions without arguments to avoid problems.Shaer
@Shaer thanks for advice, I will take care about itGabriel
Probably I'm not the global vision of the problem, but weak is designed to allow to "override" function, isn't it? So you can define/implement your same_name_function to override the weak one.Warr
@Warr I have already do that. I override the weak function with a same_name_function use as a wrapper to function doing real job. I wonder a method to directly override weak function with function doing real jobGabriel
S
2

The only way I see to achieve what you want to do is to patch the symbol table of the object file containing the blink symbol with the ISR78 symbol.

objcopy [...] --redefine-sym blink=ISR78

should do that. The linker should then automatically insert the address of the former blink into the vector table. Obviously, your blink symbol is gone after that and shouldn't be called from other places.

I would, however, consider this a hack.

In case _vector_table is globally accessible and in writable memory (not assumed, that's probably too simple...), you could simply patch it from your own code by

_vector_table [0x78] = blink;

at runtime.

Significant answered 1/8, 2016 at 12:25 Comment(7)
I will take a look at this method. I prefer a method with __attribute__ or any specific or generic C code or with linker (linker option or linker script), but it's already a first method. I agree with you considering this as a hack. Also, it can be dangerous if blink is use in another place (but because it manages an ISR, it shouldn't be call else where)Gabriel
@Gabriel maybe you could use ld's --defsym command with an argument like ISR78=blink (or is it the other way round?) - I have, however, never tried this myself and don't really know what other implications this might have or whether it will work at all.Significant
Finally, it's not the answer I wanted, but it seams to be the only possible answer. Thanks a lotGabriel
@Gabriel Have you tried my last proposal? It looked promising to me.Significant
I didn't try your last proposal because __vector_table is in an read only memory... but thank, I will remember it if you move __vector_table into RAMGabriel
@Gabriel Actually, with "last proposal" I meant using ld --defsym ISR78=blinkSignificant
Actually it works really fine, it's just not what I did expect.Gabriel
B
2

tl;dr: you have a working solution already, which appears to be the solution explicitly supported and encouraged by using weak symbols in the first place. What improvement do you expect from a different solution?


Linker symbols are looked up by name, so the only alternatives to using the expected name are:

  1. tofro's suggestion to modify the link step directly
  2. modify the function pointer table yourself

The whole point of making ISR78 a weak symbol in the first place is to allow exactly the override (by symbol name) you've already used.

I can't see any way modifying the interrupt vector table is better than just using the expected function name directly, even if it's possible.

Beebread answered 1/8, 2016 at 12:33 Comment(2)
I can understand the OP wants to remove that one indirection (which would typically evaluate to one single branch instruction) - There are problems where every cycle counts.Significant
Which one indirection? You're calling through a table, and the linker will fix up the table - including choosing between the weak and other symbols - at link time. There is no impact at function call time whichever of these you choose (unless you use dynamic linking and there's a trampoline function, at least).Beebread
A
2

You can use __asm("symbolname") to do this. This lets you change the name of the symbol used in the object file to be a specific string. Both gcc and clang support this.

Example:

void blinkLed(void) __asm("ISR78");  // Can only be used on a prototype
void blinkLed(void)
{ ... }

In your code, everything can use the name blinkLed. But in the object file the name will be ISR78 and it will override the weak version and be used as the interrupt vector.

This also works as a way to avoid C++ name mangling, even when you can't use extern "C".

template <int id>
struct Timer {
   static void blinkled();
};

template <> Timer<1>::blinkled() __asm("ISR71");
template <> Timer<2>::blinkled() __asm("ISR72");

Above we have a class template with an ISR, blinkled(), as a static method. Then we define two specializations of that ISR for two different timers, and give each correct name for the interrupt vector it is on.

Apus answered 13/10, 2023 at 6:20 Comment(3)
Really interesting solution, Badly, I can't modify header/source containing blinkLed so can't change prototype, but I will take this way to another project ! specially to manage with c++name mangling.Gabriel
You can still use it. One can write the prototype of a function multiple times. It's not an error unless they conflict. The __asm() in the prototype is additive. Putting it in on a prototype changes the name. Omitting it on another doesn't undo the name change. So you can add it your own header and then include that one in the source that defines and it doesn't conflict with the prototype in the existing header.Apus
Keep in mind that ISRs are special functions. Usually one needs to use something like __attribute__((interrupt)) or __attribute__((signal)) when declaring them to get special code generated. A normal function can't directly be an ISR.Apus

© 2022 - 2024 — McMap. All rights reserved.