* Added the ELFFile class - top-level interface to ELF files
authorebenders <devnull@localhost>
Thu, 8 Sep 2011 08:27:53 +0000 (11:27 +0300)
committerebenders <devnull@localhost>
Thu, 8 Sep 2011 08:27:53 +0000 (11:27 +0300)
* Added exceptions classes

elftools/construct/adapters.py
elftools/elf/constants.py
elftools/elf/elffile.py [new file with mode: 0644]
elftools/elf/enums.py
elftools/elf/structs.py
elftools/exceptions.py [new file with mode: 0644]
z.py

index 98fda723e7bc6b146586b3e97af01fca55a72edb..69c84d91debe82dede12cfecd4cdf10db650ae8d 100644 (file)
@@ -85,7 +85,8 @@ class MappingAdapter(Adapter):
             return self.encoding[obj]\r
         except (KeyError, TypeError):\r
             if self.encdefault is NotImplemented:\r
-                raise MappingError("no encoding mapping for %r" % (obj,))\r
+                raise MappingError("no encoding mapping for %r [%s]" % (\r
+                    obj, self.subcon.name))\r
             if self.encdefault is Pass:\r
                 return obj\r
             return self.encdefault\r
@@ -94,7 +95,8 @@ class MappingAdapter(Adapter):
             return self.decoding[obj]\r
         except (KeyError, TypeError):\r
             if self.decdefault is NotImplemented:\r
-                raise MappingError("no decoding mapping for %r"  % (obj,))\r
+                raise MappingError("no decoding mapping for %r [%s]"  % (\r
+                    obj, self.subcon.name))\r
             if self.decdefault is Pass:\r
                 return obj\r
             return self.decdefault\r
index 1c64a8f29ba2b558b3f9818e11d7983dd6c20f45..5af83725675c9dc73b95e8d909814252c7809671 100644 (file)
@@ -1,4 +1,11 @@
+#-------------------------------------------------------------------------------\r
+# elftools: elf/constants.py\r
+#\r
 # Constants and flags, placed into classes for namespacing\r
