Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I make a function have library-internal linkage?

For example, if I have two files foo.c and bar.o, and foo.c contains a function foo() that references a function bar() in bar.o:

int foo(int x) { x = bar(x); /* ... */ }

How can I compile a static or dynamic library that exposes foo(), but does not expose bar()? In other words, I want bar() to be linked only inside the library.

like image 278
Matt Avatar asked Feb 18 '15 16:02

Matt


People also ask

What is internal linkage?

Internal linkage refers to everything only in scope of a translation unit. External linkage refers to things that exist beyond a particular translation unit. In other words, accessible through the whole program, which is the combination of all translation units (or object files).

What is internal linkage in C?

Internal Linkage: An identifier implementing internal linkage is not accessible outside the translation unit it is declared in. Any identifier within the unit can access an identifier having internal linkage. It is implemented by the keyword static .

What are the types of linkages internal and external?

1. External linkage: It means that the variable could be defined outside the file you are working on. 2. Internal linkage: It means that the variable must either be defined in any of the included libraries, or in the same file.

What are the types of linkages * 2 points internal and external external internal and none external and none internal?

Explanation: External Linkage-> means global, non-static variables and functions. Internal Linkage-> means static variables and functions with file scope. None Linkage-> means Local variables.


1 Answers

With standard C you can only either export the function or not, there is no “export only to these files” option. So basically you would have to move bar() to foo.c and declare it as static. If you wish to keep the file separate, an ugly hack would be to #include it from foo.c (and not compile bar.o)…

With tools outside the scope of standard C you can remove the public export from the library at or after linking. Some linker solutions are shown below, and with GCC and clang (in cases where you can modify the code) you can hide a function by prefixing it with the non-standard attribute: __attribute__ ((visibility ("hidden"))) – the compilation-unit-wide equivalent of this would be the option -fvisibility=hidden when compiling, e.g., bar.c.

C workaround

If you can edit the C code freely, a workaround in standard C would be to make bar() static within bar.c and deliver a function pointer to it for use in foo() through some means, e.g., export a pointer to a struct containing the function pointer (and any other “private” data), and do not expose the details of the struct outside of a private header used only by your library.

For example:

In bar.h (private, do not share with the library's user):

struct bar_private_struct { int (*bar)(int); };
extern struct bar_private_struct *bar_functions;

In bar.c:

#include "bar.h"
static int bar (int x) { /* … */ return x; }
static struct bar_private_struct functions = { bar };
struct bar_private_struct *bar_functions = &functions;

In foo.c:

#include "bar.h"
int foo (int x) { x = bar_functions->bar(x); /* … */ }

In this solution there will be an exported pointer called bar_functions, but no details about the data/functions pointed to are revealed through this export. Without access to bar.h the user of the library would have to reverse engineer the contents to call the “private” functions correctly. In case of multiple “private” functions this approach can also condense them to a single exported pointer, removing clutter from the list of exports.

Linker solutions

Exploring specific linkers, I found a way to exclude specific symbols from a dynamic library:

With GNU ld, create a version script, such as libfoobar.version:

FOOBAR {
    global: *;
    local: bar;
};

Invocation through gcc:

gcc -shared -o libfoobar.so foo.o bar.o -Wl,-version-script=libfoobar.version

With the clang ld (on OS X) create a list of unexported symbols, such as unexported (one symbol per line):

_bar

Invocation through clang:

clang -shared -o libfoobar.dylib foo.o bar.o -Wl,-unexported_symbols_list,unexported

In both cases the function bar is hidden and externally uncallable but foo remains operational (and calls bar internally), even though both had the same external visibility in their respective source (and object) files.

Test code, foo.c:

int bar(int x);
int foo (int x) { return bar(x) * 3; }

bar.c:

int bar (int x) { return x * 2; }

main.c (link against the library before removing the export for bar):

#include <stdio.h>
int foo(int x);
int bar(int x);
int main () {
    (void) printf("foo(2) = %d\n", foo(2));
    (void) printf("bar(2) = %d\n", bar(2));
    return 0;
}

Test case:

# before unexporting bar:
$ nm -gU libfoobar.dylib
0000000000000f70 T _bar
0000000000000f50 T _foo
$ ./main
foo(2) = 12
bar(2) = 4

# after unexporting bar:
$ nm -gU libfoobar.dylib
0000000000000f50 T _foo
$ ./main
foo(2) = 12
dyld: lazy symbol binding failed: Symbol not found: _bar
like image 113
Arkku Avatar answered Sep 30 '22 05:09

Arkku