diff --git a/vndk/tools/definition-tool/tests/test_elf.py b/vndk/tools/definition-tool/tests/test_elf.py index d8153b58a..84bec350c 100755 --- a/vndk/tools/definition-tool/tests/test_elf.py +++ b/vndk/tools/definition-tool/tests/test_elf.py @@ -6,6 +6,7 @@ import os import sys sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +import tempfile import unittest from compat import StringIO @@ -39,6 +40,24 @@ class ElfSymTest(unittest.TestCase): class ELFTest(unittest.TestCase): + def test_get_ei_class_from_name(self): + self.assertEqual(ELF.ELFCLASS32, ELF.get_ei_class_from_name('32')) + self.assertEqual(ELF.ELFCLASS64, ELF.get_ei_class_from_name('64')) + + def test_get_ei_data_from_name(self): + self.assertEqual(ELF.ELFDATA2LSB, + ELF.get_ei_data_from_name('Little-Endian')) + self.assertEqual(ELF.ELFDATA2MSB, + ELF.get_ei_data_from_name('Big-Endian')) + + def test_get_e_machine_from_name(self): + self.assertEqual(0, ELF.get_e_machine_from_name('EM_NONE')) + self.assertEqual(3, ELF.get_e_machine_from_name('EM_386')) + self.assertEqual(8, ELF.get_e_machine_from_name('EM_MIPS')) + self.assertEqual(40, ELF.get_e_machine_from_name('EM_ARM')) + self.assertEqual(62, ELF.get_e_machine_from_name('EM_X86_64')) + self.assertEqual(183, ELF.get_e_machine_from_name('EM_AARCH64')) + def test_repr(self): elf = ELF() self.assertEqual(elf, eval(repr(elf))) @@ -116,5 +135,44 @@ class ELFTest(unittest.TestCase): self.assertEqual('hello\nworld\n', actual_output) + def test_parse_dump_file(self): + data = ('EI_CLASS\t64\n' + 'EI_DATA\t\tLittle-Endian\n' + 'E_MACHINE\tEM_AARCH64\n' + 'DT_RPATH\trpath_1\n' + 'DT_RPATH\trpath_2\n' + 'DT_RUNPATH\trunpath_1\n' + 'DT_RUNPATH\trunpath_2\n' + 'DT_NEEDED\tlibc.so\n' + 'DT_NEEDED\tlibm.so\n' + 'EXP_SYMBOL\texported_1\n' + 'EXP_SYMBOL\texported_2\n' + 'IMP_SYMBOL\timported_1\n' + 'IMP_SYMBOL\timported_2\n') + + def check_parse_dump_file_result(res): + self.assertEqual(ELF.ELFCLASS64, res.ei_class) + self.assertEqual(ELF.ELFDATA2LSB, res.ei_data) + self.assertEqual(183, res.e_machine) + self.assertEqual(['rpath_1', 'rpath_2'], res.dt_rpath) + self.assertEqual(['runpath_1', 'runpath_2'], res.dt_runpath) + self.assertEqual(['libc.so', 'libm.so'], res.dt_needed) + self.assertSetEqual({'exported_1', 'exported_2'}, + res.exported_symbols) + self.assertSetEqual({'imported_1', 'imported_2'}, + res.imported_symbols) + + # Parse ELF dump from the string buffer. + check_parse_dump_file_result(ELF.load_dumps(data)) + + # Parse ELF dump from the given file path. + with tempfile.NamedTemporaryFile('w+') as f: + f.write(data) + f.flush() + f.seek(0) + + check_parse_dump_file_result(ELF.load_dump(f.name)) + + if __name__ == '__main__': unittest.main() diff --git a/vndk/tools/definition-tool/vndk_definition_tool.py b/vndk/tools/definition-tool/vndk_definition_tool.py index fdf291b14..15dcadf96 100755 --- a/vndk/tools/definition-tool/vndk_definition_tool.py +++ b/vndk/tools/definition-tool/vndk_definition_tool.py @@ -141,6 +141,26 @@ class ELF(object): } + @staticmethod + def _dict_find_key_by_value(d, dst): + for key, value in d.items(): + if value == dst: + return key + raise KeyError(dst) + + @staticmethod + def get_ei_class_from_name(name): + return ELF._dict_find_key_by_value(ELF._ELF_CLASS_NAMES, name) + + @staticmethod + def get_ei_data_from_name(name): + return ELF._dict_find_key_by_value(ELF._ELF_DATA_NAMES, name) + + @staticmethod + def get_e_machine_from_name(name): + return ELF._dict_find_key_by_value(ELF._ELF_MACHINE_IDS, name) + + __slots__ = ('ei_class', 'ei_data', 'e_machine', 'dt_rpath', 'dt_runpath', 'dt_needed', 'exported_symbols', 'imported_symbols',) @@ -388,6 +408,47 @@ class ELF(object): with mmap(f.fileno(), st.st_size, access=ACCESS_READ) as image: self._parse_from_buf(image) + def _parse_from_dump_lines(self, path, lines): + patt = re.compile('^([A-Za-z_]+)\t+(.*)$') + for line_no, line in enumerate(lines): + match = patt.match(line) + if not match: + print('error: {}: {}: failed to parse' + .format(path, line_no + 1), file=sys.stderr) + continue + key = match.group(1) + value = match.group(2) + + if key == 'EI_CLASS': + self.ei_class = ELF.get_ei_class_from_name(value) + elif key == 'EI_DATA': + self.ei_data = ELF.get_ei_data_from_name(value) + elif key == 'E_MACHINE': + self.e_machine = ELF.get_e_machine_from_name(value) + elif key == 'DT_RPATH': + self.dt_rpath.append(intern(value)) + elif key == 'DT_RUNPATH': + self.dt_runpath.append(intern(value)) + elif key == 'DT_NEEDED': + self.dt_needed.append(intern(value)) + elif key == 'EXP_SYMBOL': + self.exported_symbols.add(intern(value)) + elif key == 'IMP_SYMBOL': + self.imported_symbols.add(intern(value)) + else: + print('error: {}: {}: unknown tag name: {}' + .format(path, line_no + 1, key), file=sys.stderr) + + def _parse_from_dump_file(self, path): + """Load information from ELF dump file.""" + with open(path, 'r') as f: + self._parse_from_dump_lines(path, f) + + def _parse_from_dump_buf(self, buf): + """Load information from ELF dump buffer.""" + self._parse_from_dump_lines(''.format(id(buf)), + buf.splitlines()) + @staticmethod def load(path): """Create an ELF instance from the file path""" @@ -402,6 +463,20 @@ class ELF(object): elf._parse_from_buf(buf) return elf + @staticmethod + def load_dump(path): + """Create an ELF instance from a dump file path""" + elf = ELF() + elf._parse_from_dump_file(path) + return elf + + @staticmethod + def load_dumps(buf): + """Create an ELF instance from a dump file buffer""" + elf = ELF() + elf._parse_from_dump_buf(buf) + return elf + #------------------------------------------------------------------------------ # NDK and Banned Libraries