1#!/usr/bin/env python3 2# 3# Copyright (c) 2020 Intel Corporation 4# 5# SPDX-License-Identifier: Apache-2.0 6 7import logging 8import struct 9 10import elftools 11from elftools.elf.elffile import ELFFile 12from enum import IntEnum 13 14 15# ELF section flags 16SHF_WRITE = 0x1 17SHF_ALLOC = 0x2 18SHF_EXEC = 0x4 19SHF_WRITE_ALLOC = SHF_WRITE | SHF_ALLOC 20SHF_ALLOC_EXEC = SHF_ALLOC | SHF_EXEC 21 22# Must match enum in thread_info.c 23class ThreadInfoOffset(IntEnum): 24 THREAD_INFO_OFFSET_VERSION = 0 25 THREAD_INFO_OFFSET_K_CURR_THREAD = 1 26 THREAD_INFO_OFFSET_K_THREADS = 2 27 THREAD_INFO_OFFSET_T_ENTRY = 3 28 THREAD_INFO_OFFSET_T_NEXT_THREAD = 4 29 THREAD_INFO_OFFSET_T_STATE = 5 30 THREAD_INFO_OFFSET_T_USER_OPTIONS = 6 31 THREAD_INFO_OFFSET_T_PRIO = 7 32 THREAD_INFO_OFFSET_T_STACK_PTR = 8 33 THREAD_INFO_OFFSET_T_NAME = 9 34 THREAD_INFO_OFFSET_T_ARCH = 10 35 THREAD_INFO_OFFSET_T_PREEMPT_FLOAT = 11 36 THREAD_INFO_OFFSET_T_COOP_FLOAT = 12 37 THREAD_INFO_OFFSET_T_ARM_EXC_RETURN = 13 38 THREAD_INFO_OFFSET_T_ARC_RELINQUISH_CAUSE = 14 39 40 def __int__(self): 41 return self.value 42 43 44logger = logging.getLogger("parser") 45 46 47class CoredumpElfFile(): 48 """ 49 Class to parse ELF file for memory content in various sections. 50 There are read-only sections (e.g. text and rodata) where 51 the memory content does not need to be dumped via coredump 52 and can be retrieved from the ELF file. 53 """ 54 55 def __init__(self, elffile): 56 self.elffile = elffile 57 self.fd = None 58 self.elf = None 59 self.memory_regions = list() 60 self.kernel_thread_info_offsets = None 61 self.kernel_thread_info_num_offsets = None 62 self.kernel_thread_info_size_t_size = None 63 64 def open(self): 65 self.fd = open(self.elffile, "rb") 66 self.elf = ELFFile(self.fd) 67 68 def close(self): 69 self.fd.close() 70 71 def get_memory_regions(self): 72 return self.memory_regions 73 74 def get_kernel_thread_info_size_t_size(self): 75 return self.kernel_thread_info_size_t_size 76 77 def has_kernel_thread_info(self): 78 return self.kernel_thread_info_offsets is not None 79 80 def get_kernel_thread_info_offset(self, thread_info_offset_index): 81 if self.has_kernel_thread_info() and thread_info_offset_index <= ThreadInfoOffset.THREAD_INFO_OFFSET_T_ARC_RELINQUISH_CAUSE: 82 return self.kernel_thread_info_offsets[thread_info_offset_index] 83 else: 84 return None 85 86 def parse(self): 87 if self.fd is None: 88 self.open() 89 90 kernel_thread_info_offsets_segment = None 91 kernel_thread_info_num_offsets_segment = None 92 _kernel_thread_info_offsets = None 93 _kernel_thread_info_num_offsets = None 94 _kernel_thread_info_size_t_size = None 95 96 for section in self.elf.iter_sections(): 97 # Find symbols for _kernel_thread_info data 98 if isinstance(section, elftools.elf.sections.SymbolTableSection): 99 _kernel_thread_info_offsets = section.get_symbol_by_name("_kernel_thread_info_offsets") 100 _kernel_thread_info_num_offsets = section.get_symbol_by_name("_kernel_thread_info_num_offsets") 101 _kernel_thread_info_size_t_size = section.get_symbol_by_name("_kernel_thread_info_size_t_size") 102 103 # REALLY NEED to match exact type as all other sections 104 # (debug, text, etc.) are descendants where 105 # isinstance() would match. 106 if type(section) is not elftools.elf.sections.Section: # pylint: disable=unidiomatic-typecheck 107 continue 108 109 size = section['sh_size'] 110 flags = section['sh_flags'] 111 sec_start = section['sh_addr'] 112 sec_end = sec_start + size - 1 113 114 store = False 115 sect_desc = "?" 116 117 if section['sh_type'] == 'SHT_PROGBITS': 118 if (flags & SHF_ALLOC_EXEC) == SHF_ALLOC_EXEC: 119 # Text section 120 store = True 121 sect_desc = "text" 122 elif (flags & SHF_WRITE_ALLOC) == SHF_WRITE_ALLOC: 123 # Data section 124 # 125 # Running app changes the content so no need 126 # to store 127 pass 128 elif (flags & SHF_ALLOC) == SHF_ALLOC: 129 # Read only data section 130 store = True 131 sect_desc = "read-only data" 132 133 if store: 134 mem_region = {"start": sec_start, "end": sec_end, "data": section.data()} 135 logger.info("ELF Section: 0x%x to 0x%x of size %d (%s)" % 136 (mem_region["start"], 137 mem_region["end"], 138 len(mem_region["data"]), 139 sect_desc)) 140 141 self.memory_regions.append(mem_region) 142 143 if _kernel_thread_info_size_t_size is not None and \ 144 _kernel_thread_info_num_offsets is not None and \ 145 _kernel_thread_info_offsets is not None: 146 for seg in self.elf.iter_segments(): 147 if seg.header['p_type'] != 'PT_LOAD': 148 continue 149 150 # Store segment of kernel_thread_info_offsets 151 info_offsets_symbol = _kernel_thread_info_offsets[0] 152 if info_offsets_symbol['st_value'] >= seg['p_vaddr'] and info_offsets_symbol['st_value'] < seg['p_vaddr'] + seg['p_filesz']: 153 kernel_thread_info_offsets_segment = seg 154 155 # Store segment of kernel_thread_info_num_offsets 156 num_offsets_symbol = _kernel_thread_info_num_offsets[0] 157 if num_offsets_symbol['st_value'] >= seg['p_vaddr'] and num_offsets_symbol['st_value'] < seg['p_vaddr'] + seg['p_filesz']: 158 kernel_thread_info_num_offsets_segment = seg 159 160 # Read and store size_t size 161 size_t_size_symbol = _kernel_thread_info_size_t_size[0] 162 if size_t_size_symbol['st_value'] >= seg['p_vaddr'] and size_t_size_symbol['st_value'] < seg['p_vaddr'] + seg['p_filesz']: 163 offset = size_t_size_symbol['st_value'] - seg['p_vaddr'] + seg['p_offset'] 164 self.elf.stream.seek(offset) 165 self.kernel_thread_info_size_t_size = struct.unpack('B', self.elf.stream.read(size_t_size_symbol['st_size']))[0] 166 167 struct_format = "I" 168 if self.kernel_thread_info_size_t_size == 8: 169 struct_format = "Q" 170 171 # Read and store count of offset values 172 num_offsets_symbol = _kernel_thread_info_num_offsets[0] 173 offset = num_offsets_symbol['st_value'] - kernel_thread_info_num_offsets_segment['p_vaddr'] + kernel_thread_info_num_offsets_segment['p_offset'] 174 self.elf.stream.seek(offset) 175 self.kernel_thread_info_num_offsets = struct.unpack(struct_format, self.elf.stream.read(num_offsets_symbol['st_size']))[0] 176 177 array_format = "" 178 for _ in range(self.kernel_thread_info_num_offsets): 179 array_format = array_format + struct_format 180 181 # Read and store array of offset values 182 info_offsets_symbol = _kernel_thread_info_offsets[0] 183 offset = info_offsets_symbol['st_value'] - kernel_thread_info_offsets_segment['p_vaddr'] + kernel_thread_info_offsets_segment['p_offset'] 184 self.elf.stream.seek(offset) 185 self.kernel_thread_info_offsets = struct.unpack(array_format, self.elf.stream.read(info_offsets_symbol['st_size'])) 186 187 return True 188