--- ./Lib/ctypes/__init__.py.ORG	2017-10-17 04:21:56 -0500
+++ ./Lib/ctypes/__init__.py	2017-10-17 04:23:11 -0500
@@ -352,6 +352,16 @@
             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 ")",
+               asin "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_NOW - not until Python3.7
+            if name and name.endswith(")") and ".a(" in name:
+                RTLD_MEMBER = 0x00040000
+                mode |= RTLD_MEMBER
 
         class _FuncPtr(_CFuncPtr):
             _flags_ = flags
--- ./Lib/ctypes/_aix.py.ORG	2017-10-17 04:29:00 -0500
+++ ./Lib/ctypes/_aix.py	2017-10-17 04:29:42 -0500
@@ -0,0 +1,317 @@
+"""Internal platform implementation of find_library() for AIX.
+Lib/ctype support for LoadLibrary interface to dlopen() for AIX
+s implemented as a separate file, similiar to Darwin support ctype.macholib.*
+rather than as separate sections in utils.py for improved
+isolation and (I hope) readability
+
+dlopen() is implemented on AIX as initAndLoad() - official documentation at:
+https://www.ibm.com/support/knowledgecenter/ssw_aix_53/\
+com.ibm.aix.basetechref/doc/basetrf1/dlopen.htm?lang=en and
+https://www.ibm.com/support/knowledgecenter/ssw_aix_53/\
+com.ibm.aix.basetechref/doc/basetrf1/load.htm?lang=en
+"""
+# Author: M Felt, aixtools.net, 2016
+# I thank Martin Panter for his patience and comments
+
+import os
+import re
+import sys
+import subprocess
+
+def aixABI():
+    """
+    Internal support function:
+    return executable size - 32-bit, or 64-bit
+    This is vital to the search for suitable member in an archive
+    """
+    if (sys.maxsize < 2**32):
+        return 32
+    else:
+        return 64
+
+def get_dumpH(file):
+    """Internal support function:
+    dump -H output provides info on archive/executable contents
+    and related paths to shareable members.
+    """
+    #dumpH parseing:
+    # 1. Line starts with /, ./, or ../ - set object name
+    # 2. When the string "INDEX" is located -> return object
+    # 3. get path/archive/member info (lines starting with digits [0-9])
+
+    def get_object(p, object=None):
+        for line in p.stdout:
+            if (line.startswith('/')
+                    or line.startswith('./')
+                    or line.startswith('../')):
+                object = line
+            elif "INDEX" in line:
+                return object
+        return None
+
+    def get_objectinfo(p):
+    # as an object was found, return known paths, archives and members
+    # these lines start with a digit
+    # an empty line or end-of-file terminates the for loop
+        lines = None
+        for line in p.stdout:
+            if re.match("[0-9]", line, flags=0):
+                if lines is None:
+                    lines = line
+                else:
+                    lines += line
+            else:
+                break
+        # lines should never be blank, the if: block is only for safety
+        if lines:
+            return lines.strip()
+        else:
+            return ""
+
+    # The subprocess calls dump -H without shell assistence.
+    # Unneeded lines are removed by get_object() and get_objectinfo()
+    p = subprocess.Popen(["/usr/bin/dump", "-X%s"%aixABI(), "-H", file],
+        universal_newlines=True, stdout=subprocess.PIPE, shell=False)
+    lines = None
+    while 1:
+        object = get_object(p)
+        # get_object() returns None when archive has
+        # no (more) shareable members or end-of-file was reached 
+        if object is None:
+            break
+        if lines is None:
+            lines = object
+        else:
+            lines += "\n\n%s" % object
+        lines += get_objectinfo(p)
+
+    p.stdout.close()
+    p.wait()
+    return lines
+
+def get_members(input):
+    """Internal support function: return a list of all shareable objects"""
+    # the character "[" is identifies object_lines
+    # so that "/usr/lib/libFOO.a[member.so]:" returns as "[member.so]"
+    list=[]
+    if input is None:
+        return(list)
+    lines = input.split("\n")
+    for line in lines:
+        # potential member lines contain "["
+        # otherwise, no processing needed
+        if "[" in line:
+            list.append(line[line.find("["):-1])
+    return(list)
+
+def get_exactMatch(expr, lines):
+    """Internal support function:
+    Must be only one match, otherwise result is None
+    When there is a match, strip the leading "[" and trailing "]"
+    """
+    matches = [m.group(0) for line in lines for m in [
+                  re.search(expr, line)] if m]
+    if len(matches) == 1:
+        return matches[0][1:-1]
+    else:
+        return None
+
+# additional processing to deal with AIX legacy names for 64-bit members
+def get_legacy(members):
+    """Internal support function:
+    This routine resolves various legacy naming schemes starting in AIX4
+    This routine catches various schemes for 64-bit legacy names
+    """
+    if aixABI() == 64:
+        # AIX 64-bit member is usually shr_64.o although
+        # shr64.o or shr4_64.o have been seen - in "ancient" archives
+        expr = '\[shr4?_?64.o\]'
+        member = get_exactMatch(expr, members)
+        if member:
+            return member
+    # 32-bit legacy names
+    # shr.o and shr4.o are both available - frequently
+    # shr.o is preferred and returned rather than shr4.o
+    # i.e., shr4.o is returned only when shr.o does not exist
+    else:
+        for expr in [
+            r'\[shr.o\]',
+            r'\[shr4.o\]']:
+                member = get_exactMatch(expr, members)
+                if member:
+                    return member
+    return None
+
+# Get the most recent/highest numbered version - if it exists
+# In an new ABI (perhaps for Python3.7) could be called
+# as a request for the latest version installed, or a specific version
+# Currently, only called when when unversioned (libFOO.so) is not available
+def get_version(name, members):
+    """Internal support function:
+    Examine member list and return highest version number
+    """
+    # This is called when libFFO.a(libFFO.so) has not been found.
+    # Version numbering is expected to follow GNU LIBTOOL conventions:
+    # d.1 find [libFoo.so.XX]
+    # d.2 find [libFoo.so.XX.YY]
+    # d.3 find [libFoo.so.XX.Y.ZZZ]
+
+    # Before the GNU convention took hold there was a convention in AIX
+    # to use GNU convention "asis" for 32-bit archive members but in
+    # 64-bit members add either 64 or _64 between libFOO and .so 
+    # to generally libFOO_64.so, but occaisionally libFOO64.so
+    # Generally, the second expression that looks for "64"
+    # in the member name will not be needed.
+
+    # "libxyz.so.CURRENT.REVISION.AGE" => [ CURRENT, REVISION, AGE ]
+    # the original idea for _num_version comes from
+    # an earlier ctype.util internal function
+    def _num_version(libname):
+        parts = libname.split(".")
+        nums = []
+        try:
+            while parts:
+                nums.insert(0, int(parts.pop()))
+        except ValueError:
+            pass
+        return nums or [ sys.maxsize ]
+
+    # A "versioned" member name ends with .so.X plus
+    # additional, but optional dot + digit pairs
+    # while multiple, more specific expressions could be specified
+    # to search for .so.X, .so.X.Y and .so.X.Y.Z
+    # we chose to use a single re that finds the required part
+    # and then accepts additional '.' and digits
+    # 
+    # imho, anything other that .so.digit(s).digit(s).digit(s)
+    # with only the first .so.digit(s) required is someone purposely
+    # trying to break the parser
+    for expr in [
+        r'lib%s\.so\.[0-9]+[0-9.]*' % name,
+        r'lib%s_?64\.so\.[0-9]+[0-9.]*' % name]:
+
+        versions = [m.group(0) for line in members for m in [
+            re.search(expr, line)] if m]
+        if versions:
+            versions.sort(key=_num_version)
+            return versions[-1]
+    return None
+
+def get_member(name, members):
+    """Internal support function:
+    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 lastly, legacy AIX naming scheme
+    """
+    # look first for a generic match - prepend lib and append .so
+    # '\[lib%s\.so\]' % name       -> most common
+    expr = r'\[lib%s.so\]' % name
+    member = get_exactMatch(expr, members)
+    if member:
+        return member
+    # libFOO.so was not found ...
+    member = get_version(name, members)
+    if member:
+        return member
+    else:
+        return get_legacy(members)
+
+def getExecLibPath_aix():
+    """Internal support function:
+    On AIX, the searchpath is stored in the executable.
+    The command dump -H can extract this info.
+    Prefix searched libraries with LIBPATH, or LD_LIBRARY_PATH if defined.
+    Paths are appended based rather than inserted.
+    This mimics AIX dlopen() behavior.
+    """
+
+    # os.environ.get commented out in Python2.7 - see issue#9998
+    # libpaths = os.environ.get("LIBPATH")
+    # if libpaths is None:
+        # libpaths = os.environ.get("LD_LIBRARY_PATH")
+    # so, libpaths is manually set to None
+    libpaths = None
+    lines = get_dumpH(sys.executable).split("\n")
+    for line in lines:
+        # valid lines begin with a digit (INDEX)
+        # the second (optional) argument is PATH if it includes a /
+        if re.match("[0-9]", line, flags=0):
+            path = line.split()[1]
+        else:
+            continue
+        if "/" in path:
+            if libpaths is None:
+                libpaths = path
+            else:
+                libpaths = "%s:%s" % (libpaths,path)
+    return libpaths
+
+def find_library(name):
+    """AIX platform implementation of find_library()
+    This routine is called by ctype.util.find_library().
+    This routine should not be called directly!
+    
+    AIX loader behavior:
+    Find a library looking first for an archive (.a) with a suitable member
+    For the loader the member name is not relevant. The first archive object
+    shared or static that resolves an unknown symbol is used by "ld"
+
+    To mimic AIX rtld behavior this routines looks first for an archive
+    libFOO.a and then examines the contents of the archive for shareable
+    members that also match either libFOO.so, libFOO.so.X.Y.Z (so-called
+    versioned library names) and finally AIX legacy names
+    usually shr.o (32-bit members) and shr_64.o (64-bit members)
+    
+    When no archive(member) is found, look for a libFOO.so file
+    When versioning is required - it must be provided via hard
+    or soft-link from libFOO.so to the correct version.
+    RTLD aka dlopen() does not do versioning.
+    """
+
+    # paths is where to look for archives "basename"
+    #  _member is the member name based on archive basename
+    def find_shared(paths, name):
+        """
+        Search "paths" for archive, and if an archive is found
+        return the result of get_member(member, archive).
+        If no archives are found return None
+        """
+        for dir in paths.split(":"):
+            # /lib is a symbolic link to /usr/lib, skip it
+            if dir == "/lib":
+                continue
+            # "lib" is prefixed to emulate compiler name resolution,
+            # e.g., -lFOO to libFOO
+            base = 'lib%s.a' % name
+            archive = os.path.join(dir, "%s" % base)
+            if os.path.exists(archive):
+                members =  get_members(get_dumpH(archive))
+                member = get_member(name, members)
+                if member != None:
+                    return(base, member)
+                else:
+                    return(None, None)
+        return(None, None)
+
+    libpaths = getExecLibPath_aix()
+    (base, member) = find_shared(libpaths, name)
+    if base != None:
+        return "%s(%s)" % (base, member)
+
+    # Being here means 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
+
+    # Check libpaths for .so file
+    soname = "lib%s.so" % name
+    for dir in libpaths.split(":"):
+        # /lib is a symbolic link to /usr/lib, skip it
+        if dir == "/lib":
+            continue
+        shlib = os.path.join(dir, "%s" % soname)
+        if os.path.exists(shlib):
+            return soname
+    # Being here means nothing suitable was found
+    return None
+
--- ./Lib/ctypes/test/test_loading.py.ORG	2017-10-17 04:31:18 -0500
+++ ./Lib/ctypes/test/test_loading.py	2017-10-17 04:34:34 -0500
@@ -11,6 +11,11 @@
     libc_name = "coredll"
 elif sys.platform == "cygwin":
     libc_name = "cygwin1.dll"
