Переопределить слабую функцию 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: у вас уже есть работающее решение, которое, по-видимому, является решением, явно поддерживаемым и поощряемым благодаря использованию слабых символов. Какое улучшение вы ожидаете от другого решения?


Символы компоновщика ищутся по имени, поэтому единственными альтернативами использованию ожидаемого имени являются:

  1. предложение Тофро изменить шаг ссылки напрямую
  2. изменить таблицу указателей на функции самостоятельно

Весь смысл сделать 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 для двух разных таймеров и даем каждому правильное имя для вектора прерывания, на котором он находится.

Другие вопросы по тегам