Add ElfParser.IsExecutable and GetProgramInterpreter

This commit adds:
- Types and structure of program header
- ElfParser.IsExecutable
- ElfParser.GetProgramInterpreter
- Unit tests for the above methods and MatchCpuAbi.

Bug: 115567177
Test: python -m vts.utils.python.library.elf_parser_test
Test: vts-tradefed run vts -m VtsVndkDependency
Change-Id: Id5656c2d80b528eb08e8daf972ea6000d8989e54
Merged-In: Id5656c2d80b528eb08e8daf972ea6000d8989e54
(cherry picked from commit e08f2cab91d0cf0a5bc583e4969834f4b79bd8cc)
diff --git a/utils/python/library/elf/consts.py b/utils/python/library/elf/consts.py
index 43b19e8..8f95584 100644
--- a/utils/python/library/elf/consts.py
+++ b/utils/python/library/elf/consts.py
@@ -133,6 +133,32 @@
 STT_LOPROC = 13
 STT_HIPROC = 15
 
+# Segment types
+PT_NULL = 0
+PT_LOAD = 1
+PT_DYNAMIC = 2
+PT_INTERP = 3
+PT_NOTE = 4
+PT_SHLIB = 5
+PT_PHDR = 6
+PT_TLS = 7
+PT_LOOS = 0x60000000
+PT_HIOS = 0x6fffffff
+PT_LOPROC = 0x70000000
+PT_HIPROC = 0x7fffffff
+PT_GNU_EH_FRAME = 0x6474e550
+PT_SUNW_EH_FRAME = 0x6474e550
+PT_SUNW_UNWIND = 0x6464e550
+PT_GNU_STACK = 0x6474e551
+PT_GNU_RELRO = 0x6474e552
+PT_ARM_ARCHEXT = 0x70000000
+PT_ARM_EXIDX = 0x70000001
+PT_ARM_UNWIND = 0x70000001
+PT_MIPS_REGINFO = 0x70000000
+PT_MIPS_RTPROC = 0x70000001
+PT_MIPS_OPTIONS = 0x70000002
+PT_MIPS_ABIFLAGS = 0x70000003
+
 # Dynamic array tags
 # Name     Value        d_un          Executable  Shared Object
 DT_NULL = 0             # ignored     mandatory   mandatory
diff --git a/utils/python/library/elf/structs.py b/utils/python/library/elf/structs.py
index 4508152..ac55b2f 100644
--- a/utils/python/library/elf/structs.py
+++ b/utils/python/library/elf/structs.py
@@ -306,3 +306,27 @@
     _fields_ = [('r_offset', Elf64_Addr),
                 ('r_info', Elf64_Xword),
                 ('r_addend', Elf64_Sxword)]
+
+
+class Elf32_Phdr(CStructure):
+    """ELF 32-bit program header."""
+    _fields_ = [('p_type', Elf32_Word),
+                ('p_offset', Elf32_Off),
+                ('p_vaddr', Elf32_Addr),
+                ('p_paddr', Elf32_Addr),
+                ('p_filesz', Elf32_Word),
+                ('p_memsz', Elf32_Word),
+                ('p_flags', Elf32_Word),
+                ('p_align', Elf32_Word)]
+
+
+class Elf64_Phdr(CStructure):
+    """ELF 64-bit program header."""
+    _fields_ = [('p_type', Elf64_Word),
+                ('p_flags', Elf64_Word),
+                ('p_offset', Elf64_Off),
+                ('p_vaddr', Elf64_Addr),
+                ('p_paddr', Elf64_Addr),
+                ('p_filesz', Elf64_Xword),
+                ('p_memsz', Elf64_Xword),
+                ('p_align', Elf64_Xword)]
diff --git a/utils/python/library/elf/testing/libtest.so b/utils/python/library/elf/testing/libtest.so
index f21d0b3..74448dd 100755
--- a/utils/python/library/elf/testing/libtest.so
+++ b/utils/python/library/elf/testing/libtest.so
Binary files differ
diff --git a/utils/python/library/elf/testing/test-section-2.s b/utils/python/library/elf/testing/test-section-2.s
index cab44f6..4e88b55 100644
--- a/utils/python/library/elf/testing/test-section-2.s
+++ b/utils/python/library/elf/testing/test-section-2.s
@@ -17,3 +17,8 @@
 
 .section test.dup,"",@note
 nop
+
+# Test path name of program interpreter:
+
+.section .interp,"a",@progbits
+.string "/lib64/ld-linux-x86-64.so.2"
diff --git a/utils/python/library/elf_parser.py b/utils/python/library/elf_parser.py
index 616e5cf..90efe79 100644
--- a/utils/python/library/elf_parser.py
+++ b/utils/python/library/elf_parser.py
@@ -58,6 +58,7 @@
         Elf_Sym: ELF symbol entry class.
         Elf_Rel: ELF relocation entry class.
         Elf_Rela: ELF relocation entry class with explicit addend.
+        Elf_Phdr: ELF program header class.
     """
 
     def __init__(self, file_path, begin_offset=0):
@@ -112,6 +113,7 @@
             self.Elf_Sym = structs.Elf32_Sym
             self.Elf_Rel = structs.Elf32_Rel
             self.Elf_Rela = structs.Elf32_Rela
+            self.Elf_Phdr = structs.Elf32_Phdr
         else:
             self.bitness = 64
             self.Elf_Addr = structs.Elf64_Addr
@@ -125,6 +127,7 @@
             self.Elf_Sym = structs.Elf64_Sym
             self.Elf_Rel = structs.Elf64_Rel
             self.Elf_Rela = structs.Elf64_Rela
+            self.Elf_Phdr = structs.Elf64_Phdr
 
         try:
             self.Ehdr = self._SeekReadStruct(0, self.Elf_Ehdr)
@@ -515,6 +518,10 @@
             raise ElfError("Cannot find dynamic string table.")
         return [self.GetString(strtab, off) for off in name_offsets]
 
+    def IsExecutable(self):
+        """Returns whether the ELF is executable."""
+        return self.Ehdr.e_type == consts.ET_EXEC
+
     def MatchCpuAbi(self, abi):
         """Returns whether the ELF matches the ABI.
 
@@ -598,3 +605,17 @@
         """
         return self.ListGlobalSymbols(include_weak,
                                       consts.DYNSYM, consts.DYNSTR)
+
+    def GetProgramInterpreter(self):
+        """Gets the path to the program interpreter of the ELF.
+
+        Returns:
+            A string, the contents of .interp section.
+            None if the section is not found.
+        """
+        for ph_index in range(self.Ehdr.e_phnum):
+            ph = self._SeekReadStruct(
+                self.Ehdr.e_phoff + ph_index * self.Ehdr.e_phentsize,
+                self.Elf_Phdr)
+            if ph.p_type == consts.PT_INTERP:
+                return self._SeekReadString(ph.p_offset)
diff --git a/utils/python/library/elf_parser_test.py b/utils/python/library/elf_parser_test.py
index d8c3d5c..5b23b11 100644
--- a/utils/python/library/elf_parser_test.py
+++ b/utils/python/library/elf_parser_test.py
@@ -107,6 +107,16 @@
             relocs.append((rela.r_offset, rela.r_info, rela.r_addend))
         self.assertEqual(relocs, _ANDROID_RELOCATIONS)
 
+    def testIsExecutable(self):
+        """Tests that IsExecutable determines file type correctly."""
+        is_executable = self.elf_file.IsExecutable()
+        self.assertFalse(is_executable)
+
+    def testMatchCpuAbi(self):
+        """Tests that MatchCpuAbi determines machine type correctly."""
+        self.assertTrue(self.elf_file.MatchCpuAbi("x86_64"))
+        self.assertFalse(self.elf_file.MatchCpuAbi("x86"))
+
     def testListDependencies(self):
         """Tests that ListDependencies lists ELF dependencies correctly."""
         deps = self.elf_file.ListDependencies()
@@ -117,6 +127,11 @@
         syms = self.elf_file.ListGlobalSymbols(False, '.dynsym', '.dynstr')
         self.assertFalse(_EXPORTED_SYMBOLS.difference(syms))
 
+    def testGetProgramInterpreter(self):
+        """Tests that GetProgramInterpreter parses segment type correctly."""
+        interp = self.elf_file.GetProgramInterpreter()
+        self.assertEqual(interp, "/lib64/ld-linux-x86-64.so.2")
+
 
 if __name__ == '__main__':
     unittest.main()