Add ability to parse the NT_FILE note found in core files (#220)
authorzephyrj <jono.sykes15@gmail.com>
Mon, 22 Apr 2019 12:07:27 +0000 (13:07 +0100)
committerEli Bendersky <eliben@users.noreply.github.com>
Mon, 22 Apr 2019 12:07:27 +0000 (05:07 -0700)
elftools/elf/notes.py
elftools/elf/structs.py
test/test_core_notes.py

index d34198b8ed011e3035af11ed29f6deddda8511b0..d69007f3abe3dbc8f540d14da45e7dbac370f80a 100644 (file)
@@ -40,6 +40,10 @@ def iter_notes(elffile, offset, size):
             note['n_desc'] = struct_parse(elffile.structs.Elf_Prpsinfo,
                                           elffile.stream,
                                           offset)
+        elif note['n_type'] == 'NT_FILE':
+            note['n_desc'] = struct_parse(elffile.structs.Elf_Nt_File,
+                                          elffile.stream,
+                                          offset)
         else:
             note['n_desc'] = desc_data
         offset += roundup(note['n_descsz'], 2)
index 6b5610b7fc311b4d63bd5502c3d7d9417ad5a347..789656d1ac2f402934e3d894c4e7fc43b6f671b3 100644 (file)
@@ -374,6 +374,21 @@ class ELFStructs(object):
                 String('pr_psargs', 80),
             )
 
+        # A PT_NOTE of type NT_FILE matching the definition in
+        # https://chromium.googlesource.com/
+        # native_client/nacl-binutils/+/upstream/master/binutils/readelf.c
+        # Line 15121
+        self.Elf_Nt_File = Struct('Elf_Nt_File',
+                                  self.Elf_xword("num_map_entries"),
+                                  self.Elf_xword("page_size"),
+                                  Array(lambda ctx: ctx.num_map_entries,
+                                        Struct('Elf_Nt_File_Entry',
+                                             self.Elf_addr('vm_start'),
+                                             self.Elf_addr('vm_end'),
+                                             self.Elf_offset('page_offset'))),
+                                  Array(lambda ctx: ctx.num_map_entries,
+                                        CString('filename')))
+
     def _create_stabs(self):
         # Structure of one stabs entry, see binutils/bfd/stabs.c
         # Names taken from https://sourceware.org/gdb/current/onlinedocs/stabs.html#Overview
index 41ee6f8dd898c6fa943d6e4380c724545bf5bc9f..4caef8626df07a14e03b74c2f52928d5db0d7897 100644 (file)
@@ -10,38 +10,183 @@ import os
 from elftools.elf.elffile import ELFFile
 from elftools.elf.segments import NoteSegment
 
+
 class TestCoreNotes(unittest.TestCase):
     """ This test makes sure than core dump specific
         sections are properly analyzed.
     """
+    @classmethod
+    def setUpClass(cls):
+        cls._core_file = open(os.path.join('test',
+                              'testfiles_for_unittests', 'core_linux64.elf'),
+                              'rb')
 
     def test_core_prpsinfo(self):
