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!

Reply via email to