This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

Author Michael.Felt
Recipients Michael.Felt, haubi, martin.panter
Date 2017-02-03.20:52:42
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <1486155163.13.0.690023990803.issue27435@psf.upfronthosting.co.za>
In-reply-to
Content
On 31/01/2017 20:22, Michael Haubenwallner wrote:
> Michael Haubenwallner added the comment:
>
> Feels like it is up to me to shed some light on various topics here:
Many thanks!
>
> AIX defines (static) "Object" files and "Shared Object" files. Both can (but do not need to) be members of an "Archive Library".
>
> When talking about (dynamically loading) a "shared library" on non-AIX systems, the correct wording for AIX is (dynamically loading) a "Shared Object" - independent of whether it is a standalone file or an archive member.
>
> As you already agreed on, ctypes.util.find_library() indeed should return the location of the Shared Object - as either "/path/to/file" or "/path/to/file(member)". And ctypes.CDLL() should add the RTLD_MEMBER flag to _ctypes.dlopen() if there is the "(member)" part.
>
> However, that does not necessarily mean the RTLD_MEMBER value needs to be available through the ctypes API. Instead, RTLD_MEMBER feels more appropriate for the _ctypes API - and on AIX only.
>
> Anyway:
> Unfortunately, there is no concept of embedding something like ELF's DT_SONAME tag into the Shared Object. The very (PATH,BASE,MEMBER) value as (specified to and) discovered by the linker is recorded into the just-linked executable (or Shared Object). This implies that the runtime loader does search for the very same filename (and member eventually) as the linker (at linktime).
I assume this is why there are many systems besides AIX that do not 
support/use DT_SONAME. At least I see many references to "Shared 
Objects" libFOO.so.X.Y.Z, libFOO.so.X.Y, libFOO.so.X and libFOO.so (with 
the latter three being symbolic links to the first).
>
> However, to still get some kind of shared library versioning, multiple versions of one Shared Object are put into one single Archive Library, where the "old" versions get the LOADONLY flag set (via 'strip -e') - so executables linked against an old version still can load their "old" Shared Object, while the linker discovers the "current" version only.
>
> But this "single-filename" based shared library versioning is a huge burden for packages managers - either human or software, even when they maintain their packages in a private prefix (like /opt/freeware, /usr/local and similar). Neither can they put their "current" Shared Object's version into the single (system's) Archive Library, nor can they mark existing Shared Objects as LOADONLY.
> On the other hand, they also do not want to pull all the system versions into their own Archive Library.
Another issue is support for what I believe MacOS calls "fat" objects - 
that support both 32-bit and 64-bit applications - rather than /XXX/lib 
for 32-bit objects and /XXX/lib/lib64 or /XXX/lib64 for 64-bit objects.
>
> So for package managers it is crucial to have "multi-filename" based shared library versioning instead.
>
> But there is help:
> AIX also provides (plain text) "Import Files": They contain a list of symbols for the linker, and a (PATH,BASE,MEMBER) tuple naming the Shared Object that provides these symbols at runtime.
a) Yes to above
b) One of the difficulties I faced is trying to guess what version -lFOO 
should find when there is more than one version available. Applications 
that are already linked do not look for a latest version - they know the 
member name that the linker found initially. ctypes.utils.find_library() 
does not provide a way to say which version should be found. Each 
implementation has it's way to find a version (e.g., a search of 
ldconfig -p output for a best guess)
> So yes, Import Files can be used for "multi-filename" based shared library versioning.
>
> Thus, libtool now offers the "--with-aix-soname=svr4" configure flag, where "libNAME.so.X" really is created as an Archive Library, containing the Shared Object "shr.o" with the LOADONLY flag set, and the Import File "shr.imp" referring to "libNAME.so.X(shr.o)" for runtime. Combined with the symlink "libNAME.so", for the "-lNAME" argument the linker discovers the Import File "libNAME.so(shr.imp)" now, while recording "libNAME.so.X(shr.o)" into the executable for the runtime loader.
> Note that for 64bit the Shared Object and Import File is named "shr_64.o" and "shr_64.imp", respectively.
>
> While libtool's "--with-aix-soname=svr4" variant creates "libNAME.a" from static Objects only, both libtool's "--with-aix-soname=aix" and "--with-aix-soname=both" - for backwards compatibility - create "libNAME.a" for traditional "single-filename" based versioning: Without any static Object - as even Shared Objects can be linked statically(!). But to statically link the Shared Object (as an archive member), neither the LOADONLY flag must be set, nor an Import File must be found (as Import Files cannot serve for static linking).
>
> And "--with-aix-soname=aix", libtool still creates standalone "libNAME.so.X.Y.Z" along the (versioning-wise useless) symlinks.
>
> So it is up to the package manager which "aix-soname" variant to choose within its prefix: For example, in Gentoo Prefix I'm using "--with-aix-soname=svr4" only.
>
> But still, how to get ctypes.find_library() working - ideally for each variant, is another story. Right now it does not work for any variant,
Do you mean all systems, or specific to AIX - I am assuming you mean AIX.
> but I guess that search algorithm should follow how the linker discovers the (PATH,BASE,MEMBER) values to
I am not a tool builder. My comments are based on observations and 
experience from when I was a developer 25+ years ago. The AIX linker is 
not interested in the member name - it seems to go through the 
PATH/libBASE.a looking for the first object it can find to resolve a 
symbol. The name of the object it finds becomes the MEMBER it records in 
it's internal table of where to look later when the application runs.