+#\r
+# Eli Bendersky (eliben@gmail.com)\r
+# This code is in the public domain\r
+#-------------------------------------------------------------------------------\r
 \r
 class SHN_INDICES(object):\r
     """ Special section indices
diff --git a/elftools/elf/elffile.py b/elftools/elf/elffile.py
new file mode 100644 (file)
index 0000000..29dc095
--- /dev/null
@@ -0,0 +1,89 @@
+#-------------------------------------------------------------------------------\r
+# elftools: elf/elffile.py\r
+#\r
+# ELFFile - main class for accessing ELF files\r
+#\r
+# Eli Bendersky (eliben@gmail.com)\r
+# This code is in the public domain\r
+#-------------------------------------------------------------------------------\r
+\r
+from .structs import ELFStructs\r
+from ..exceptions import ELFError, ELFParseError\r
+from ..construct import ConstructError\r
+\r
+\r
+class ELFFile(object):\r
+    """ Accessible attributes:\r
+        \r
+            elfclass: \r
+                32 or 64 - specifies the word size of the target machine\r
+            \r
+            little_endian:\r
+                boolean - specifies the target machine's endianness     \r
+\r
+            header:\r
+                the complete ELF file header
+    """
+    def __init__(self, stream):
+        self.stream = stream\r
+        self._identify_file()\r
+        self.structs = ELFStructs(\r
+            little_endian=self.little_endian,\r
+            elfclass=self.elfclass)\r
+        self.header = self._parse_elf_header()\r
+    \r
+    def __getitem__(self, name):
+        """ Implement dict-like access to header entries
+        """\r
+        return self.header[name]\r
+        \r
+    def _identify_file(self):
+        """ Verify the ELF file and identify its class and endianness.
+        """\r
+        # Note: this code reads the stream directly, without using ELFStructs,\r
+        # since we don't yet know its exact format. ELF was designed to be \r
+        # read like this - its e_ident field is word-size and endian agnostic.\r
+        #\r
+        self.stream.seek(0)\r
+        magic = self.stream.read(4)\r
+        self._assert(magic == '\x7fELF', 'Magic number does not match')\r
+        \r
+        ei_class = self.stream.read(1)\r
+        if ei_class == '\x01':\r
+            self.elfclass = 32\r
+        elif ei_class == '\x02':\r
+            self.elfclass = 64\r
+        else:\r
+            raise ELFError('Invalid EI_CLASS %s' % repr(ei_class))\r
+        \r
+        ei_data = self.stream.read(1)\r
+        if ei_data == '\x01':\r
+            self.little_endian = True\r
+        elif ei_data == '\x02':\r
+            self.little_endian = False\r
+        else:\r
+            raise ELFError('Invalid EI_DATA %s' % repr(ei_data))\r
+    \r
+    def _parse_elf_header(self):
+        """ Parses the ELF file header and assigns the result to attributes\r
+            of this object.
+        """\r
+        self.stream.seek(0)\r
+        return self._struct_parse(self.structs.Elf_Ehdr)\r
+    \r
+    def _struct_parse(self, struct):
+        """ Convenience method for parsing at the current stream location with\r
+            the given struct. Also wraps the error thrown by construct with our\r
+            own error.
+        """\r
+        try:\r
+            return struct.parse_stream(self.stream)\r
+        except ConstructError as e:\r
+            raise ELFParseError(e.message)\r
+    \r
+    def _assert(self, cond, msg=''):
+        """ Assert that cond is True, otherwise raise ELFError(msg)
+        """\r
+        if not cond:\r
+            raise ELFError(msg)\r
+\r
index 076297994b9a6f9806a0493cbf6ee62ff57a060d..1217d74773f09e15f1e6238d3e6566bed4b5d975 100644 (file)
@@ -1,5 +1,11 @@
-# Mappings of enum names<->values to be inserted into construct's Enum adapter\r
+#-------------------------------------------------------------------------------\r
+# elftools: elf/enums.py\r
 #\r
+# Mappings of enum names to values\r
+#\r
+# Eli Bendersky (eliben@gmail.com)\r
+# This code is in the public domain\r
+#-------------------------------------------------------------------------------\r
 \r
 # e_ident[EI_CLASS] in the ELF header\r
 ENUM_EI_CLASS = dict(\r
index 8c610dc01fda2c9072051fab0539d79a184726ca..0b9f58f1f703fabee4497adaec192793958aea2e 100644 (file)
@@ -1,3 +1,13 @@
+#-------------------------------------------------------------------------------\r
+# elftools: elf/structs.py\r
+#\r
+# Encapsulation of Construct structs for parsing an ELF file, adjusted for\r
+# correct endianness and word-size.\r
+#\r
+# Eli Bendersky (eliben@gmail.com)\r
+# This code is in the public domain\r
+#-------------------------------------------------------------------------------\r
+\r
 from ..construct import (\r
     UBInt8, UBInt16, UBInt32, UBInt64,\r
     ULInt8, ULInt16, ULInt32, ULInt64,\r
@@ -8,7 +18,25 @@ from ..construct import (
 from .enums import *\r
 \r
 \r
-class ELFStructs(object):
+class ELFStructs(object):\r
+    """ Accessible attributes:\r
+    \r
+            Elf_{byte|half|word|addr|offset|sword|xword|xsword}:\r
+                Data chunks, as specified by the ELF standard, adjusted for \r
+                correct endianness and word-size.\r
+            \r
+            Elf_Ehdr:\r
+                ELF file header\r
+            \r
+            Elf_Phdr:\r
+                Program header\r
+            \r
+            Elf_Shdr:\r
+                Section header\r
+            \r
+            Elf_Sym:\r
+                Symbol table entry
+    """
     def __init__(self, little_endian=True, elfclass=32):\r
         assert elfclass == 32 or elfclass == 64\r
         self.little_endian = little_endian\r
diff --git a/elftools/exceptions.py b/elftools/exceptions.py
new file mode 100644 (file)
index 0000000..b61b463
--- /dev/null
@@ -0,0 +1,15 @@
+#-------------------------------------------------------------------------------\r
+# elftools: exceptions.py\r
+#\r
+# Exception classes for elftools\r
+#\r
+# Eli Bendersky (eliben@gmail.com)\r
+# This code is in the public domain\r
+#-------------------------------------------------------------------------------\r
+\r
+class ELFError(Exception): \r
+    pass
+\r
+class ELFParseError(ELFError):\r
+    pass\r
+\r
diff --git a/z.py b/z.py
index aa4672eef7e0a8314547a6fa65b72779e4bbe1a4..e959632c4051c1915a9d05c2f29718bedfe9ffd6 100644 (file)
--- a/z.py
+++ b/z.py
@@ -1,16 +1,20 @@
 import sys\r
 from elftools.elf.structs import ELFStructs\r
+from elftools.elf.elffile import ELFFile\r
 \r
 # read a little-endian, 64-bit file\r
 es = ELFStructs(True, 64)\r
 \r
 stream = open('binfiles/z.elf', 'rb')\r
-eheader = es.Elf_Ehdr.parse_stream(stream)\r
 \r
-print eheader\r
+efile = ELFFile(stream)\r
 \r
-shtable_offset = eheader.e_shoff\r
-strtable_section_offset = shtable_offset + eheader.e_shstrndx * eheader.e_shentsize\r
+#~ print efile.header\r
+#~ print dir(efile)\r
+#~ print efile['e_type']\r
+\r
+shtable_offset = efile['e_shoff']\r
+strtable_section_offset = shtable_offset + efile['e_shstrndx'] * efile['e_shentsize']\r
 \r
 # get to the section header for the sh string table\r
 print strtable_section_offset\r