findlibraryname returns the pathname of a library or none return the version of msvc that was used to build python for python 2 3 and up the version number is included in sys version for earlier versions assume the compiler is msvc 6 this function was copied from libdistutilsmsvccompiler py i don t think paths are affected by minor version in version 6 else we don t know what version of the compiler this is return the name of the vc runtime dll version getbuildversion if version is none better be safe than sorry return none if version 6 clibname msvcrt elif version 13 clibname msvcrd version 10 else crt is no longer directly loadable see issue23606 for the discussion about alternative approaches return none if python was built with in debug mode import importlib machinery if d pyd in importlib machinery extensionsuffixes clibname d return clibname dll def findlibraryname if name in c m return findmsvcrt see msdn for the real search order for directory in os environ path splitos pathsep fname os path joindirectory name if os path isfilefname return fname if fname lower endswith dll continue fname fname dll if os path isfilefname return fname return none elif os name posix and sys platform darwin from ctypes macholib dyld import dyldfind as dyldfind def findlibraryname possible libs dylib name s dylib name s frameworks name name for name in possible try return dyldfindname except valueerror continue return none elif sys platform startswithaix aix has two styles of storing shared libraries gnu autotools 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 issue26439 and aix py for more details from ctypes aix import findlibrary elif os name posix andreas degert s find functions using gcc sbinldconfig objdump import re tempfile def iselffilename return true if the given file is an elf file elfheader b x7felf with openfilename br as thefile return thefile read4 elfheader def findlibgccname run gcc s linker with the t aka trace option and examine the library name it prints out the gcc command will fail because we haven t supplied a proper program with main but that does not matter expr os fsencoder slibs s re escapename ccompiler shutil which gcc if not ccompiler ccompiler shutil which cc if not ccompiler no c compiler available give up return none temp tempfile namedtemporaryfile try args ccompiler wl t o temp name l name env dictos environ env lcall c env lang c try proc subprocess popenargs stdoutsubprocess pipe stderrsubprocess stdout envenv except oserror e g bad executable return none with proc trace proc stdout read finally try temp close except filenotfounderror raised if the file was already removed which is the normal behaviour of gcc if linking fails pass res re findallexpr trace if not res return none for file in res check if the given file is an elf file gcc can report some files that are linker scripts and not actual shared objects see bpo41976 for more details if not iselffile continue return os fsdecodefile if sys platform sunos5 use usrccsbindump on solaris def getsonamef if not f return none try proc subprocess popenusrccsbindump lpv f stdoutsubprocess pipe stderrsubprocess devnull except oserror e g command not found return none with proc data proc stdout read res re searchbr ssonamess data if not res return none return os fsdecoderes group1 else def getsonamef assuming gnu binutils elf if not f return none objdump shutil which objdump if not objdump objdump is not available give up return none try proc subprocess popenobjdump p j dynamic f stdoutsubprocess pipe stderrsubprocess devnull except oserror e g bad executable return none with proc dump proc stdout read res re searchbr ssonamess dump if not res return none return os fsdecoderes group1 if sys platform startswithfreebsd openbsd dragonfly def numversionlibname libxyz so major minor major minor parts libname splitb nums try while parts nums insert0 intparts pop except valueerror pass return nums or sys maxsize def findlibraryname ename re escapename expr r ls s slibs s ename ename expr os fsencodeexpr try proc subprocess popen sbinldconfig r stdoutsubprocess pipe stderrsubprocess devnull except oserror e g command not found data b else with proc data proc stdout read res re findallexpr data if not res return getsonamefindlibgccname res sortkeynumversion return os fsdecoderes1 elif sys platform sunos5 def findlibcrlename is64 if not os path exists usrbincrle return none env dictos environ env lcall c if is64 args usrbincrle 64 else args usrbincrle paths none try proc subprocess popenargs stdoutsubprocess pipe stderrsubprocess devnull envenv except oserror e g bad executable return none with proc for line in proc stdout line line strip if line startswithb default library path elf paths os fsdecodeline split4 if not paths return none for dir in paths split libfile os path joindir libs so name if os path existslibfile return libfile return none def findlibraryname is64 false return getsonamefindlibcrlename is64 or findlibgccname else def findsonameldconfigname import struct if struct calcsize l 4 machine os uname machine 32 else machine os uname machine 64 machmap x866464 libc6 x8664 ppc6464 libc6 64bit sparc6464 libc6 64bit s390x64 libc6 64bit ia6464 libc6 ia64 abitype machmap getmachine libc6 xxx assuming glibc s ldconfig with option p regex r slibs sss regex os fsencoderegex re escapename abitype try with subprocess popen sbinldconfig p stdinsubprocess devnull stderrsubprocess devnull stdoutsubprocess pipe env lcall c lang c as p res re searchregex p stdout read if res return os fsdecoderes group1 except oserror pass def findlibldname see issue 9998 for why this is needed expr r slibs s re escapename cmd ld t libpath os environ get ldlibrarypath if libpath for d in libpath split cmd extend l d cmd extend o os devnull ls name result none try p subprocess popencmd stdoutsubprocess pipe stderrsubprocess pipe universalnewlinestrue out p communicate res re findallexpr os fsdecodeout for file in res check if the given file is an elf file gcc can report some files that are linker scripts and not actual shared objects see bpo41976 for more details if not iselffile continue return os fsdecodefile except exception pass result will be none return result def findlibraryname see issue 9998 return findsonameldconfigname or getsonamefindlibgccname or getsonamefindlibldname test code def test from ctypes import cdll if os name nt printcdll msvcrt printcdll loadmsvcrt printfindlibrarymsvcrt if os name posix find and loadversion printfindlibrarym printfindlibraryc printfindlibrarybz2 load if sys platform darwin printcdll loadlibrarylibm dylib printcdll loadlibrarylibcrypto dylib printcdll loadlibrarylibsystem dylib printcdll loadlibrarysystem frameworksystem issue26439 fix broken test call for aix elif sys platform startswithaix from ctypes import cdll if sys maxsize 232 printfusing cdllname os rtldmember cdll libc ashr o os rtldmember printfusing cdll loadlibrary cdll loadlibrary libc ashr o librpm so is only available as 32bit shared library printfindlibraryrpm printcdll loadlibrarylibrpm so else printfusing cdllname os rtldmember cdll libc ashr64 o os rtldmember printfusing cdll loadlibrary cdll loadlibrary libc ashr64 o printfcryptt findlibrary crypt printfcryptt cdll loadlibraryfindlibrary crypt printfcryptot findlibrary crypto printfcryptot cdll loadlibraryfindlibrary crypto else printcdll loadlibrarylibm so printcdll loadlibrarylibcrypt so printfindlibrarycrypt if name main test find_library name returns the pathname of a library or none return the version of msvc that was used to build python for python 2 3 and up the version number is included in sys version for earlier versions assume the compiler is msvc 6 this function was copied from lib distutils msvccompiler py i don t think paths are affected by minor version in version 6 else we don t know what version of the compiler this is return the name of the vc runtime dll better be safe than sorry crt is no longer directly loadable see issue23606 for the discussion about alternative approaches if python was built with in debug mode see msdn for the real search order 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 andreas degert s find functions using gcc sbin ldconfig objdump run gcc s linker with the t aka trace option and examine the library name it prints out the gcc command will fail because we haven t supplied a proper program with main but that does not matter no c compiler available give up e g bad executable raised if the file was already removed which is the normal behaviour of gcc if linking fails check if the given file is an elf file gcc can report some files that are linker scripts and not actual shared objects see bpo 41976 for more details use usr ccs bin dump on solaris e g command not found assuming gnu binutils elf objdump is not available give up e g bad executable libxyz so major minor major minor e g command not found e g bad executable xxx assuming glibc s ldconfig with option p see issue 9998 for why this is needed check if the given file is an elf file gcc can report some files that are linker scripts and not actual shared objects see bpo 41976 for more details result will be none see issue 9998 test code find and load_version load issue 26439 fix broken test call for aix librpm so is only available as 32 bit shared library
import os
import shutil
import subprocess
import sys


