4
votes

So far I've assumed that objects with static linkage (i.e. static functions and static variables) in C do not collide with other objects (of static or external linkage) in other compilation units (i.e. .c files) so I've used "short" names for internal helper functions rather than prefixing everything with the library name. Recently a user of my library experienced a crash due to a name collision with an exported function from another shared library. On investigation it turned out that several of my static functions are part of the symbol table of the shared library. Since it happens with several GCC major versions I assume I'm missing something (such a major bug would be noticed and fixed).

I managed to get it down to the following minimum example:

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

Both the static variable bool_a and the static function parse_bool_var are visible in the symbol table of the object file and the shared library:

$ 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

I've dived into C11, Ulrich Drepper's "How to Write Shared Libraries" and a couple of other sources explaining visibility of symbols, but I'm still at a loss. Why are bool_a and parse_bool_var ending up in the dynamic symbol table even though they're declared static?

1

1 Answers

3
votes

The lower case letter in the second column of nm output means they're local (if they were upper case, it would be a different story). Those symbols won't conflict with other symbols of the same name and AFAIK, are basically there only for debugging purposes. Local symbols won't go into the dynamic symbol table (printable with nm -D but only in shared libraries) either and they are strippable along with exported symbols (upper case letters in the second column of nm output) that aren't dynamic.

(As you will have learned from Drepper's How to Write Shared Libraries, you can control visibility with -fvisibility=(default|hidden) (shouldn't use protected) and with visibility attributes.)