Basic MIPS support
authorKarl Vogel <karl.vogel@gmail.com>
Sun, 26 Oct 2014 11:09:15 +0000 (12:09 +0100)
committerKarl Vogel <karl.vogel@gmail.com>
Thu, 30 Oct 2014 22:16:13 +0000 (23:16 +0100)
elftools/dwarf/callframe.py
elftools/dwarf/descriptions.py
elftools/elf/constants.py
elftools/elf/descriptions.py
elftools/elf/elffile.py
elftools/elf/enums.py
elftools/elf/relocation.py
scripts/readelf.py
test/test_mips_support.py [new file with mode: 0644]
test/testfiles_for_readelf/simple_mips_gcc.o.elf [new file with mode: 0644]
test/testfiles_for_unittests/simple_gcc.elf.mips [new file with mode: 0644]

index 264adb8dc31b317b6f76bdc87c27529e97db3bf3..22f6a71041f91f31b1e68499d7d09e6d35a086b8 100644 (file)
@@ -263,7 +263,7 @@ class CFIEntry(object):
             cie = self.cie
             cie_decoded_table = cie.get_decoded()
             last_line_in_CIE = copy.copy(cie_decoded_table.table[-1])
-            cur_line = last_line_in_CIE
+            cur_line = copy.copy(last_line_in_CIE)
             cur_line['pc'] = self['initial_location']
             reg_order = copy.copy(cie_decoded_table.reg_order)
 
@@ -346,10 +346,10 @@ class CFIEntry(object):
                 dwarf_assert(
                     isinstance(self, FDE),
                     '%s instruction must be in a FDE' % name)
-                dwarf_assert(
-                    instr.args[0] in last_line_in_CIE,
-                    '%s: can not find register in CIE')
-                cur_line[instr.args[0]] = last_line_in_CIE[instr.args[0]]
+                if instr.args[0] in last_line_in_CIE:
+                    cur_line[instr.args[0]] = last_line_in_CIE[instr.args[0]]
+                else:
+                    cur_line.pop(instr.args[0], None)
             elif name == 'DW_CFA_remember_state':
                 line_stack.append(cur_line)
             elif name == 'DW_CFA_restore_state':
index ca00cd1ee7cb1989cd3c3e57f3b2a74c5427e5e0..ac8f772c8dbaf924153ad6c9c6e81adb417505ae 100644 (file)
@@ -49,7 +49,11 @@ def describe_CFI_instructions(entry):
             'Unexpected instruction "%s" for a CIE' % instr)
 
     def _full_reg_name(regnum):
-        return 'r%s (%s)' % (regnum, describe_reg_name(regnum))
+        regname = describe_reg_name(regnum, _MACHINE_ARCH, False)
+        if regname:
+            return 'r%s (%s)' % (regnum, regname)
+        else:
+            return 'r%s' % regnum
 
     if isinstance(entry, CIE):
         cie = entry
@@ -146,7 +150,7 @@ def describe_DWARF_expr(expr, structs):
     return '(' + dwarf_expr_dumper.get_str() + ')'
 
 
-def describe_reg_name(regnum, machine_arch=None):
+def describe_reg_name(regnum, machine_arch=None, default=True):
     """ Provide a textual description for a register name, given its serial
         number. The number is expected to be valid.
     """
@@ -157,8 +161,10 @@ def describe_reg_name(regnum, machine_arch=None):
         return _REG_NAMES_x86[regnum]
     elif machine_arch == 'x64':
         return _REG_NAMES_x64[regnum]
+    elif default:
+        return 'r%s' % regnum
     else:
-        return '<none>'
+        return None
 
 #-------------------------------------------------------------------------------
 
index b41f35a96f4f2cd204baa03d89a3fa945024981e..df6e0af50e9beb36ff92b795ead253448e7e5429 100644 (file)
@@ -26,6 +26,25 @@ class E_FLAGS(object):
     EF_ARM_ABI_FLOAT_SOFT=0x00000200
     EF_ARM_ABI_FLOAT_HARD=0x00000400
 