if os.name == "nt":

    def _get_build_version():
        
        
        prefix = "MSC v."
        i = sys.version.find(prefix)
        if i == -1:
            return 6
        i = i + len(prefix)
        s, rest = sys.version[i:].split(" ", 1)
        majorVersion = int(s[:-2]) - 6
        if majorVersion >= 13:
            majorVersion += 1
        minorVersion = int(s[2:3]) / 10.0
        
        if majorVersion == 6:
            minorVersion = 0
        if majorVersion >= 6:
            return majorVersion + minorVersion
        
        return None

    def find_msvcrt():
        
        version = _get_build_version()
        if version is None:
            
            return None
        if version <= 6:
            clibname = 'msvcrt'
        elif version <= 13:
            clibname = 'msvcr%d' % (version * 10)
        else:
            
            
            return None

        
        import importlib.machinery
        if '_d.pyd' in importlib.machinery.EXTENSION_SUFFIXES:
            clibname += 'd'
        return clibname+'.dll'

    def find_library(name):
        if name in ('c', 'm'):
            return find_msvcrt()
        
        for directory in os.environ['PATH'].split(os.pathsep):
            fname = os.path.join(directory, name)
            if os.path.isfile(fname):
                return fname
            if fname.lower().endswith(".dll"):
                continue
            fname = fname + ".dll"
            if os.path.isfile(fname):
                return fname
        return None

