Каковы практические применения слабых связей?

Используя специальные команды компилятора, символ может быть объявлен слабым. Согласно Википедии:

слабый символ - это определение символа в объектном файле или динамической библиотеке, которая может быть переопределена другими определениями символов

В каких сценариях или для каких приложений вам нужны слабые символы? Каковы типичные варианты использования?

Ответ 1

Одним из слабых ссылок является реализация сменных функций в стандарте С++. А именно:

void *operator new(std::size_t);
void *operator new(std::size_t, std::nothrow_t const &) noexcept;
void *operator new[](std::size_t);
void *operator new[](std::size_t, const std::nothrow_t&) noexcept;
void operator delete(void *) noexcept;
void operator delete(void *, std::nothrow_t const &) noexcept;
void operator delete[](void *) noexcept;
void operator delete[](void *, std::nothrow_t const &) noexcept;

Это функции, которые должны быть реализованы реализацией, но если программа реализует их, то реализация программы заменяет или отменяет версию реализации. Это легко реализовать через слабую связь.

Ответ 2

В встроенной разработке, когда у вас есть, например, вектор указателей прерываний, очень удобно использовать слабую связь для получения обработчиков по умолчанию для прерываний, которые вам не интересны.

Это работает, определяя пустой обработчик (один раз), а затем вводит один новый правильно названный символ для каждого указателя прерывания, который вам нужен, который слабо связан с обработчиком по умолчанию.

Затем вектор заполняется этими символами, которые будут указывать на один и тот же фактический код, пока вы не решите реализовать один из них, используя одно и то же (правильное) имя, тогда ваш код "пересиливает" слабое звено, вызывая указатель на ваш код, который будет установлен в таблице прерываний.

Это часто реализуется в некоторой смеси C и сборки, но с использованием псевдокода C мы можем иметь что-то вроде:

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. */

Затем вы можете просто реализовать свою собственную функцию при необходимости:

void timer1_isr(void)
{
  /* Handler ISR from timer1. */
}

не изменяя ничего, он "просто работает". До тех пор, пока ваше имя будет тем, что ожидает "код поддержки", указанный выше.

Ответ 3

Слабый атрибут заставляет объявление выдаваться как слабый а не глобальным. Это в первую очередь полезно при определении библиотечные функции, которые могут быть переопределены в коде пользователя, хотя это может также могут использоваться с не-функциональными объявлениями. Слабые символы поддерживается для целей ELF, а также для целей a.out при использовании GNU-сборщик и компоновщик.

Пример слабого атрибута:

weak.c

extern void foo() __attribute__((weak));

int main() {
if (foo) foo();
} 

foo.c

void foo() {
printf("in foo.\n");
} 

strong.c

extern void foo() ;

int main() {
if (foo) foo();
} 

Компиляция

$ cc weak.c // Compiles OK
$ cc strong.c // undefined reference to `foo'

Когда "foo" объявляется слабым, его определение можно опустить, или заменены разными библиотеками, с видом "времени ссылки" связывание ". Линкер заполнит 0 для undefined слабых символов.

Ответ 4

Типичный и повседневный прецедент - это встроенные и шаблонные функции.

Например, эта часть кода при компиляции с g++ -shared -fPIC:

extern void a();

inline void foo() { a(); }

void bar() { foo(); }
void baz() { foo(); }

Будет ли символ bar и baz отмечен как T (нормальный) на nm, а foo будет отмечен как W - слабый.

(т.е. mn -C ./a.out)

Обоснование:

встроенные функции и шаблоны могут быть определены в заголовках и обычно определяются несколько раз в разных частях источников и, наконец, только один остается активным.

Если он не будет отмечен как "Слабый", это будет столкновение нескольких "foo" символов или компилятор не сможет отключить вложение

Ответ 5

Обычно вы используете слабое соединение, когда хотите переопределить объявление функции в другой части вашего кода. Обычно это относится к библиотекам, которые указывают, например, обработчик ошибок по умолчанию, который вы можете переопределить с помощью специальной функции, если вы используете библиотеку.