This is why (on AIX) when a new archive is installed - even though there 
is a MEMBER that could resolve a symbol, if the MEMBER name has changed 
the rtld activity will fail. This is where (at least on AIX) using the 
LDONLY flag is important. The "old" MEMBER is there for "old" 
applications, but "new" applications will use a different object to 
resolve symbols. I would say the linker seraches for something to 
resolve a symbol (get it defined) while the rtld (run-time loader) 
locates a known BASE,MEMBER pair based on the internal "blibpath" and/or 
an optional environmental variable (LD_LIB_PATH, LD_LIBRARY - must check 
exact names - in any case AIX dlopen() knows, better recognizes two 
environmental variables, as a list of PATHs to follow to locate the 
BASE(MEMBER) pair.
>   write into just-linked executables, combined with how the runtime loader finds the Shared Object to actually load.
I worked on a patch - to do all that - taking into consideration the way 
libtool names .so files/members and then looking into/at "legacy" aka 
IBM dev ways they did things before the libtool model was so prominent.

My algorithm - attempts to solve the (PATH, BASE, MEMBER) problem as 
"dynamically" as possible. PATH and BASE are fairly straight forward - 
but MEMBER is clearly more complex.

PATH: start by looking at the python executable - and looking at it's 
"blibpath" - and using that as the default colon separated list of PATHs 
to search for BASE.a archive. Once a BASE.a file is found it is examined 
for a MEMBER. If all PATH/BASE.a do not find a potential MEMBER then the 
PATHs are examined again for PATH/BASE.so. When a .so file is found that 
is returned - versioning must be accomplished via a symbolic link to a 
versioned library.

The program "dump -H" provides this information for both executables and 
archive (aka BASE) members.

Starting from the "blibpath" values in the executable mean a cpython 
packager can add a specific PATH by adding it to 
LDFLAGS="-L/my/special/libdir:$LDFLAGS". Note that AIX archives also 
have their own "blibpath" - so libraries dynamically loaded may also 
follow additional paths that the executable is not aware of (nor need to 
be).

So - once the PATHS are determined the system is examined looking for 
${PATH}/BASE.a. If a target BASE.a is found, it is examined for a MEMBER 
is set to BASE.so (now used a MEMBER.so) . If MEMBER.so is not found 
then look for the "highest X[.Y[.Z]] aka MEMBER.so.X[.Y[.Z]] name. If 
that is not found check AIX legacy names (mainly shr.o or shr_64.o, 
although there are also certain key libraries that have additional 
variations (boring)).

Again, if PATH, BASE, MEMBER is not located as a .a archive - look for 
libFOO.so in all the PATH directories known to the executable.


I hope above is clear enough to continue the discussion. Thanks again 
for the discussion.

Michael

> ----------
>
> _______________________________________
> Python tracker <report@bugs.python.org>
> <http://bugs.python.org/issue27435>
> _______________________________________
History
Date User Action Args
2017-02-03 20:52:43Michael.Feltsetrecipients: + Michael.Felt, haubi, martin.panter
2017-02-03 20:52:43Michael.Feltsetmessageid: <1486155163.13.0.690023990803.issue27435@psf.upfronthosting.co.za>
2017-02-03 20:52:43Michael.Feltlinkissue27435 messages
2017-02-03 20:52:42Michael.Feltcreate