]> sigrok.org Git - sigrok-util.git/commitdiff
add parsepe tool
authorBert Vermeulen <redacted>
Mon, 18 Jun 2012 01:14:11 +0000 (03:14 +0200)
committerBert Vermeulen <redacted>
Mon, 18 Jun 2012 01:14:11 +0000 (03:14 +0200)
firmware/README.parsepe [new file with mode: 0644]
firmware/parsepe.py [new file with mode: 0755]

diff --git a/firmware/README.parsepe b/firmware/README.parsepe
new file mode 100644 (file)
index 0000000..8a69b6f
--- /dev/null
@@ -0,0 +1,11 @@
+The parsepe.py tool can list all sections and symbols from a PE (portable
+executable) binary file, and extract the contents of a symbol.
+
+usage:
+parsepe.py -l <filename>                list all sections and symbols in file
+parsepe.py -s <symbol> -x <filename>    extract symbol from file
+
+TODO:
+- currently only handles COFF symbol tables
+- can only extract external (global) symbols
+
diff --git a/firmware/parsepe.py b/firmware/parsepe.py
new file mode 100755 (executable)
index 0000000..74d639f
--- /dev/null
@@ -0,0 +1,183 @@
+#!/usr/bin/python3
+##
+## This file is part of the sigrok project.
+##
+## Copyright (C) 2012 Bert Vermeulen <bert@biot.com>
+##
+## 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 3 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, see <http://www.gnu.org/licenses/>.
+##
+
+import sys
+import os
+from getopt import getopt
+import struct
+
+
+def parse(filename):
+       f = open(filename, 'rb')
+       if f.read(2) != b'MZ':
+               raise Exception("MZ signature not found.")
+
+       sections = []
+       # long e_lfanew
+       f.seek(0x3c)
+       pe_ptr = struct.unpack("<L", f.read(4))[0]
+       f.seek(pe_ptr)
+       if f.read(4) != b'\x50\x45\x00\x00':
+               raise Exception("PE signature not found.")
+       # skip Machine
+       f.seek(f.tell() + 2)
+       sections.append(['header', 324, 0])
+       num_sections = struct.unpack("<H", f.read(2))[0]
+       # skip TimeDateStamp
+       f.seek(f.tell() + 4)
+       symboltable_address = struct.unpack("<L", f.read(4))[0]
+       num_symbols = struct.unpack("<L", f.read(4))[0]
+       optheader_size = struct.unpack("<H", f.read(2))[0]
+       # skip past PE header and PE optional header
+       f.seek(f.tell() + 2 + optheader_size)
+
+       for i in range(num_sections):
+               name = f.read(8).decode('ascii', errors='ignore').strip('\x00')
+               # skip past Misc and VirtualAddress
+               f.seek(f.tell() + 8)
+               # SizeOfRawData
+               size = struct.unpack("<L", f.read(4))[0]
+               # PointerToRawData
+               ptr = struct.unpack("<L", f.read(4))[0]
+               # skip to next section header
+               f.seek(f.tell() + 16)
+               sections.append([name, size, ptr])
+
+       symbols = []
+       addr = symboltable_address
+       stringtable_address = symboltable_address + num_symbols * 18
+       for i in range(num_symbols):
+               f.seek(addr)
+               tmp = f.read(8)
+               symaddr = struct.unpack("<L", f.read(4))[0]
+               # skip SectionNumber and Type
+               symtype = struct.unpack("B", f.read(1))[0]
+               f.seek(f.tell() + 4)
+               if tmp[:4] == b'\x00\x00\x00\x00':
+                       # symbol name is in the string table
+                       straddr = stringtable_address + struct.unpack("<l", tmp[4:])[0]
+                       f.seek(straddr)
+                       tmpname = f.read(64)
+                       name = tmpname[:tmpname.find(b'\x00')]
+               else:
+                       name = tmp
+               name = name.decode('ascii', errors='ignore').strip('\x00')
+               # need IMAGE_SYM_CLASS_EXTERNAL
+               if symtype == 0x02:
+                       size = 0
+               else:
+                       size = None
+               if i != 0 and symbols[-1][2] is not None and symaddr > symbols[-1][1]:
+                       symbols[-1][2] = symaddr - symbols[-1][1]
+               symbols.append([name, symaddr, size])
+               addr += 18
+
+       f.close()
+
+       return sections, symbols
+
+
+def list_all(filename):
+       sections, symbols = parse(filename)
+       if sections:
+               print("Sections:\n    Name         Size\t  Position")
+               cnt = 0
+               for name, size, address in sections:
+                       print("%-3d %-8s    %5d\t  0x%.8x" % (cnt, name, size, address))
+                       cnt += 1
+       if symbols:
+               print("\nSymbol table:\n   Address     Size    Symbol")
+               for symbol, address, size in symbols:
+                       if size is not None:
+                               sizestr = "%5d" % size
+                       else:
+                               sizestr = "     "
+                       print("0x%.8x  %s    %-8s" % (address, sizestr, symbol))
+               print()
+
+
+def extract_symbol(filename, symbol):
+       sections, symbols = parse(filename)
+       if not symbols:
+               return None
+       data = None
+       for symbolname, address, size in symbols:
+               if symbolname == symbol:
+                       if size is None:
+                               raise Exception("symbol %s found, but has unknown size")
+                       f = open(filename, 'rb')
+                       f.seek(address)
+                       data = f.read(size)
+                       f.close()
+                       if len(data) != size:
+                               raise Exception("short file")
+                       break
+
+       if data is None:
+               raise Exception("symbol %s not found" % symbol)
+
+       return data
+
+
+
+def usage():
+       print("usage: parsepe.py [-s <symbol>] <-l|-x> <filename>")
+       print("  -l   list all sections and symbols in file")
+       print("  -x   extract symbol from file (specify symbol name with -s)")
+       sys.exit()
+
+
+#
+# main
+#
+
+if __name__ == '__main__':
+       filename = symbol = mode = None
+       opts, args = getopt(sys.argv[1:], 's:lx')
+       for opt, arg in opts:
+               if opt == '-s':
+                       symbol = arg
+               elif opt == '-l':
+                       mode = 'list'
+               elif opt == '-x':
+                       mode = 'extract'
+
+       if len(args) != 1:
+               usage()
+       if mode is None and symbol is None:
+               usage()
+
+       try:
+               filename = args[0]
+               if mode == 'list':
+                       list_all(filename)
+               elif mode == 'extract':
+                       if symbol is None:
+                               raise Exception("specify a symbol to extract")
+                       data = extract_symbol(filename, symbol)
+                       outfile = os.path.splitext(filename)[0] + symbol
+                       open(outfile, 'wb').write(data)
+                       print("saved %d bytes to %s" % (len(data), outfile))
+               else:
+                       raise Exception("specify -l or -x")
+       except Exception as e:
+               print("Error: %s" % str(e))
+
+