静态函数和变量在共享库中导出
Static function and variable get exported in shared library
到目前为止,我假设 C 中具有静态链接的对象(即静态函数和静态变量)不会与其他编译单元(即 .c
中的其他对象(静态或外部链接)发生冲突文件),所以我使用 "short" 名称作为内部辅助函数的名称,而不是在所有内容前加上库名称作为前缀。最近,由于名称与另一个共享库的导出函数发生冲突,我的库的用户遇到了崩溃。经过调查,我发现我的几个静态函数是共享库符号 table 的一部分。由于它发生在几个 GCC 主要版本中,我假设我遗漏了一些东西(这样一个主要错误会被注意到并修复)。
我设法将其简化为以下最小示例:
#include <stdbool.h>
#include <stdlib.h>
bool ext_func_a(void *param_a, char const *param_b, void *param_c);
bool ext_func_b(void *param_a);
static bool bool_a, bool_b;
static void parse_bool_var(char *doc, char const *var_name, bool *var)
{
char *var_obj = NULL;
if (!ext_func_a(doc, var_name, &var_obj)) {
return;
}
*var = ext_func_b(var_obj);
}
static void parse_config(void)
{
char *root_obj = getenv("FOO");
parse_bool_var(root_obj, "bool_a", &bool_a);
parse_bool_var(root_obj, "bool_b", &bool_b);
}
void libexample_init(void)
{
parse_config();
}
静态变量bool_a
和静态函数parse_bool_var
在目标文件和共享库的符号table中均可见:
$ gcc -Wall -Wextra -std=c11 -O2 -fPIC -c -o example.o example.c
$ objdump -t example.o|egrep 'parse_bool|bool_a'
0000000000000000 l O .bss 0000000000000001 bool_a
0000000000000000 l F .text 0000000000000050 parse_bool_var
$ gcc -shared -Wl,-soname,libexample.so.1 -o libexample.so.1.1 x.o -fPIC
$ nm libexample.so.1.1 |egrep 'parse_bool|bool_a'
0000000000200b79 b bool_a
0000000000000770 t parse_bool_var
我已经深入研究了 C11、Ulrich Drepper 的 "How to Write Shared Libraries" 和其他一些解释符号可见性的来源,但我仍然不知所措。为什么 bool_a
和 parse_bool_var
最终出现在动态符号 table 中,即使它们被声明为 static
?
nm
输出第二列中的小写字母表示它们是本地的(如果它们是大写字母,那就另当别论了)。
这些符号不会与同名和 AFAIK 的其他符号冲突,基本上仅用于调试目的。
本地符号也不会进入动态符号 table(printable 与 nm -D
但仅在共享库中)并且它们 strip
pable 与导出符号(上nm
输出第二列中的大小写字母)不是动态的。
(正如您从 Drepper 的如何编写共享库中了解到的,您可以使用 -fvisibility=(default|hidden)
(不应使用受保护的)和可见性属性来控制可见性。)
到目前为止,我假设 C 中具有静态链接的对象(即静态函数和静态变量)不会与其他编译单元(即 .c
中的其他对象(静态或外部链接)发生冲突文件),所以我使用 "short" 名称作为内部辅助函数的名称,而不是在所有内容前加上库名称作为前缀。最近,由于名称与另一个共享库的导出函数发生冲突,我的库的用户遇到了崩溃。经过调查,我发现我的几个静态函数是共享库符号 table 的一部分。由于它发生在几个 GCC 主要版本中,我假设我遗漏了一些东西(这样一个主要错误会被注意到并修复)。
我设法将其简化为以下最小示例:
#include <stdbool.h>
#include <stdlib.h>
bool ext_func_a(void *param_a, char const *param_b, void *param_c);
bool ext_func_b(void *param_a);
static bool bool_a, bool_b;
static void parse_bool_var(char *doc, char const *var_name, bool *var)
{
char *var_obj = NULL;
if (!ext_func_a(doc, var_name, &var_obj)) {
return;
}
*var = ext_func_b(var_obj);
}
static void parse_config(void)
{
char *root_obj = getenv("FOO");
parse_bool_var(root_obj, "bool_a", &bool_a);
parse_bool_var(root_obj, "bool_b", &bool_b);
}
void libexample_init(void)
{
parse_config();
}
静态变量bool_a
和静态函数parse_bool_var
在目标文件和共享库的符号table中均可见:
$ gcc -Wall -Wextra -std=c11 -O2 -fPIC -c -o example.o example.c
$ objdump -t example.o|egrep 'parse_bool|bool_a'
0000000000000000 l O .bss 0000000000000001 bool_a
0000000000000000 l F .text 0000000000000050 parse_bool_var
$ gcc -shared -Wl,-soname,libexample.so.1 -o libexample.so.1.1 x.o -fPIC
$ nm libexample.so.1.1 |egrep 'parse_bool|bool_a'
0000000000200b79 b bool_a
0000000000000770 t parse_bool_var
我已经深入研究了 C11、Ulrich Drepper 的 "How to Write Shared Libraries" 和其他一些解释符号可见性的来源,但我仍然不知所措。为什么 bool_a
和 parse_bool_var
最终出现在动态符号 table 中,即使它们被声明为 static
?
nm
输出第二列中的小写字母表示它们是本地的(如果它们是大写字母,那就另当别论了)。
这些符号不会与同名和 AFAIK 的其他符号冲突,基本上仅用于调试目的。
本地符号也不会进入动态符号 table(printable 与 nm -D
但仅在共享库中)并且它们 strip
pable 与导出符号(上nm
输出第二列中的大小写字母)不是动态的。
(正如您从 Drepper 的如何编写共享库中了解到的,您可以使用 -fvisibility=(default|hidden)
(不应使用受保护的)和可见性属性来控制可见性。)