diff -ru Lib/ctypes.160823/__init__.py Lib/ctypes/__init__.py --- Lib/ctypes.160823/__init__.py 2016-08-23 18:08:35 +0000 +++ Lib/ctypes/__init__.py 2016-09-04 10:55:25 +0000 @@ -339,11 +339,12 @@ 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 + 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 so that AIX dlopen() looks in the archive. + Without this additional mode bit AIX dlopen() looks for an exact filename match. """ - if name and name.endswith(")") and name.rfind(".a(") > 0: + if name and ".a(" in name and name.endswith(")"): from _ctypes import RTLD_MEMBER mode |= RTLD_MEMBER diff -ru Lib/ctypes.160823/_aix.py Lib/ctypes/_aix.py --- Lib/ctypes.160823/_aix.py 2016-08-23 17:57:26 +0000 +++ Lib/ctypes/_aix.py 2016-09-04 18:02:53 +0000 @@ -24,58 +24,56 @@ else: return 64 -def get_dumpH(object, p=None, lines=None): +def get_dumpH(file, lines=None): """ Internal support function: dump -H output provides info on archive/executable contents and related paths. This function call dump -H as a subprocess - and strips the output of "noise" aka empty lines, leading/trailing spaces. - As there is concern (via feedback) of a lockup, p.wait() is not called, - only p.stdout.close() is called - that should ensure the subprocess exits - Note: shell=False, i.e., dump is called without shell interaction + and strips the output of "noise". + Note: shell=False, i.e., /usr/bin/dump is called without shell assistence """ + #dumpH parseing: + # 1. Line starts with /, ./, or ../ - set object name + # 2. If "INDEX" return object + # 3. get extra info (lines starting with [0-9]) + import subprocess - p = subprocess.Popen(["/usr/bin/dump", "-X%s"%aixABI(), "-H", object], - universal_newlines=True, stdout=subprocess.PIPE) - # not interested in blank lines, leading/trailing spaces - lines=[line.strip() for line in p.stdout.readlines() if line.strip()] - # close process and wait (verify) it has completed + def get_object(p, object=None): + for x in p.stdout: + if (x.startswith('/') or x.startswith('./') or x.startswith('../')): + object = x + elif "INDEX" in x: + return object + return None + + def get_objectinfo(p, lines=None): + # as an object was found, return known paths, archives and members + # these lines start with a digit + for line in p.stdout: + if re.match("[0-9]", line, flags=0): + if lines is None: + lines = line + else: + lines += line + else: + break + return lines.strip() + + p = subprocess.Popen(["/usr/bin/dump", "-X%s"%aixABI(), "-H", file], + universal_newlines=True, stdout=subprocess.PIPE, shell=False) + while 1: + object = get_object(p) + 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() ## comment rather than risk blocking while waiting + p.wait() return lines -# get_shared() - prefix documentation -# get_shared() examines dumpH output and returns a list of any shareable object -# i.e., is striping information re: static archive members -# any archive members with loader sections, is considered shareable -# In other words, members followed by: "Loader section is not available" -# are static members, not shared - -### An excerpt of (stripped) dump -X64 -H output looks like: -### That is, the first two ## are my comments, the first line with three # -### characters is normal output beginning with a single # -### (i.e., the line starting as #IMPfilID) - -##/usr/lib/libc.a[vsaveres_64.o]: -##Loader section is not available -##/usr/lib/libc.a[ptrgl_64_64.o]: -##Loader section is not available -##/usr/lib/libc.a[shr_64.o]: -##***Loader Section*** -##Loader Header Information -##VERSION# #SYMtableENT #RELOCent LENidSTR -##0x00000001 0x00000ab4 0x00002c63 0x0000002d -###IMPfilID OFFidSTR LENstrTBL OFFstrTBL -##0x00000003 0x0003c748 0x00009121 0x0003c775 -##***Import File Strings*** -##INDEX PATH BASE MEMBER -##0 /usr/lib:/lib -##1 / unix -##2 libcrypt.a shr_64.o -##/usr/lib/libc.a[posix_aio_64.o]: -##***Loader Section*** -##Loader Header Information - def get_shared(input, list=None, cpy=None, idx=0): """ Internal support function: @@ -84,21 +82,15 @@ the character "[" is used to strip off the path information Note: the "[" and "]" characters that are part of dump -H output are not processed here """ - cpy=input list=[] - for line in input: - idx = idx + 1 - # potential member lines start with a "/" - # then the next line informs whether there is a loader section, or not + if input is None: + return(list) + lines = input.split("\n") + for line in lines: + # potential member lines contain "[" # otherwise, no processing needed - if line.startswith("/"): - next = cpy.pop(idx) - # if next line does not contain "not available", - # i.e., is not "Loader section is not available" - # then it has loader information and the member including - # e.g., append [shr_64.o] from: "/usr/lib/libc.a[shr_64.o]:" - if not "not available" in next: - list.append(line[line.find("["):line.rfind(":")]) + if "[" in line: + list.append(line[line.find("["):-1]) return(list) def get_exactMatch(expr, lines, line=None, member=None): @@ -122,29 +114,31 @@ Internal support function: This routine resolves historical aka legacy naming schemes started in AIX4 for shared library support for library members names for, e.g., libc.a - This routine addresses specific issues for 64-bit leagacy issues + This routine addresses specific issues for 64-bit legacy issues """ # Recall that member names are still bracketed between [] # an old convention - insert _64 between libFOO and .so - # '\[%s_*64\.so\]' % name, -> has either _64 or 64 added to name + # '\[%s_?64\.so\]' % name, -> has either _64 or 64 added to name # Note: "version" line succeeds here only when there is only one - # versioned member. - # '\[lib%s_*64\.o\]' % name, -> legacy AIX scheme for libFOO 64-bit as a .o + # versioned member with a "_?64" added to the name string + # In other words, multiple versioned legacy names are neither expected nor supported. + # '\[lib%s_?64\.o\]' % name, -> legacy AIX scheme for libFOO 64-bit as a .o # '\[lib%s.so.[0-9]+.*\]' % name, -> versioning # '\[shr4*_*64.o\]']: -> legacy AIX names: shr_64.o, shr_64.o, shr64.o for expr in [ - '\[lib%s_*64\.so\]' % name, - '\[lib%s.so.[0-9]+.*\]' % name, - '\[lib%s_*64\.o\]' % name, - '\[shr4*_*64.o\]']: + '\[lib%s_?64\.so\]' % name, # look first at .so entries that used legacy model + '\[lib%s_?64\.so.[0-9]+.*\]' % name, # look at .so versioned entries that used legacy model + '\[lib%s_?64\.o\]' % name, # classix AIX legacy 'libFOO' .o mode + '\[shr4*_?64.o\]']: # initial AIX .o member name (ld default -g named output) member = get_exactMatch(expr, members) if member: return member return None # Get the most recent/highest numbered version - if it exists -# Only called when versioned is requested -# CHECKME - superfluous due to find_library() convention? +# 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: @@ -171,11 +165,11 @@ # return an archive member matching name # (versioned) .so members have priority over legacy AIX member name -# member names in in the dumpH output are between square brackets +# member names in the dumpH output are between square brackets def get_member(name, members, member=None): """ Internal support function: - Given an list of members find and return the most appropriate result + 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 """ @@ -185,6 +179,9 @@ member = get_exactMatch(expr, members) if member: return member + # because an exact match with .so as extension of member name + # was not found, look for a versioned name + # When a versioned name is also not found, look for AIX legacy member name member = get_version(name, members) if member: return member @@ -200,29 +197,28 @@ return member return None -def getExecLibPath_aix(libpaths=None, x=None, lines=None, - line=None, skipping=None): +def getExecLibPath_aix(libpaths=None): """ Internal support function: On AIX, the buildtime searchpath is stored in the executable. the command dump -H can extract this info - Looking at LIBPATH is an enhancement, and as issue9998 is still undecided, it is commented out + Prefix searched libraries with LIBPATH, or LD_LIBRARY_PATH if defined + This mimics AIX dlopen() behavior """ - # if LIBPATH is defined, add it to the paths to search - # to mimic dlopen() search behavior - # libpaths = os.environ.get("LIBPATH") - lines = get_dumpH(sys.executable) - skipping = True; + + libpaths = os.environ.get("LIBPATH") + if libpaths is None: + libpaths = os.environ.get("LD_LIBRARY_PATH") + lines = get_dumpH(sys.executable).split("\n") for line in lines: - if skipping == False: + if re.match("[0-9]", line, flags=0): x = line.split()[1] - if (x.startswith('/') or x.startswith('./') - or x.startswith('../')): - if libpaths is not None: - libpaths = "%s:%s" % (libpaths, x) - else: - libpaths = x - elif line.find("INDEX PATH") == 0: - skipping = False; + else: + continue + if (x.startswith('/') or x.startswith('./') or x.startswith('../')): + if libpaths is None: + libpaths = x + elif not re.search(x, libpaths, flags=0): + libpaths = "%s:%s" % (x, libpaths) return libpaths def find_library(name, _name=None, lines=None, Only in Lib/ctypes: xxx.py