elif os.name == "posix" and sys.platform == "darwin":
    from ctypes.macholib.dyld import dyld_find as _dyld_find
    def find_library(name):
        possible = ['lib%s.dylib' % name,
                    '%s.dylib' % name,
                    '%s.framework/%s' % (name, name)]
        for name in possible:
            try:
                return _dyld_find(name)
            except ValueError:
                continue
        return None

elif sys.platform.startswith("aix"):
    
    
    
    
    

    from ctypes._aix import find_library

elif os.name == "posix":
    
    import re, tempfile

    def _is_elf(filename):
        "Return True if the given file is an ELF file"
        elf_header = b'\x7fELF'
        with open(filename, 'br') as thefile:
            return thefile.read(4) == elf_header

    def _findLib_gcc(name):
        
        
        
        
        expr = os.fsencode(r'[^\(\)\s]*lib%s\.[^\(\)\s]*' % re.escape(name))

        c_compiler = shutil.which('gcc')
        if not c_compiler:
            c_compiler = shutil.which('cc')
        if not c_compiler:
            
            return None

        temp = tempfile.NamedTemporaryFile()
        try:
            args = [c_compiler, '-Wl,-t', '-o', temp.name, '-l' + name]

            env = dict(os.environ)
            env['LC_ALL'] = 'C'
            env['LANG'] = 'C'
            try:
                proc = subprocess.Popen(args,
                                        stdout=subprocess.PIPE,
                                        stderr=subprocess.STDOUT,
                                        env=env)
            except OSError:  
                return None
            with proc:
                trace = proc.stdout.read()
        finally:
            try:
                temp.close()
            except FileNotFoundError:
                
                
                pass
        res = re.findall(expr, trace)
        if not res:
            return None

        for file in res:
            
            
            
            if not _is_elf(file):
                continue
            return os.fsdecode(file)


    if sys.platform == "sunos5":
        
        def _get_soname(f):
            if not f:
                return None

            try:
                proc = subprocess.Popen(("/usr/ccs/bin/dump", "-Lpv", f),
                                        stdout=subprocess.PIPE,
                                        stderr=subprocess.DEVNULL)
            except OSError:  
                return None
            with proc:
                data = proc.stdout.read()
            res = re.search(br'\[.*\]\sSONAME\s+([^\s]+)', data)
            if not res:
                return None
            return os.fsdecode(res.group(1))
    else:
        def _get_soname(f):
            
            if not f:
                return None
            objdump = shutil.which('objdump')
            if not objdump:
                
                return None

            try:
                proc = subprocess.Popen((objdump, '-p', '-j', '.dynamic', f),
                                        stdout=subprocess.PIPE,
                                        stderr=subprocess.DEVNULL)
            except OSError:  
                return None
            with proc:
                dump = proc.stdout.read()
            res = re.search(br'\sSONAME\s+([^\s]+)', dump)
            if not res:
                return None
            return os.fsdecode(res.group(1))

    if sys.platform.startswith(("freebsd", "openbsd", "dragonfly")):

        def _num_version(libname):
            
            parts = libname.split(b".")
            nums = []
            try:
                while parts:
                    nums.insert(0, int(parts.pop()))
            except ValueError:
                pass
            return nums or [sys.maxsize]

        def find_library(name):
            ename = re.escape(name)
            expr = r':-l%s\.\S+ => \S*/(lib%s\.\S+)' % (ename, ename)
            expr = os.fsencode(expr)

            try:
                proc = subprocess.Popen(('/sbin/ldconfig', '-r'),
                                        stdout=subprocess.PIPE,
                                        stderr=subprocess.DEVNULL)
            except OSError:  
                data = b''
            else:
                with proc:
                    data = proc.stdout.read()

            res = re.findall(expr, data)
            if not res:
                return _get_soname(_findLib_gcc(name))
            res.sort(key=_num_version)
            return os.fsdecode(res[-1])

    elif sys.platform == "sunos5":

        def _findLib_crle(name, is64):
            if not os.path.exists('/usr/bin/crle'):
                return None

            env = dict(os.environ)
            env['LC_ALL'] = 'C'

            if is64:
                args = ('/usr/bin/crle', '-64')
            else:
                args = ('/usr/bin/crle',)

            paths = None
            try:
                proc = subprocess.Popen(args,
                                        stdout=subprocess.PIPE,
                                        stderr=subprocess.DEVNULL,
                                        env=env)
            except OSError:  
                return None
            with proc:
                for line in proc.stdout:
                    line = line.strip()
                    if line.startswith(b'Default Library Path (ELF):'):
                        paths = os.fsdecode(line).split()[4]

            if not paths:
                return None

            for dir in paths.split(":"):
                libfile = os.path.join(dir, "lib%s.so" % name)
                if os.path.exists(libfile):
                    return libfile

            return None

        def find_library(name, is64 = False):
            return _get_soname(_findLib_crle(name, is64) or _findLib_gcc(name))

    else:

        def _findSoname_ldconfig(name):
            import struct
            if struct.calcsize('l') == 4:
                machine = os.uname().machine + '-32'
            else:
                machine = os.uname().machine + '-64'
            mach_map = {
                'x86_64-64': 'libc6,x86-64',
                'ppc64-64': 'libc6,64bit',
                'sparc64-64': 'libc6,64bit',
                's390x-64': 'libc6,64bit',
                'ia64-64': 'libc6,IA-64',
                }
            abi_type = mach_map.get(machine, 'libc6')

            
            regex = r'\s+(lib%s\.[^\s]+)\s+\(%s'
            regex = os.fsencode(regex % (re.escape(name), abi_type))
            try:
                with subprocess.Popen(['/sbin/ldconfig', '-p'],
                                      stdin=subprocess.DEVNULL,
                                      stderr=subprocess.DEVNULL,
                                      stdout=subprocess.PIPE,
                                      env={'LC_ALL': 'C', 'LANG': 'C'}) as p:
                    res = re.search(regex, p.stdout.read())
                    if res:
                        return os.fsdecode(res.group(1))
            except OSError:
                pass

        def _findLib_ld(name):
            
            expr = r'[^\(\)\s]*lib%s\.[^\(\)\s]*' % re.escape(name)
            cmd = ['ld', '-t']
            libpath = os.environ.get('LD_LIBRARY_PATH')
            if libpath:
                for d in libpath.split(':'):
                    cmd.extend(['-L', d])
            cmd.extend(['-o', os.devnull, '-l%s' % name])
            result = None
            try:
                p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
                                     stderr=subprocess.PIPE,
                                     universal_newlines=True)
                out, _ = p.communicate()
                res = re.findall(expr, os.fsdecode(out))
                for file in res:
                    
                    
                    
                    if not _is_elf(file):
                        continue
                    return os.fsdecode(file)
            except Exception:
                pass  
            return result

        def find_library(name):
            
            return _findSoname_ldconfig(name) or \
                   _get_soname(_findLib_gcc(name)) or _get_soname(_findLib_ld(name))




