弱连接的实际应用有哪些?

32

使用特殊编译器命令,可以声明符号为弱符号根据维基百科

弱符号是指在目标文件或动态库中定义的符号,可能会被其他符号定义覆盖。

在哪些情况下或者应用程序中需要使用弱符号?有哪些典型的用例?


1
https://ofekshilon.com/2014/02/10/linker-weak-symbols/ - Ofek Shilon
5个回答

24
在嵌入式开发中,如果你有一个中断指针向量,那么使用弱链接来获得你不感兴趣的默认中断处理程序非常方便。
这是通过定义一个空处理程序(一次)来实现的,然后为每个需要的中断指针引入一个新的适当命名的符号,该符号与默认处理程序弱链接。
然后,向量填充了这些符号,这些符号将都指向同一实际代码,直到你决定使用相同的(适当的)名称之一来实现其中一个,然后你的代码“压倒”弱链接,导致指向你的代码的指针被安装在中断表中。
这通常是以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. */
}

不需要更改任何其他内容,它就可以“正常工作”。 当然,只要您的名称是上述“支持代码”所期望的名称。


24

弱链接的一种用途是实现C++标准中的可替换函数。具体来说:

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;

这些是必须由实现提供的函数,但如果程序实现了它们,那么程序的实现将替换或覆盖实现版本。通过弱链接很容易实现此功能。



10

典型的日常使用情况是内联和模板函数。

例如,使用以下代码片段编译:g++ -shared -fPIC

extern void a();

inline void foo() { a(); }

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

通过使用nm -C ./a.out命令,符号条(bar)和符号baz将被标记为T(正常),而符号foo将被标记为W-弱。

原因:

内联函数和模板可能在头文件中定义,并通常在源码的不同部分定义多次,最终只有一个处于活动状态。

如果不将其标记为弱,则会导致多个"foo"符号的冲突或编译器无法禁用内联。


1
因此,大多数模板函数也将是弱函数。 - Matthieu M.
@MatthieuM. 确实,已添加到答案中。 - Artyom

7

属性会使声明被发出为弱符号而不是全局符号。这在定义可以在用户代码中重写的库函数时非常有用,尽管它也可以与非函数声明一起使用。弱符号支持ELF目标,以及在使用GNU汇编器和链接器时的a.out目标。

弱属性示例

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来填充未定义的弱符号。

6

当你想要在代码的另一部分覆盖函数定义时,通常会使用弱链接。这通常用于规定了例如默认错误处理程序的库中,如果您使用该库,则可以使用自定义函数来覆盖它。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接