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 9from enum import IntEnum 10 11import elftools 12from elftools.elf.elffile import ELFFile 13 14# ELF section flags 15SHF_WRITE = 0x1 16SHF_ALLOC = 0x2 17SHF_EXEC = 0x4 18SHF_WRITE_ALLOC = SHF_WRITE | SHF_ALLOC 19SHF_ALLOC_EXEC = SHF_ALLOC | SHF_EXEC 20 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 ( 82 self.has_kernel_thread_info() 83 and thread_info_offset_index 84 <= ThreadInfoOffset.THREAD_INFO_OFFSET_T_ARC_RELINQUISH_CAUSE 85 ): 86 return self.kernel_thread_info_offsets[thread_info_offset_index] 87 else: 88 return None 89 90 def parse(self): 91 if self.fd is None: 92 self.open() 93 94 kernel_thread_info_offsets_segment = None 95 kernel_thread_info_num_offsets_segment = None 96 _kernel_thread_info_offsets = None 97 _kernel_thread_info_num_offsets = None 98 _kernel_thread_info_size_t_size = None 99 100 for section in self.elf.iter_sections(): 101 # Find symbols for _kernel_thread_info data 102 if isinstance(section, elftools.elf.sections.SymbolTableSection): 103 _kernel_thread_info_offsets = section.get_symbol_by_name( 104 "_kernel_thread_info_offsets" 105 ) 106 _kernel_thread_info_num_offsets = section.get_symbol_by_name( 107 "_kernel_thread_info_num_offsets" 108 ) 109 _kernel_thread_info_size_t_size = section.get_symbol_by_name( 110 "_kernel_thread_info_size_t_size" 111 ) 112 113 # REALLY NEED to match exact type as all other sections 114 # (debug, text, etc.) are descendants where 115 # isinstance() would match. 116 if type(section) is not elftools.elf.sections.Section: # pylint: disable=unidiomatic-typecheck 117 continue 118 119 size = section['sh_size'] 120 flags = section['sh_flags'] 121 sec_start = section['sh_addr'] 122 sec_end = sec_start + size - 1 123 124 store = False 125 sect_desc = "?" 126 127 if section['sh_type'] == 'SHT_PROGBITS': 128 if (flags & SHF_ALLOC_EXEC) == SHF_ALLOC_EXEC: 129 # Text section 130 store = True 131 sect_desc = "text" 132 elif (flags & SHF_WRITE_ALLOC) == SHF_WRITE_ALLOC: 133 # Data section 134 # 135 # Running app changes the content so no need 136 # to store 137 pass 138 elif (flags & SHF_ALLOC) == SHF_ALLOC: 139 # Read only data section 140 store = True 141 sect_desc = "read-only data" 142 143 if store: 144 mem_region = {"start": sec_start, "end": sec_end, "data": section.data()} 145 logger.info( 146 f'ELF Section: 0x{mem_region["start"]:x} to 0x{mem_region["end"]:x} ' 147 f'of size {mem_region["data"]:d} ({sect_desc:s})' 148 ) 149 150 self.memory_regions.append(mem_region) 151 152 if ( 153 _kernel_thread_info_size_t_size is not None 154 and _kernel_thread_info_num_offsets is not None 155 and _kernel_thread_info_offsets is not None 156 ): 157 for seg in self.elf.iter_segments(): 158 if seg.header['p_type'] != 'PT_LOAD': 159 continue 160 161 # Store segment of kernel_thread_info_offsets 162 info_offsets_symbol = _kernel_thread_info_offsets[0] 163 if ( 164 info_offsets_symbol['st_value'] >= seg['p_vaddr'] 165 and info_offsets_symbol['st_value'] < seg['p_vaddr'] + seg['p_filesz'] 166 ): 167 kernel_thread_info_offsets_segment = seg 168 169 # Store segment of kernel_thread_info_num_offsets 170 num_offsets_symbol = _kernel_thread_info_num_offsets[0] 171 if ( 172 num_offsets_symbol['st_value'] >= seg['p_vaddr'] 173 and num_offsets_symbol['st_value'] < seg['p_vaddr'] + seg['p_filesz'] 174 ): 175 kernel_thread_info_num_offsets_segment = seg 176 177 # Read and store size_t size 178 size_t_size_symbol = _kernel_thread_info_size_t_size[0] 179 if ( 180 size_t_size_symbol['st_value'] >= seg['p_vaddr'] 181 and size_t_size_symbol['st_value'] < seg['p_vaddr'] + seg['p_filesz'] 182 ): 183 offset = size_t_size_symbol['st_value'] - seg['p_vaddr'] + seg['p_offset'] 184 self.elf.stream.seek(offset) 185 self.kernel_thread_info_size_t_size = struct.unpack( 186 'B', self.elf.stream.read(size_t_size_symbol['st_size']) 187 )[0] 188 189 struct_format = "I" 190 if self.kernel_thread_info_size_t_size == 8: 191 struct_format = "Q" 192 193 # Read and store count of offset values 194 num_offsets_symbol = _kernel_thread_info_num_offsets[0] 195 offset = ( 196 num_offsets_symbol['st_value'] 197 - kernel_thread_info_num_offsets_segment['p_vaddr'] 198 + kernel_thread_info_num_offsets_segment['p_offset'] 199 ) 200 self.elf.stream.seek(offset) 201 self.kernel_thread_info_num_offsets = struct.unpack( 202 struct_format, self.elf.stream.read(num_offsets_symbol['st_size']) 203 )[0] 204 205 array_format = "" 206 for _ in range(self.kernel_thread_info_num_offsets): 207 array_format = array_format + struct_format 208 209 # Read and store array of offset values 210 info_offsets_symbol = _kernel_thread_info_offsets[0] 211 offset = ( 212 info_offsets_symbol['st_value'] 213 - kernel_thread_info_offsets_segment['p_vaddr'] 214 + kernel_thread_info_offsets_segment['p_offset'] 215 ) 216 self.elf.stream.seek(offset) 217 self.kernel_thread_info_offsets = struct.unpack( 218 array_format, self.elf.stream.read(info_offsets_symbol['st_size']) 219 ) 220 221 return True 222