def test():
    from ctypes import cdll
    if os.name == "nt":
        print(cdll.msvcrt)
        print(cdll.load("msvcrt"))
        print(find_library("msvcrt"))

    if os.name == "posix":
        
        print(find_library("m"))
        print(find_library("c"))
        print(find_library("bz2"))

        
        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"))
        
        elif sys.platform.startswith("aix"):
            from ctypes import CDLL
            if sys.maxsize < 2**32:
                print(f"Using CDLL(name, os.RTLD_MEMBER): {CDLL('libc.a(shr.o)', os.RTLD_MEMBER)}")
                print(f"Using cdll.LoadLibrary(): {cdll.LoadLibrary('libc.a(shr.o)')}")
                
                print(find_library("rpm"))
                print(cdll.LoadLibrary("librpm.so"))
            else:
                print(f"Using CDLL(name, os.RTLD_MEMBER): {CDLL('libc.a(shr_64.o)', os.RTLD_MEMBER)}")
                print(f"Using cdll.LoadLibrary(): {cdll.LoadLibrary('libc.a(shr_64.o)')}")
            print(f"crypt\t:: {find_library('crypt')}")
            print(f"crypt\t:: {cdll.LoadLibrary(find_library('crypt'))}")
            print(f"crypto\t:: {find_library('crypto')}")
            print(f"crypto\t:: {cdll.LoadLibrary(find_library('crypto'))}")
        else:
            print(cdll.LoadLibrary("libm.so"))
            print(cdll.LoadLibrary("libcrypt.so"))
            print(find_library("crypt"))

if __name__ == "__main__":
    test()