【问题标题】:Dead code removal if implementation is overwritten如果实现被覆盖,则删除死代码
【发布时间】:2016-01-14 15:37:31
【问题描述】:

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

int mylib_set_sha256_impl( /* function pointers */ );

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

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

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

【问题讨论】:

  • 也许看看weak aliasing
  • 弱别名是主要编译器供应商(即 MSVC、gcc、clang、Intel C...)支持的东西
  • 肯定是 GCC,其他的可能也是。
  • 我会对此进行调查。感谢您的提示。
  • 如果用户提供的函数指针将在运行时解析(通过调用 API),我认为链接器不太可能省略默认实现。

标签: c c99 dead-code


【解决方案1】:

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

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

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

  1. 仅在用户可调用的可选default_init函数中引用可替换函数。

  2. 不得在代码中的任何位置调用default_init,也不得在default_init 之外的任何位置引用任何函数。

  3. 将可替换函数和 init 函数放入至少一个 .c 或不用于任何其他代码的程序集文件中。

为了让您的用户替换所有函数,他们根本不需要调用default_init 函数。如果您希望函数可以一个一个地替换,您还必须:

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

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

实际上,您所做的并不是“覆盖”任何实现,而是根本不使用它。

示例(包括为清楚起见省略的防护):

// 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();
}

【讨论】:

  • 我基本上得出了这个结论。有一个set_sha256_defaults() 很好,但是我的库有很多这样的覆盖(即其他哈希实现、RNG、HMAC、CRC 等),这会用一堆*_defaults() 函数乱扔API。我希望有一个更优雅的解决方案,但它开始看起来好像一个可能并不比你发布的那个更好。
  • 唯一的“优雅”是默认函数没有_default 后缀。否则,使用弱符号只会带来一个好处:您根本不需要使用函数指针。具有相同名称的用户提供的函数将简单地覆盖您的函数。我对这是否是一件好事感到矛盾。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-06-06
  • 1970-01-01
  • 2014-12-31
  • 2018-01-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多