Subject: shared library version semantics
To: None <tech-userlevel@NetBSD.ORG>
From: Arne H. Juul <arnej@pvv.unit.no>
List: tech-userlevel
Date: 07/13/1995 03:35:18
We were recently bitten by the bug reported in bin/1146:
ld.so fails after installing new minor version of shared library.

The problem is that when a new version of a shared library
is installed and ldconfig is run, programs that were linked
with the old minor version will start to fail, even if the
shared library they were linked against still exists.
For example, we have libXpm.so.4.3, 4.5, and 4.6. Any programs
that were originally linked against 4.3 or 4.5 will fail.

This happens because the hash mapping created by ldconfig and
used by ld.so uses name+major+minor as a key with which to
find libraries.  However, ldconfig only enters the highest
minor into the map.  So programs linked against libXpm.so.4.3
will lookup(libXpm.so.4.3) and find nothing.  The fallback
only looks in /usr/lib, not in the additional directories
given to ldconfig.

This set us thinking about the wanted semantics of ldconfig/ld.so
when looking for shared libraries. Here's my (personal) proposal:

We want to get the right name and major number for a
library that a program was linked with, and the highest
minor number available.  However, we want to use the first
directory with a library of the right major revision,
irrespective of the minor version number of this library.

The hash map should only use name+major.
The directories given to ldconfig should be
remembered for later (fallback) use.

I don't remember the last discussion on this in detail,
so please feel free to tell me if this isn't what you all want.
If anyone has a detailed description of the exact
semantics of other shared-library systems (SVr4, OSF/rose?),
we might want to compare with those, too.

Given that we agree on these goals, here's a quick take
with pseudo-code to describe algorithms:

ldconfig (directories)
  1) append /usr/lib to directories if it is not already present
     (inhibited by -s option to ldconfig)
  2) for each directory, find shared libraries lib*.so.*
     2a) for each unique name+major, find the highest version number.
         add name+major -> file name mapping if it wasn't found
         in another directory before.
  3) put the directories used into ld.so.hints for later fallback by
     ld.so.
We also need to agree on the exact semantics of the merge option
(-m) to ldconfig.  Should it append, prepend, use existing position
of the dir given?  Anyway it needs to check entries already in
the map, so why not build the whole map afresh anyway?
I don't understand the reason for having -m at all,
so I won't try to propose anything for it.


ld.so (name, major, minor)
 A) if LD_LIBRARY_PATH is set, search it.
 B) if run-time path is compiled into program, search it.
 C) if ld.so.hints exists:
  C1) look up (name+major) in the ld.so.hints map.
     if found, use this.
  C2) search directories from ld.so.hints (from item 3 above)
 D) if ld.so.hints does not exist, search /usr/lib.

 E) if (found minor < wanted minor) give warning (?)


algorithm for searching directories:
search(path, name, major)
  loop: for (dir in path) {
      find files matching lib<name>.so.<major>.*
      if none, continue loop
      among files, find highest minor and return("dir/file")
  }
  none found, return(NULL)



Well, I hope this is food for thought, leading to
productive discussion.

Yours,

  -  Arne H. J.