+    EF_MIPS_NOREORDER=1
+    EF_MIPS_PIC=2
+    EF_MIPS_CPIC=4
+    EF_MIPS_XGOT=8
+    EF_MIPS_64BIT_WHIRL=16
+    EF_MIPS_ABI2=32
+    EF_MIPS_ABI_ON32=64
+    EF_MIPS_NAN2008=1024
+    EF_MIPS_ARCH=0xf0000000
+    EF_MIPS_ARCH_1=0x00000000
+    EF_MIPS_ARCH_2=0x10000000
+    EF_MIPS_ARCH_3=0x20000000
+    EF_MIPS_ARCH_4=0x30000000
+    EF_MIPS_ARCH_5=0x40000000
+    EF_MIPS_ARCH_32=0x50000000
+    EF_MIPS_ARCH_64=0x60000000
+    EF_MIPS_ARCH_32R2=0x70000000
+    EF_MIPS_ARCH_64R2=0x80000000
+
 class SHN_INDICES(object):
     """ Special section indices
     """
index a25e51b81dc9d8c8ef76e045c35694a15a83664d..f085b8786df4f03de505275688c69c6f25e9083e 100644 (file)
@@ -7,8 +7,9 @@
 # This code is in the public domain
 #-------------------------------------------------------------------------------
 from .enums import (
-    ENUM_D_TAG, ENUM_E_VERSION, ENUM_RELOC_TYPE_i386, ENUM_RELOC_TYPE_x64,
-        ENUM_RELOC_TYPE_ARM, ENUM_RELOC_TYPE_AARCH64)
+    ENUM_D_TAG, ENUM_E_VERSION, ENUM_P_TYPE, ENUM_SH_TYPE,
+    ENUM_RELOC_TYPE_i386, ENUM_RELOC_TYPE_x64,
+    ENUM_RELOC_TYPE_ARM, ENUM_RELOC_TYPE_AARCH64, ENUM_RELOC_TYPE_MIPS)
 from .constants import P_FLAGS, SH_FLAGS, SUNW_SYMINFO_FLAGS, VER_FLAGS
 from ..common.py3compat import iteritems
 
@@ -38,7 +39,12 @@ def describe_e_version_numeric(x):
     return '0x%x' % ENUM_E_VERSION[x]
 
 def describe_p_type(x):
-    return _DESCR_P_TYPE.get(x, _unknown)
+    if x in _DESCR_P_TYPE:
+        return _DESCR_P_TYPE.get(x)
+    elif x >= ENUM_P_TYPE['PT_LOOS'] and x <= ENUM_P_TYPE['PT_HIOS']:
+        return 'LOOS+%lx' % (x - ENUM_P_TYPE['PT_LOOS'])
+    else:
+        return _unknown
 
 def describe_p_flags(x):
     s = ''
@@ -47,7 +53,12 @@ def describe_p_flags(x):
     return s
 
 def describe_sh_type(x):
-    return _DESCR_SH_TYPE.get(x, _unknown)
+    if x in _DESCR_SH_TYPE:
+        return _DESCR_SH_TYPE.get(x)
+    elif x >= ENUM_SH_TYPE['SHT_LOOS'] and x < ENUM_SH_TYPE['SHT_GNU_versym']:
+        return 'loos+%lx' % (x - ENUM_SH_TYPE['SHT_LOOS'])
+    else:
+        return _unknown
 
 def describe_sh_flags(x):
     s = ''
@@ -81,6 +92,8 @@ def describe_reloc_type(x, elffile):
         return _DESCR_RELOC_TYPE_ARM.get(x, _unknown)
     elif arch == 'AArch64':
         return _DESCR_RELOC_TYPE_AARCH64.get(x, _unknown)
+    elif arch == 'MIPS':
+        return _DESCR_RELOC_TYPE_MIPS.get(x, _unknown)
     else:
         return 'unrecognized: %-7x' % (x & 0xFFFFFFFF)
 
