在库中隐藏非 API 符号

Hiding non-API symbols in library

假设我有一个库 foo,它包含模块 fooutil 以及具有以下源代码树:

foo/
    foo.c
    foo.h
    util.c
    util.h

库的 public API 在 foo.h 中定义,所有全局标识符都以 foo_ 为前缀或 util_。模块 util 仅由 foo 使用。为了防止名称与其他名为 util 的模块发生冲突,我想创建一个(静态)库,其中只有来自模块 foo 的标识符可见。我该怎么做?

编辑:我在互联网上进行了相当广泛的搜索,但令人惊讶的是,这似乎是计算机科学中未解决的问题之一。

可能还有其他可能的方法,但这里有一个:

您可以考虑将文件 util.c 包含在 foo.c 中并将所有实用程序函数/全局变量设为静态。即:

#include "util.c"
// ...

这与 *.h 文件的工作方式相同,它只是将整个源代码移植到 foo.c,嵌套 util.c 并使所有静态数据可用。

执行此操作时,我将文件重命名为 .inc(即 util.c => util.inc)...

#include "util.inc"
// ...

...这是我在某处找到的旧约定,尽管它可能与汇编程序文件冲突,因此您必须自行判断。

编辑

另一种方法可能需要特定于链接器的指令。例如,this SO answer details GNU's ld to achieve this goal。还有其他方法,列在同一个线程中。

以下是特定于 GCC 的。

您可以用

标记每个效用函数
__attribute__((visibility ("hidden")))

这将防止它被链接到另一个共享对象。

您可以将其应用到一系列声明中,方法是用

包围它们
#pragma GCC visibility push(hidden)
/* ... */
#pragma GCC visibility pop

或在编译对象时使用-fvisibility=hidden,这适用于没有明确可见性的声明(例如,既不是__attribute__((visibility))也不是#pragma GCC visibility)。

util.h中的每个变量和函数声明之前,定义一个宏常量,通过添加库前缀foo_来重命名声明的标识符,例如

#define util_x foo_util_x
extern int util_x;   

#define util_f foo_util_f
void util_f(void);

...

有了这些定义,不需要更改代码的其他部分,并且目标文件 util.o 中的所有全局符号都将以 foo_ 为前缀。这意味着不太可能发生名称冲突。