Enhance MIPS64 testing and simplify handling code for its peculiar relocations (...
authorPierre-Marie de Rodat <pmderodat@kawie.fr>
Tue, 17 Mar 2020 12:01:45 +0000 (13:01 +0100)
committerGitHub <noreply@github.com>
Tue, 17 Mar 2020 12:01:45 +0000 (05:01 -0700)
* Add handling for SHF_MASKOS section flags

* Add readelf testcases for MIPS64 specificities

* Simplify the decoding of MIPS64 relocations

Instead of using "fake" fields to parse the relocation structure and
then use complex shift/masks to recover the conveyed information (once
for big endian binaries, twice for little endian ones), use fields
actually described in the spec and use straightforward shifts to
synthetize the "fake" fields.

elftools/elf/descriptions.py
elftools/elf/structs.py
test/testfiles_for_readelf/mips64-relocs-be.o.elf [new file with mode: 0644]
test/testfiles_for_readelf/mips64-relocs-le.o.elf [new file with mode: 0644]
test/testfiles_for_readelf/mips64-relocs.c [new file with mode: 0644]

index c35b1154823b6e11155b212eb88e8298e91a257a..bacefd752216e1b72a2cec8daab8a425a84ecf5a 100644 (file)
@@ -95,7 +95,8 @@ def describe_sh_flags(x):
             SH_FLAGS.SHF_WRITE, SH_FLAGS.SHF_ALLOC, SH_FLAGS.SHF_EXECINSTR,
             SH_FLAGS.SHF_MERGE, SH_FLAGS.SHF_STRINGS, SH_FLAGS.SHF_INFO_LINK,
             SH_FLAGS.SHF_LINK_ORDER, SH_FLAGS.SHF_OS_NONCONFORMING,
-            SH_FLAGS.SHF_GROUP, SH_FLAGS.SHF_TLS, SH_FLAGS.SHF_EXCLUDE):
+            SH_FLAGS.SHF_GROUP, SH_FLAGS.SHF_TLS, SH_FLAGS.SHF_MASKOS,
+            SH_FLAGS.SHF_EXCLUDE):
         s += _DESCR_SH_FLAGS[flag] if (x & flag) else ''
     if x & SH_FLAGS.SHF_MASKPROC:
         s += 'p'
@@ -404,6 +405,7 @@ _DESCR_SH_FLAGS = {
     SH_FLAGS.SHF_OS_NONCONFORMING: 'O',
     SH_FLAGS.SHF_GROUP: 'G',
     SH_FLAGS.SHF_TLS: 'T',
+    SH_FLAGS.SHF_MASKOS: 'o',
     SH_FLAGS.SHF_EXCLUDE: 'E',
 }
 
index 2fb56a7a0caf7cc1648d2b28741d27183a1120e2..b9203c6b635d797f529d3b90446e6e7191bebbe4 100644 (file)
@@ -215,64 +215,56 @@ class ELFStructs(object):
         self.Elf_Chdr = Struct('Elf_Chdr', *fields)
 
     def _create_rel(self):
-        r_info = self.Elf_xword('r_info')
-
-        # r_info is also taken apart into r_info_sym and r_info_type, plus
-        # r_info_type2 and r_info_type3 on ELF64 MIPS. This is done in Value
-        # to avoid endianity issues while parsing.
+        # r_info is also taken apart into r_info_sym and r_info_type. This is
+        # done in Value to avoid endianity issues while parsing.
         if self.elfclass == 32:
-            fields = [Value('r_info_sym',
+            fields = [self.Elf_xword('r_info'),
+                      Value('r_info_sym',
                             lambda ctx: (ctx['r_info'] >> 8) & 0xFFFFFF),
                       Value('r_info_type',
                             lambda ctx: ctx['r_info'] & 0xFF)]
         elif self.e_machine == 'EM_MIPS': # ELF64 MIPS
-            # The r_info field in MIPS ELF64 binaries (called r_raw_info, here)
-            # isn't a 64-bit field, but rather two 32-bit fields (the symbol
-            # index, then three bytes for relocation types). See the
-            # specification:
-            # <https://www.linux-mips.org/pub/linux/mips/doc/ABI/elf64-2.4.pdf>
-            # Note that the specification describes the fields more directly,
-            # but here we stick to the general "r_info" field to be compatible
-            # with other architectures and simplify testing.
-
-            def compute_r_info(ctx):
-                raw = ctx['r_raw_info']
-                if not self.little_endian:
-                    return raw
-                # little endian requires an additional byteswap
-                return (((raw & 0xffffffff) << 32)
-                        | ((raw >> 56) & 0xff)
-                        | ((raw >> 40) & 0xff00)
-                        | ((raw >> 24) & 0xff0000)
-                        | ((raw >> 8) & 0xff000000))
-
-            r_info = self.Elf_xword('r_raw_info')
             fields = [
-                Value('r_info', compute_r_info),
-                Value('r_info_sym',
-                      lambda ctx: (ctx['r_info'] >> 32) & 0xFFFFFFFF),
-                Value('r_info_type3',
-                      lambda ctx: (ctx['r_info'] >> 16) & 0xFF),
-                Value('r_info_type2',
-                      lambda ctx: (ctx['r_info'] >> 8) & 0xFF),
-                Value('r_info_type',
-                      lambda ctx: ctx['r_info'] & 0xFF)
+                # The MIPS ELF64 specification
+                # (https://www.linux-mips.org/pub/linux/mips/doc/ABI/elf64-2.4.pdf)
+                # provides a non-standard relocation structure definition.
+                self.Elf_word('r_sym'),
+                self.Elf_byte('r_ssym'),
+                self.Elf_byte('r_type3'),
+                self.Elf_byte('r_type2'),
+                self.Elf_byte('r_type'),
+
+                # Synthetize usual fields for compatibility with other
+                # architectures. This allows relocation consumers (including
+                # our readelf tests) to work without worrying about MIPS64
+                # oddities.
+                Value('r_info_sym', lambda ctx: ctx['r_sym']),
+                Value('r_info_ssym', lambda ctx: ctx['r_ssym']),
+                Value('r_info_type', lambda ctx: ctx['r_type']),
+                Value('r_info_type2', lambda ctx: ctx['r_type2']),
+                Value('r_info_type3', lambda ctx: ctx['r_type3']),
+                Value('r_info',
+                      lambda ctx: (ctx['r_sym'] << 32)
+                                  | (ctx['r_ssym'] << 24)
+                                  | (ctx['r_type3'] << 16)
+                                  | (ctx['r_type2'] << 8)
+                                  | ctx['r_type']),
             ]
         else: # Other 64 ELFs
-            fields = [Value('r_info_sym',
+            fields = [self.Elf_xword('r_info'),
+                      Value('r_info_sym',
                             lambda ctx: (ctx['r_info'] >> 32) & 0xFFFFFFFF),
                       Value('r_info_type',
                             lambda ctx: ctx['r_info'] & 0xFFFFFFFF)]
 
         self.Elf_Rel = Struct('Elf_Rel',
-            self.Elf_addr('r_offset'),
-            r_info,
-            *fields
-        )
+                              self.Elf_addr('r_offset'),
+                              *fields)
+
+        fields_and_addend = fields + [self.Elf_sxword('r_addend')]
         self.Elf_Rela = Struct('Elf_Rela',
-            self.Elf_addr('r_offset'),
-            r_info,
-            *(fields + [self.Elf_sxword('r_addend')])
+                               self.Elf_addr('r_offset'),
+                               *fields_and_addend
         )
 
     def _create_dyn(self):
diff --git a/test/testfiles_for_readelf/mips64-relocs-be.o.elf b/test/testfiles_for_readelf/mips64-relocs-be.o.elf
new file mode 100644 (file)
index 0000000..fb6d587
Binary files /dev/null and b/test/testfiles_for_readelf/mips64-relocs-be.o.elf differ
diff --git a/test/testfiles_for_readelf/mips64-relocs-le.o.elf b/test/testfiles_for_readelf/mips64-relocs-le.o.elf
new file mode 100644 (file)
index 0000000..028d413
Binary files /dev/null and b/test/testfiles_for_readelf/mips64-relocs-le.o.elf differ
diff --git a/test/testfiles_for_readelf/mips64-relocs.c b/test/testfiles_for_readelf/mips64-relocs.c
new file mode 100644 (file)
index 0000000..707b625
--- /dev/null
@@ -0,0 +1,19 @@
+/* This source was compiled for MIPS64 (big endian) and MIPS64EL (little
+   endial):
+
+   mips64-unknown-linux-gnu-gcc   -c mips64-relocs.c -o mips64-relocs-be.o.elf -mabi=64
+   mips64el-unknown-linux-gnu-gcc -c mips64-relocs.c -o mips64-relocs-le.o.elf -mabi=64
+*/
+
+extern struct {
+  int i, j;
+} data;
+
+extern int bar (void);
+
+int
+foo (int a)
+{
+  data.i += a;
+  data.j -= bar();
+}