srcanamdw/codescanner/pyinstaller/bindepend.py
changeset 1 22878952f6e2
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/srcanamdw/codescanner/pyinstaller/bindepend.py	Thu Feb 18 12:29:02 2010 +0530
@@ -0,0 +1,330 @@
+#! /usr/bin/env python
+# Find external dependencies of binary libraries.
+# Copyright (C) 2005, Giovanni Bajo
+# Based on previous work under copyright (c) 2002 McMillan Enterprises, Inc.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+
+# use dumpbin.exe (if present) to find the binary
+# dependencies of an extension module.
+# if dumpbin not available, pick apart the PE hdr of the binary
+# while this appears to work well, it is complex and subject to
+# problems with changes to PE hdrs (ie, this works only on 32 bit Intel
+# Windows format binaries)
+#
+# Note also that you should check the results to make sure that the
+# dlls are redistributable. I've listed most of the common MS dlls
+# under "excludes" below; add to this list as necessary (or use the
+# "excludes" option in the INSTALL section of the config file).
+
+import os
+import time
+import string
+import sys
+import re
+
+seen = {}
+_bpath = None
+iswin = sys.platform[:3] == 'win'
+cygwin = sys.platform == 'cygwin'
+excludes = {'KERNEL32.DLL':1,
+      'ADVAPI.DLL':1,
+      'MSVCRT.DLL':1,
+      'ADVAPI32.DLL':1,
+      'COMCTL32.DLL':1,
+      'CRTDLL.DLL':1,
+      'GDI32.DLL':1,
+      'MFC42.DLL':1,
+      'NTDLL.DLL':1,
+      'OLE32.DLL':1,
+      'OLEAUT32.DLL':1,
+      'RPCRT4.DLL':1,
+      'SHELL32.DLL':1,
+      'USER32.DLL':1,
+      'WINSPOOL.DRV':1,
+      'WS2HELP.DLL':1,
+      'WS2_32.DLL':1,
+      'WSOCK32.DLL':1,
+      'MSWSOCK.DLL':1,
+      'WINMM.DLL':1,
+      'COMDLG32.DLL':1,
+##      'ZLIB.DLL':1,   # test with python 1.5.2
+      'ODBC32.DLL':1,
+      'VERSION.DLL':1,
+      'IMM32.DLL':1,
+      'DDRAW.DLL':1,
+      'DCIMAN32.DLL':1,
+      'OPENGL32.DLL':1,
+      'GLU32.DLL':1,
+      'GLUB32.DLL':1,
+      '/usr/lib':1,
+      '/lib':1,}
+
+def getfullnameof(mod, xtrapath = None):
+  """Return the full path name of MOD.
+
+      MOD is the basename of a dll or pyd.
+      XTRAPATH is a path or list of paths to search first.
+      Return the full path name of MOD.
+      Will search the full Windows search path, as well as sys.path"""
+  epath = getWindowsPath() + sys.path
+  if xtrapath is not None:
+    if type(xtrapath) == type(''):
+      epath.insert(0, xtrapath)
+    else:
+      epath = xtrapath + epath
+  for p in epath:
+    npth = os.path.join(p, mod)
+    if os.path.exists(npth):
+      return npth
+  return ''
+
+def getImports1(pth):
+    """Find the binary dependencies of PTH.
+
+        This implementation (not used right now) uses the MSVC utility dumpbin"""
+    import tempfile
+    rslt = []
+    tmpf = tempfile.mktemp()
+    os.system('dumpbin /IMPORTS "%s" >%s' %(pth, tmpf))
+    time.sleep(0.1)
+    txt = open(tmpf,'r').readlines()
+    os.remove(tmpf)
+    i = 0
+    while i < len(txt):
+        tokens = string.split(txt[i])
+        if len(tokens) == 1 and string.find(tokens[0], '.') > 0:
+            rslt.append(string.strip(tokens[0]))
+        i = i + 1
+    return rslt
+
+def getImports2x(pth):
+    """Find the binary dependencies of PTH.
+
+        This implementation walks through the PE header"""
+    import struct
+    rslt = []
+    try:
+      f = open(pth, 'rb').read()
+      pehdrd = struct.unpack('l', f[60:64])[0]  #after the MSDOS loader is the offset of the peheader
+      magic = struct.unpack('l', f[pehdrd:pehdrd+4])[0] # pehdr starts with magic 'PE\000\000' (or 17744)
+                                                        # then 20 bytes of COFF header
+      numsecs = struct.unpack('h', f[pehdrd+6:pehdrd+8])[0] # whence we get number of sections
+      opthdrmagic = struct.unpack('h', f[pehdrd+24:pehdrd+26])[0]
+      if opthdrmagic == 0x10b: # PE32 format
+          numdictoffset = 116
+          importoffset = 128
+      elif opthdrmagic == 0x20b: # PE32+ format
+          numdictoffset = 132
+          importoffset = 148
+      else:
+          print "E: bindepend cannot analyze %s - unknown header format! %x" % (pth, opthdrmagic)
+          return rslt
+      numdirs = struct.unpack('l', f[pehdrd+numdictoffset:pehdrd+numdictoffset+4])[0]
+      idata = ''
+      if magic == 17744:
+          importsec, sz = struct.unpack('2l', f[pehdrd+importoffset:pehdrd+importoffset+8])
+          if sz == 0:
+              return rslt
+          secttbl = pehdrd + numdictoffset + 4 + 8*numdirs
+          secttblfmt = '8s7l2h'
+          seclist = []
+          for i in range(numsecs):
+              seclist.append(struct.unpack(secttblfmt, f[secttbl+i*40:secttbl+(i+1)*40]))
+              #nm, vsz, va, rsz, praw, preloc, plnnums, qrelocs, qlnnums, flags \
+              # = seclist[-1]
+          for i in range(len(seclist)-1):
+              if seclist[i][2] <= importsec < seclist[i+1][2]:
+                  break
+          vbase = seclist[i][2]
+          raw = seclist[i][4]
+          idatastart = raw + importsec - vbase
+          idata = f[idatastart:idatastart+seclist[i][1]]
+          i = 0
+          while 1:
+              chunk = idata[i*20:(i+1)*20]
+              if len(chunk) != 20:
+                  print "E: premature end of import table (chunk is %d, not 20)" % len(chunk)
+                  break
+              vsa =  struct.unpack('5l', chunk)[3]
+              if vsa == 0:
+                  break
+              sa = raw + vsa - vbase
+              end = string.find(f, '\000', sa)
+              nm = f[sa:end]
+              if nm:
+                  rslt.append(nm)
+              i = i + 1
+      else:
+          print "E: bindepend cannot analyze %s - file is not in PE format!" % pth
+    except IOError:
+        print "E: bindepend cannot analyze %s - file not found!" % pth
+    #except struct.error:
+    #    print "E: bindepend cannot analyze %s - error walking thru pehdr" % pth
+    return rslt
+
+def getImports2(path):
+    """Find the binary dependencies of PTH.
+
+        This implementation walks through the PE header"""
+    import struct
+    f = open(path, 'rb')
+    # skip the MSDOS loader
+    f.seek(60)
+    # get offset to PE header
+    offset = struct.unpack('l', f.read(4))[0]
+    f.seek(offset)
+    signature = struct.unpack('l', f.read(4))[0]
+    coffhdrfmt = 'hhlllhh'
+    rawcoffhdr = f.read(struct.calcsize(coffhdrfmt))
+    coffhdr = struct.unpack(coffhdrfmt, rawcoffhdr)
+    coffhdr_numsections = coffhdr[1]
+
+    opthdrfmt = 'hbblllllllllhhhhhhllllhhllllll'
+    rawopthdr = f.read(struct.calcsize(opthdrfmt))
+    opthdr = struct.unpack(opthdrfmt, rawopthdr)
+    opthdr_numrvas = opthdr[-1]
+
+    datadirs = []
+    datadirsize = struct.calcsize('ll') # virtual address, size
+    for i in range(opthdr_numrvas):
+        rawdatadir = f.read(datadirsize)
+        datadirs.append(struct.unpack('ll', rawdatadir))
+
+    sectionfmt = '8s6l2hl'
+    sectionsize = struct.calcsize(sectionfmt)
+    sections = []
+    for i in range(coffhdr_numsections):
+        rawsection = f.read(sectionsize)
+        sections.append(struct.unpack(sectionfmt, rawsection))
+
+    importva, importsz = datadirs[1]
+    if importsz == 0:
+        return []
+    # figure out what section it's in
+    NAME, MISC, VIRTADDRESS, RAWSIZE, POINTERTORAW = range(5)
+    for j in range(len(sections)-1):
+        if sections[j][VIRTADDRESS] <= importva < sections[j+1][VIRTADDRESS]:
+            importsection = sections[j]
+            break
+    else:
+        if importva >= sections[-1][VIRTADDRESS]:
+            importsection = sections[-1]
+        else:
+            print "E: import section is unavailable"
+            return []
+    f.seek(importsection[POINTERTORAW] + importva - importsection[VIRTADDRESS])
+    data = f.read(importsz)
+    iidescrfmt = 'lllll'
+    CHARACTERISTICS, DATETIME, FWDRCHAIN, NAMERVA, FIRSTTHUNK = range(5)
+    iidescrsz = struct.calcsize(iidescrfmt)
+    dlls = []
+    while data:
+        iid = struct.unpack(iidescrfmt, data[:iidescrsz])
+        if iid[NAMERVA] == 0:
+            break
+        f.seek(importsection[POINTERTORAW] + iid[NAMERVA] - importsection[VIRTADDRESS])
+        nm = f.read(256)
+        nm, jnk = string.split(nm, '\0', 1)
+        if nm:
+            dlls.append(nm)
+        data = data[iidescrsz:]
+    return dlls
+
+def Dependencies(lTOC):
+  """Expand LTOC to include all the closure of binary dependencies.
+
+     LTOC is a logical table of contents, ie, a seq of tuples (name, path).
+     Return LTOC expanded by all the binary dependencies of the entries
+     in LTOC, except those listed in the module global EXCLUDES"""
+  for nm, pth, typ in lTOC:
+    fullnm = string.upper(os.path.basename(pth))
+    if seen.get(string.upper(nm),0):
+      continue
+    #print "I: analyzing", pth
+    seen[string.upper(nm)] = 1
+    dlls = getImports(pth)
+    for lib in dlls:
+        #print "I: found", lib
+        if not iswin and not cygwin:
+            npth = lib
+            dir, lib = os.path.split(lib)
+            if excludes.get(dir,0):
+                continue
+        if excludes.get(string.upper(lib),0):
+            continue
+        if seen.get(string.upper(lib),0):
+            continue
+        if iswin or cygwin:
+            npth = getfullnameof(lib, os.path.dirname(pth))
+        if npth:
+            lTOC.append((lib, npth, 'BINARY'))
+        else:
+            print "E: lib not found:", lib, "dependency of", pth
+  return lTOC
+
+def getImports3(pth):
+    """Find the binary dependencies of PTH.
+
+        This implementation is for ldd platforms"""
+    rslt = []
+    for line in os.popen('ldd "%s"' % pth).readlines():
+        m = re.search(r"\s+(.*?)\s+=>\s+(.*?)\s+\(.*\)", line)
+        if m:
+            name, lib = m.group(1), m.group(2)
+            if name[:10] == 'linux-gate':
+                # linux-gate is a fake library which does not exist and
+                # should be ignored. See also:
+                # http://www.trilithium.com/johan/2005/08/linux-gate/
+                continue
+            if os.path.exists(lib):
+                rslt.append(lib)
+            else:
+                print 'E: cannot find %s in path %s (needed by %s)' % \
+                      (name, lib, pth)
+    return rslt
+
+def getImports(pth):
+    """Forwards to either getImports2 or getImports3
+    """
+    if sys.platform[:3] == 'win' or sys.platform == 'cygwin':
+        return getImports2(pth)
+    return getImports3(pth)
+
+def getWindowsPath():
+    """Return the path that Windows will search for dlls."""
+    global _bpath
+    if _bpath is None:
+        _bpath = []
+        if iswin:
+            try:
+                import win32api
+            except ImportError:
+                print "W: Cannot determine your Windows or System directories"
+                print "W: Please add them to your PATH if .dlls are not found"
+                print "W: or install starship.python.net/skippy/win32/Downloads.html"
+            else:
+                sysdir = win32api.GetSystemDirectory()
+                sysdir2 = os.path.normpath(os.path.join(sysdir, '..', 'SYSTEM'))
+                windir = win32api.GetWindowsDirectory()
+                _bpath = [sysdir, sysdir2, windir]
+        _bpath.extend(string.split(os.environ.get('PATH', ''), os.pathsep))
+    return _bpath
+
+if __name__ == "__main__":
+  if len(sys.argv) < 2:
+    print "Usage: python %s BINARYFILE" % sys.argv[0]
+    sys.exit(0)
+  print getImports(sys.argv[1])