Fixup error on empty .debug_pubtypes section (#215)
authorVasily E <laer.18@gmail.com>
Thu, 31 Jan 2019 14:17:14 +0000 (17:17 +0300)
committerEli Bendersky <eliben@users.noreply.github.com>
Thu, 31 Jan 2019 14:17:14 +0000 (06:17 -0800)
* tox: explicitly set locale

Locale affects GNU binutils output translation which cause
run_readelf_tests.py to fail if system language is not English.

Signed-off-by: Efimov Vasily <real@ispras.ru>
* test: unittest reproducing error with empty ".debug_pubtypes" section

Signed-off-by: Efimov Vasily <real@ispras.ru>
* NameLUT: use `construct.If` to declare "name" field

This patch also fixes problem with empty first entry.

Signed-off-by: Efimov Vasily <real@ispras.ru>
* NameLUT._get_entries: remove unused `bytes_read`

Signed-off-by: Efimov Vasily <real@ispras.ru>
elftools/dwarf/namelut.py
test/test_pubtypes.py [new file with mode: 0644]
test/testfiles_for_unittests/empty_pubtypes/Makefile [new file with mode: 0644]
test/testfiles_for_unittests/empty_pubtypes/main [new file with mode: 0755]
test/testfiles_for_unittests/empty_pubtypes/main.c [new file with mode: 0644]
tox.ini

index b7de7985d07ba46cb578c6db7b830602d6ed31c7..d24a5de6380c92e51f4359228bacf8aa4a631c2c 100755 (executable)
@@ -12,7 +12,7 @@ from collections import OrderedDict
 from ..common.utils import struct_parse
 from bisect import bisect_right
 import math
-from ..construct import CString, Struct
+from ..construct import CString, Struct, If
 
 NameLUTEntry = collections.namedtuple('NameLUTEntry', 'cu_ofs die_ofs')
 
@@ -158,11 +158,14 @@ class NameLUT(collections.Mapping):
         entries = OrderedDict()
         cu_headers = []
         offset = 0
+        # According to 6.1.1. of DWARFv4, each set of names is terminated by
+        # an offset field containing zero (and no following string). Because
+        # of sequential parsing, every next entry may be that terminator.
+        # So, field "name" is conditional.
         entry_struct = Struct("Dwarf_offset_name_pair",
                 self._structs.Dwarf_offset('die_ofs'),
-                CString('name'))
-        die_ofs_struct = self._structs.Dwarf_offset('die_ofs')
-                
+                If(lambda ctx: ctx['die_ofs'],CString('name')))
+
         # each run of this loop will fetch one CU worth of entries.
         while offset < self._size:
 
@@ -174,29 +177,21 @@ class NameLUT(collections.Mapping):
             offset = (offset + namelut_hdr.unit_length +
                      self._structs.initial_length_field_size())
 
-            bytes_read = 0
             # before inner loop, latch data that will be used in the inner
             # loop to avoid attribute access and other computation.
             hdr_cu_ofs = namelut_hdr.debug_info_offset
-            # read the first tuple for this CU.
-            entry = struct_parse(entry_struct,
-                    self._stream)
+
             # while die_ofs of the entry is non-zero (which indicates the end) ...
             while True:
+                entry = struct_parse(entry_struct, self._stream)
+
+                # if it is zero, then we done.
+                if entry.die_ofs == 0:
+                    break
                 # add this entry to the look-up dictionary.
                 entries[entry.name.decode('utf-8')] = NameLUTEntry(
                         cu_ofs = hdr_cu_ofs,
                         die_ofs = hdr_cu_ofs + entry.die_ofs)
-                # get the DIE offset entry alone.
-                die_ofs = struct_parse(die_ofs_struct, self._stream)
-                # if it is zero, then we done.
-                if die_ofs == 0:
-                    break
-                else:
-                    # else this is a valid DIE, get the name as well and 
-                    # construct the entry
-                    entry.name = struct_parse(CString('name'), self._stream)
-                    entry.die_ofs = die_ofs
 
         # return the entries parsed so far.
         return (entries, cu_headers)
diff --git a/test/test_pubtypes.py b/test/test_pubtypes.py
new file mode 100644 (file)
index 0000000..a0eb642
--- /dev/null
@@ -0,0 +1,25 @@
+#-------------------------------------------------------------------------------
+# elftools tests
+#
+# Efimov Vasiliy (real@ispras.ru)
+# This code is in the public domain
+#-------------------------------------------------------------------------------
+import os
+import unittest
+
+from elftools.elf.elffile import ELFFile
+
+
+class TestEmptyPubtypes(unittest.TestCase):
+    def test_empty_pubtypes(self):
+        test_dir = os.path.join('test', 'testfiles_for_unittests')
+        with open(os.path.join(test_dir, 'empty_pubtypes', 'main'), 'rb') as f:
+            elf = ELFFile(f)
+
+            # This test targets `ELFParseError` caused by buggy handling
+            # of ".debug_pubtypes" section which only has zero terminator
+            # entry.
+            self.assertEqual(len(elf.get_dwarf_info().get_pubtypes()), 0)
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/test/testfiles_for_unittests/empty_pubtypes/Makefile b/test/testfiles_for_unittests/empty_pubtypes/Makefile
new file mode 100644 (file)
index 0000000..20decd4
--- /dev/null
@@ -0,0 +1,2 @@
+main:
+main: CFLAGS=-gpubnames -g -O0
diff --git a/test/testfiles_for_unittests/empty_pubtypes/main b/test/testfiles_for_unittests/empty_pubtypes/main
new file mode 100755 (executable)
index 0000000..fc6c9b3
Binary files /dev/null and b/test/testfiles_for_unittests/empty_pubtypes/main differ
diff --git a/test/testfiles_for_unittests/empty_pubtypes/main.c b/test/testfiles_for_unittests/empty_pubtypes/main.c
new file mode 100644 (file)
index 0000000..ab73b3a
--- /dev/null
@@ -0,0 +1 @@
+void main() {}
diff --git a/tox.ini b/tox.ini
index 7e9b48468c902a4f8f3d3791b05ae00ccbac0427..461a248adafb3c686058b1bb7d4ac833c20da235 100644 (file)
--- a/tox.ini
+++ b/tox.ini
@@ -2,6 +2,8 @@
 envlist = py27,py34,py35
 
 [testenv]
+setenv =
+    LC_ALL = en_US
 commands =
     python test/run_all_unittests.py
     python test/run_examples_test.py