I only cares about default/hidden visibility.
The .o file is not compiled with IPO.
How to find out a symbol's visibility in the .o file?
Why I have to find it out from .o file:
On certain platform, I ran into a gcov.a which appears problematic.
And I have to figure out where it is wrong.
1. I cannot know how exactly the toolchain is configured and built.
2. As part of libgcc magic, figure it out from source code is extremely difficult.
You can find out the visibility of a symbol in an object file by examining
the file's symbol table with objdump -t. If the symbol is hidden it
will be labelled .hidden in the 6th field of its objdump record, followed
by its name. If its visibility is default there will be no such label and
the 6th field will be the name (the usual case). For example:
foo.c (default visibility)
#include <stdio.h>
void foo(void)
{
puts("foo");
}
Compile and examine:
$ gcc -c -fPIC foo.c
$ objdump -t foo.o | grep foo
foo.o: file format elf64-x86-64
0000000000000000 l df *ABS* 0000000000000000 foo.c
0000000000000000 g F .text 0000000000000013 foo
foo.c (hidden visibility)
#include <stdio.h>
__attribute__ ((visibility ("hidden"))) void foo(void)
{
puts("foo");
}
Recompile and re-examine:
$ gcc -c -fPIC foo.c
$ objdump -t foo.o | grep foo
foo.o: file format elf64-x86-64
0000000000000000 l df *ABS* 0000000000000000 foo.c
0000000000000000 g F .text 0000000000000013 .hidden foo
Related
pqy#localhost ~/src/test/a $ cat m.c
#include <stdio.h>
int aaaaa __attribute__ ((weak)) =8;
int main(void){
printf("%d\n", aaaaa);
return 0;
}
pqy#localhost ~/src/test/a $ cat lib.c
int aaaaa = 5;
pqy#localhost ~/src/test/a $ gcc lib.c -fPIC -shared -o libb.so;gcc m.c -o m -L. -lb -Wl,-rpath=$PWD;./m
8
Above is my code and test result. I am confused why it does not work as expected.
Also try function, not work ether. Below is the test result.
pqy#localhost ~/src/test/a $ cat lib.c
int fun() {
return 5;
}
pqy#localhost ~/src/test/a $ cat m.c
#include <stdio.h>
__attribute__((weak)) int fun() {
return 8;
}
int main(void){
printf("%d\n", fun());
return 0;
}
pqy#localhost ~/src/test/a $ gcc lib.c -fPIC -shared -o libb.so;gcc m.c -O0 -o m -L. -lb -Wl,-rpath=$PWD;./m
8
pqy#localhost ~/src/test/a $ ldd m
linux-vdso.so.1 (0x00007ffd819ec000)
libb.so => /home/pqy/src/test/a/libb.so (0x00007f7226738000)
libc.so.6 => /lib64/libc.so.6 (0x00007f7226533000)
/lib64/ld-linux-x86-64.so.2 (0x00007f7226744000)
pqy#localhost ~/src/test/a $
At bottom what you have observed here is just the fact that the linker will not
resolve a symbol dynamically if it can resolve it statically. See:
main.c
extern void foo(void);
extern void need_dynamic_foo(void);
extern void need_static_foo(void);
int main(void){
foo();
need_dynamic_foo();
need_static_foo();
return 0;
}
dynamic_foo.c
#include <stdio.h>
void foo(void)
{
puts("foo (dynamic)");
}
void need_dynamic_foo(void)
{
puts(__func__);
}
static_foo.c
#include <stdio.h>
void foo(void)
{
puts("foo (static)");
}
void need_static_foo(void)
{
puts(__func__);
}
Compile the sources so:
$ gcc -Wall -c main.c static_foo.c
$ gcc -Wall -fPIC -c dynamic_foo.c
Make a shared library:
$ gcc -shared -o libfoo.so dynamic_foo.o
And link a program:
$ gcc -o prog main.o static_foo.o libfoo.so -Wl,-rpath=$PWD
It runs like:
$ ./prog
foo (static)
need_dynamic_foo
need_static_foo
So foo and need_static_foo were statically resolved to the definitions from static_foo.o and
the definition of foo from libfoo.so was ignored, despite the fact that libfoo.so
was needed and provided the definition of need_dynamic_foo. It makes no difference
if we change the linkage order to:
$ gcc -o prog main.o libfoo.so static_foo.o -Wl,-rpath=$PWD
$ ./prog
foo (static)
need_dynamic_foo
need_static_foo
It also makes no difference if we replace static_foo.c with:
static_weak_foo.c
#include <stdio.h>
void __attribute__((weak)) foo(void)
{
puts("foo (static weak)");
}
void need_static_foo(void)
{
puts(__func__);
}
Compile that and relink:
$ gcc -Wall -c static_weak_foo.c
$ gcc -o prog main.o libfoo.so static_weak_foo.o -Wl,-rpath=$PWD
$ ./prog
foo (static weak)
need_dynamic_foo
need_static_foo
Although the definition of foo in static_weak_foo.c is now declared weak,
the fact that foo can be statically resolved to this definition
still preempts any need to resolve it dynamically.
Now if we write another source file containing another strong definition of
foo:
static_strong_foo.c
#include <stdio.h>
void foo(void)
{
puts("foo (static strong)");
}
and compile it and link as follows:
$ gcc -Wall -c static_strong_foo.c
$ gcc -o prog main.o static_weak_foo.o libfoo.so static_strong_foo.o -Wl,-rpath=$PWD
we see:
$ ./prog
foo (static strong)
need_dynamic_foo
need_static_foo
Now, libfoo.so still provides the definition of need_dynamic_foo, because there
is no other; static_weak_foo.o still provides the only definition of need_static_foo,
and the definition of foo in libfoo.so is still ignored because the symbol
can be statically resolved.
But in this case there are two definitions of foo in different files that are
available to resolve it statically: the weak definition in static_weak_foo.o and
the strong definition in static_strong_foo.o. By the linkage rules that you are
familiar with, the strong definition wins.
If both of these statically linked definitions of foo were strong, there would of course be a
multiple definition error, just like:
$ gcc -o prog main.o static_foo.o libfoo.so static_strong_foo.o -Wl,-rpath=$PWD
static_strong_foo.o: In function `foo':
static_strong_foo.c:(.text+0x0): multiple definition of `foo'
static_foo.o:static_foo.c:(.text+0x0): first defined here
collect2: error: ld returned 1 exit status
in which the dynamic definition in libfoo.so plays no part. So you can
be guided by this practical principle: The rules you are familiar with for arbitrating
between weak and strong definitions of the same symbol in a linkage only apply
to rival definitions which would provoke a multiple definition error in the absence
of the weak attribute.
The symbol is resolved at link stage, during the link stage only the weak symbol aaaaa = 8 is visible.
If the symbol can be resolved in the link stage, it won't generate a relocation entry, then nothing will happen at load stage
There are no aaaaa in the relocation table:
% objdump -R m
m: file format elf64-x86-64
DYNAMIC RELOCATION RECORDS
OFFSET TYPE VALUE
0000000000003dc8 R_X86_64_RELATIVE *ABS*+0x0000000000001130
0000000000003dd0 R_X86_64_RELATIVE *ABS*+0x00000000000010f0
0000000000004028 R_X86_64_RELATIVE *ABS*+0x0000000000004028
0000000000003fd8 R_X86_64_GLOB_DAT _ITM_deregisterTMCloneTable
0000000000003fe0 R_X86_64_GLOB_DAT __libc_start_main#GLIBC_2.2.5
0000000000003fe8 R_X86_64_GLOB_DAT __gmon_start__
0000000000003ff0 R_X86_64_GLOB_DAT _ITM_registerTMCloneTable
0000000000003ff8 R_X86_64_GLOB_DAT __cxa_finalize#GLIBC_2.2.5
0000000000004018 R_X86_64_JUMP_SLOT printf#GLIBC_2.2.5
Сompiler or linker builds files from the command line in reverse order. In other words, files with ((weak)) should be located earlier in the command line than dynamic ones.
In Windows, to call a function in a DLL, the function must have an explicit export declaration. For example, __declspec(dllexport) or .def file.
Other than Windows, we can call a function in a .so(shared object file) even if the function has no export declaration. It is much easier for me to make .so than .dll in terms of this.
Meanwhile, I am curious about how non-Windows enables functions defined in .so be called by other programs without having explicit export declaration. I roughly guess that all of the functions in .so file are automatically exported, but I am not sure of it.
An .so file is conventionally a DSO (Dynamic Shared Object, a.k.a shared library) in unix-like OSes. You want to
know how symbols defined in such a file are made visible to the runtime loader
for dynamic linkage of the DSO into the process of some program when
it's executed. That's what you mean by "exported". "Exported" is a somewhat
Windows/DLL-ish term, and is also apt to be confused with "external" or "global",
so we'll say dynamically visible instead.
I'll explain how dynamic visibility of symbols can be controlled in the context of
DSOs built with the GNU toolchain - i.e. compiled with a GCC compiler (gcc,
g++,gfortran, etc.) and linked with the binutils linker ld (or compatible
alternative compiler and linker). I'll illustrate with C code. The mechanics are
the same for other languages.
The symbols defined in an object file are the file-scope variables in the C source code. i.e. variables
that are not defined within any block. Block-scope variables:
{ int i; ... }
are defined only when the enclosing block is being executed and have no permanent
place in an object file.
The symbols defined in an object file generated by GCC are either local or global.
A local symbol can be referenced within the object file where it's defined but
the object file does not reveal it for linkage at all. Not for static linkage.
Not for dynamic linkage. In C, a file-scope variable definition is global
by default and local if it is qualified with the static storage class. So
in this source file:
foobar.c (1)
static int foo(void)
{
return 42;
}
int bar(void)
{
return foo();
}
foo is a local symbol and bar is a global one. If we compile this file
with -save-temps:
$ gcc -save-temps -c -fPIC foobar.c
then GCC will save the assembly listing in foobar.s, and there we can
see how the generated assembly code registers the fact that bar is global and foo is not:
foobar.s (1)
.file "foobar.c"
.text
.type foo, #function
foo:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl $42, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size foo, .-foo
.globl bar
.type bar, #function
bar:
.LFB1:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
call foo
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE1:
.size bar, .-bar
.ident "GCC: (Ubuntu 8.2.0-7ubuntu1) 8.2.0"
.section .note.GNU-stack,"",#progbits
The assembler directive .globl bar means that bar is a global symbol.
There is no .globl foo; so foo is local.
And if we inspect the symbols in the object file itself, with
$ readelf -s foobar.o
Symbol table '.symtab' contains 10 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 FILE LOCAL DEFAULT ABS foobar.c
2: 0000000000000000 0 SECTION LOCAL DEFAULT 1
3: 0000000000000000 0 SECTION LOCAL DEFAULT 2
4: 0000000000000000 0 SECTION LOCAL DEFAULT 3
5: 0000000000000000 11 FUNC LOCAL DEFAULT 1 foo
6: 0000000000000000 0 SECTION LOCAL DEFAULT 5
7: 0000000000000000 0 SECTION LOCAL DEFAULT 6
8: 0000000000000000 0 SECTION LOCAL DEFAULT 4
9: 000000000000000b 11 FUNC GLOBAL DEFAULT 1 bar
the message is the same:
5: 0000000000000000 11 FUNC LOCAL DEFAULT 1 foo
...
9: 000000000000000b 11 FUNC GLOBAL DEFAULT 1 bar
The global symbols defined in the object file, and only the global symbols,
are available to the static linker for resolving references in other object files. Indeed
the local symbols only appear in the symbol table of the file at all for possible
use by a debugger or some other object-file probing tool. If we redo the compilation
with even minimal optimisation:
$ gcc -save-temps -O1 -c -fPIC foobar.c
$ readelf -s foobar.o
Symbol table '.symtab' contains 9 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 FILE LOCAL DEFAULT ABS foobar.c
2: 0000000000000000 0 SECTION LOCAL DEFAULT 1
3: 0000000000000000 0 SECTION LOCAL DEFAULT 2
4: 0000000000000000 0 SECTION LOCAL DEFAULT 3
5: 0000000000000000 0 SECTION LOCAL DEFAULT 5
6: 0000000000000000 0 SECTION LOCAL DEFAULT 6
7: 0000000000000000 0 SECTION LOCAL DEFAULT 4
8: 0000000000000000 6 FUNC GLOBAL DEFAULT 1 bar
then foo disappears from the symbol table.
Since global symbols are available to the static linker, we can link a program
with foobar.o that calls bar from another object file:
main.c
#include <stdio.h>
extern int foo(void);
int main(void)
{
printf("%d\n",bar());
return 0;
}
Like so:
$ gcc -c main.c
$ gcc -o prog main.o foobar.o
$ ./prog
42
But as you've noticed, we do not need to change foobar.o in any way to make
bar dynamically visible to the loader. We can just link it as it is into
a shared library:
$ gcc -shared -o libbar.so foobar.o
then dynamically link the same program with that shared library:
$ gcc -o prog main.o libbar.so
and it's fine:
$ ./prog
./prog: error while loading shared libraries: libbar.so: cannot open shared object file: No such file or directory
...Oops. It's fine as long as we let the loader know where libbar.so is, since my
working directory here isn't one of the search directories that it caches by default:
$ export LD_LIBRARY_PATH=.
$ ./prog
42
The object file foobar.o has a table of symbols as we've seen,
in the .symtab section, including (at least) the global symbols that are available to the static linker.
The DSO libbar.so has a symbol table in its .symtab section too. But it also has a dynamic symbol table,
in it's .dynsym section:
$ readelf -s libbar.so
Symbol table '.dynsym' contains 6 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 NOTYPE WEAK DEFAULT UND __cxa_finalize
2: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_registerTMCloneTable
3: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_deregisterTMCloneTab
4: 0000000000000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__
5: 00000000000010f5 6 FUNC GLOBAL DEFAULT 9 bar
Symbol table '.symtab' contains 45 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
...
...
21: 0000000000000000 0 FILE LOCAL DEFAULT ABS crtstuff.c
22: 0000000000001040 0 FUNC LOCAL DEFAULT 9 deregister_tm_clones
23: 0000000000001070 0 FUNC LOCAL DEFAULT 9 register_tm_clones
24: 00000000000010b0 0 FUNC LOCAL DEFAULT 9 __do_global_dtors_aux
25: 0000000000004020 1 OBJECT LOCAL DEFAULT 19 completed.7930
26: 0000000000003e88 0 OBJECT LOCAL DEFAULT 14 __do_global_dtors_aux_fin
27: 00000000000010f0 0 FUNC LOCAL DEFAULT 9 frame_dummy
28: 0000000000003e80 0 OBJECT LOCAL DEFAULT 13 __frame_dummy_init_array_
29: 0000000000000000 0 FILE LOCAL DEFAULT ABS foobar.c
30: 0000000000000000 0 FILE LOCAL DEFAULT ABS crtstuff.c
31: 0000000000002094 0 OBJECT LOCAL DEFAULT 12 __FRAME_END__
32: 0000000000000000 0 FILE LOCAL DEFAULT ABS
33: 0000000000003e90 0 OBJECT LOCAL DEFAULT 15 _DYNAMIC
34: 0000000000004020 0 OBJECT LOCAL DEFAULT 18 __TMC_END__
35: 0000000000004018 0 OBJECT LOCAL DEFAULT 18 __dso_handle
36: 0000000000001000 0 FUNC LOCAL DEFAULT 6 _init
37: 0000000000002000 0 NOTYPE LOCAL DEFAULT 11 __GNU_EH_FRAME_HDR
38: 00000000000010fc 0 FUNC LOCAL DEFAULT 10 _fini
39: 0000000000004000 0 OBJECT LOCAL DEFAULT 17 _GLOBAL_OFFSET_TABLE_
40: 0000000000000000 0 NOTYPE WEAK DEFAULT UND __cxa_finalize
41: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_registerTMCloneTable
42: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_deregisterTMCloneTab
43: 00000000000010f5 6 FUNC GLOBAL DEFAULT 9 bar
The symbols in the dynamic symbol table are the ones that are dynamically visible -
available to the runtime loader. You
can see that bar appears both in the .symtab and in the .dynsym of libbar.so.
In both cases, the symbol has GLOBAL in the bind ( = binding)
column and DEFAULT in the vis ( = visibility) column.
If you want readelf to show you just the dynamic symbol table, then:
readelf --dyn-syms libbar.so
will do it, but not for foobar.o, because an object file has no dynamic symbol table:
$ readelf --dyn-syms foobar.o; echo Done
Done
So the linkage:
$ gcc -shared -o libbar.so foobar.o
creates the dynamic symbol table of libbar.so, and populates it with symbols
the from global symbol table of foobar.o (and various GCC boilerplate
files that GCC adds to the linkage by defauilt).
This makes it look like your guess:
I roughly guess that all of the functions in .so file are automatically exported
is right. In fact it's close, but not correct.
See what happens if I recompile foobar.c
like this:
$ gcc -save-temps -fvisibility=hidden -c -fPIC foobar.c
Let's take another look at the assembly listing:
foobar.s (2)
...
...
.globl bar
.hidden bar
.type bar, #function
bar:
.LFB1:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
call foo
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
...
...
Notice the assembler directive:
.hidden bar
that wasn't there before. .globl bar is still there; bar is still a global
symbol. I can still statically link foobar.o in this program:
$ gcc -o prog main.o foobar.o
$ ./prog
42
And I can still link this shared library:
$ gcc -shared -o libbar.so foobar.o
But I can no longer dynamically link this program:
$ gcc -o prog main.o libbar.so
/usr/bin/ld: main.o: in function `main':
main.c:(.text+0x5): undefined reference to `bar'
collect2: error: ld returned 1 exit status
In foobar.o, bar is still in the symbol table:
$ readelf -s foobar.o | grep bar
1: 0000000000000000 0 FILE LOCAL DEFAULT ABS foobar.c
9: 000000000000000b 11 FUNC GLOBAL HIDDEN 1 bar
but it is now marked HIDDEN in the vis ( = visibility) column of the output.
And bar is still in the symbol table of libbar.so:
$ readelf -s libbar.so | grep bar
29: 0000000000000000 0 FILE LOCAL DEFAULT ABS foobar.c
41: 0000000000001100 11 FUNC LOCAL DEFAULT 9 bar
But this time, it is a LOCAL symbol. It will not be available to the static
linker from libbar.so - as we saw just now when our linkage failed. And it is no longer in the
dynamic symbol table at all:
$ readelf --dyn-syms libbar.so | grep bar; echo done
done
So the effect of -fvisibility=hidden, when compiling foobar.c, is to make
the compiler annotate .globl symbols as .hidden in foobar.o. Then, when
foobar.o is linked into libbar.so, the linker converts every global hidden
symbol to a local symbol in libbar.so, so that it cannot be used to resolve references
whenever libbar.so is linked with something else. And it does not add the hidden
symbols to the dynamic symbol table of libbar.so, so the runtime loader cannot
see them to resolve references dynamically.
The story so far: When the linker creates a shared library, it adds to the dynamic
symbol table all of the global symbols that are defined in the input object files and are not marked hidden
by the compiler. These become the dynamically visible symbols of the shared library. Global symbols are not
hidden by default, but we can hide them with the compiler option -fvisibility=hidden. The visibility
that this option refers to is dynamic visibility.
Now the ability to remove global symbols from dynamic visibility with -fvisibility=hidden
doesn't look very useful yet, because it seems that any object file we compile with
that option can contribute no dynamically visible symbols to a shared library.
But actually, we can control individually which global symbols defined in an object file
will be dynamically visible and which will not. Let's change foobar.c as follows:
foobar.c (2)
static int foo(void)
{
return 42;
}
int __attribute__((visibility("default"))) bar(void)
{
return foo();
}
The __attribute__ syntax you see here is a GCC language extension
that is used to specify properties of symbols that are not expressible in the standard language - such as dynamic visibility. Microsoft's
declspec(dllexport) is an Microsoft language extension with the same effect as GCC's __attribute__((visibility("default"))),
But for GCC, global symbols defined in an object file will possess __attribute__((visibility("default"))) by default, and you
have to compile with -fvisibility=hidden to override that.
Recompile like last time:
$ gcc -fvisibility=hidden -c -fPIC foobar.c
And now the symbol table of foobar.o:
$ readelf -s foobar.o | grep bar
1: 0000000000000000 0 FILE LOCAL DEFAULT ABS foobar.c
9: 000000000000000b 11 FUNC GLOBAL DEFAULT 1 bar
shows bar with DEFAULT visibility once again, despite -fvisibility=hidden. And if we relink libbar.so:
$ gcc -shared -o libbar.so foobar.o
we see that bar is back in the dynamic symbol table:
$ readelf --dyn-syms libbar.so | grep bar
5: 0000000000001100 11 FUNC GLOBAL DEFAULT 9 bar
So, -fvisibility=hidden tells the compiler to mark a global symbol as hidden
unless, in the source code, we explicitly specify a countervailing dynamic visibility
for that symbol.
That's one way to select precisely the symbols from an object file that we wish
to make dynamically visible: pass -fvisibility=hidden to the compiler, and
individually specify __attribute__((visibility("default"))), in the source code, for just
the symbols we want to be dynamically visible.
Another way is not to pass -fvisibility=hidden to the compiler, and indvidually
specify __attribute__((visibility("hidden"))), in the source code, for just the
symbols that we don't want to be dynamically visible. So if we change foobar.c again
like so:
foobar.c (3)
static int foo(void)
{
return 42;
}
int __attribute__((visibility("hidden"))) bar(void)
{
return foo();
}
then recompile with default visibility:
$ gcc -c -fPIC foobar.c
bar reverts to hidden in the object file:
$ readelf -s foobar.o | grep bar
1: 0000000000000000 0 FILE LOCAL DEFAULT ABS foobar.c
9: 000000000000000b 11 FUNC GLOBAL HIDDEN 1 bar
And after relinking libbar.so, bar is again absent from its dynamic symbol
table:
$ gcc -shared -o libbar.so foobar.o
$ readelf --dyn-syms libbar.so | grep bar; echo Done
Done
The professional approach is to minimize the dynamic API of
a DSO to exactly what is specified. With the apparatus we've discussed,
that means compiling with -fvisibility=hidden and using __attribute__((visibility("default"))) to
expose the specified API. A dynamic API can also be controlled - and versioned - with the GNU linker
using a type of linker script called a version-script: that is a
yet more professional approach.
Further reading:
GCC Wiki: Visibility
GCC Manual: Common Function Attributes -> visibility ("visibility_type")
I went through this The hidden visibility does get applied in my shared libraries but works for static libraries on linux
and
Is there any way to override the -fvisibility=hidden at link time?
but I have follow up question
File: lib_mylib.h
int fun();
int fun1(void);
int fun2(void);
File: lib_mylib.c
#include <stdio.h>
int fun() {
return 2;
}
int fun1(void) {
return 2;
}
int fun2(void) {
return 2;
}
File: my_lib_shared.c
#include "my_lib.h"
int foo(void) {
return fun() + 4;
}
File: my_lib_shared.h
int foo();
File driver.c
/* filename: driver.c */
#include "my_lib.h"
#include "my_lib_shared.h"
#include <stdio.h>
int main() {
int x = foo() ;
return 0;
}
If I execute following steps
1) `gcc -fPIC -fvisibility=hidden -c my_lib.c -o lib_mylib.o`
2) `gcc -fPIC -fvisibility=hidden -shared -o libMyLibHidden.so lib_mylib.o`
3) `gcc -fPIC -c my_lib_shared.c -o my_lib_shared.o`
4) `gcc -shared -o libMyShared2.so my_lib_shared.o`
5) `gcc -L. -Wall -o test_hidden driver.c -lMyLibHidden -lMyShared2`
the output of objdump is as
objdump -T libMyLibHidden.so
libMyLibHidden.so: file format elf64-x86-64
DYNAMIC SYMBOL TABLE:
0000000000000448 l d .init 0000000000000000 .init
0000000000000000 w D *UND* 0000000000000000 __gmon_start__
0000000000000000 w D *UND* 0000000000000000 _Jv_RegisterClasses
0000000000000658 g DF .fini 0000000000000000 Base _fini
0000000000000000 w D *UND* 0000000000000000 _deregisterTMCloneTable
0000000000000000 w D *UND* 0000000000000000 _ITM_registerTMCloneTable
0000000000000000 w DF *UND* 0000000000000126 GLIBC_2.2.5 _cxa_finalize
0000000000200938 g D *ABS* 0000000000000000 Base __bss_start
0000000000200948 g D *ABS* 0000000000000000 Base _end
0000000000200938 g D *ABS* 0000000000000000 Base _edata
0000000000000448 g DF .init 0000000000000000 Base _init
I did not any reference of functions of fun, fun1 and fun2 in the above objdump. Which is expected
and I get following error at final linking step
/tmp/ccMsEpFq.o: In function main':
driver.c:(.text+0xe): undefined reference tofun' collect2: error: ld returned 1 exit status
but in my actual code and shared libraries of libQtGui.so i can see in my Makefile -fvisibility=hidden is given as flag but still the symbols are available in my dynamic symbol table of libQtGui.so
=--------------------------------My question
is is there any flag in gcc linux or way where we can override the hidden visibility and make the symbols available in shared libraries
or there any flag in gcc linux where we can export all the symbols in the shared library
The flags of my Makefile while making shared library of libQtGui.so are
CFLAGS = -m64 -pipe -g -O2 -fvisibility=hidden -Wall -W -D_REENTRANT -fPIC $(DEFINES)
CXXFLAGS = -m64 -pipe -fpermissive -g -pthread -I/usr/include/glib-2.0 -I/usr/lib64/glib-2.0/include -O2 -fvisibility=hidden -fvisibility-inlines-hidden -Wall -W -D_REENTRANT -fPIC $(DEFINES)
INCPATH = -I../../mkspecs/linux-g++-64 -I. -I../../include/QtCore -I../../include/QtCore -I../../include -I../../include/QtGui -I../3rdparty/libpng -I../3rdparty/zlib -I/usr/include/freetype2 -I../3rdparty/harfbuzz/src -Idialogs -I.moc/release-shared -I/usr/X11R6/include -I.uic/release-shared
LINK = g++
LFLAGS = -m64 -Wl,-rpath/shared_libary_path -shared -Wl,-soname,libQtGui.so.4
Just remove the -fvisibility=hidden flag and all functions will be exported.
I am compiling a C program on linux with gcc. The program itself links libc (and not much else) at build-time, so that ldd gives this output :
$ ldd myprogram
linux-vdso.so.1 => (0x00007fffd31fe000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f7a991c0000)
/lib64/ld-linux-x86-64.so.2 (0x00007f7a99bba000)
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f7a98fbb000)
At run-time this program dlopen()s library B, which dependes on a library A, which of course dlopen also loads before returning. A exports a function called re_exec, which B invokes (B is linked against A). libc also exports a function called re_exec. readelf output :
$ readelf -as A.so | grep re_exec
104: 00000000000044ff 803 FUNC GLOBAL PROTECTED 11 re_exec
469: 00000000000044ff 803 FUNC GLOBAL PROTECTED 11 re_exec
$ readelf -as /lib/x86_64-linux-gnu/libc.so.6 | grep re_exec
2165: 00000000000e4ae0 39 FUNC WEAK DEFAULT 12 re_exec##GLIBC_2.2.5
The problem is that when B invokes re_exec, the re_exec inside libc is called, NOT the re_exc inside of A.
If, when I invoke the program, I include LD_LIBRARY_PRELOAD=/path/to/A.so, then everything works as expected : Bs invocation of re_exec correctly calls A, and not libc.
The dlopen call passes RTLD_NOW | RTLD_GLOBAL. I have tried with and without DEEPBIND, and get the same behavior in either case.
I have also tried dlopen()ing A directly, before B, both with and without DEEPBIND, which did not affect the behavior.
The question : is it possible to dlopen A/B with higher precedence than libraries that were included at link-time (libc, in this case) ?
(please don't suggest that I rename the call to something other than re_exec ; not useful)
Well, you know, I can't reproduce your error. Please take a look:
puts.c:
#include <stdio.h>
int puts(const char* _s) {
return printf("custom puts: %s\n", _s);
}
built with:
cc -Wall -fPIC -c puts.c -o puts.o
cc -shared -o libputs.so -fPIC -Wl,-soname,libputs.so puts.o
foo.c:
#include <stdio.h>
void foo() {
puts("Hello, world! I'm foo!");
}
built with:
cc -Wall -fPIC -c foo.c -o foo.o
cc -L`pwd` -shared -o libfoo.so -fPIC -Wl,-soname,libfoo.so foo.o -lputs
and rundl.c:
#include <dlfcn.h>
#include <assert.h>
#include <stdio.h>
typedef void (*FooFunc)();
int main(void) {
void *foolib = dlopen("./libfoo.so", RTLD_NOW | RTLD_GLOBAL | RTLD_DEEPBIND);
assert(foolib != NULL);
FooFunc foo = (FooFunc)dlsym(foolib, "foo");
assert(foo != NULL);
foo();
return 0;
}
built with:
cc -c -Wall rundl.c -o rundl.o
cc -o rundl rundl.o -ldl
now we can run rundl with LD_LIBRARY_PATH=$(pwd) (it's needed because libputs.so isn't in the ld.so known paths so libfoo.so can't be loaded w/ dlopen() & Co):
alex#rhyme ~/tmp/dynlib $ LD_LIBRARY_PATH=`pwd` ./rundl
custom puts: Hello, world! I'm foo!
alex#rhyme ~/tmp/dynlib $ _
if we move libputs.so to a directory known to ld.so and (re)run ldconfig to update caches then the code runs without any special environment variables:
alex#rhyme ~/tmp/dynlib $ ldd ./libfoo.so
linux-vdso.so.1 (0x00007fff48db8000)
libputs.so => /usr/local/lib64/libputs.so (0x00007f8595450000)
libc.so.6 => /lib64/libc.so.6 (0x00007f85950a0000)
/lib64/ld-linux-x86-64.so.2 (0x00007f8595888000)
alex#rhyme ~/tmp/dynlib $ ./rundl
custom puts: Hello, world! I'm foo!
If I link libfoo.so w/o -lputs foo() invokes the standard puts() from libc. That's it.
I tried to link my executable program with 2 static libraries using g++. The 2 static libraries have the same function name. I'm expecting a "multiple definition" linking error from the linker, but I did not received. Can anyone help to explain why is this so?
staticLibA.h
#ifndef _STATIC_LIBA_HEADER
#define _STATIC_LIBA_HEADER
int hello(void);
#endif
staticLibA.cpp
#include "staticLibA.h"
int hello(void)
{
printf("\nI'm in staticLibA\n");
return 0;
}
output:
g++ -c -Wall -fPIC -m32 -o staticLibA.o staticLibA.cpp
ar -cvq ../libstaticLibA.a staticLibA.o
a - staticLibA.o
staticLibB.h
#ifndef _STATIC_LIBB_HEADER
#define _STATIC_LIBB_HEADER
int hello(void);
#endif
staticLibB.cpp
#include "staticLibB.h"
int hello(void)
{
printf("\nI'm in staticLibB\n");
return 0;
}
output:
g++ -c -Wall -fPIC -m32 -o staticLibB.o staticLibB.cpp
ar -cvq ../libstaticLibB.a staticLibB.o
a - staticLibB.o
main.cpp
extern int hello(void);
int main(void)
{
hello();
return 0;
}
output:
g++ -c -o main.o main.cpp
g++ -o multipleLibsTest main.o -L. -lstaticLibA -lstaticLibB -lstaticLibC -ldl -lpthread -lrt
The linker does not look at staticLibB, because by the time staticLibA is linked, there are no unfulfilled dependencies.
That's an easy one. An object is only pulled out of a library if the symbol referenced hasn't already been defined. Only one of the hellos are pulled (from A). You'd get errors if you linked with the .o files.
When the linker tries to link main.o into multipleLibsTest and sees that hello() is unresolved, it starts searching the libraries in the order given on the command line. It will find the definition of hello() in staticLibA and will terminate the search.
It will not look in staticLibB or staticLibC at all.
If staticLibB.o contained another symbol not in staticLibA and that was pulled into the final executable, you then get a multiple definition of hello error, as individual .o files are pulled out of the library and two of them would have hello(). Reversing the order of staticLibA and staticLibB on the link command line would then make that error go away.