Reputation: 9416
Consider 3 C source files:
/* widgets.c */
void widgetTwiddle ( struct widget * w ) {
utilityTwiddle(&w->bits, 1);
}
and
/* wombats.c */
void wombatTwiddle ( struct wombat * w ) {
utilityTwiddle(&w->bits, 1);
}
and
/* utility.c */
void utilityTwiddle ( int * bitsPtr, int bits ) {
*bitsPtr ^= bits;
}
which get compiled and put in a library (say, either libww.a or libww.so).
Is there a way to make utilityTwiddle()
visible and usable by the other two library members, but not be visible to to those who link to the library? That is, given this:
/* appl.c */
extern void utilityTwiddle ( int * bitsPtr, int bits );
int main ( void ) {
int bits;
utilityTwiddle(&bits, 1);
return 0;
}
and
cc -o appl appl.c -lww
it would fail to link because utilityTwiddle()
is not visible to appl.c
. And, consequently appl.c
would be free to define its own utilityTwiddle
function or variable.
[EDIT] And hopefully obviously, we would like this to work:
/* workingappl.c */
extern void wombatTwiddle ( struct wombat * wPtr );
int main ( void ) {
struct wombat w = { .bits = 0 };
wombatTwiddle(&w);
return 0;
}
This Limiting visibility of symbols when linking shared libraries seems related, but it doesn't seem to address whether the symbols suppressed are available to other library members.
[EDIT2] I have sort-of figured out a way to do it without modifying the C source. Add a map file:
/* utility.map */
{ local: *; };
and then do:
$ gcc -shared -o utility.so utility.c -fPIC -Wl,--version-script=utility.map
gives us a dynamic symbol table w/o utilityTwiddle
:
$ nm -D utility.so
w _Jv_RegisterClasses
w __cxa_finalize
w __gmon_start__
but it's not clear to me how to effectively go from this to building a shared library with all three source files. If I put all three source files on the command line, the symbols from all three are hidden. If there is a way to incrementally build the shared library, I could have two simple map files (one to export nothing, one to export everything). Is this doable or is the only option something like this:
/* libww.map */
{ global: list; of; all; symbols; to; export; local: *; };
and
$ gcc -shared -o libww.so *.c -fPIC -Wl,--version-script=libww.map
[EDIT3] Boy, it sure seems like this also ought to be possible without using shared libraries. If I do:
ld -r -o wboth.o widgets.o wombats.o utility.o
I can see that the linker has resolved to location of utilityTwiddle()
where widgetTwiddle()
and wombatTwiddle()
call it:
$ objdump -d wboth.o
0000000000000000 <widgetTwiddle>:
0: be 01 00 00 00 mov $0x1,%esi
5: e9 00 00 00 00 jmpq a <widgetTwiddle+0xa>
0000000000000010 <wombatTwiddle>:
10: be 01 00 00 00 mov $0x1,%esi
15: e9 00 00 00 00 jmpq 1a <wombatTwiddle+0xa>
0000000000000020 <utilityTwiddle>:
20: 31 37 xor %esi,(%rdi)
22: c3 retq
but utilityTwiddle
remains as a symbol:
$ nm wboth.o
U _GLOBAL_OFFSET_TABLE_
0000000000000020 T utilityTwiddle
0000000000000000 T widgetTwiddle
0000000000000010 T wombatTwiddle
and so if you could find a way to remove that symbol, you could still successfully link against wboth.o
(I have tested this by binary editing wboth.o) and it still links and runs fine:
$ nm wboth.o
U _GLOBAL_OFFSET_TABLE_
0000000000000000 T widgetTwiddle
0000000000000010 T wombatTwiddle
0000000000000020 T xtilityTwiddle
Upvotes: 3
Views: 1631
Reputation: 61307
You can't achieve what you want by creating a static library libww.a
. If you
read static-libraries you
will see why. A static library can be used to offer a bunch N of object files
to the linker, from which it will extract k (possibly = 0) that it needs and link them. So you
can't achieve anything by linking with the static library that you can't achieve by
linking those k object files directly. For linkage purposes, static libraries don't really
exist.
But shared libraries really do exist for linkage purposes and the global symbols exposed by shared library acquire an additional property, dynamic visibility, that exists precisely for your purpose. The dynamically visible symbols are a subset of the global symbols: they are the global symbols that are visible for dynamic linkage, i.e. for linking the shared library with something else (a program or another shared library).
Dynamic visibility is not an attribute that source language standards say anything about, because they don't say anything about dynamic linkage. So controlling the dynamic visibility of symbols has to be done in an individual way by a toolchain that does support dynamic linkage. GCC does it with the compiler-specific declaration qualifier1:
__attribute__((visibility("default|hidden|protected|internal")
and/or the compiler switch2:
-fvisibility=default|hidden|protected|internal
Here's a demo of how build libww.so
so that utilityTwiddle
is hidden from
clients of the library while wombatTwiddle
and widgetTwiddle
are visible.
Your source code needs fleshed out a bit in one way or another to compile. Here's a first cut:
ww.h (1)
#ifndef WW_H
#define WW_H
struct widget {
int bits;
};
struct wombat {
int bits;
};
extern void widgetTwiddle ( struct widget * w );
extern void wombatTwiddle ( struct wombat * w );
#endif
utility.h (1)
#ifndef UTILITY_H
#define UTILITY_H
extern void utilityTwiddle ( int * bitsPtr, int bits );
#endif
utility.c
#include "utility.h"
void utilityTwiddle ( int * bitsPtr, int bits ) {
*bitsPtr ^= bits;
}
wombats.c
#include "utility.h"
#include "ww.h"
void wombatTwiddle ( struct wombat * w ) {
utilityTwiddle(&w->bits, 1);
}
widgets.c
#include "utility.h"
#include "ww.h"
void widgetTwiddle ( struct widget * w ) {
utilityTwiddle(&w->bits, 1);
}
Compile all the *.c
files to *.o
files in the default manner:
$ gcc -Wall -Wextra -c widgets.c wombats.c utility.c
and link them into libww.so
in the default manner:
$ gcc -shared -o libww.so widgets.o wombats.o utility.o
Here are *Twiddle
symbols in the global symbol table of libww.so
$ nm libww.so | egrep '*Twiddle'
000000000000063a T utilityTwiddle
00000000000005fa T widgetTwiddle
000000000000061a T wombatTwiddle
This is just the sum of the global (extern
) *Twiddle
symbols that went into the linkage
of libww.so
from the object files. They're all defined (T
), as they'd have to be
if the library itself was to be linked without external *Twiddle
dependencies.
Any ELF file (object file, shared library, program) has a global symbol table, but
a shared library also has a dynamic symbol table. Here are the *Twiddle
symbols in the dynamic symbol table of libww.so
:
$ nm -D libww.so | egrep '*Twiddle'
000000000000063a T utilityTwiddle
00000000000005fa T widgetTwiddle
000000000000061a T wombatTwiddle
They're exactly the same. That's what we want to change, so that utilityTwiddle
disappears.
Here's a second cut. We have to change the source code slightly.
utility.h (2)
#ifndef UTILITY_H
#define UTILITY_H
extern void utilityTwiddle ( int * bitsPtr, int bits ) __attribute__((visibility("hidden")));
#endif
Then recompile and relink, just as before:
$ gcc -Wall -Wextra -c widgets.c wombats.c utility.c
$ gcc -shared -o libww.so widgets.o wombats.o utility.o
Here are the *Twiddle
symbols now in the global symbol table:
$ nm libww.so | egrep '*Twiddle'
000000000000063a T utilityTwiddle
00000000000005fa T widgetTwiddle
000000000000061a T wombatTwiddle
No change there. And here are the *Twiddle
symbols now in the dynamic symbol table:
$ nm -D libww.so | egrep '*Twiddle'
00000000000005aa T widgetTwiddle
00000000000005ca T wombatTwiddle
utilityTwiddle
is gone.
Here's a third cut that achieves the same result differently. It's more long-winded
but illustrates how the -fvisibility
compiler option plays. This time,
utility.h
is again as per (1), but ww.h
is:
ww.h (2)
#ifndef WW_H
#define WW_H
struct widget {
int bits;
};
struct wombat {
int bits;
};
extern void widgetTwiddle ( struct widget * w ) __attribute__((visibility("default")));
extern void wombatTwiddle ( struct wombat * w ) __attribute__((visibility("default")));
#endif
Now we recompile like so:
$ gcc -Wall -Wextra -fvisibility=hidden -c widgets.c wombats.c utility.c
We're telling the compiler to annotate every global symbol it generates with
__attribute__((visibility("hidden")))
unless there is a countervailing
__attribute__((visibility("...")))
explicitly in the source code.
Then relink the shared library just as previously. Again we see in the global symbol table:
$ nm libww.so | egrep '*Twiddle'
00000000000005ea t utilityTwiddle
00000000000005aa T widgetTwiddle
00000000000005ca T wombatTwiddle
and in the dynamic symbol table:
$ nm -D libww.so | egrep '*Twiddle'
00000000000005aa T widgetTwiddle
00000000000005ca T wombatTwiddle
Finally, to show that removing utilityTwiddle
from the dynamic symbol table
of libww.so
in one of these ways really does hide it from clients linking with
libww.so
. Here's a program that wants to call all the *Twiddle
s:
prog.c
#include <ww.h>
extern void utilityTwiddle ( int * bitsPtr, int bits );
int main()
{
struct widget wi = {1};
struct wombat wo = {2};
widgetTwiddle(&wi);
wombatTwiddle(&wo);
utilityTwiddle(&wi.bits,wi.bits);
return 0;
}
We have no problem building it like:
$ gcc -Wall -Wextra -I. -c prog.c
$ gcc -o prog prog.o utility.o widgets.o wombats.o
But nobody can build it like:
$ gcc -Wall -Wextra -I. -c prog.c
$ gcc -o prog prog.o -L. -lww
prog.o: In function `main':
prog.c:(.text+0x4a): undefined reference to `utilityTwiddle'
collect2: error: ld returned 1 exit status
Be clear that -fvisibility
is a compilation option, not a linkage option.
You pass it to your compilation commands and not to your linkage commands,
because it's effect is the same as sprinkling __attribute__((visibility("...")))
qualifiers over the declarations in your source code, which the compiler has
to honour by injecting linkage information into the object files that it generates. If
you care to see the evidence of that you can just repeat that last compilation
and request that the assembly files be saved:
$ gcc -Wall -Wextra -fvisibility=hidden -c widgets.c wombats.c utility.c -save-temps
Then compare say:
widgets.s
.file "widgets.c"
.text
.globl widgetTwiddle
.type widgetTwiddle, @function
widgetTwiddle:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16, %rsp
movq %rdi, -8(%rbp)
movq -8(%rbp), %rax
movl $1, %esi
movq %rax, %rdi
call utilityTwiddle@PLT
nop
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size widgetTwiddle, .-widgetTwiddle
.ident "GCC: (Ubuntu 7.3.0-16ubuntu3) 7.3.0"
.section .note.GNU-stack,"",@progbits
with:
utility.s
.file "utility.c"
.text
.globl utilityTwiddle
.hidden utilityTwiddle
^^^^^^^^^^^^^^^^^^^^^^
.type utilityTwiddle, @function
utilityTwiddle:
...
...
[2] See the GCC Manual, 3.16 Options for Code Generation Conventions.
Upvotes: 5