Dear Philip / Janne / list,
So this turns out to be a fairly nooby dependency loading question.
Maybe combined with a totally intricate libpthread loading question,
which I split off to a separate thread for clarity. So here we go:
Context: Process A dlopen():s shared library B. B has a dynamic object
dependency to shared library C. And also, both B and C have dynamic
object dependencies to libpthread.
Debugging my issue further using LD_DEBUG=yes , I realize that it was
not loading of the dependency C that failed, but the loading of the
libpthread dependency.
And, that being my issue, makes the whole thing more strange to me, and
gives me a hunch that maybe there is some more fundamental aspect of how
dynamic object links or dlopen() works, that I have totally
misunderstood.
Also, perhaps (hopefully) coincidentally, I had some libpthread-specific
linking problems (described below). (Also described below is the only
unusual thing about this program, which is that it links both libstdc++
and libestdc++ which could be a reason for the secondary SIGSEGV error
that I saw in a corner-case that is also described below.)
So, below now, I first respond to the previous email and then show the
weird libpthread dependency loading issue. I guess it's not
libpthread-specific, so I guess this is just a dependency loading
question.
So my question is:
Why does not the OS load the dependency (libpthread and C)
automatically, how do I make it do that, and on this topic, to your
perception is there anything I not understood?
Looking forward a lot to your insight on this one.
Thanks! :)
-> Response to last email
On 2016-07-04 17:48, Philip Guenther wrote:
On Mon, Jul 4, 2016 at 12:44 AM, Janne Johansson <[email protected]>
wrote:
OpenBSD will not make B load C just by A loading B, so the program A
needs
to ask for both B and C while being linked in order to work.
Right, B needs to specify its dependency to C either by means of dynamic
object linkage (set up at compilation time via "-ldependency" and as
dumped by "ldd") or via dlopen().
BUT, A does not need to "ask for both B and C" - that is, "-lB -lC" or
dlopen() both B and C, right - but *only* B, as long as B was compiled
with "-lC" right??
It depends on what "depends" means. :-)
"-ldependency" or dlopen("dependency").
2016-07-03 16:49 GMT+02:00 Tinker <[email protected]>:
Am debugging something and am not quite clear about details.
My executable A loads library B dynamically, and B depends on library
C.
What do you (the original poster) mean by "depends on"? If you just
mean "needs, requires the resources of" then no, how would the OS
know? If you mean "has listed as a dependency via DT_NEEDED entries"
then yes, OpenBSD supports dependencies that way.
"DT_NEEDED" is the list of dynamic object dependencies that "ldd" dumps
right?
A loads/depends on B by the means of dlopen() only (dlopen("B")).
B loads/depends on C by the means of dynamic object dependency only
("-lC").
B and C load/depend on libpthread by the means of dynamic object
dependency only ("-lpthread").
"somehow"? Come on, Tinker, you're not new to this! Not giving the
exact error messages just wastes peoples time by making them guess!
Exact description below.
It *sounds* like C wasn't specified as a dependency of whatever (A?
B?) actually requires it, which would have been a error in how the
depending object was built. Or maybe not, since we have no
information at all about these objects or the failure.
Exact description below.
At link time, when searching for files to resolve a -l option to gcc
or ld, no, /usr/local/lib is not in the default search path.
At run time, when searching for a shared-object for dlopen() or as a
DT_NEEDED dependency of the executable or another shared-object, yes,
/usr/local/lib is in the default search path.
Noted, thank you very much.
-> Debug output from launching A, "ldd" output
So let's see what the system says when I launch my A binary (which will
dlopen() B, which in turn has a dynamic object dependency to C and to
libpthread , and C has a dynamic object dependency to libpthread also).
I do:
$ LD_DEBUG=yes /home/myuser/A
A loads the standard library and some other stuff nicely, and it does
some other dlopen() that proceeds nicely too (the other dlopen():s are
of shared libraries that only depend on the standard library, which have
already been loaded into the host process i.e. A, so I can see a
distinction between those other libraries and C, in that C is the only
one that is depending on anything other than has been loaded by A
already).
An then comes the dlopen() of B! :-o
dlopen: loading: /home/myuser/B
flags /home/myuser/B = 0x0
head /home/myuser/B
obj /home/myuser/B has /home/myuser/B as head
linking /home/myuser/B as dlopen()ed
head [/home/myuser/B]
examining: '/home/myuser/B'
loading: libpthread.so.19.0 required by /home/myuser/B
flags /usr/lib/libpthread.so.19.0 = 0x68
dlopen: failed to open libpthread.so.19.0
unload_shlib called on /home/myuser/B
unload_shlib unloading on /home/myuser/B
dlopen: /home/myuser/B: done (failed).
So.. dlopen() doesn't even get as far as to even touch C, but instead
fails on the step of loading libpthread, which indeed B links to also,
by the means of a dynamic object dependency.
So the issue was that libpthread failed to load for some magic reason.
For clarity:
$ ldd A
A:
Start End Type Open Ref GrpRef Name
000011a801e00000 000011a802e20000 exe 1 0 0 A
000011aa2e532000 000011aa2e93e000 rlib 0 1 0
/usr/lib/libutil.so.12.1
000011aa81b7a000 000011aa8209c000 rlib 0 1 0
/usr/local/lib/libestdc++.so.16.0
000011aa4d922000 000011aa4dd4a000 rlib 0 2 0
/usr/lib/libm.so.9.0
000011aa6e854000 000011aa6ed2e000 rlib 0 1 0
/usr/lib/libc.so.80.1
000011aaf9a00000 000011aaf9a00000 rtld 0 1 0
/usr/libexec/ld.so
$ ldd B
B:
Start End Type Open Ref GrpRef Name
0000160c5dac9000 0000160c5ded6000 dlib 1 0 0
/home/myuser/B
0000160c6bbc9000 0000160c6bfdb000 rlib 0 9 0
/usr/lib/libpthread.so.19.0
0000160c95603000 0000160c95e68000 rlib 0 1 0
/home/myuser/C
0000160c6e5da000 0000160c6eafc000 rlib 0 1 0
/usr/local/lib/libestdc++.so.16.0
0000160cd792c000 0000160cd7d54000 rlib 0 14 0
/usr/lib/libm.so.9.0
(A ton of C's dependencies are listed here)
0000160c8f5ef000 0000160c8f9f1000 rlib 0 3 0
/usr/X11R6/lib/libpthread-stubs.so.2.0
(another ton)
0000160c90f50000 0000160c91466000 rlib 0 1 0
/usr/lib/libstdc++.so.57.0
$ ldd C
C:
Start End Type Open Ref GrpRef Name
00000c31b1817000 00000c31b207c000 dlib 1 0 0
/home/myuser/C
(A ton of dependencies including:)
00000c311ff83000 00000c31203ab000 rlib 0 12 0
/usr/lib/libm.so.9.0
00000c30dc518000 00000c30dc92a000 rlib 0 8 0
/usr/lib/libpthread.so.19.0
00000c3149332000 00000c3149734000 rlib 0 3 0
/usr/X11R6/lib/libpthread-stubs.so.2.0
00000c313113b000 00000c3131651000 rlib 0 1 0
/usr/lib/libstdc++.so.57.0
$ ldd /usr/lib/libpthread.so.19.0
/usr/lib/libpthread.so.19.0:
Start End Type Open Ref GrpRef Name
0000093aaf477000 0000093aaf889000 dlib 1 1 0
/usr/lib/libpthread.so.19.0
Just to further the debugging a bit as to perhaps understand the problem
better that way, let's try to ld.so-force load of libpthread and see
what happens then:
$ LD_DEBUG=yes LD_PRELOAD=/usr/lib/libpthread.so.19.0
/home/myuser/A
[...]
dlopen: loading: /home/myuser/B
flags /home/myuser/B = 0x0
head /home/myuser/B
obj /home/myuser/B has /home/myuser/B as head
linking /home/myuser/B as dlopen()ed
head [/home/myuser/B]
examining: '/home/myuser/B'
loading: libestdc++.so.16.0 required by /home/myuser/B
loading: libpthread.so.19.0 required by /home/myuser/B
loading: C required by /home/myuser/B
flags /home/myuser/C = 0x0
obj /home/myuser/C has /home/myuser/B as head
loading: libm.so.9.0 required by /home/myuser/B
linking dep /usr/lib/libpthread.so.19.0 as child of /home/myuser/B
linking dep /home/myuser/C as child of /home/myuser/B
linking dep /usr/local/lib/libestdc++.so.16.0 as child of
/home/myuser/B
linking dep /usr/lib/libm.so.9.0 as child of /home/myuser/B
examining: '/home/myuser/C'
loading: libWHATEVER.. required by /home/myuser/C
flags ..libWHATEVER.. = 0x0
[...]
dlopen: /home/myuser/B: done (success).
So it works then. Nice but weird.
To further debugging even some more, let's recompile A with "-lpthread"
and and re-run A to see if it helps us understand the problem even
further:
$ LD_DEBUG=yes /home/myuser/A
[...]
dlopen: loading: /home/myuser/B
flags /home/myuser/B = 0x0
head /home/myuser/B
obj /home/myuser/B has /home/myuser/B as head
linking /home/myuser/B as dlopen()ed
head [/home/myuser/B]
examining: '/home/myuser/B'
loading: libestdc++.so.16.0 required by /home/myuser/B
loading: libm.so.9.0 required by /home/myuser/B
loading: C required by /home/myuser/B
flags /home/myuser/C = 0x0
obj /home/myuser/C has /home/myuser/B as head
loading: libpthread.so.19.0 required by /home/myuser/B
linking dep /usr/lib/libpthread.so.19.0 as child of /home/myuser/B
linking dep /home/myuser/C as child of /home/myuser/B
linking dep /usr/local/lib/libestdc++.so.16.0 as child of
/home/myuser/B
linking dep /usr/lib/libm.so.9.0 as child of /home/myuser/B
examining: '/home/myuser/C'
loading: ..libSOMEDEP.. required by /home/myuser/C
[...]
dlopen: /home/myuser/B: done (success).
..So it works then too. So this again is like a receipt that there is
something I not understood about how the linking works.
-> Analysis and question about the main issue, which is, how make the OS
automatically load libpthread and C?
The main issue about what I'm trying to do is that libpthread obviously
was supposed to be automatically loaded in the first place both by C and
by B, without me needing to force it using LD_PRELOAD or by "-lpthread"
when compiling A .
This whole picture leads me to understand that there is something
fundamental about library linking by the means of dynamic object linkage
works, or how dlopen() works, that I have not understood, or that there
is something quirky about libpthread in particular that I have not
understood.
libpthread doesn't even have any dependencies so from a dependency point
of view it should be trivial to work with.
So, what am I missing / how fix?
-> Very secondary issue: libpthread loading order may cause SIGSEGV, is
libpthreads allergic to being loaded via ldopen() or any idea why? -
putting this one in a separate thread
Secondarily there is one more weird thing going on, which is that if
only C is compiled with "-lpthreads" (and B and A are not and it's not
forced to be loaded using LD_PRELOAD), then my process would SIGSEGV.
How weird is that, I must have missed something.
The only unusual thing I can see about my process is that it loads TWO
versions of libstdc++ (namely libstdc++ and libestdc++).
For certain G++ versions I have seen that cause problems (that is, C++
exception catching undergoing a total meltdown). However this particular
G++ version combination works well to the best of my awareness.
And, I don't see any reason for that that would interfere with
libpthreads anyhow, libpthreads doesn't even have any dependencies.
Could it be that libpthreads not goes well together with being loaded by
ldopen(), that it has some initialization code?
Since this issue was mitigated by just compiling B with "-lpthread" I
won't touch very much more on this question though.
http://www.gnu.org/software/hurd/open_issues/libpthread_dlopen.html
mentions that LibC on Linux have single-threading and multi-threading
modes and that dlopen() causes a switch from single to multi which is
only supported since some version.
I'll split this one out to a separate thread for this thread to be
focused only on how to make auto-loading of dependencies work.
Thanks!