在链接时弱定义函数,并测试覆盖

Weakly defining function at link-time, and testing for override

本文关键字:测试覆盖 函数 定义 链接      更新时间:2023-10-16

(环境:gcc 4.6、c++11、glibc 2.13、binutils 2.21)

请考虑以下"链接"演示作为背景:

foo.h

#pragma once
#include <iostream>
void foo();

foo1.cpp

#include "foo.h"
void foo()
{
    std::cout << "foo1";
}

foo2.cpp

#include "foo.h"
void foo()
{
    std::cout << "foo2";
}

main.cpp

#include "foo.h"
int main()
{
    foo();
}

生成文件

compile: main.o foo1.o foo2.o
case1: compile
    g++ -o case1.x main.o
case2: compile
    g++ -o case2.x main.o foo1.cpp
case3: compile
    g++ -o case3.x main.o foo1.o foo2.o
clean:
    rm *.o *.x

我们有一个函数声明如下:

void foo();

我们有多个定义,一个在foo1.cpp中,另一个在foo2.cpp中

我们将.cpp文件编译为.o文件:

$ make compile

没问题,生成了main.o、foo1.o和foo2.o。

案例1

然后我们试着把主链接起来。o:

$ make case1
error: undefined reference to 'foo()'

好的,main.o引用了foo,但没有链接任何定义。

情况2

现在我们尝试链接main.o和foo1.o

$ make case2

foo1.o中只有一个foo的定义,所以它的链接没有错误。

案例3

现在我们尝试链接main.ofoo1.o和foo2.o

$ make case3
error: multiple definition of 'foo()'

链接器在foo1.o和foo2.o中找到了两个foo的定义,因此它没有采用随机的定义,而是快速失败并出现错误,并且没有链接。

问题

我的问题是:

  1. 是否可以将foo1.cpp版本的foo的定义(可能带有某种gcc属性或pragma)标记为"弱"?具体地说,我希望情况1和情况2的行为保持不变,而情况3的行为更改为成功链接而没有警告或错误,并且链接了foo2.cpp中foo的定义(覆盖foo1.cpp中的"弱"定义)。

  2. 是否可以在运行时测试(并分配给布尔变量)是链接了foo的弱版本,还是链接了强版本(显然不执行函数)。即主要实现如下功能。

    template<typename FunctionPtr>
    bool is_weak_linked(FunctionPtr fp);
    

is_weak_linked(foo)在情况2中应返回true,在情况3中应返回false。

至#1:

void foo() __attribute__((weak));
void foo()
{
    ...
}

对#2:我不这么认为,不是你正在做的静态链接。该信息在链接时丢失。

编辑

你总是可以用抽象的方式绕过#2。一些例子:

// Foo.h
extern void foo();
extern "C" void default_foo();
extern const bool is_weak_foo_linked;
template <void (*)()> bool is_weak_linked();
// Foo1.cc
extern "C" void default_foo() {...}
void foo() __attribute__((weak, alias("default_foo")));
extern const bool __attribute__((weak)) is_weak_foo_linked=true;
template <> bool is_weak_linked<foo>() __attribute__((weak));
template <> bool is_weak_linked<foo>() {return true;}
// Foo2.cc
void foo() {...}
extern const bool is_weak_foo_linked=false;
template <> bool is_weak_linked<foo>() {return false;}
// main.cc
#include "foo.h"
#include <iostream>
int main() {
    foo();
    std::cout<<is_weak_foo_linked<<std::endl;
    std::cout<<is_weak_linked<foo>()<<std::endl;
    std::cout<<(foo==default_foo)<<std::endl;
}