如果实现被覆盖,则删除死代码

Dead code removal if implementation is overwritten

我正在编写一个提供 sha256 实现的库。该库将提供给可能希望提供针对其平台优化的自己的 sha256 函数的供应商。因此,这个库的 API 允许客户端将函数指针传递给他们的 sha256 代码。

int mylib_set_sha256_impl( /* function pointers */ );

今后,所有算法都将使用内部提供的函数指针,而不是库提供的库存 sha256 代码。

问题是:如何在 link 时间内促进死代码的删除,从而删除此库中的默认 sha256 实现?

这是一个 API 设计问题,也是一个编译器优化问题。

对于链接器没有特殊要求,不需要考虑弱别名。

基本规则是:只要用户代码不引用您链接到库中的给定 .c.s 文件中的任何符号,该文件的内容就不会t 最终出现在可执行文件中。

在您描述的场景中,您的函数可能永远不会成为死代码,因为您的 "live" 代码可能会引用它们的地址来设置默认函数指针值。为确保不会发生这种情况,您必须执行以下操作:

  1. 仅在 user-callable 可选 default_init 函数中引用可替换函数。

  2. 绝不能在代码中的任何地方调用 default_init,也绝不能在 default_init.

    [=57 以外的任何地方引用任何函数=]
  3. 将可替换函数和 init 函数放入至少一个 .c 或未用于任何其他代码的汇编文件中。

为了让您的用户替换 所有 函数,他们只需永远不要调用 default_init 函数。如果您希望函数可替换 one-by-one,您还必须:

  1. 每个可替换函数都在其自己的 .c 文件中。

  2. 让用户不直接调用 default_init,而是将所需的默认或 user-provided 实现传递给您的 init 函数。

实际上,您所做的并不是 "overwriting" 任何实现,而只是根本不使用它。

示例(包括为清楚起见而省略的守卫):

// api.h
void api_fun1(void);
void api_fun2(void);
void api_default_init(void);
void api_user_init(void (*f1)(void), void (*f2)(void));
void api_use_funs(void);

// api_internal.h
extern void (*api_f1)(void);
extern void (*api_f2)(void);    

// common.c
#include "api.h"
#include "api_internal.h"
void (*api_f1)(void);
void (*api_f2)(void);

void api_user_init(void (*f1)(void), void (*f2)(void)) {
  api_f1 = f1;
  api_f2 = f2;
}

void api_use_funs(void) {
  api_f1();
  api_f2();
}

// api_fun1.c
#include "api.h"
void api_fun1(void) {}

// api_fun2.c
#include "api.h"
void api_fun2(void) {}

// api_default_init.c
#include "api.h"
#include "api_internal.h"

void api_default_init(void) {
  api_f1 = api_fun1;
  api_f2 = api_fun2;
}

假设用户想要用他们自己的覆盖 api_fun2

// main.c
#include "api.h"
#include <stdio.h>

void my_fun2() {
  printf("%s\n", __FUNCTION__);
}

int main() {
  api_user_init(api_fun1, my_fun2);
  api_use_funs();
}