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.
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).
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 .
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.
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.
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
.
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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With