[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Re: Should .a library contains non-reallocatable code?



* Ian Jackson <ijackson@chiark.greenend.org.uk> [150223 20:09]:
> Bernhard R. Link writes ("Re: Should .a library contains non-reallocatable code?"):
> > Jeff Epler <jepler@unpythonic.net> [150220 00:19]:
> > >  * libbar has a stable API, so it should be shipped as a .so,
> > >    but if it links libfoo.a, and libfoo.a is not -fPIC, then
> > >    libbar has to be shipped as a a static library too
> 
> Jeff is correct.
> > 
> > This is wrong. If libbar.so needs libfoo.a then libfoo.a does not
> > need to be PIC:
> > 
> > echo 'int foo(void) {return 17;}' > foo.c
> > echo 'int bar(void) {return foo();}' > bar.c
> > echo 'int main() {return bar();}' > main.c
> > gcc -c -Wall foo.c
> > ar rs libfoo.a foo.o
> > gcc -shared -fPIC -Wall bar.c -o bar.so
> > gcc -Wall main.c -L. -lbar -lfoo
> > ./a.out
> > echo $?
> > 
> > works just fine.
> 
> Did you actually try this before posting ?

Oh, I copied the wrong lines from the backlog here. It seems they
were a bit to trivial so autocorrection made me miss them reading over
them.

> Furthermore, as Simon Richter points out, this happens to work only
> because 1. your foo.c happens not to contain any data references, and

Data references work fine, too:

echo 'extern int val; void foo(int s) {val=s;}' >
echo 'int val = 2; void foo(int s) {val=s;}' > foo.c
echo 'extern int val;int bar(void) {return val;}' >
gcc -c -Wall foo.c
ar rs libfoo.a foo.o
gcc -shared -fPIC -Wall bar.c -o libbar.so
echo 'int main() {foo(17);return bar();}' > main.c
gcc -Wall main.c -L. -lbar -lfoo
LD_LIBRARY_PATH=. ./a.out
echo $?

> 2. your construction of bar.so (should be libbar.so) produces a shared
> library with unresolved references to libfoo, which is not usual or
> desirable.

Using a static library is undesirable. Unresolved symbols is just the
way dependencies of libraries works. The only thing missing here is
the missing dependency on the static library. But that is simply not
possible with static libraries.

> (In particular, this would vitiate libbar's stable
> API/ABI.)

This you have to explain.

If you think there is another way a dynamic library can use a static
library please tell so.

> (64)ian@zealot:~$ echo 'int x=17; int foo(void){ return x; }' >foo.c
> (64)ian@zealot:~$ echo 'int bar(void) {return foo();}' > bar.c
> (64)ian@zealot:~$ echo 'int main() {return bar();}' > main.c
> (64)ian@zealot:~$ gcc -c -Wall foo.c
> (64)ian@zealot:~$ ar rs libfoo.a foo.o
> (64)ian@zealot:~$ gcc -shared -fPIC -Wall bar.c -L. -lfoo -o libbar.so
> bar.c: In function ?bar?:
> bar.c:1:1: warning: implicit declaration of function ?foo?
> [-Wimplicit-function-declaration]
> /usr/bin/ld: ./libfoo.a(foo.o): relocation R_X86_64_PC32 against
> symbol `x' can not be used when making a shared object; recompile with
> -fPIC
> /usr/bin/ld: final link failed: Bad value
> collect2: error: ld returned 1 exit status

What you do here is creating a dynamic library that merges the contents
of foo and bar. This is totally broken. If anything else uses the same
static library you get multiple symbols. If there versions differ that
means harvoc. With data parts you may end up with multiple versions.

There are only two possibilities:

You want a static library, then the static library has to be linked into
the executeable directly.

You foo within a dynamic library. Then instead of creating a dynamic
library that includes foo you can just as well create dynamic libfoo.so
and link against that.

> > > * foomodule is a Python wrapper for libfoo, so it must be shipped
> > >    as a .so, but if it links libfoo.a, and libfoo.a is not -fPIC,
> > >    it is not possible to build foomodule at all
> >
> > That is indeed the case. Note that simply compiling it with -fPIC might
> > not be enough here. As you need to include the actual library in the
> > module, this might mean you might end up with multiple copies of the
> > library, which might also mean multiple copies of any data it has or
> > with different versions of the same library in the same executeable
> > (and you cannot really have symbol versioning with a static library).
>
> That not usually a problem.  Providing that only the relevant symbols
> are exported from the .so, the executable simply results in multiple
> completely independent copies of the static library.

Your example above misses that.

echo "int foo(void) {return 7;}" > foo1.c
echo "int foo(void) {return 13;}" > foo2.c
echo 'int bar(void) {return foo();}' > bar.c
echo 'int main() {return bar();}' > main.c
gcc -c foo1.c
gcc -c foo2.c
gcc -fPIC -c foo1.c
rm libfoo.a
ar rs libfoo.a foo1.o
gcc -shared -o libbar.so -fPIC bar.c -L. -lfoo
gcc foo2.c main.c -L. -lbar
LD_LIBRARY_PATH=. ./a.out 
echo $?

gives 13

Also note that you cannot avoid exporting symbols totally.
The above example you can avoid with a linker script, like:

printf 'LIBBAR_1.0 {\n global:\n  bar*;\n local:\n  *;\n};\n' >libbar.map
and then give -Wl,--version-script=libbar.map


> 
> See transcript below.  In my example foo[12].[ch] represents a library
> with an unstable API/ABI, provided as a static library only.  Note
> that it has symbols `x' and `foo' which behave differently in the
> different versions.

Note that this uses -Bsymbolic, by which you hide some issues and might
introduce other problems (depending on your library).

You still pollute the ABI with the details of the internals:

if you try to change main.c to:
#include <stdio.h>
#include "foo1.h"
#include "bar2.h"
int main() {
  double rr1;
  foo(&rr1);
  int r1 = rr1;
  int r2 = bar2();
  printf("%d %d\n", r1, r2);
  return 0;
}

and run gcc -Wall main.c -L. -lbar2 -lfoo1 ; LD_LIBRARY_PATH=. ./a.out
you get:
foo2: 0x7f42db6f0a48
foo2: 0x7f42db6f0a48
0 17

(In this specific case a version script might helps, but I'd not bet
on helping in all cases).


	Bernhard R. Link
-- 
F8AC 04D5 0B9B 064B 3383  C3DA AFFC 96D1 151D FFDC


Reply to: