1#!/usr/bin/env python
2#
3# Copyright (C) 2018 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#      http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16#
17"""This file contains unit tests for elf_parser."""
18
19import os
20import unittest
21
22from vts.utils.python.library import elf_parser as elf
23
24
25_SECTION_NAMES = {'test.rela', 'test.honeycomb', 'test.jellybean',
26                  'test.nougat', 'test.oreo', 'test.pie', 'test.dup'}
27
28_EXPORTED_SYMBOLS = {'global_var_1', 'global_var_2',
29                     '_Z15exported_func_1v', '_Z15exported_func_2v'}
30
31R_X86_64_GLOB_DAT = 6
32
33_RELOCATIONS = {(R_X86_64_GLOB_DAT, 'global_var_1', 0),
34                (R_X86_64_GLOB_DAT, 'global_var_2', 0)}
35
36_ANDROID_RELOCATIONS = [(0x200008, (1 << 32) | 1, 0),
37                        (0x200010, (2 << 32) | 1, 0),
38                        (0x200020, 8, 128),
39                        (0x200030, 8, 136),
40                        (0x200040, 8, 152),
41                        (0x200050, 8, 184)]
42
43_RELR_RELOCATIONS = [(0x300000, 0),
44                     (0x300000 + 2 * 8, 0),
45                     (0x300000 + 60 * 8, 0),
46                     (0x300000 + 64 * 8, 0),
47                     (0x400000, 0)]
48
49_DEPENDENCIES = ['libc.so.6', 'libm.so.6']
50
51_RUNPATHS = ['/runpath1', '/runpath2']
52
53
54class ElfParserTest(unittest.TestCase):
55    """Unit tests for ElfParser from elf_parser."""
56
57    def setUp(self):
58        """Creates an ElfParser."""
59        dir_path = os.path.dirname(os.path.realpath(__file__))
60        self.elf_file_path = os.path.join(dir_path, 'elf', 'testing',
61                                          'libtest.so')
62        self.elf_file = elf.ElfParser(self.elf_file_path)
63
64    def tearDown(self):
65        """Closes the ElfParser."""
66        self.elf_file.Close()
67
68    def testGetSectionName(self):
69        """Tests that GetSectionName parses section names correctly."""
70        sh_names = [self.elf_file.GetSectionName(sh)
71                    for sh in self.elf_file.Shdr]
72        self.assertFalse(_SECTION_NAMES.difference(sh_names))
73
74    def testGetSectionsByName(self):
75        """Tests that GetSectionsByName finds all sections of the same name."""
76        none_secs = list(self.elf_file.GetSectionsByName('no.such.section'))
77        dup_secs = list(self.elf_file.GetSectionsByName('test.dup'))
78        self.assertEqual(len(none_secs), 0)
79        self.assertEqual(len(dup_secs), 2)
80
81    def testGetSectionByName(self):
82        """Tests that GetSectionByName finds section by name correctly."""
83        none_sec = self.elf_file.GetSectionByName('no.such.section')
84        self.assertEqual(none_sec, None)
85        for section_name in _SECTION_NAMES:
86            sh = self.elf_file.GetSectionByName(section_name)
87            self.assertIsNotNone(sh)
88
89    def testGetSymbols(self):
90        """Tests that GetSymbols parses symbol table correctly."""
91        symtab = self.elf_file.GetSectionByName('.symtab')
92        strtab = self.elf_file.Shdr[symtab.sh_link]
93        syms = self.elf_file.GetSymbols(symtab)
94        sym_names = [self.elf_file.GetString(strtab, sym.st_name)
95                     for sym in syms]
96        self.assertFalse(_EXPORTED_SYMBOLS.difference(sym_names))
97
98    def testGetRelocations(self):
99        """Tests that GetRelocations parses relocation table correctly."""
100        reltab = self.elf_file.GetSectionByName('.rela.dyn')
101        symtab = self.elf_file.Shdr[reltab.sh_link]
102        strtab = self.elf_file.Shdr[symtab.sh_link]
103        relocs = []
104        for rela in self.elf_file.GetRelocations(reltab):
105            sym = self.elf_file.GetRelocationSymbol(symtab, rela)
106            sym_name = self.elf_file.GetString(strtab, sym.st_name)
107            relocs.append((rela.GetType(), sym_name, rela.r_addend))
108        self.assertFalse(_RELOCATIONS.difference(relocs))
109
110    def testGetRelocations_Android(self):
111        """Tests that GetRelocations parses Android packed format correctly."""
112        android_rela = self.elf_file.GetSectionByName('test.rela')
113        relocs = []
114        for rela in self.elf_file.GetRelocations(android_rela):
115            relocs.append((rela.r_offset, rela.r_info, rela.r_addend))
116        self.assertEqual(relocs, _ANDROID_RELOCATIONS)
117
118    def testGetRelocations_Relr(self):
119        """Tests that GetRelocations parses RELR section correctly."""
120        reltab = self.elf_file.GetSectionByName('.relr.dyn')
121        # It isn't actually a relocation section generated by linker.
122        reltab.sh_entsize = 8
123        relocs = []
124        for rela in self.elf_file.GetRelocations(reltab):
125            relocs.append((rela.r_offset, rela.r_info))
126        self.assertEqual(relocs, _RELR_RELOCATIONS)
127
128    def testIsExecutable(self):
129        """Tests that IsExecutable determines file type correctly."""
130        is_executable = self.elf_file.IsExecutable()
131        self.assertFalse(is_executable)
132
133    def testIsSharedObject(self):
134        """Tests that IsSharedObject determines file type correctly."""
135        is_shared_object = self.elf_file.IsSharedObject()
136        self.assertTrue(is_shared_object)
137
138    def testHasAndroidIdent(self):
139        """Tests that HasAndroidIdent finds .note.android.ident section."""
140        has_android_ident = self.elf_file.HasAndroidIdent()
141        self.assertTrue(has_android_ident)
142
143    def testMatchCpuAbi(self):
144        """Tests that MatchCpuAbi determines machine type correctly."""
145        self.assertTrue(self.elf_file.MatchCpuAbi("x86_64"))
146        self.assertFalse(self.elf_file.MatchCpuAbi("x86"))
147
148    def testListDependencies(self):
149        """Tests that ListDependencies lists ELF dependencies correctly."""
150        deps, runpaths = self.elf_file.ListDependencies()
151        self.assertEqual(deps, _DEPENDENCIES)
152        self.assertEqual(runpaths, _RUNPATHS)
153
154    def testListGlobalSymbols(self):
155        """Tests that ListGlobalSymbols lists global symbols correctly."""
156        syms = self.elf_file.ListGlobalSymbols(False, '.dynsym', '.dynstr')
157        self.assertFalse(_EXPORTED_SYMBOLS.difference(syms))
158
159    def testGetProgramInterpreter(self):
160        """Tests that GetProgramInterpreter parses segment type correctly."""
161        interp = self.elf_file.GetProgramInterpreter()
162        self.assertEqual(interp, "/lib64/ld-linux-x86-64.so.2")
163
164
165if __name__ == '__main__':
166    unittest.main()
167