Harrison Mutai | af5b49e | 2023-02-23 10:33:58 +0000 | [diff] [blame] | 1 | # |
Chris Kay | ed0c801 | 2025-01-28 18:04:11 +0000 | [diff] [blame] | 2 | # Copyright (c) 2023-2025, Arm Limited. All rights reserved. |
Harrison Mutai | af5b49e | 2023-02-23 10:33:58 +0000 | [diff] [blame] | 3 | # |
| 4 | # SPDX-License-Identifier: BSD-3-Clause |
| 5 | # |
| 6 | |
Harrison Mutai | d9d5eb1 | 2023-02-23 11:30:17 +0000 | [diff] [blame] | 7 | import re |
Harrison Mutai | cc60aba | 2023-02-23 11:30:55 +0000 | [diff] [blame] | 8 | from dataclasses import asdict, dataclass |
Chris Kay | b658ccd | 2025-04-22 17:02:49 +0100 | [diff] [blame] | 9 | from typing import ( |
Chris Kay | 6acd79a | 2025-04-17 16:53:32 +0100 | [diff] [blame] | 10 | Any, |
Chris Kay | b658ccd | 2025-04-22 17:02:49 +0100 | [diff] [blame] | 11 | BinaryIO, |
| 12 | Dict, |
| 13 | Iterable, |
| 14 | List, |
| 15 | Optional, |
| 16 | Tuple, |
| 17 | Union, |
| 18 | ) |
Harrison Mutai | af5b49e | 2023-02-23 10:33:58 +0000 | [diff] [blame] | 19 | |
| 20 | from elftools.elf.elffile import ELFFile |
Chris Kay | 6acd79a | 2025-04-17 16:53:32 +0100 | [diff] [blame] | 21 | from elftools.elf.sections import Section, SymbolTableSection |
Chris Kay | f59b6fb | 2025-05-01 17:00:06 +0100 | [diff] [blame] | 22 | from elftools.elf.segments import Segment |
Harrison Mutai | af5b49e | 2023-02-23 10:33:58 +0000 | [diff] [blame] | 23 | |
Chris Kay | bc74cfc | 2025-04-15 10:51:00 +0100 | [diff] [blame] | 24 | from memory.image import Image, Region |
Chris Kay | 5bce4e6 | 2025-04-14 17:30:59 +0100 | [diff] [blame] | 25 | |
Harrison Mutai | af5b49e | 2023-02-23 10:33:58 +0000 | [diff] [blame] | 26 | |
Harrison Mutai | cc60aba | 2023-02-23 11:30:55 +0000 | [diff] [blame] | 27 | @dataclass(frozen=True) |
| 28 | class TfaMemObject: |
| 29 | name: str |
| 30 | start: int |
| 31 | end: int |
| 32 | size: int |
Chris Kay | 205248e | 2025-04-28 13:28:00 +0100 | [diff] [blame] | 33 | children: List["TfaMemObject"] |
Harrison Mutai | cc60aba | 2023-02-23 11:30:55 +0000 | [diff] [blame] | 34 | |
| 35 | |
Chris Kay | bc74cfc | 2025-04-15 10:51:00 +0100 | [diff] [blame] | 36 | class TfaElfParser(Image): |
Harrison Mutai | af5b49e | 2023-02-23 10:33:58 +0000 | [diff] [blame] | 37 | """A class representing an ELF file built for TF-A. |
| 38 | |
| 39 | Provides a basic interface for reading the symbol table and other |
| 40 | attributes of an ELF file. The constructor accepts a file-like object with |
| 41 | the contents an ELF file. |
| 42 | """ |
| 43 | |
Chris Kay | 205248e | 2025-04-28 13:28:00 +0100 | [diff] [blame] | 44 | def __init__(self, elf_file: BinaryIO) -> None: |
| 45 | self._segments: Dict[int, TfaMemObject] = {} |
| 46 | self._memory_layout: Dict[str, Dict[str, int]] = {} |
Harrison Mutai | af5b49e | 2023-02-23 10:33:58 +0000 | [diff] [blame] | 47 | |
| 48 | elf = ELFFile(elf_file) |
| 49 | |
Chris Kay | 6acd79a | 2025-04-17 16:53:32 +0100 | [diff] [blame] | 50 | symtab = elf.get_section_by_name(".symtab") |
| 51 | assert isinstance(symtab, SymbolTableSection) |
| 52 | |
Chris Kay | 205248e | 2025-04-28 13:28:00 +0100 | [diff] [blame] | 53 | self._symbols: Dict[str, int] = { |
Chris Kay | 6acd79a | 2025-04-17 16:53:32 +0100 | [diff] [blame] | 54 | sym.name: sym.entry["st_value"] for sym in symtab.iter_symbols() |
Harrison Mutai | af5b49e | 2023-02-23 10:33:58 +0000 | [diff] [blame] | 55 | } |
| 56 | |
Harrison Mutai | cc60aba | 2023-02-23 11:30:55 +0000 | [diff] [blame] | 57 | self.set_segment_section_map(elf.iter_segments(), elf.iter_sections()) |
Harrison Mutai | d9d5eb1 | 2023-02-23 11:30:17 +0000 | [diff] [blame] | 58 | self._memory_layout = self.get_memory_layout_from_symbols() |
Chris Kay | 205248e | 2025-04-28 13:28:00 +0100 | [diff] [blame] | 59 | self._start: int = elf["e_entry"] |
| 60 | self._size: int |
| 61 | self._free: int |
Harrison Mutai | cc60aba | 2023-02-23 11:30:55 +0000 | [diff] [blame] | 62 | self._size, self._free = self._get_mem_usage() |
Chris Kay | 205248e | 2025-04-28 13:28:00 +0100 | [diff] [blame] | 63 | self._end: int = self._start + self._size |
Harrison Mutai | d9d5eb1 | 2023-02-23 11:30:17 +0000 | [diff] [blame] | 64 | |
Chris Kay | bc74cfc | 2025-04-15 10:51:00 +0100 | [diff] [blame] | 65 | self._footprint: Dict[str, Region] = {} |
| 66 | |
| 67 | for mem, attrs in self._memory_layout.items(): |
| 68 | self._footprint[mem] = Region( |
| 69 | attrs["start"], |
| 70 | attrs["end"], |
| 71 | attrs["length"], |
| 72 | ) |
| 73 | |
Harrison Mutai | af5b49e | 2023-02-23 10:33:58 +0000 | [diff] [blame] | 74 | @property |
Chris Kay | b658ccd | 2025-04-22 17:02:49 +0100 | [diff] [blame] | 75 | def symbols(self) -> Dict[str, int]: |
| 76 | return self._symbols |
Harrison Mutai | d9d5eb1 | 2023-02-23 11:30:17 +0000 | [diff] [blame] | 77 | |
Harrison Mutai | cc60aba | 2023-02-23 11:30:55 +0000 | [diff] [blame] | 78 | @staticmethod |
Chris Kay | 205248e | 2025-04-28 13:28:00 +0100 | [diff] [blame] | 79 | def tfa_mem_obj_factory( |
| 80 | elf_obj: Union[Segment, Section], |
| 81 | name: Optional[str] = None, |
| 82 | children: Optional[List[TfaMemObject]] = None, |
| 83 | ) -> TfaMemObject: |
Harrison Mutai | cc60aba | 2023-02-23 11:30:55 +0000 | [diff] [blame] | 84 | """Converts a pyelfparser Segment or Section to a TfaMemObject.""" |
| 85 | # Ensure each segment is provided a name since they aren't in the |
| 86 | # program header. |
Chris Kay | f59b6fb | 2025-05-01 17:00:06 +0100 | [diff] [blame] | 87 | assert not (isinstance(elf_obj, Segment) and name is None), ( |
Chris Kay | 1bed770 | 2025-04-01 17:20:56 +0100 | [diff] [blame] | 88 | "Attempting to make segment without a name" |
| 89 | ) |
Harrison Mutai | cc60aba | 2023-02-23 11:30:55 +0000 | [diff] [blame] | 90 | |
Harrison Mutai | cc60aba | 2023-02-23 11:30:55 +0000 | [diff] [blame] | 91 | # Segment and sections header keys have different prefixes. |
Chris Kay | f59b6fb | 2025-05-01 17:00:06 +0100 | [diff] [blame] | 92 | vaddr = "p_vaddr" if isinstance(elf_obj, Segment) else "sh_addr" |
| 93 | size = "p_memsz" if isinstance(elf_obj, Segment) else "sh_size" |
Harrison Mutai | cc60aba | 2023-02-23 11:30:55 +0000 | [diff] [blame] | 94 | |
Chris Kay | 6acd79a | 2025-04-17 16:53:32 +0100 | [diff] [blame] | 95 | name = name if isinstance(elf_obj, Segment) else elf_obj.name |
| 96 | assert name is not None |
| 97 | |
Harrison Mutai | cc60aba | 2023-02-23 11:30:55 +0000 | [diff] [blame] | 98 | # TODO figure out how to handle free space for sections and segments |
| 99 | return TfaMemObject( |
Chris Kay | 6acd79a | 2025-04-17 16:53:32 +0100 | [diff] [blame] | 100 | name, |
Harrison Mutai | cc60aba | 2023-02-23 11:30:55 +0000 | [diff] [blame] | 101 | elf_obj[vaddr], |
| 102 | elf_obj[vaddr] + elf_obj[size], |
| 103 | elf_obj[size], |
Chris Kay | 6acd79a | 2025-04-17 16:53:32 +0100 | [diff] [blame] | 104 | children or [], |
Harrison Mutai | cc60aba | 2023-02-23 11:30:55 +0000 | [diff] [blame] | 105 | ) |
| 106 | |
Chris Kay | 205248e | 2025-04-28 13:28:00 +0100 | [diff] [blame] | 107 | def _get_mem_usage(self) -> Tuple[int, int]: |
Harrison Mutai | cc60aba | 2023-02-23 11:30:55 +0000 | [diff] [blame] | 108 | """Get total size and free space for this component.""" |
| 109 | size = free = 0 |
| 110 | |
| 111 | # Use information encoded in the segment header if we can't get a |
| 112 | # memory configuration. |
| 113 | if not self._memory_layout: |
| 114 | return sum(s.size for s in self._segments.values()), 0 |
| 115 | |
| 116 | for v in self._memory_layout.values(): |
| 117 | size += v["length"] |
| 118 | free += v["start"] + v["length"] - v["end"] |
| 119 | |
| 120 | return size, free |
| 121 | |
Chris Kay | 205248e | 2025-04-28 13:28:00 +0100 | [diff] [blame] | 122 | def set_segment_section_map( |
| 123 | self, |
| 124 | segments: Iterable[Segment], |
| 125 | sections: Iterable[Section], |
| 126 | ) -> None: |
Harrison Mutai | cc60aba | 2023-02-23 11:30:55 +0000 | [diff] [blame] | 127 | """Set segment to section mappings.""" |
Chris Kay | 6acd79a | 2025-04-17 16:53:32 +0100 | [diff] [blame] | 128 | segments = filter(lambda seg: seg["p_type"] == "PT_LOAD", segments) |
| 129 | segments_list = list(segments) |
Harrison Mutai | cc60aba | 2023-02-23 11:30:55 +0000 | [diff] [blame] | 130 | |
| 131 | for sec in sections: |
Chris Kay | 6acd79a | 2025-04-17 16:53:32 +0100 | [diff] [blame] | 132 | for n, seg in enumerate(segments_list): |
Harrison Mutai | cc60aba | 2023-02-23 11:30:55 +0000 | [diff] [blame] | 133 | if seg.section_in_segment(sec): |
Chris Kay | 6acd79a | 2025-04-17 16:53:32 +0100 | [diff] [blame] | 134 | if n not in self._segments: |
Harrison Mutai | cc60aba | 2023-02-23 11:30:55 +0000 | [diff] [blame] | 135 | self._segments[n] = self.tfa_mem_obj_factory( |
Chris Kay | f59b6fb | 2025-05-01 17:00:06 +0100 | [diff] [blame] | 136 | seg, name=f"{n:#02}" |
Harrison Mutai | cc60aba | 2023-02-23 11:30:55 +0000 | [diff] [blame] | 137 | ) |
| 138 | |
Chris Kay | 1bed770 | 2025-04-01 17:20:56 +0100 | [diff] [blame] | 139 | self._segments[n].children.append(self.tfa_mem_obj_factory(sec)) |
Harrison Mutai | cc60aba | 2023-02-23 11:30:55 +0000 | [diff] [blame] | 140 | |
Chris Kay | 205248e | 2025-04-28 13:28:00 +0100 | [diff] [blame] | 141 | def get_memory_layout_from_symbols(self) -> Dict[str, Dict[str, int]]: |
Harrison Mutai | d9d5eb1 | 2023-02-23 11:30:17 +0000 | [diff] [blame] | 142 | """Retrieve information about the memory configuration from the symbol |
| 143 | table. |
| 144 | """ |
Chris Kay | 6acd79a | 2025-04-17 16:53:32 +0100 | [diff] [blame] | 145 | assert self._symbols, "Symbol table is empty!" |
Harrison Mutai | d9d5eb1 | 2023-02-23 11:30:17 +0000 | [diff] [blame] | 146 | |
Chris Kay | 015a8fd | 2025-04-17 12:14:38 +0100 | [diff] [blame] | 147 | expr = r".*(.?R.M)_REGION.*(START|END|LENGTH)" |
Harrison Mutai | d9d5eb1 | 2023-02-23 11:30:17 +0000 | [diff] [blame] | 148 | region_symbols = filter(lambda s: re.match(expr, s), self._symbols) |
Chris Kay | 205248e | 2025-04-28 13:28:00 +0100 | [diff] [blame] | 149 | memory_layout: Dict[str, Dict[str, int]] = {} |
Harrison Mutai | d9d5eb1 | 2023-02-23 11:30:17 +0000 | [diff] [blame] | 150 | |
| 151 | for symbol in region_symbols: |
Chris Kay | 6acd79a | 2025-04-17 16:53:32 +0100 | [diff] [blame] | 152 | region, _, attr = symbol.lower().strip("__").split("_") |
Harrison Mutai | d9d5eb1 | 2023-02-23 11:30:17 +0000 | [diff] [blame] | 153 | if region not in memory_layout: |
| 154 | memory_layout[region] = {} |
| 155 | |
| 156 | # Retrieve the value of the symbol using the symbol as the key. |
| 157 | memory_layout[region][attr] = self._symbols[symbol] |
| 158 | |
| 159 | return memory_layout |
| 160 | |
Chris Kay | 6acd79a | 2025-04-17 16:53:32 +0100 | [diff] [blame] | 161 | def get_seg_map_as_dict(self) -> List[Dict[str, Any]]: |
Harrison Mutai | cc60aba | 2023-02-23 11:30:55 +0000 | [diff] [blame] | 162 | """Get a dictionary of segments and their section mappings.""" |
Chris Kay | 6acd79a | 2025-04-17 16:53:32 +0100 | [diff] [blame] | 163 | return [asdict(segment) for segment in self._segments.values()] |
Harrison Mutai | cc60aba | 2023-02-23 11:30:55 +0000 | [diff] [blame] | 164 | |
Chris Kay | 5bce4e6 | 2025-04-14 17:30:59 +0100 | [diff] [blame] | 165 | def get_memory_layout(self) -> Dict[str, Region]: |
Harrison Mutai | d9d5eb1 | 2023-02-23 11:30:17 +0000 | [diff] [blame] | 166 | """Get the total memory consumed by this module from the memory |
| 167 | configuration. |
Harrison Mutai | d9d5eb1 | 2023-02-23 11:30:17 +0000 | [diff] [blame] | 168 | """ |
Chris Kay | 5bce4e6 | 2025-04-14 17:30:59 +0100 | [diff] [blame] | 169 | mem_dict: Dict[str, Region] = {} |
Harrison Mutai | d9d5eb1 | 2023-02-23 11:30:17 +0000 | [diff] [blame] | 170 | |
| 171 | for mem, attrs in self._memory_layout.items(): |
Chris Kay | 5bce4e6 | 2025-04-14 17:30:59 +0100 | [diff] [blame] | 172 | mem_dict[mem] = Region( |
| 173 | attrs["start"], |
| 174 | attrs["end"], |
| 175 | attrs["length"], |
| 176 | ) |
| 177 | |
Harrison Mutai | d9d5eb1 | 2023-02-23 11:30:17 +0000 | [diff] [blame] | 178 | return mem_dict |
Harrison Mutai | cc60aba | 2023-02-23 11:30:55 +0000 | [diff] [blame] | 179 | |
Chris Kay | bc74cfc | 2025-04-15 10:51:00 +0100 | [diff] [blame] | 180 | @property |
| 181 | def footprint(self) -> Dict[str, Region]: |
| 182 | return self._footprint |
| 183 | |
Chris Kay | 205248e | 2025-04-28 13:28:00 +0100 | [diff] [blame] | 184 | def get_mod_mem_usage_dict(self) -> Dict[str, int]: |
Harrison Mutai | cc60aba | 2023-02-23 11:30:55 +0000 | [diff] [blame] | 185 | """Get the total memory consumed by the module, this combines the |
| 186 | information in the memory configuration. |
| 187 | """ |
| 188 | return { |
| 189 | "start": self._start, |
| 190 | "end": self._end, |
| 191 | "size": self._size, |
| 192 | "free": self._free, |
| 193 | } |