From 0dad8e9f9fb3ead9fee3f0f40215db38c7501568 Mon Sep 17 00:00:00 2001 From: Michael Felt Date: Thu, 11 Jan 2018 09:55:23 +0000 Subject: [PATCH 1/8] ignore .gitignore --- .gitignore | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.gitignore b/.gitignore index a8c73d4b14b3ac..e1640f41ca8528 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,10 @@ # Two-trick pony for OSX and other case insensitive file systems: # Ignore ./python binary on Unix but still look into ./Python/ directory. +.gitignore /python !/Python/ +.buildaix/ +buildaix/ *.cover *.o *.orig @@ -22,6 +25,7 @@ Doc/tools/pygments/ Doc/tools/sphinx/ Lib/lib2to3/*.pickle Lib/test/data/* +Lib/plat-aix?/ Makefile Makefile.pre Misc/python.pc @@ -30,6 +34,7 @@ Modules/Setup.config Modules/Setup.local Modules/config.c Modules/ld_so_aix +Modules/python.exp PC/python_nt*.h PC/pythonnt_rc*.h PC/*/*.exe From e5b86079934c3966312ac12f7522497b48fd106e Mon Sep 17 00:00:00 2001 From: Michael Felt Date: Thu, 11 Jan 2018 09:55:52 +0000 Subject: [PATCH 2/8] add link to aix find_library add some specific AIX print statements as part of the self-tests --- Lib/ctypes/util.py | 39 ++++++++++++++++++++++++++++++++------- 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/Lib/ctypes/util.py b/Lib/ctypes/util.py index ab10ec52ee8c35..b0bb3b24fa90d8 100644 --- a/Lib/ctypes/util.py +++ b/Lib/ctypes/util.py @@ -70,6 +70,15 @@ def find_library(name): def find_library(name): return name +if sys.platform.startswith("aix"): + # AIX has two styles of storing shared libraries + # GNU auto_tools refer to these as svr4 and aix + # svr4 (System V Release 4) is a regular file, often with .so as suffix + # AIX style uses an archive (suffix .a) with members (e.g., shr.o, libssl.so) + # see issue#26439 and _aix.py for more details + + from ctypes._aix import find_library + if os.name == "posix" and sys.platform == "darwin": from ctypes.macholib.dyld import dyld_find as _dyld_find def find_library(name): @@ -295,14 +304,30 @@ def test(): # load if sys.platform == "darwin": - print cdll.LoadLibrary("libm.dylib") - print cdll.LoadLibrary("libcrypto.dylib") - print cdll.LoadLibrary("libSystem.dylib") - print cdll.LoadLibrary("System.framework/System") + print(cdll.LoadLibrary("libm.dylib")) + print(cdll.LoadLibrary("libcrypto.dylib")) + print(cdll.LoadLibrary("libSystem.dylib")) + print(cdll.LoadLibrary("System.framework/System")) + # issue-26439 - fix broken test call for AIX + elif sys.platform.startswith("aix"): + from ctypes import CDLL + if sys.maxsize < 2**32: + print("Using CDLL(name, os.RTLD_MEMBER): " % CDLL('libc.a(shr.o)', os.RTLD_MEMBER)) + print("Using cdll.LoadLibrary(): " % cdll.LoadLibrary('libc.a(shr.o)')) + # librpm.so is only available as 32-bit shared library + print(find_library("rpm")) + print(cdll.LoadLibrary("librpm.so")) + else: + print("Using CDLL(name, os.RTLD_MEMBER): " % CDLL('libc.a(shr_64.o)', os.RTLD_MEMBER)) + print("Using cdll.LoadLibrary(): " % cdll.LoadLibrary('libc.a(shr_64.o)')) + print("crypt\t:: " % {find_library('crypt')}) + print("crypt\t:: " % {cdll.LoadLibrary(find_library('crypt'))}) + print("crypto\t:: " % {find_library('crypto')}) + print("crypto\t:: " % {cdll.LoadLibrary(find_library('crypto'))}) else: - print cdll.LoadLibrary("libm.so") - print cdll.LoadLibrary("libcrypt.so") - print find_library("crypt") + print(cdll.LoadLibrary("libm.so")) + print(cdll.LoadLibrary("libcrypt.so")) + print(find_library("crypt")) if __name__ == "__main__": test() From 4b0255a40f12bb61aa70980ba77bcc6f2c4c8bbb Mon Sep 17 00:00:00 2001 From: Michael Felt Date: Thu, 11 Jan 2018 09:57:27 +0000 Subject: [PATCH 3/8] remove f-string syntax - may need additional syntax corrections --- Lib/ctypes/_aix.py | 331 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 331 insertions(+) create mode 100644 Lib/ctypes/_aix.py diff --git a/Lib/ctypes/_aix.py b/Lib/ctypes/_aix.py new file mode 100644 index 00000000000000..0c171924aa26eb --- /dev/null +++ b/Lib/ctypes/_aix.py @@ -0,0 +1,331 @@ +""" +Lib/ctypes.util.find_library() support for AIX +Similar approach as done for Darwin support by using separate files +but unlike Darwin - no extension such as ctypes.macholib.* + +dlopen() is an interface to AIX initAndLoad() - primary documentation at: +https://www.ibm.com/support/knowledgecenter/en/ssw_aix_61/com.ibm.aix.basetrf1/dlopen.htm +https://www.ibm.com/support/knowledgecenter/en/ssw_aix_61/com.ibm.aix.basetrf1/load.htm + +AIX supports two styles for dlopen(): svr4 (System V Release 4) which is common on posix +platforms, but also a BSD style - aka SVR3. + +From AIX 5.3 Difference Addendum (December 2004) +2.9 SVR4 linking affinity +Nowadays, there are two major object file formats used by the operating systems: +XCOFF: The COFF enhanced by IBM and others. The original COFF (Common +Object File Format) was the base of SVR3 and BSD 4.2 systems. +ELF: Executable and Linking Format that was developed by AT&T and is a +base for SVR4 UNIX. + +While the shared library content is identical on AIX - one is located as a filepath name +(svr4 style) and the other is located as a member of an archive (and the archive +is located as a filepath name). + +The key difference arises when supporting multiple abi formats (i.e., 32 and 64 bit). +For svr4 either only one ABI is supported, or there are two directories, or there +are different file names. The most common solution for multiple ABI is multiple +directories. + +For the XCOFF (aka AIX) style - one directory (one archive file) is sufficient +as multiple shared libraries can be in the archive - even sharing the same name. +In documentation the archive is also referred to as the "base" and the shared +library object is referred to as the "member". + +For dlopen() on AIX (read initAndLoad()) the calls are similiar. +Default activity occurs when no path information is provided. When path +information is provided dlopen() does not search any other directories. + +For SVR4 - the shared library name is the name of the file expected: libFOO.so +For AIX - the shared library is expressed as base(member). The search is for the +base (e.g., libFOO.a) and once the base is found the shared library - identified by +member (e.g., libFOO.so, or shr.o) is located and loaded. + +The mode bit RTLD_MEMBER tells initAndLoad() that it needs to use the AIX (SVR3) +naming style. +""" +__author__ = "Michael Felt " + +import re +from os import environ, path +from sys import executable +from ctypes import c_void_p, sizeof +from subprocess import Popen, PIPE, DEVNULL + +# Executable bit size - 32 or 64 +# Used to filter the search in an archive by size, e.g., -X64 +AIX_ABI = sizeof(c_void_p) * 8 + + +from sys import maxsize +def _last_version(libnames, sep): + def _num_version(libname): + # "libxyz.so.MAJOR.MINOR" => [MAJOR, MINOR] + parts = libname.split(sep) + nums = [] + try: + while parts: + nums.insert(0, int(parts.pop())) + except ValueError: + pass + return nums or [maxsize] + return max(reversed(libnames), key=_num_version) + +def get_ld_header(p): + # "nested-function, but placed at module level + ld_header = None + for line in p.stdout: + if line.startswith(('/', './', '../')): + ld_header = line + elif "INDEX" in line: + return ld_header.rstrip('\n') + return None + +def get_ld_header_info(p): + # "nested-function, but placed at module level + # as an ld_header was found, return known paths, archives and members + # these lines start with a digit + info = [] + for line in p.stdout: + if re.match("[0-9]", line): + info.append(line) + else: + # blank line (seperator), consume line and end for loop + break + return info + +def get_ld_headers(file): + """ + Parse the header of the loader section of executable and archives + This function calls /usr/bin/dump -H as a subprocess + and returns a list of (ld_header, ld_header_info) tuples. + """ + # get_ld_headers parsing: + # 1. Find a line that starts with /, ./, or ../ - set as ld_header + # 2. If "INDEX" in occurs in a following line - return ld_header + # 3. get info (lines starting with [0-9]) + ldr_headers = [] + p = Popen(["/usr/bin/dump", "-X%{AIX_ABI}", "-H", file], + universal_newlines=True, stdout=PIPE, stderr=DEVNULL) + # be sure to read to the end-of-file - getting all entries + while True: + ld_header = get_ld_header(p) + if ld_header: + ldr_headers.append((ld_header, get_ld_header_info(p))) + else: + break + p.stdout.close() + p.wait + return ldr_headers + +def get_shared(ld_headers): + """ + extract the shareable objects from ld_headers + character "[" is used to strip off the path information. + Note: the "[" and "]" characters that are part of dump -H output + are not removed here. + """ + shared = [] + for (line, _) in ld_headers: + # potential member lines contain "[" + # otherwise, no processing needed + if "[" in line: + # Strip off trailing colon (:) + shared.append(line[line.index("["):-1]) + return shared + +def get_one_match(expr, lines): + """ + Must be only one match, otherwise result is None. + When there is a match, strip leading "[" and trailing "]" + """ + # member names in the ld_headers output are between square brackets + expr = r'\[({expr})\]' + matches = list(filter(None, (re.search(expr, line) for line in lines))) + if len(matches) == 1: + return matches[0].group(1) + else: + return None + +# additional processing to deal with AIX legacy names for 64-bit members +def get_legacy(members): + """ + This routine provides historical aka legacy naming schemes started + in AIX4 shared library support for library members names. + e.g., in /usr/lib/libc.a the member name shr.o for 32-bit binary and + shr_64.o for 64-bit binary. + """ + if AIX_ABI == 64: + # AIX 64-bit member is one of shr64.o, shr_64.o, or shr4_64.o + expr = r'shr4?_?64\.o' + member = get_one_match(expr, members) + if member: + return member + else: + # 32-bit legacy names - both shr.o and shr4.o exist. + # shr.o is the preffered name so we look for shr.o first + # i.e., shr4.o is returned only when shr.o does not exist + for name in ['shr.o', 'shr4.o']: + member = get_one_match(re.escape(name), members) + if member: + return member + return None + +def get_version(name, members): + """ + Sort list of members and return highest numbered version - if it exists. + This function is called when an unversioned libFOO.a(libFOO.so) has + not been found. + + Versioning for the member name is expected to follow + GNU LIBTOOL conventions: the highest version (x, then X.y, then X.Y.z) + * find [libFoo.so.X] + * find [libFoo.so.X.Y] + * find [libFoo.so.X.Y.Z] + + Before the GNU convention became the standard scheme regardless of + binary size AIX packagers used GNU convention "as-is" for 32-bit + archive members but used an "distinguishing" name for 64-bit members. + This scheme inserted either 64 or _64 between libFOO and .so + - generally libFOO_64.so, but occasionally libFOO64.so + """ + # the expression ending for versions must start as + # '.so.[0-9]', i.e., *.so.[at least one digit] + # while multiple, more specific expressions could be specified + # to search for .so.X, .so.X.Y and .so.X.Y.Z + # after the first required 'dot' digit + # any combination of additional 'dot' digits pairs are accepted + # anything more than libFOO.so.digits.digits.digits + # should be seen as a member name outside normal expectations + exprs = [r'lib{name}\.so\.[0-9]+[0-9.]*', + r'lib{name}_?64\.so\.[0-9]+[0-9.]*'] + for expr in exprs: + versions = [] + for line in members: + m = re.search(expr, line) + if m: + versions.append(m.group(0)) + if versions: + return _last_version(versions, '.') + return None + +def get_member(name, members): + """ + Return an archive member matching the request in name. + Name is the library name without any prefix like lib, suffix like .so, + or version number. + Given a list of members find and return the most appropriate result + Priority is given to generic libXXX.so, then a versioned libXXX.so.a.b.c + and finally, legacy AIX naming scheme. + """ + # look first for a generic match - prepend lib and append .so + expr = r'lib{name}\.so' + member = get_one_match(expr, members) + if member: + return member + elif AIX_ABI == 64: + expr = r'lib{name}64\.so' + member = get_one_match(expr, members) + if member: + return member + # since an exact match with .so as suffix was not found + # look for a versioned name + # If a versioned name is not found, look for AIX legacy member name + member = get_version(name, members) + if member: + return member + else: + return get_legacy(members) + +def get_libpaths(): + """ + On AIX, the buildtime searchpath is stored in the executable. + as "loader header information". + The command /usr/bin/dump -H extracts this info. + Prefix searched libraries with LD_LIBRARY_PATH (preferred), + or LIBPATH if defined. These paths are appended to the paths + to libraries the python executable is linked with. + This mimics AIX dlopen() behavior. + """ + libpaths = environ.get("LD_LIBRARY_PATH") + if libpaths is None: + libpaths = environ.get("LIBPATH") + if libpaths is None: + libpaths = [] + else: + libpaths = libpaths.split(":") + objects = get_ld_headers(executable) + for (_, lines) in objects: + for line in lines: + # the second (optional) argument is PATH if it includes a / + path = line.split()[1] + if "/" in path: + libpaths.extend(path.split(":")) + return libpaths + +def find_shared(paths, name): + """ + paths is a list of directories to search for an archive. + name is the abbreviated name given to find_library(). + Process: search "paths" for archive, and if an archive is found + return the result of get_member(). + If an archive is not found then return None + """ + for dir in paths: + # /lib is a symbolic link to /usr/lib, skip it + if dir == "/lib": + continue + # "lib" is prefixed to emulate compiler name resolution, + # e.g., -lc to libc + base = "lib%s.a % name" + archive = path.join(dir, base) + if path.exists(archive): + members = get_shared(get_ld_headers(archive)) + member = get_member(re.escape(name), members) + if member != None: + return (base, member) + else: + return (None, None) + return (None, None) + +def find_library(name): + """AIX implementation of ctypes.util.find_library() + Find an archive member that will dlopen(). If not available, + also search for a file (or link) with a .so suffix. + + AIX supports two types of schemes that can be used with dlopen(). + The so-called SystemV Release4 (svr4) format is commonly suffixed + with .so while the (default) AIX scheme has the library (archive) + ending with the suffix .a + As an archive has multiple members (e.g., 32-bit and 64-bit) in one file + the argument passed to dlopen must include both the library and + the member names in a single string. + + find_library() looks first for an archive (.a) with a suitable member. + If no archive+member pair is found, look for a .so file. + """ + + libpaths = get_libpaths() + (base, member) = find_shared(libpaths, name) + if base != None: + return "{base}({member})" + + # To get here, a member in an archive has not been found + # In other words, either: + # a) a .a file was not found + # b) a .a file did not have a suitable member + # So, look for a .so file + # Check libpaths for .so file + # Note, the installation must prepare a link from a .so + # to a versioned file + # This is common practice by GNU libtool on other platforms + soname = "lib{name}.so" + for dir in libpaths: + # /lib is a symbolic link to /usr/lib, skip it + if dir == "/lib": + continue + shlib = path.join(dir, soname) + if path.exists(shlib): + return soname + # if we are here, we have not found anything plausible + return None From 05c97141b51a20b892dfbac94ebe906e8b852e4e Mon Sep 17 00:00:00 2001 From: Michael Felt Date: Thu, 11 Jan 2018 10:01:02 +0000 Subject: [PATCH 4/8] remove reference to DEVNULL, not available in Python2 --- Lib/ctypes/_aix.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/ctypes/_aix.py b/Lib/ctypes/_aix.py index 0c171924aa26eb..e87e3f2b207c37 100644 --- a/Lib/ctypes/_aix.py +++ b/Lib/ctypes/_aix.py @@ -50,7 +50,7 @@ from os import environ, path from sys import executable from ctypes import c_void_p, sizeof -from subprocess import Popen, PIPE, DEVNULL +from subprocess import Popen, PIPE # Executable bit size - 32 or 64 # Used to filter the search in an archive by size, e.g., -X64 @@ -106,7 +106,7 @@ def get_ld_headers(file): # 3. get info (lines starting with [0-9]) ldr_headers = [] p = Popen(["/usr/bin/dump", "-X%{AIX_ABI}", "-H", file], - universal_newlines=True, stdout=PIPE, stderr=DEVNULL) + universal_newlines=True, stdout=PIPE) # be sure to read to the end-of-file - getting all entries while True: ld_header = get_ld_header(p) From 32bfcdad5847d362cffb7d25973e4b471bada01b Mon Sep 17 00:00:00 2001 From: Michael Felt Date: Thu, 11 Jan 2018 10:41:23 +0000 Subject: [PATCH 5/8] Added RTLD_MEMBER, but doubt if it is really needed in Python2 --- Modules/dlmodule.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Modules/dlmodule.c b/Modules/dlmodule.c index 7a6686e3a6dfc4..36bca68a236e2c 100644 --- a/Modules/dlmodule.c +++ b/Modules/dlmodule.c @@ -284,4 +284,7 @@ initdl(void) #ifdef RTLD_NODELETE INSINT(RTLD_NODELETE); #endif +#ifdef RTLD_MEMBER + INSINT(RTLD_MEMBER); +#endif } From 8e03e3a900b9971b863c75c10454acb950333d5b Mon Sep 17 00:00:00 2001 From: Michael Felt Date: Thu, 11 Jan 2018 10:41:53 +0000 Subject: [PATCH 6/8] Added RTLD_MEMBER, and RTLD_NOW - so they could be imported FYI: RTLD_NOW is already used, but buried in current code These are added so that they can be imported rather than static define in .py code Note - in Python3 these will be os.RTLD_MEMBER and os.RTLD_NOW --- Modules/_ctypes/_ctypes.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index 4e192fb3ad2818..6e7a5002ba0827 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -5737,6 +5737,12 @@ init_ctypes(void) PyModule_AddObject(m, "RTLD_LOCAL", PyInt_FromLong(RTLD_LOCAL)); PyModule_AddObject(m, "RTLD_GLOBAL", PyInt_FromLong(RTLD_GLOBAL)); +#ifdef RTLD_NOW + PyModule_AddObject(m, "RTLD_NOW", PyInt_FromLong(RTLD_NOW)); +#endif +#ifdef RTLD_MEMBER + PyModule_AddObject(m, "RTLD_MEMBER", PyInt_FromLong(RTLD_MEMBER)); +#endif PyExc_ArgError = PyErr_NewException("ctypes.ArgumentError", NULL, NULL); if (PyExc_ArgError) { From 12315bc380cbd5720b97e5c1c1bf4d9578faf573 Mon Sep 17 00:00:00 2001 From: Michael Felt Date: Thu, 11 Jan 2018 10:45:22 +0000 Subject: [PATCH 7/8] basic changes to make _aix.find_library() callable --- Lib/ctypes/__init__.py | 10 ++++++++++ Lib/ctypes/util.py | 5 +++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/Lib/ctypes/__init__.py b/Lib/ctypes/__init__.py index 88c85ff3887506..e5006d6c066a51 100644 --- a/Lib/ctypes/__init__.py +++ b/Lib/ctypes/__init__.py @@ -356,6 +356,16 @@ def __init__(self, name, mode=DEFAULT_MODE, handle=None, flags |= _FUNCFLAG_USE_ERRNO if use_last_error: flags |= _FUNCFLAG_USE_LASTERROR + if _sys.platform.startswith("aix"): + """When the name contains ".a(" and ends with ")", + e.g., "libFOO.a(libFOO.so)" - this is taken to be an + archive(member) syntax for dlopen(), and the mode is adjusted. + Otherwise, name is presented to dlopen() as a file argument. + """ + from _ctypes import RTLD_MEMBER, RTLD_NOW + if name and name.endswith(")") and ".a(" in name: + mode |= ( RTLD_MEMBER | RTLD_NOW ) + class _FuncPtr(_CFuncPtr): _flags_ = flags diff --git a/Lib/ctypes/util.py b/Lib/ctypes/util.py index b0bb3b24fa90d8..2855a5d05a8022 100644 --- a/Lib/ctypes/util.py +++ b/Lib/ctypes/util.py @@ -311,14 +311,15 @@ def test(): # issue-26439 - fix broken test call for AIX elif sys.platform.startswith("aix"): from ctypes import CDLL + from _ctypes import RTLD_MEMBER if sys.maxsize < 2**32: - print("Using CDLL(name, os.RTLD_MEMBER): " % CDLL('libc.a(shr.o)', os.RTLD_MEMBER)) + print("Using CDLL(name, RTLD_MEMBER): " % CDLL('libc.a(shr.o)', RTLD_MEMBER)) print("Using cdll.LoadLibrary(): " % cdll.LoadLibrary('libc.a(shr.o)')) # librpm.so is only available as 32-bit shared library print(find_library("rpm")) print(cdll.LoadLibrary("librpm.so")) else: - print("Using CDLL(name, os.RTLD_MEMBER): " % CDLL('libc.a(shr_64.o)', os.RTLD_MEMBER)) + print("Using CDLL(name, RTLD_MEMBER): " % CDLL('libc.a(shr_64.o)', RTLD_MEMBER)) print("Using cdll.LoadLibrary(): " % cdll.LoadLibrary('libc.a(shr_64.o)')) print("crypt\t:: " % {find_library('crypt')}) print("crypt\t:: " % {cdll.LoadLibrary(find_library('crypt'))}) From 803f571c5eb7cacb64b9e23428b9a6b40369dccb Mon Sep 17 00:00:00 2001 From: Michael Felt Date: Thu, 11 Jan 2018 11:57:52 +0000 Subject: [PATCH 8/8] finish changes needed to undo "f-string" feature of Python3.7 also - move "aix" block in util.py so that it is processed correctly --- Lib/ctypes/_aix.py | 21 ++++++++++++--------- Lib/ctypes/util.py | 37 ++++++++++++++++++------------------- 2 files changed, 30 insertions(+), 28 deletions(-) diff --git a/Lib/ctypes/_aix.py b/Lib/ctypes/_aix.py index e87e3f2b207c37..8a069faf393c48 100644 --- a/Lib/ctypes/_aix.py +++ b/Lib/ctypes/_aix.py @@ -105,7 +105,7 @@ def get_ld_headers(file): # 2. If "INDEX" in occurs in a following line - return ld_header # 3. get info (lines starting with [0-9]) ldr_headers = [] - p = Popen(["/usr/bin/dump", "-X%{AIX_ABI}", "-H", file], + p = Popen(["/usr/bin/dump", "-X%s"%AIX_ABI, "-H", file], universal_newlines=True, stdout=PIPE) # be sure to read to the end-of-file - getting all entries while True: @@ -140,7 +140,10 @@ def get_one_match(expr, lines): When there is a match, strip leading "[" and trailing "]" """ # member names in the ld_headers output are between square brackets - expr = r'\[({expr})\]' + # expr = r'\[({expr})\]' + # expr = r'\[({%s})\]' % expr + # expr = r'\[{%s}\]' % expr + expr = r'\[(%s)\]' % expr matches = list(filter(None, (re.search(expr, line) for line in lines))) if len(matches) == 1: return matches[0].group(1) @@ -197,8 +200,8 @@ def get_version(name, members): # any combination of additional 'dot' digits pairs are accepted # anything more than libFOO.so.digits.digits.digits # should be seen as a member name outside normal expectations - exprs = [r'lib{name}\.so\.[0-9]+[0-9.]*', - r'lib{name}_?64\.so\.[0-9]+[0-9.]*'] + exprs = [r'lib%s\.so\.[0-9]+[0-9.]*' % name, + r'lib%s_?64\.so\.[0-9]+[0-9.]*' % name] for expr in exprs: versions = [] for line in members: @@ -219,12 +222,12 @@ def get_member(name, members): and finally, legacy AIX naming scheme. """ # look first for a generic match - prepend lib and append .so - expr = r'lib{name}\.so' + expr = r'lib%s\.so' % name member = get_one_match(expr, members) if member: return member elif AIX_ABI == 64: - expr = r'lib{name}64\.so' + expr = r'lib%s64\.so' % name member = get_one_match(expr, members) if member: return member @@ -277,7 +280,7 @@ def find_shared(paths, name): continue # "lib" is prefixed to emulate compiler name resolution, # e.g., -lc to libc - base = "lib%s.a % name" + base = "lib%s.a" % name archive = path.join(dir, base) if path.exists(archive): members = get_shared(get_ld_headers(archive)) @@ -308,7 +311,7 @@ def find_library(name): libpaths = get_libpaths() (base, member) = find_shared(libpaths, name) if base != None: - return "{base}({member})" + return "%s(%s)" % (base,member) # To get here, a member in an archive has not been found # In other words, either: @@ -319,7 +322,7 @@ def find_library(name): # Note, the installation must prepare a link from a .so # to a versioned file # This is common practice by GNU libtool on other platforms - soname = "lib{name}.so" + soname = "lib%s.so" % name for dir in libpaths: # /lib is a symbolic link to /usr/lib, skip it if dir == "/lib": diff --git a/Lib/ctypes/util.py b/Lib/ctypes/util.py index 2855a5d05a8022..f2ce1fc139855b 100644 --- a/Lib/ctypes/util.py +++ b/Lib/ctypes/util.py @@ -70,15 +70,6 @@ def find_library(name): def find_library(name): return name -if sys.platform.startswith("aix"): - # AIX has two styles of storing shared libraries - # GNU auto_tools refer to these as svr4 and aix - # svr4 (System V Release 4) is a regular file, often with .so as suffix - # AIX style uses an archive (suffix .a) with members (e.g., shr.o, libssl.so) - # see issue#26439 and _aix.py for more details - - from ctypes._aix import find_library - if os.name == "posix" and sys.platform == "darwin": from ctypes.macholib.dyld import dyld_find as _dyld_find def find_library(name): @@ -92,6 +83,15 @@ def find_library(name): continue return None +elif sys.platform.startswith("aix"): + # AIX has two styles of storing shared libraries + # GNU auto_tools refer to these as svr4 and aix + # svr4 (System V Release 4) is a regular file, often with .so as suffix + # AIX style uses an archive (suffix .a) with members (e.g., shr.o, libssl.so) + # see issue#26439 and _aix.py for more details + + from ctypes._aix import find_library + elif os.name == "posix": # Andreas Degert's find functions, using gcc, /sbin/ldconfig, objdump import re, tempfile, errno @@ -313,18 +313,17 @@ def test(): from ctypes import CDLL from _ctypes import RTLD_MEMBER if sys.maxsize < 2**32: - print("Using CDLL(name, RTLD_MEMBER): " % CDLL('libc.a(shr.o)', RTLD_MEMBER)) - print("Using cdll.LoadLibrary(): " % cdll.LoadLibrary('libc.a(shr.o)')) + print("Using CDLL(name, RTLD_MEMBER): %s" % CDLL('libc.a(shr.o)', RTLD_MEMBER)) + print("Using cdll.LoadLibrary(): %s" % cdll.LoadLibrary('libc.a(shr.o)')) # librpm.so is only available as 32-bit shared library - print(find_library("rpm")) - print(cdll.LoadLibrary("librpm.so")) + print("rpm: %s %s" % (find_library("rpm"), cdll.LoadLibrary("librpm.so"))) else: - print("Using CDLL(name, RTLD_MEMBER): " % CDLL('libc.a(shr_64.o)', RTLD_MEMBER)) - print("Using cdll.LoadLibrary(): " % cdll.LoadLibrary('libc.a(shr_64.o)')) - print("crypt\t:: " % {find_library('crypt')}) - print("crypt\t:: " % {cdll.LoadLibrary(find_library('crypt'))}) - print("crypto\t:: " % {find_library('crypto')}) - print("crypto\t:: " % {cdll.LoadLibrary(find_library('crypto'))}) + print("Using CDLL(name, RTLD_MEMBER): %s", CDLL('libc.a(shr_64.o)', RTLD_MEMBER)) + print("Using cdll.LoadLibrary(): %s", cdll.LoadLibrary('libc.a(shr_64.o)')) + print("crypt :: %s" % find_library('crypt')) + print("crypt :: %s" % cdll.LoadLibrary(find_library('crypt'))) + print("crypto:: %s" % find_library('crypto')) + print("crypto:: %s" % cdll.LoadLibrary(find_library('crypto'))) else: print(cdll.LoadLibrary("libm.so")) print(cdll.LoadLibrary("libcrypt.so"))