#------------------------------------------------------------------------------
# elftools tests
#
# Maxim Akhmedov (max42@yandex-team.ru)
# This code is in the public domain
#------------------------------------------------------------------------------
import unittest
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):
        elf = ELFFile(self._core_file)
        for segment in elf.iter_segments():
            if not isinstance(segment, NoteSegment):
                continue
            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
                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()
