It was thus said that the Great Philippe Verdy once stated:
> Le dim. 25 nov. 2018 à 02:23, Sean Conner <sean@conman.org> a écrit :
>
> > It was thus said that the Great Philippe Verdy once stated:
> > > Le sam. 24 nov. 2018 à 23:20, Sean Conner <sean@conman.org> a écrit :
> > % uname
> > Linux
> > % cc -c -o main.o main.c
> > % cc -c -o myfunc2.o myfunc2.c
> > % cc -shared -fPIC -o func.ss func.c
> > % ar rv libfuncall.so func.ss
> > % ar: creating libfuncall.so
> > % a - func.ss
> > % cc -Wl,-rpath,/tmp/foo -o smain2 main.o myfunc2.o libfuncall.so
> > % ./smain2
> > Hello from main
> > Hello from func1
> > Hello from myfunc2
> > Back to func1
> > Back to main
> > Hello from myfunc2
> > Back to main
> >
> > Happy now?
>
> That's what I wanted. And demonstrates what I wanted to show: this is the
> only portable and expected behavior !
I am confused.
Here's the Windows example. This has func1() and func2() in separate C
files that are compiled, and both files are used to create the shared
library:
>cl /c main.c
>cl /c func1.c
>cl /c func2.c
>cl /c myfunc2.c
>link /dll /out:mylib.dll func1.obj func2.obj
Creating library mylib.lib and object mylib.exp
>link /out:main.exe main.obj myfunc2.obj mylib.lib
>main
Hello from main
Hello from func1
Hello from func2 ********
Back to func1
Back to main
Hello from myfunc2
Back to main
Look closely at the line marked with '********' (which I just added by hand,
to the output). Notice the function that was called---func2(). NOT
myfunc2(). func2().
Here's the same, but under Linux:
% cc -c -o main.o main.c
% cc -c -o myfunc2.o myfunc2.c
% cc -shared -fPIC -o func1.ss func1.c
% cc -shared -fPIC -o func2.ss func2.c
% cc -shared -o libfunc.so func1.ss func2.ss
% cc -Wl,-rpath,/tmp/foo -o smain4 main.o myfunc2.o libfunc.so
% ./smain4
Hello from main
Hello from func1
Hello from myfunc2 ********
Back to func1
Back to main
Hello from myfunc2
Back to main
Again, look closely at the line marked with '********'. Notice the function
that was called---myfunc2(). NOT func2(). myfunc2().
You've used the PIC option which means that the shared library does not call directly the functions, but passes through a "call gate" to perform the indirection.
Call gates ARE overridable when you link a DLL with other units specified before it, because these call gates are NOT part of the linked module but are part of a table indexed by the exported functions.
This is the difference with direct calls that, once linked, are not overriden as the target is resolved internally in the code sections.
PIC code used via call gates are in fact NOT resolved internally by these shared library formats. So they can be overriden. Still, the order of resolution is predicatable (this is not like with classic libraries): the "pre-linked" shared library in that cases is warrantied to resolve all internal calls to use the same call gate. But function calls via call gates suffer from an additiona indirection (which causes runtime performance penalties). However they have the advantage that the PIC code does not need to be "patched" in paged memory so this PIC code can be in shared read-only pages; only the memory segment containining the call gates is private per process: using PIC code is then only interesting if a shared library is likely to be used by LOT of concurrent processes (e.g. for the standard C library); there's no advantage when shared libaries are used by very few processes (the total number of threads using it does not matter as all threads in the same process shared the same virtual memory, except their TLS data and interrupt stack; the normal stack is usually allocated in the shared heap unless the program uses TLS allocation to create new threads).