@@ -242,6 +255,41 @@ _DESCR_SH_TYPE = dict(
     SHT_ARM_PREEMPTMAP='ARM_PREEMPTMAP',
     SHT_ARM_ATTRIBUTES='ARM_ATTRIBUTES',
     SHT_ARM_DEBUGOVERLAY='ARM_DEBUGOVERLAY',
+    SHT_MIPS_LIBLIST='MIPS_LIBLIST',
+    SHT_MIPS_DEBUG='MIPS_DEBUG',
+    SHT_MIPS_REGINFO='MIPS_REGINFO',
+    SHT_MIPS_PACKAGE='MIPS_PACKAGE',
+    SHT_MIPS_PACKSYM='MIPS_PACKSYM',
+    SHT_MIPS_RELD='MIPS_RELD',
+    SHT_MIPS_IFACE='MIPS_IFACE',
+    SHT_MIPS_CONTENT='MIPS_CONTENT',
+    SHT_MIPS_OPTIONS='MIPS_OPTIONS',
+    SHT_MIPS_SHDR='MIPS_SHDR',
+    SHT_MIPS_FDESC='MIPS_FDESC',
+    SHT_MIPS_EXTSYM='MIPS_EXTSYM',
+    SHT_MIPS_DENSE='MIPS_DENSE',
+    SHT_MIPS_PDESC='MIPS_PDESC',
+    SHT_MIPS_LOCSYM='MIPS_LOCSYM',
+    SHT_MIPS_AUXSYM='MIPS_AUXSYM',
+    SHT_MIPS_OPTSYM='MIPS_OPTSYM',
+    SHT_MIPS_LOCSTR='MIPS_LOCSTR',
+    SHT_MIPS_LINE='MIPS_LINE',
+    SHT_MIPS_RFDESC='MIPS_RFDESC',
+    SHT_MIPS_DELTASYM='MIPS_DELTASYM',
+    SHT_MIPS_DELTAINST='MIPS_DELTAINST',
+    SHT_MIPS_DELTACLASS='MIPS_DELTACLASS',
+    SHT_MIPS_DWARF='MIPS_DWARF',
+    SHT_MIPS_DELTADECL='MIPS_DELTADECL',
+    SHT_MIPS_SYMBOL_LIB='MIPS_SYMBOL_LIB',
+    SHT_MIPS_EVENTS='MIPS_EVENTS',
+    SHT_MIPS_TRANSLATE='MIPS_TRANSLATE',
+    SHT_MIPS_PIXIE='MIPS_PIXIE',
+    SHT_MIPS_XLATE='MIPS_XLATE',
+    SHT_MIPS_XLATE_DEBUG='MIPS_XLATE_DEBUG',
+    SHT_MIPS_WHIRL='MIPS_WHIRL',
+    SHT_MIPS_EH_REGION='MIPS_EH_REGION',
+    SHT_MIPS_XLATE_OLD='MIPS_XLATE_OLD',
+    SHT_MIPS_PDR_EXCEPTION='MIPS_PDR_EXCEPTION',
 )
 
 _DESCR_SH_FLAGS = {
@@ -350,5 +398,8 @@ _DESCR_RELOC_TYPE_ARM = dict(
 _DESCR_RELOC_TYPE_AARCH64 = dict(
         (v, k) for k, v in iteritems(ENUM_RELOC_TYPE_AARCH64))
 
+_DESCR_RELOC_TYPE_MIPS = dict(
+        (v, k) for k, v in iteritems(ENUM_RELOC_TYPE_MIPS))
+
 _DESCR_D_TAG = dict(
         (v, k) for k, v in iteritems(ENUM_D_TAG))
index 4132c1ed109fa3b03d17872f877e2e99a62a84e4..cbb1428bde2591a41d8ad0de122fee878e7c9d40 100644 (file)
@@ -177,6 +177,8 @@ class ELFFile(object):
             return 'ARM'
         elif self['e_machine'] == 'EM_AARCH64':
             return 'AArch64'
+        elif self['e_machine'] == 'EM_MIPS':
+            return 'MIPS'
         else:
             return '<unknown>'
 
index ea4dcf157072f00d0052e10be6d5e998c7de6a38..494827f789b3cb41a443458c83e8d1eaf039cee0 100644 (file)
@@ -188,7 +188,7 @@ ENUM_SH_TYPE = dict(
     SHT_GNU_HASH=0x6ffffff6,
     SHT_GNU_verdef=0x6ffffffd,  # also SHT_SUNW_verdef
     SHT_GNU_verneed=0x6ffffffe, # also SHT_SUNW_verneed
-    SHT_GNU_versym=0x6fffffff,  # also SHT_SUNW_versym
+    SHT_GNU_versym=0x6fffffff,  # also SHT_SUNW_versym, SHT_HIOS
     SHT_LOPROC=0x70000000,
     SHT_HIPROC=0x7fffffff,
     SHT_LOUSER=0x80000000,
@@ -196,10 +196,45 @@ ENUM_SH_TYPE = dict(
     SHT_AMD64_UNWIND=0x70000001,
     SHT_SUNW_LDYNSYM=0x6ffffff3,
     SHT_SUNW_syminfo=0x6ffffffc,
-    SHT_ARM_EXIDX=0x70000001,
-    SHT_ARM_PREEMPTMAP=0x70000002,
-    SHT_ARM_ATTRIBUTES=0x70000003,
-    SHT_ARM_DEBUGOVERLAY=0x70000004,
+    SHT_ARM_EXIDX=0x70000001,        # also SHT_MIPS_MSYM
+    SHT_ARM_PREEMPTMAP=0x70000002,   # also SHT_MIPS_CONFLICT
+    SHT_ARM_ATTRIBUTES=0x70000003,   # also SHT_MIPS_GPTAB
+    SHT_ARM_DEBUGOVERLAY=0x70000004, # also SHT_MIPS_UCODE
+    SHT_MIPS_LIBLIST=0x70000000,
+    SHT_MIPS_DEBUG=0x70000005,
+    SHT_MIPS_REGINFO=0x70000006,
+    SHT_MIPS_PACKAGE=0x70000007,
+    SHT_MIPS_PACKSYM=0x70000008,
+    SHT_MIPS_RELD=0x70000009,
+    SHT_MIPS_IFACE=0x7000000b,
+    SHT_MIPS_CONTENT=0x7000000c,
+    SHT_MIPS_OPTIONS=0x7000000d,
+    SHT_MIPS_SHDR=0x70000010,
+    SHT_MIPS_FDESC=0x70000011,
+    SHT_MIPS_EXTSYM=0x70000012,
+    SHT_MIPS_DENSE=0x70000013,
+    SHT_MIPS_PDESC=0x70000014,
+    SHT_MIPS_LOCSYM=0x70000015,
+    SHT_MIPS_AUXSYM=0x70000016,
+    SHT_MIPS_OPTSYM=0x70000017,
+    SHT_MIPS_LOCSTR=0x70000018,
+    SHT_MIPS_LINE=0x70000019,
+    SHT_MIPS_RFDESC=0x7000001a,
+    SHT_MIPS_DELTASYM=0x7000001b,
+    SHT_MIPS_DELTAINST=0x7000001c,
+    SHT_MIPS_DELTACLASS=0x7000001d,
+    SHT_MIPS_DWARF=0x7000001e,
+    SHT_MIPS_DELTADECL=0x7000001f,
+    SHT_MIPS_SYMBOL_LIB=0x70000020,
+    SHT_MIPS_EVENTS=0x70000021,
+    SHT_MIPS_TRANSLATE=0x70000022,
+    SHT_MIPS_PIXIE=0x70000023,
+    SHT_MIPS_XLATE=0x70000024,
+    SHT_MIPS_XLATE_DEBUG=0x70000025,
+    SHT_MIPS_WHIRL=0x70000026,
+    SHT_MIPS_EH_REGION=0x70000027,
+    SHT_MIPS_XLATE_OLD=0x70000028,
+    SHT_MIPS_PDR_EXCEPTION=0x70000029,
     _default_=Pass,
 )
 
@@ -214,6 +249,8 @@ ENUM_P_TYPE = dict(
     PT_SHLIB=5,
     PT_PHDR=6,
     PT_TLS=7,
+    PT_LOOS=0x60000000,
+    PT_HIOS=0x6fffffff,
     PT_LOPROC=0x70000000,
     PT_HIPROC=0x7fffffff,
     PT_GNU_EH_FRAME=0x6474e550,
@@ -373,6 +410,61 @@ ENUM_D_TAG = dict(
     _default_=Pass,
 )
 
+ENUM_RELOC_TYPE_MIPS = dict(
+    R_MIPS_NONE=0,
+    R_MIPS_16=1,
+    R_MIPS_32=2,
+    R_MIPS_REL32=3,
+    R_MIPS_26=4,
+    R_MIPS_HI16=5,
+    R_MIPS_LO16=6,
+    R_MIPS_GPREL16=7,
+    R_MIPS_LITERAL=8,
+    R_MIPS_GOT16=9,
+    R_MIPS_PC16=10,
+    R_MIPS_CALL16=11,
+    R_MIPS_GPREL32=12,
+    R_MIPS_SHIFT5=16,
+    R_MIPS_SHIFT6=17,
+    R_MIPS_64=18,
+    R_MIPS_GOT_DISP=19,
+    R_MIPS_GOT_PAGE=20,
+    R_MIPS_GOT_OFST=21,
+    R_MIPS_GOT_HI16=22,
+    R_MIPS_GOT_LO16=23,
+    R_MIPS_SUB=24,
+    R_MIPS_INSERT_A=25,
+    R_MIPS_INSERT_B=26,
+    R_MIPS_DELETE=27,
+    R_MIPS_HIGHER=28,
+    R_MIPS_HIGHEST=29,
+    R_MIPS_CALL_HI16=30,
+    R_MIPS_CALL_LO16=31,
+    R_MIPS_SCN_DISP=32,
+    R_MIPS_REL16=33,
+    R_MIPS_ADD_IMMEDIATE=34,
+    R_MIPS_PJUMP=35,
+    R_MIPS_RELGOT=36,
+    R_MIPS_JALR=37,
+    R_MIPS_TLS_DTPMOD32=38,
+    R_MIPS_TLS_DTPREL32=39,
+    R_MIPS_TLS_DTPMOD64=40,
+    R_MIPS_TLS_DTPREL64=41,
+    R_MIPS_TLS_GD=42,
+    R_MIPS_TLS_LDM=43,
+    R_MIPS_TLS_DTPREL_HI16=44,
+    R_MIPS_TLS_DTPREL_LO16=45,
+    R_MIPS_TLS_GOTTPREL=46,
+    R_MIPS_TLS_TPREL32=47,
+    R_MIPS_TLS_TPREL64=48,
+    R_MIPS_TLS_TPREL_HI16=49,
+    R_MIPS_TLS_TPREL_LO16=50,
+    R_MIPS_GLOB_DAT=51,
+    R_MIPS_COPY=126,
+    R_MIPS_JUMP_SLOT=127,
+    _default_=Pass,
+)
+
 ENUM_RELOC_TYPE_i386 = dict(
     R_386_NONE=0,
     R_386_32=1,
index 176f7c5404abf421fd0016ec4e5d96bfaaca4048..9d9701ff68bf673982e19dff412bfadaceb65412 100644 (file)
@@ -11,7 +11,7 @@ from collections import namedtuple
 from ..common.exceptions import ELFRelocationError
 from ..common.utils import elf_assert, struct_parse
 from .sections import Section
-from .enums import ENUM_RELOC_TYPE_i386, ENUM_RELOC_TYPE_x64
+from .enums import ENUM_RELOC_TYPE_i386, ENUM_RELOC_TYPE_x64, ENUM_RELOC_TYPE_MIPS
 
 
 class Relocation(object):
@@ -147,6 +147,11 @@ class RelocationHandler(object):
                 raise ELFRelocationError(
                     'Unexpected REL relocation for x64: %s' % reloc)
             recipe = self._RELOCATION_RECIPES_X64.get(reloc_type, None)
+        elif self.elffile.get_machine_arch() == 'MIPS':
+            if reloc.is_RELA():
+                raise ELFRelocationError(
+                    'Unexpected RELA relocation for MIPS: %s' % reloc)
+            recipe = self._RELOCATION_RECIPES_MIPS.get(reloc_type, None)
 
         if recipe is None:
             raise ELFRelocationError(
@@ -210,6 +215,15 @@ class RelocationHandler(object):
     def _reloc_calc_sym_plus_addend_pcrel(value, sym_value, offset, addend=0):
         return sym_value + addend - offset
 
+    # https://dmz-portal.mips.com/wiki/MIPS_relocation_types
+    _RELOCATION_RECIPES_MIPS = {
+        ENUM_RELOC_TYPE_MIPS['R_MIPS_NONE']: _RELOCATION_RECIPE_TYPE(
+            bytesize=4, has_addend=False, calc_func=_reloc_calc_identity),
+        ENUM_RELOC_TYPE_MIPS['R_MIPS_32']: _RELOCATION_RECIPE_TYPE(
+            bytesize=4, has_addend=False,
+            calc_func=_reloc_calc_sym_plus_value),
+    }
+
     _RELOCATION_RECIPES_X86 = {
         ENUM_RELOC_TYPE_i386['R_386_NONE']: _RELOCATION_RECIPE_TYPE(
             bytesize=4, has_addend=False, calc_func=_reloc_calc_identity),
index 967fb0e1d681f6e9fe3d27a95d5a93e8bc6a8459..02508d2caccbbd9ab2da90d78503b59cb2f119a5 100755 (executable)
@@ -127,6 +127,16 @@ class ReadElf(object):
             version = flags & E_FLAGS.EF_ARM_EABIMASK
             if version == E_FLAGS.EF_ARM_EABI_VER5:
                 description += ", Version5 EABI"
+        elif self.elffile['e_machine'] == "EM_MIPS":
+            if flags & E_FLAGS.EF_MIPS_NOREORDER:
+                description += ", noreorder"
+            if flags & E_FLAGS.EF_MIPS_CPIC:
+                description += ", cpic"
+            if not (flags & E_FLAGS.EF_MIPS_ABI2) and not (flags & E_FLAGS.EF_MIPS_ABI_ON32):
+                description += ", o32"
+            if (flags & E_FLAGS.EF_MIPS_ARCH) == E_FLAGS.EF_MIPS_ARCH_1:
+                description += ", mips1"
+
         return description
 
     def display_program_headers(self, show_heading=True):
@@ -1005,15 +1015,19 @@ class ReadElf(object):
             reg_order = sorted(ifilter(
                 lambda r: r != ra_regnum,
                 decoded_table.reg_order))
+            if len(decoded_table.reg_order):
+
+                # Headings for the registers
+                for regnum in reg_order:
+                    self._emit('%-6s' % describe_reg_name(regnum))
+                self._emitline('ra      ')
 
-            # Headings for the registers
-            for regnum in reg_order:
-                self._emit('%-6s' % describe_reg_name(regnum))
-            self._emitline('ra      ')
+                # Now include ra_regnum in reg_order to print its values similarly
+                # to the other registers.
+                reg_order.append(ra_regnum)
+            else:
+                self._emitline()
 
-            # Now include ra_regnum in reg_order to print its values similarly
-            # to the other registers.
-            reg_order.append(ra_regnum)
             for line in decoded_table.table:
                 self._emit(self._format_hex(
                     line['pc'], fullhex=True, lead0x=False))
diff --git a/test/test_mips_support.py b/test/test_mips_support.py
new file mode 100644 (file)
index 0000000..c0537d0
--- /dev/null
@@ -0,0 +1,30 @@
+#-------------------------------------------------------------------------------
+# elftools tests
+#
+# Karl Vogel (karl.vogel@gmail.com)
+# This code is in the public domain
+#-------------------------------------------------------------------------------
+try:
+    import unittest2 as unittest
+except ImportError:
+    import unittest
+import os
+
+from utils import setup_syspath; setup_syspath()
+from elftools.elf.elffile import ELFFile
+
+class TestMIPSSupport(unittest.TestCase):
+    def test_hello(self):
+        with open(os.path.join('test', 'testfiles_for_unittests',
+                               'simple_gcc.elf.mips'), 'rb') as f:
+            elf = ELFFile(f)
+            self.assertEqual(elf.get_machine_arch(), 'MIPS')
+
+            # Check some other properties of this ELF file derived from readelf
+            self.assertEqual(elf['e_entry'], 0x0)
+            self.assertEqual(elf.num_sections(), 25)
+            self.assertEqual(elf.num_segments(), 0)
+
+if __name__ == '__main__':
+    unittest.main()
+
diff --git a/test/testfiles_for_readelf/simple_mips_gcc.o.elf b/test/testfiles_for_readelf/simple_mips_gcc.o.elf
new file mode 100644 (file)
index 0000000..af20c82
Binary files /dev/null and b/test/testfiles_for_readelf/simple_mips_gcc.o.elf differ
diff --git a/test/testfiles_for_unittests/simple_gcc.elf.mips b/test/testfiles_for_unittests/simple_gcc.elf.mips
new file mode 100644 (file)
index 0000000..af20c82
Binary files /dev/null and b/test/testfiles_for_unittests/simple_gcc.elf.mips differ