-        with open(os.path.join('test',
-                               'testfiles_for_unittests', 'core_linux64.elf'),
-                  'rb') as f:
-            elf = ELFFile(f)
-            for segment in elf.iter_segments():
-                if not isinstance(segment, NoteSegment):
+        elf = ELFFile(self._core_file)
+        for segment in elf.iter_segments():
+            if not isinstance(segment, NoteSegment):
+                continue
+            notes = list(segment.iter_notes())
+            for note in segment.iter_notes():
+                if note['n_type'] != 'NT_PRPSINFO':
+                    continue
+                desc = note['n_desc']
+                self.assertEqual(desc['pr_state'], 0)
+                self.assertEqual(desc['pr_sname'], b'R')
+                self.assertEqual(desc['pr_zomb'], 0)
+                self.assertEqual(desc['pr_nice'], 0)
+                self.assertEqual(desc['pr_flag'], 0x400600)
+                self.assertEqual(desc['pr_uid'], 1000)
+                self.assertEqual(desc['pr_gid'], 1000)
+                self.assertEqual(desc['pr_pid'], 23395)
+                self.assertEqual(desc['pr_ppid'], 23187)
+                self.assertEqual(desc['pr_pgrp'], 23395)
+                self.assertEqual(desc['pr_sid'], 23187)
+                self.assertEqual(
+                    desc['pr_fname'],
+                    b'coredump_self\x00\x00\x00')
+                self.assertEqual(
+                    desc['pr_psargs'],
+                    b'./coredump_self foo bar 42 ' + b'\x00' * (80 - 27))
+
+    def test_core_nt_file(self):
+        """
+        Test that the parsing of the NT_FILE note within a core file is
+        correct.
+        The assertions are made against the output of eu-readelf.
+
+        eu-readelf -n core_linux64.elf
+        ...
+        CORE                 621  FILE
+        10 files:
+        00400000-00401000 00000000 4096
+          /home/max42/pyelftools/test/coredump_self
+        00600000-00601000 00000000 4096
+          /home/max42/pyelftools/test/coredump_self
+        00601000-00602000 00001000 4096
+          /home/max42/pyelftools/test/coredump_self
+        7fa4593ae000-7fa45956d000 00000000 1830912
+          /lib/x86_64-linux-gnu/libc-2.23.so
+        7fa45956d000-7fa45976d000 001bf000 2097152
+          /lib/x86_64-linux-gnu/libc-2.23.so
+        7fa45976d000-7fa459771000 001bf000 16384
+          /lib/x86_64-linux-gnu/libc-2.23.so
+        7fa459771000-7fa459773000 001c3000 8192
+          /lib/x86_64-linux-gnu/libc-2.23.so
+        7fa459777000-7fa45979d000 00000000 155648
+          /lib/x86_64-linux-gnu/ld-2.23.so
+        7fa45999c000-7fa45999d000 00025000 4096
+          /lib/x86_64-linux-gnu/ld-2.23.so
+        7fa45999d000-7fa45999e000 00026000 4096
+          /lib/x86_64-linux-gnu/ld-2.23.so
+        ...
+        """
+        elf = ELFFile(self._core_file)
+        nt_file_found = False
+        for segment in elf.iter_segments():
+            if not isinstance(segment, NoteSegment):
+                continue
+            for note in segment.iter_notes():
+                if note['n_type'] != 'NT_FILE':
                     continue
