Переопределить слабую функцию A функцией B
Для встроенного устройства у меня есть файл, содержащий массив с указателями функций, хранящими обработчики прерываний, определяйте так (я не могу его изменить):
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,
...
}
У меня есть второй файл, который определяет некоторые функции, которые я не могу изменить. Этот файл похож на:
void blinkLed(void)
{ ... }
Наконец, у меня есть основной исходный файл, с main
функция и конфигурация устройства. На прерывании 78 я бы хотел мигнуть светодиодом. Поэтому я пишу сильную функцию ISR78
как это:
void ISR78(void)
{
blinkLed();
}
Я задавался вопросом, было ли решение переопределить слабую функцию ISR78
непосредственно blinkLed
хранение адреса blinkLed
внутри __vector_table
без изменения или переименования функции?
РЕДАКТИРОВАТЬ:
Я на самом деле использую GNU gcc 4.9.3 и связанный с ним компоновщик (GNU ld 2.24.0). Я могу изменить main.c
и Makefile, связанный с проектом.
3 ответа
Единственный способ добиться того, что вы хотите сделать - это исправить таблицу символов объектного файла, содержащего blink
символ с ISR78
условное обозначение.
objcopy [...] -redefine-sym blink=ISR78
должен сделать это. Затем компоновщик должен автоматически вставить адрес бывшего blink
в таблицу векторов. Очевидно, ваш blink
символ исчез после этого и не должен вызываться из других мест.
Я, однако, считаю это взломать.
В случае _vector_table
глобально доступен и находится в доступной для записи памяти (не предполагается, что это, вероятно, слишком просто...), вы можете просто исправить это из своего собственного кода с помощью
_vector_table [0x78] = blink;
во время выполнения.
tl;dr: у вас уже есть работающее решение, которое, по-видимому, является решением, явно поддерживаемым и поощряемым благодаря использованию слабых символов. Какое улучшение вы ожидаете от другого решения?
Символы компоновщика ищутся по имени, поэтому единственными альтернативами использованию ожидаемого имени являются:
- предложение Тофро изменить шаг ссылки напрямую
- изменить таблицу указателей на функции самостоятельно
Весь смысл сделать ISR78 слабым символом, прежде всего, состоит в том, чтобы разрешить именно то переопределение (по имени символа), которое вы уже использовали.
Я не вижу способа изменить таблицу векторов прерываний лучше, чем просто использовать ожидаемое имя функции напрямую, даже если это возможно.
Вы можете использовать__asm("symbolname")
сделать это. Это позволяет вам изменить имя символа, используемого в объектном файле, на определенную строку. И gcc, и clang поддерживают это.
Пример:
void blinkLed(void) __asm("ISR78"); // Can only be used on a prototype
void blinkLed(void)
{ ... }
В вашем коде все может использовать имяblinkLed
. Но в объектном файле имя будетISR78
и он переопределит слабую версию и будет использоваться в качестве вектора прерывания.
Это также помогает избежать искажения имен C++, даже если вы не можете использоватьextern "C"
.
template <int id>
struct Timer {
static void blinkled();
};
template <> Timer<1>::blinkled() __asm("ISR71");
template <> Timer<2>::blinkled() __asm("ISR72");
Выше у нас есть шаблон класса с ISR,blinked(), в качестве статического метода. Затем мы определяем две специализации этого ISR для двух разных таймеров и даем каждому правильное имя для вектора прерывания, на котором он находится.