Reputation: 3490
I'm working on Ubuntu Linux to build Android and Linux binary. I have a static library which has been linked by two shared libraries, and there is a global object in the static library.
By my understanding, the global object will be exist in both *.so
, and will cause issues because the function symbols in each shared library access different global variables.
(I referred the static library in the command line when building both shared libraries. I have been using -Wl,--whole-archive
and -Wl,-z,defs
switches. So the shared library contains the symbols of the static library.)
So the question is:
-----------------Edit 1-----------------------
As R Soha said we shouldn't have status in static library, or should provide an shared library.
And I think it is bad and sad to have this limitation: Static library should not have status or should be provide as an shared libraries.
The reason is: 1. The global variable is common used, for example in my case, it is an singleton object. 2. The static library may be provided by an third party. And maybe the user also using an shared library provide by another third party, which has already linked the static library, for example boost.
Upvotes: 1
Views: 718
Reputation: 753665
Here is some modest code that demonstrates two shared libraries both defining a symbol (function) abc_123()
along with other functions calling the same function, and a test program that makes use these functions.
#ifndef JLSS_ID_LIB1_H
#define JLSS_ID_LIB1_H
extern void abc_123(int i);
extern void def_345(int i);
#endif
#ifndef JLSS_ID_LIB2_H
#define JLSS_ID_LIB2_H
extern void abc_123(int i);
extern void ghi_678(int i);
#endif
#include <stdio.h>
#include "lib1.h"
void abc_123(int i)
{
printf("Library 1:%s:%d:%s() - %d\n", __FILE__, __LINE__, __func__, i);
printf("Note this extra message\n");
}
#include <stdio.h>
#include "lib1.h"
#include "lib2.h"
void def_345(int i)
{
printf("Library 1:%s:%d-->>%s() - %d\n", __FILE__, __LINE__, __func__, i);
ghi_678(i+10);
printf("Library 1:%s:%d<<--%s() - %d\n", __FILE__, __LINE__, __func__, i);
}
#include <stdio.h>
#include "lib2.h"
void abc_123(int i)
{
printf("Library 2:%s:%d:%s() - %d\n", __FILE__, __LINE__, __func__, i);
printf("This is a completely different message\n");
}
#include <stdio.h>
#include "lib2.h"
void ghi_678(int i)
{
printf("Library 2:%s:%d-->>%s() - %d\n", __FILE__, __LINE__, __func__, i);
abc_123(i * 10);
printf("Library 2:%s:%d<<--%s() - %d\n", __FILE__, __LINE__, __func__, i);
}
#include <stdio.h>
#include "lib1.h"
#include "lib2.h"
int main(void)
{
printf("Cross-library linking and calling\n");
printf("Main calling abc_123(29)\n");
abc_123(29);
printf("Main calling def_345(45)\n");
def_345(45);
printf("Main calling ghi_678(57)\n");
ghi_678(57);
printf("Demonstration over\n");
return 0;
}
CC = gcc #/usr/bin/gcc
WFLAG1 = -Wall
WFLAG2 = -Wextra
WFLAG3 = -Wmissing-prototypes
WFLAG4 = -Wstrict-prototypes
WFLAG5 = -Wold-style-definition
WFLAG6 = -Werror
WFLAGS = ${WFLAG1} ${WFLAG2} ${WFLAG3} ${WFLAG4} ${WFLAG5} ${WFLAG6}
SFLAGS = -std=c11
GFLAGS = -g
OFLAGS = -O3
UFLAGS = # Set on command line
IFLAG1 = # -I${HOME}/inc
IFLAGS = # ${IFLAG1}
SOEXT = dylib
LDFLAG1 = -L.
LDLIB1 = -lrary1
LDLIB2 = -lrary2
LDFLAGS = ${LDFLAG1}
LDLIBS = ${LDLIB1} ${LDLIB2}
CFLAGS = ${OFLAGS} ${GFLAGS} ${IFLAGS} ${SFLAGS} ${WFLAGS} ${UFLAGS}
LNKSHLIB = -shared
LIBRARY1 = library1.${SOEXT}
LIBRARY2 = library2.${SOEXT}
LIB1.o = lib1a.o lib1b.o
LIB2.o = lib2a.o lib2b.o
TEST1.o = test1.o
PROGRAM = test1
all: ${PROGRAM}
${PROGRAM}: ${TEST1.o} ${LIBRARY1} ${LIBRARY2}
${CC} ${CFLAGS} -o $@ ${TEST1.o} ${LDFLAGS} ${LDLIBS}
${LIBRARY1}: ${LIB1.o} ${LIBRARY2}
${CC} ${CFLAGS} ${LNKSHLIB} -o $@ ${LIB1.o} ${LDFLAGS} ${LDLIB2}
${LIBRARY2}: ${LIB2.o}
${CC} ${CFLAGS} ${LNKSHLIB} -o $@ ${LIB2.o}
This is from an Ubuntu 14.04 LTS VM (hosted on Mac OS X 10.9.3), and the compiler is GCC 4.8.2.
$ make -B SOEXT=so UFLAGS=-fPIC
gcc -O3 -g -std=c11 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition -Werror -fPIC -c test1.c
gcc -O3 -g -std=c11 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition -Werror -fPIC -c lib1a.c
gcc -O3 -g -std=c11 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition -Werror -fPIC -c lib1b.c
gcc -O3 -g -std=c11 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition -Werror -fPIC -c lib2a.c
gcc -O3 -g -std=c11 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition -Werror -fPIC -c lib2b.c
gcc -O3 -g -std=c11 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition -Werror -fPIC -shared -o library2.so lib2a.o lib2b.o
gcc -O3 -g -std=c11 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition -Werror -fPIC -shared -o library1.so lib1a.o lib1b.o -L. -lrary2
gcc -O3 -g -std=c11 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition -Werror -fPIC -o test1 test1.o -L. -lrary1 -lrary2
$ LD_LIBRARY_PATH=$PWD:$LD_LIBRARY_PATH ./test1
Cross-library linking and calling
Main calling abc_123(29)
Library 1:lib1a.c:15:abc_123() - 29
Note this extra message
Main calling def_345(45)
Library 1:lib1b.c:17-->>def_345() - 45
Library 2:lib2b.c:15-->>ghi_678() - 55
Library 1:lib1a.c:15:abc_123() - 550
Note this extra message
Library 2:lib2b.c:17<<--ghi_678() - 55
Library 1:lib1b.c:19<<--def_345() - 45
Main calling ghi_678(57)
Library 2:lib2b.c:15-->>ghi_678() - 57
Library 1:lib1a.c:15:abc_123() - 570
Note this extra message
Library 2:lib2b.c:17<<--ghi_678() - 57
Demonstration over
$
Note that the abc_123()
from library1.so
is called each time, even when the calling function is ghi_678()
from library2.so
.
This is from Mac OS X 10.9.3 with GCC 4.9.0.
$ make -B
gcc -O3 -g -std=c11 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition -Werror -c -o test1.o test1.c
gcc -O3 -g -std=c11 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition -Werror -c -o lib1a.o lib1a.c
gcc -O3 -g -std=c11 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition -Werror -c -o lib1b.o lib1b.c
gcc -O3 -g -std=c11 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition -Werror -c -o lib2a.o lib2a.c
gcc -O3 -g -std=c11 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition -Werror -c -o lib2b.o lib2b.c
gcc -O3 -g -std=c11 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition -Werror -shared -o library2.dylib lib2a.o lib2b.o
gcc -O3 -g -std=c11 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition -Werror -shared -o library1.dylib lib1a.o lib1b.o -L. -lrary2
gcc -O3 -g -std=c11 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition -Werror -o test1 test1.o -L. -lrary1 -lrary2
$ ./test1
Cross-library linking and calling
Main calling abc_123(29)
Library 1:lib1a.c:15:abc_123() - 29
Note this extra message
Main calling def_345(45)
Library 1:lib1b.c:17-->>def_345() - 45
Library 2:lib2b.c:15-->>ghi_678() - 55
Library 2:lib2a.c:16:abc_123() - 550
This is a completely different message
Library 2:lib2b.c:17<<--ghi_678() - 55
Library 1:lib1b.c:19<<--def_345() - 45
Main calling ghi_678(57)
Library 2:lib2b.c:15-->>ghi_678() - 57
Library 2:lib2a.c:16:abc_123() - 570
This is a completely different message
Library 2:lib2b.c:17<<--ghi_678() - 57
Demonstration over
$
Note that the code in ghi_678()
calls the abc_123()
from library2.so
, not the version from library1.so
as on Linux.
You can play with the reverse linking order and see what happens then.
Don't build software that contains the same function in multiple shared libraries; you will get confused if you port between platforms.
Upvotes: 3
Reputation: 206577
You said:
By my understanding the global object will be exist in both *.so, and will cause issue because the function symbols in each shared library access different global variable.
That is correct. The global variable used by first .so will be different from the global variable used by the second .so.
As for your questions...
When link the executable, Why the GCC/LD doesn't report duplicate symbols error for this case?
As far as the executable is concerned, it only cares about the interfaces of the .so libraries. It doesn't care about the fact that they have been created by using the same static library(ies).
Does that means we never link to the same static library in an application's shared library and the executable itself?
It is OK if the static libraries don't have state. If they have state, you have to make a judgement call on whether the state needs to be global across the application or only in a shared library.
If the state needs to be global across the application, it will be better to create another shared library that provides access to the global state instead of putting it in a static library.
If the state needs to be maintained in each shared library, they putting it in the static library is OK. Even then, creating a shared library that provides access to the data will be a better solution.
Upvotes: 2