+elif sys.platform.startswith("aix"):
+    if (sys.maxsize < 2**32):
+        libc_name = "libc.a(shr.o)"
+    else:
+        libc_name = "libc.a(shr_64.o)"
 else:
     libc_name = find_library("c")
 
@@ -38,11 +43,16 @@
         self.assertRaises(OSError, cdll.LoadLibrary, self.unknowndll)
 
     def test_find(self):
+        # to track that at least one call to find_library() found something
+        found = False
         for name in ("c", "m"):
             lib = find_library(name)
             if lib:
+                found = True
                 cdll.LoadLibrary(lib)
                 CDLL(lib)
+        # test is FAILED if nothing was found
+        self.assertTrue(found)
 
     @unittest.skipUnless(os.name in ("nt", "ce"),
                          'test specific to Windows (NT/CE)')
--- ./Lib/ctypes/util.py.ORG	2017-10-17 04:36:00 -0500
+++ ./Lib/ctypes/util.py	2017-10-17 04:41:18 -0500
@@ -82,6 +82,13 @@
             except ValueError:
                 continue
         return None
+if sys.platform.startswith("aix"):
+    # find .so members in .a files
+    # using dump loader header information + sys.
+    import ctypes._aix as aix
+
+    def find_library(name):
+        return aix.find_library(name)
 
 elif os.name == "posix":
     # Andreas Degert's find functions, using gcc, /sbin/ldconfig, objdump
@@ -284,10 +291,11 @@
         print find_library("msvcrt")
 
     if os.name == "posix":
-        # find and load_version
-        print find_library("m")
-        print find_library("c")
-        print find_library("bz2")
+        # find
+        print("m\t:: %s" % find_library("m"))
+        print("c\t:: %s" % find_library("c"))
+        print("bz2\t:: %s" % find_library("bz2"))
+        print("crypt\t:: %s" % find_library("crypt"))
 
         # getattr
 ##        print cdll.m
@@ -299,6 +307,12 @@
             print cdll.LoadLibrary("libcrypto.dylib")
             print cdll.LoadLibrary("libSystem.dylib")
             print cdll.LoadLibrary("System.framework/System")
+        elif sys.platform.startswith("aix"):
+            print("crypto\t:: %s" % cdll.LoadLibrary(find_library("crypto")))
+            if (sys.maxsize < 2**32):
+                print("c\t:: %s" % cdll.LoadLibrary("libc.a(shr.o)"))
+            else:
+                print("c\t:: %s" % cdll.LoadLibrary("libc.a(shr_64.o)"))
         else:
             print cdll.LoadLibrary("libm.so")
             print cdll.LoadLibrary("libcrypt.so")
