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

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



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 ?  I just did and:
 * `gcc -shared -fPIC -Wall bar.c -o bar.so' should read
   `gcc -shared -fPIC -Wall bar.c -o libbar.so' because otherwise
   you get `/usr/bin/ld: cannot find -lbar'
 * You need `LD_LIBRARY_PATH=. ./a.out' because otherwise you get
   `./a.out: error while loading shared libraries: libbar.so: cannot
   open shared object file: No such file or directory'

Furthermore, as Simon Richter points out, this happens to work only
because 1. your foo.c happens not to contain any data references, and
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.  (In particular, this would vitiate libbar's stable
API/ABI.)

(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
(64)ian@zealot:~$

> > * 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.

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.

bar[12].[ch] represent two different libraries, both using foo, but
each of whose source code is adapted for a different version of foo.

main.c is a program using both bar1 and bar2.  You can see that both
foo1 and foo2 are included and they have different x's and that the
whole program works despite the different signatures for the function
foo.

Ian.

(64)ian@zealot:~$ ./build
+ egrep . bar.c bar1.c bar1.h bar2.c bar2.h foo.c foo1.c foo1.h foo2.c foo2.h main.c t.c build
bar.c:int bar(void) {return foo();}
bar1.c:#include "foo1.h"
bar1.c:#include "bar1.h"
bar1.c:int bar1(void) {
bar1.c:  double v;
bar1.c:  foo(&v);
bar1.c:  return v;
bar1.c:}
bar1.h:int bar1(void);
bar2.c:#include "foo2.h"
bar2.c:#include "bar2.h"
bar2.c:int bar2(void) {
bar2.c:  int v;
bar2.c:  foo(&v);
bar2.c:  return v;
bar2.c:}
bar2.h:int bar2(void);
foo.c:int x=17; int foo(void){ return x; }
foo1.c:#include <stdio.h>
foo1.c:#include "foo1.h"
foo1.c:int x=17;
foo1.c:void foo(double *r){ printf("foo1: %p\n", &x); *r = x; }
foo1.h:void foo(double *r);
foo2.c:#include <stdio.h>
foo2.c:#include "foo2.h"
foo2.c:double x=17;
foo2.c:void foo(int *r){ printf("foo2: %p\n", &x); *r = x; }
foo2.h:void foo(int *r);
main.c:#include <stdio.h>
main.c:#include "bar1.h"
main.c:#include "bar2.h"
main.c:int main() {
main.c:  int r1 = bar1();
main.c:  int r2 = bar2();
main.c:  printf("%d %d\n", r1, r2);
main.c:  return 0;
main.c:}
t.c:#include <stdio.h>
t.c:int main(int argc, char **argv) {
t.c:  printf("%s\n",strsignal(atoi(argv[1])));
t.c:}
build:#!/bin/sh
build:set -ex
build:egrep . *.[ch] build
build:gcc='gcc -Wall -Werror -Wmissing-prototypes -Wmissing-declarations'
build:for v in 1 2; do
build:  $gcc -c -fPIC -Wall foo$v.c
build:  ar rs libfoo$v.a foo$v.o
build:  $gcc -shared -fPIC -Wl,-Bsymbolic bar$v.c -L. -lfoo$v -o libbar$v.so
build:done
build:gcc -Wall main.c -L. -lbar1 -lbar2
build:LD_LIBRARY_PATH=. ./a.out
+ gcc=gcc -Wall -Werror -Wmissing-prototypes -Wmissing-declarations
+ gcc -Wall -Werror -Wmissing-prototypes -Wmissing-declarations -c -fPIC -Wall foo1.c
+ ar rs libfoo1.a foo1.o
+ gcc -Wall -Werror -Wmissing-prototypes -Wmissing-declarations -shared -fPIC -Wl,-Bsymbolic bar1.c -L. -lfoo1 -o libbar1.so
+ gcc -Wall -Werror -Wmissing-prototypes -Wmissing-declarations -c -fPIC -Wall foo2.c
+ ar rs libfoo2.a foo2.o
+ gcc -Wall -Werror -Wmissing-prototypes -Wmissing-declarations -shared -fPIC -Wl,-Bsymbolic bar2.c -L. -lfoo2 -o libbar2.so
+ gcc -Wall main.c -L. -lbar1 -lbar2
+ LD_LIBRARY_PATH=. ./a.out
foo1: 0x7ff823671a98
foo2: 0x7ff823470a98
17 17
(64)ian@zealot:~$


Reply to: