Support MIPS64 .o files - don't remove has_addend (#495)
authorNoam Yorav-Raphael <noamraph@gmail.com>
Mon, 4 Sep 2023 12:55:14 +0000 (15:55 +0300)
committerGitHub <noreply@github.com>
Mon, 4 Sep 2023 12:55:14 +0000 (05:55 -0700)
* Update relocation.py to support MIPS64 .o files. Add a test file. DWARF test still shows an error.

* Make test pass for test/testfiles_for_readelf/simple_mips_gcc.o.elf by having two _RELOCATION_RECIPES_MIPS, one for REL and one for RELA

* Adjust llvm-dwarfdump output for MIPS64 to match pyelftools and gcc-objdump.

* update dwarf_mips64el.o.elf

* Change dwarf_mips64el.c to not use globals

Now we don't depend on whether relocations on globals in .o should be performed or not.

* run_dwarfdump_test.py: add a comment to explain the special case.

---------

Co-authored-by: Noam Yorav-Raphael <noam.yoravraphael@mobileye.com>
Co-authored-by: Noam Yorav-Raphael <noamyr@mobileye.com>
elftools/elf/relocation.py
scripts/dwarfdump.py
test/run_dwarfdump_tests.py
test/testfiles_for_dwarfdump/dwarf_mips64el.o.elf [new file with mode: 0644]
test/testfiles_for_dwarfdump/dwarf_mips64el/.gitignore [new file with mode: 0644]
test/testfiles_for_dwarfdump/dwarf_mips64el/dwarf_mips64el.c [new file with mode: 0644]
test/testfiles_for_dwarfdump/dwarf_mips64el/flake.lock [new file with mode: 0644]
test/testfiles_for_dwarfdump/dwarf_mips64el/flake.nix [new file with mode: 0644]

index 0c4e754c790fb183dd41738e0c4a3c7157f358d9..c167368dec517019473b514df3188fe184c2ffcd 100644 (file)
@@ -241,9 +241,13 @@ class RelocationHandler(object):
             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 reloc_type == ENUM_RELOC_TYPE_MIPS['R_MIPS_64']:
+                    if reloc['r_type2'] != 0 or reloc['r_type3'] != 0 or reloc['r_ssym'] != 0:
+                        raise ELFRelocationError(
+                            'Multiple relocations in R_MIPS_64 are not implemented: %s' % reloc)
+                recipe = self._RELOCATION_RECIPES_MIPS_RELA.get(reloc_type, None)
+            else:
+                recipe = self._RELOCATION_RECIPES_MIPS_REL.get(reloc_type, None)
         elif self.elffile.get_machine_arch() == 'ARM':
             if reloc.is_RELA():
                 raise ELFRelocationError(
@@ -307,7 +311,7 @@ class RelocationHandler(object):
         return value
 
     def _reloc_calc_sym_plus_value(value, sym_value, offset, addend=0):
-        return sym_value + value
+        return sym_value + value + addend
 
     def _reloc_calc_sym_plus_value_pcrel(value, sym_value, offset, addend=0):
         return sym_value + value - offset
@@ -344,13 +348,23 @@ class RelocationHandler(object):
     }
 
     # https://dmz-portal.mips.com/wiki/MIPS_relocation_types
-    _RELOCATION_RECIPES_MIPS = {
+    _RELOCATION_RECIPES_MIPS_REL = {
         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_MIPS_RELA = {
+        ENUM_RELOC_TYPE_MIPS['R_MIPS_NONE']: _RELOCATION_RECIPE_TYPE(
+            bytesize=4, has_addend=True, calc_func=_reloc_calc_identity),
+        ENUM_RELOC_TYPE_MIPS['R_MIPS_32']: _RELOCATION_RECIPE_TYPE(
+            bytesize=4, has_addend=True,
+            calc_func=_reloc_calc_sym_plus_value),
+        ENUM_RELOC_TYPE_MIPS['R_MIPS_64']: _RELOCATION_RECIPE_TYPE(
+            bytesize=8, has_addend=True,
+            calc_func=_reloc_calc_sym_plus_value),
+    }
 
     _RELOCATION_RECIPES_PPC64 = {
         ENUM_RELOC_TYPE_PPC64['R_PPC64_ADDR32']: _RELOCATION_RECIPE_TYPE(
index 1ab083205909f8dc623c95d39b89e22fce9f79b8..e1148832d450e60265227767f92700546a1a8870 100644 (file)
@@ -342,7 +342,7 @@ class ReadElf(object):
         self.elffile = ELFFile(file)
         self.output = output
         self._dwarfinfo = self.elffile.get_dwarf_info()
-        arches = {"EM_386": "i386", "EM_X86_64": "x86-64", "EM_ARM": "littlearm", "EM_AARCH64": "littleaarch64", "EM_LOONGARCH64": "loongarch64", "EM_RISCV": "littleriscv"}
+        arches = {"EM_386": "i386", "EM_X86_64": "x86-64", "EM_ARM": "littlearm", "EM_AARCH64": "littleaarch64", "EM_LOONGARCH64": "loongarch64", "EM_RISCV": "littleriscv", "EM_MIPS": "mips"}
         arch = arches[self.elffile['e_machine']]
         bits = self.elffile.elfclass
         self._emitline("%s:    file format elf%d-%s" % (filename, bits, arch))
index 739130b10d6f98edf39faabff1392b74233a1600..e9710c237c1d30ea3b9e234cd6dac0b62da7abdb 100644 (file)
@@ -102,6 +102,11 @@ def compare_output(s1, s2):
         and errmsg is empty. Otherwise success is False and errmsg holds a
         description of the mismatch.
     """
+    # llvm-dwarfdump sometimes adds a comment to addresses. We still haven't invested the
+    # effort to understand exactly when. For now, removing the section comment helps us pass
+    # the test.
+    s1 = s1.replace('(0x0000000000000000 ".text")', '(0x0000000000000000)')
+
     def prepare_lines(s):
         return [line for line in s.lower().splitlines() if line.strip() != '']
 
diff --git a/test/testfiles_for_dwarfdump/dwarf_mips64el.o.elf b/test/testfiles_for_dwarfdump/dwarf_mips64el.o.elf
new file mode 100644 (file)
index 0000000..f2dad3f
Binary files /dev/null and b/test/testfiles_for_dwarfdump/dwarf_mips64el.o.elf differ
diff --git a/test/testfiles_for_dwarfdump/dwarf_mips64el/.gitignore b/test/testfiles_for_dwarfdump/dwarf_mips64el/.gitignore
new file mode 100644 (file)
index 0000000..c4a847d
--- /dev/null
@@ -0,0 +1 @@
+/result
diff --git a/test/testfiles_for_dwarfdump/dwarf_mips64el/dwarf_mips64el.c b/test/testfiles_for_dwarfdump/dwarf_mips64el/dwarf_mips64el.c
new file mode 100644 (file)
index 0000000..e744011
--- /dev/null
@@ -0,0 +1,5 @@
+int f() {
+    int var1 = 3;
+    int var2 = 5;
+    return var1 + var2;
+}
diff --git a/test/testfiles_for_dwarfdump/dwarf_mips64el/flake.lock b/test/testfiles_for_dwarfdump/dwarf_mips64el/flake.lock
new file mode 100644 (file)
index 0000000..2bfbcd0
--- /dev/null
@@ -0,0 +1,27 @@
+{
+  "nodes": {
+    "nixpkgs": {
+      "locked": {
+        "lastModified": 1692698134,
+        "narHash": "sha256-YtMmZWR/dlTypOcwiZfZTMPr3tj9fwr05QTStfSyDSg=",
+        "owner": "NixOS",
+        "repo": "nixpkgs",
+        "rev": "a16f7eb56e88c8985fcc6eb81dabd6cade4e425a",
+        "type": "github"
+      },
+      "original": {
+        "owner": "NixOS",
+        "ref": "nixos-23.05",
+        "repo": "nixpkgs",
+        "type": "github"
+      }
+    },
+    "root": {
+      "inputs": {
+        "nixpkgs": "nixpkgs"
+      }
+    }
+  },
+  "root": "root",
+  "version": 7
+}
diff --git a/test/testfiles_for_dwarfdump/dwarf_mips64el/flake.nix b/test/testfiles_for_dwarfdump/dwarf_mips64el/flake.nix
new file mode 100644 (file)
index 0000000..0364948
--- /dev/null
@@ -0,0 +1,18 @@
+{
+  description = "A flake for building a mips64el .o file for testing pyelftools";
+
+  inputs.nixpkgs.url = github:NixOS/nixpkgs/nixos-23.05;
+
+  outputs = { self, nixpkgs }: {
+
+    defaultPackage.x86_64-linux =
+      with (import nixpkgs { system = "x86_64-linux"; }).pkgsCross.mips64el-linux-gnuabi64;
+      stdenv.mkDerivation {
+        name = "dwarf_mips64el";
+        src = self;
+        buildPhase = "$CC -g -c ./dwarf_mips64el.c";
+        installPhase = "mkdir -p $out; cp dwarf_mips64el.o $out/";
+      };
+
+  };
+}