-                notes = list(segment.iter_notes())
-                for note in segment.iter_notes():
-                    if note['n_type'] != 'NT_PRPSINFO':
-                        continue
-                    desc = note['n_desc']
-                    self.assertEquals(desc['pr_state'], 0)
-                    self.assertEquals(desc['pr_sname'], b'R')
-                    self.assertEquals(desc['pr_zomb'], 0)
-                    self.assertEquals(desc['pr_nice'], 0)
-                    self.assertEquals(desc['pr_flag'], 0x400600)
-                    self.assertEquals(desc['pr_uid'], 1000)
-                    self.assertEquals(desc['pr_gid'], 1000)
-                    self.assertEquals(desc['pr_pid'], 23395)
-                    self.assertEquals(desc['pr_ppid'], 23187)
-                    self.assertEquals(desc['pr_pgrp'], 23395)
-                    self.assertEquals(desc['pr_sid'], 23187)
-                    self.assertEquals(
-                        desc['pr_fname'],
-                        b'coredump_self\x00\x00\x00')
-                    self.assertEquals(
-                        desc['pr_psargs'],
-                        b'./coredump_self foo bar 42 ' + b'\x00' * (80 - 27))
+                nt_file_found = True
+                desc = note['n_desc']
+                self.assertEqual(desc['num_map_entries'], 10)
+                self.assertEqual(desc['page_size'], 4096)
+                self.assertEqual(len(desc['Elf_Nt_File_Entry']), 10)
+                self.assertEqual(len(desc['filename']), 10)
+
+                self.validate_nt_file_entry(desc['Elf_Nt_File_Entry'][0],
+                                            desc['page_size'],
+                                            0x00400000,
+                                            0x00401000,
+                                            0x00000000)
+                self.assertEqual(desc['filename'][0],
+                                 b"/home/max42/pyelftools/test/coredump_self")
+
+                self.validate_nt_file_entry(desc['Elf_Nt_File_Entry'][1],
+                                            desc['page_size'],
+                                            0x00600000,
+                                            0x00601000,
+                                            0x00000000)
+                self.assertEqual(desc['filename'][1],
+                                 b"/home/max42/pyelftools/test/coredump_self")
+
+                self.validate_nt_file_entry(desc['Elf_Nt_File_Entry'][2],
+                                            desc['page_size'],
+                                            0x00601000,
+                                            0x00602000,
+                                            0x00001000)
+                self.assertEqual(desc['filename'][2],
+                                 b"/home/max42/pyelftools/test/coredump_self")
+
+                self.validate_nt_file_entry(desc['Elf_Nt_File_Entry'][3],
+                                            desc['page_size'],
+                                            0x7fa4593ae000,
+                                            0x7fa45956d000,
+                                            0x00000000)
+                self.assertEqual(desc['filename'][3],
+                                 b"/lib/x86_64-linux-gnu/libc-2.23.so")
+
+                self.validate_nt_file_entry(desc['Elf_Nt_File_Entry'][4],
+                                            desc['page_size'],
+                                            0x7fa45956d000,
+                                            0x7fa45976d000,
+                                            0x001bf000)
+                self.assertEqual(desc['filename'][4],
+                                 b"/lib/x86_64-linux-gnu/libc-2.23.so")
+
+                self.validate_nt_file_entry(desc['Elf_Nt_File_Entry'][5],
+                                            desc['page_size'],
+                                            0x7fa45976d000,
+                                            0x7fa459771000,
+                                            0x001bf000)
+                self.assertEqual(desc['filename'][5],
+                                 b"/lib/x86_64-linux-gnu/libc-2.23.so")
+
+                self.validate_nt_file_entry(desc['Elf_Nt_File_Entry'][6],
+                                            desc['page_size'],
+                                            0x7fa459771000,
+                                            0x7fa459773000,
+                                            0x001c3000)
+                self.assertEqual(desc['filename'][6],
+                                 b"/lib/x86_64-linux-gnu/libc-2.23.so")
+
+                self.validate_nt_file_entry(desc['Elf_Nt_File_Entry'][7],
+                                            desc['page_size'],
+                                            0x7fa459777000,
+                                            0x7fa45979d000,
+                                            0x00000000)
+                self.assertEqual(desc['filename'][7],
+                                 b"/lib/x86_64-linux-gnu/ld-2.23.so")
+
+                self.validate_nt_file_entry(desc['Elf_Nt_File_Entry'][8],
+                                            desc['page_size'],
+                                            0x7fa45999c000,
+                                            0x7fa45999d000,
+                                            0x00025000)
+                self.assertEqual(desc['filename'][8],
+                                 b"/lib/x86_64-linux-gnu/ld-2.23.so")
+
+                self.validate_nt_file_entry(desc['Elf_Nt_File_Entry'][9],
+                                            desc['page_size'],
+                                            0x7fa45999d000,
+                                            0x7fa45999e000,
+                                            0x00026000)
+                self.assertEqual(desc['filename'][9],
+                                 b"/lib/x86_64-linux-gnu/ld-2.23.so")
+        self.assertTrue(nt_file_found)
+
+    def validate_nt_file_entry(self,
+                               entry,
+                               page_size,
+                               expected_vm_start,
+                               expected_vm_end,
+                               expected_page_offset):
+        self.assertEqual(entry.vm_start, expected_vm_start)
+        self.assertEqual(entry.vm_end, expected_vm_end)
+        self.assertEqual(entry.page_offset * page_size, expected_page_offset)
+
+    @classmethod
+    def tearDownClass(cls):
+        cls._core_file.close()