blob: 019d6da8820c99243df892dfa7f7fc0ebc11cd50 [file] [log] [blame]
Harrison Mutaiaf5b49e2023-02-23 10:33:58 +00001#
Chris Kayed0c8012025-01-28 18:04:11 +00002# Copyright (c) 2023-2025, Arm Limited. All rights reserved.
Harrison Mutaiaf5b49e2023-02-23 10:33:58 +00003#
4# SPDX-License-Identifier: BSD-3-Clause
5#
6
Harrison Mutaid9d5eb12023-02-23 11:30:17 +00007import re
Harrison Mutaicc60aba2023-02-23 11:30:55 +00008from dataclasses import asdict, dataclass
Chris Kayb658ccd2025-04-22 17:02:49 +01009from typing import (
Chris Kay6acd79a2025-04-17 16:53:32 +010010 Any,
Chris Kayb658ccd2025-04-22 17:02:49 +010011 BinaryIO,
12 Dict,
13 Iterable,
14 List,
15 Optional,
16 Tuple,
17 Union,
18)
Harrison Mutaiaf5b49e2023-02-23 10:33:58 +000019
20from elftools.elf.elffile import ELFFile
Chris Kay6acd79a2025-04-17 16:53:32 +010021from elftools.elf.sections import Section, SymbolTableSection
Chris Kayf59b6fb2025-05-01 17:00:06 +010022from elftools.elf.segments import Segment
Harrison Mutaiaf5b49e2023-02-23 10:33:58 +000023
Chris Kaybc74cfc2025-04-15 10:51:00 +010024from memory.image import Image, Region
Chris Kay5bce4e62025-04-14 17:30:59 +010025
Harrison Mutaiaf5b49e2023-02-23 10:33:58 +000026
Harrison Mutaicc60aba2023-02-23 11:30:55 +000027@dataclass(frozen=True)
28class TfaMemObject:
29 name: str
30 start: int
31 end: int
32 size: int
Chris Kay205248e2025-04-28 13:28:00 +010033 children: List["TfaMemObject"]
Harrison Mutaicc60aba2023-02-23 11:30:55 +000034
35
Chris Kaybc74cfc2025-04-15 10:51:00 +010036class TfaElfParser(Image):
Harrison Mutaiaf5b49e2023-02-23 10:33:58 +000037 """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 Kay205248e2025-04-28 13:28:00 +010044 def __init__(self, elf_file: BinaryIO) -> None:
45 self._segments: Dict[int, TfaMemObject] = {}
46 self._memory_layout: Dict[str, Dict[str, int]] = {}
Harrison Mutaiaf5b49e2023-02-23 10:33:58 +000047
48 elf = ELFFile(elf_file)
49
Chris Kay6acd79a2025-04-17 16:53:32 +010050 symtab = elf.get_section_by_name(".symtab")
51 assert isinstance(symtab, SymbolTableSection)
52
Chris Kay205248e2025-04-28 13:28:00 +010053 self._symbols: Dict[str, int] = {
Chris Kay6acd79a2025-04-17 16:53:32 +010054 sym.name: sym.entry["st_value"] for sym in symtab.iter_symbols()
Harrison Mutaiaf5b49e2023-02-23 10:33:58 +000055 }
56
Harrison Mutaicc60aba2023-02-23 11:30:55 +000057 self.set_segment_section_map(elf.iter_segments(), elf.iter_sections())
Harrison Mutaid9d5eb12023-02-23 11:30:17 +000058 self._memory_layout = self.get_memory_layout_from_symbols()
Chris Kay205248e2025-04-28 13:28:00 +010059 self._start: int = elf["e_entry"]
60 self._size: int
61 self._free: int
Harrison Mutaicc60aba2023-02-23 11:30:55 +000062 self._size, self._free = self._get_mem_usage()
Chris Kay205248e2025-04-28 13:28:00 +010063 self._end: int = self._start + self._size
Harrison Mutaid9d5eb12023-02-23 11:30:17 +000064
Chris Kaybc74cfc2025-04-15 10:51:00 +010065 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 Mutaiaf5b49e2023-02-23 10:33:58 +000074 @property
Chris Kayb658ccd2025-04-22 17:02:49 +010075 def symbols(self) -> Dict[str, int]:
76 return self._symbols
Harrison Mutaid9d5eb12023-02-23 11:30:17 +000077
Harrison Mutaicc60aba2023-02-23 11:30:55 +000078 @staticmethod
Chris Kay205248e2025-04-28 13:28:00 +010079 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 Mutaicc60aba2023-02-23 11:30:55 +000084 """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 Kayf59b6fb2025-05-01 17:00:06 +010087 assert not (isinstance(elf_obj, Segment) and name is None), (
Chris Kay1bed7702025-04-01 17:20:56 +010088 "Attempting to make segment without a name"
89 )
Harrison Mutaicc60aba2023-02-23 11:30:55 +000090
Harrison Mutaicc60aba2023-02-23 11:30:55 +000091 # Segment and sections header keys have different prefixes.
Chris Kayf59b6fb2025-05-01 17:00:06 +010092 vaddr = "p_vaddr" if isinstance(elf_obj, Segment) else "sh_addr"
93 size = "p_memsz" if isinstance(elf_obj, Segment) else "sh_size"
Harrison Mutaicc60aba2023-02-23 11:30:55 +000094
Chris Kay6acd79a2025-04-17 16:53:32 +010095 name = name if isinstance(elf_obj, Segment) else elf_obj.name
96 assert name is not None
97
Harrison Mutaicc60aba2023-02-23 11:30:55 +000098 # TODO figure out how to handle free space for sections and segments
99 return TfaMemObject(
Chris Kay6acd79a2025-04-17 16:53:32 +0100100 name,
Harrison Mutaicc60aba2023-02-23 11:30:55 +0000101 elf_obj[vaddr],
102 elf_obj[vaddr] + elf_obj[size],
103 elf_obj[size],
Chris Kay6acd79a2025-04-17 16:53:32 +0100104 children or [],
Harrison Mutaicc60aba2023-02-23 11:30:55 +0000105 )
106
Chris Kay205248e2025-04-28 13:28:00 +0100107 def _get_mem_usage(self) -> Tuple[int, int]:
Harrison Mutaicc60aba2023-02-23 11:30:55 +0000108 """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 Kay205248e2025-04-28 13:28:00 +0100122 def set_segment_section_map(
123 self,
124 segments: Iterable[Segment],
125 sections: Iterable[Section],
126 ) -> None:
Harrison Mutaicc60aba2023-02-23 11:30:55 +0000127 """Set segment to section mappings."""
Chris Kay6acd79a2025-04-17 16:53:32 +0100128 segments = filter(lambda seg: seg["p_type"] == "PT_LOAD", segments)
129 segments_list = list(segments)
Harrison Mutaicc60aba2023-02-23 11:30:55 +0000130
131 for sec in sections:
Chris Kay6acd79a2025-04-17 16:53:32 +0100132 for n, seg in enumerate(segments_list):
Harrison Mutaicc60aba2023-02-23 11:30:55 +0000133 if seg.section_in_segment(sec):
Chris Kay6acd79a2025-04-17 16:53:32 +0100134 if n not in self._segments:
Harrison Mutaicc60aba2023-02-23 11:30:55 +0000135 self._segments[n] = self.tfa_mem_obj_factory(
Chris Kayf59b6fb2025-05-01 17:00:06 +0100136 seg, name=f"{n:#02}"
Harrison Mutaicc60aba2023-02-23 11:30:55 +0000137 )
138
Chris Kay1bed7702025-04-01 17:20:56 +0100139 self._segments[n].children.append(self.tfa_mem_obj_factory(sec))
Harrison Mutaicc60aba2023-02-23 11:30:55 +0000140
Chris Kay205248e2025-04-28 13:28:00 +0100141 def get_memory_layout_from_symbols(self) -> Dict[str, Dict[str, int]]:
Harrison Mutaid9d5eb12023-02-23 11:30:17 +0000142 """Retrieve information about the memory configuration from the symbol
143 table.
144 """
Chris Kay6acd79a2025-04-17 16:53:32 +0100145 assert self._symbols, "Symbol table is empty!"
Harrison Mutaid9d5eb12023-02-23 11:30:17 +0000146
Chris Kay015a8fd2025-04-17 12:14:38 +0100147 expr = r".*(.?R.M)_REGION.*(START|END|LENGTH)"
Harrison Mutaid9d5eb12023-02-23 11:30:17 +0000148 region_symbols = filter(lambda s: re.match(expr, s), self._symbols)
Chris Kay205248e2025-04-28 13:28:00 +0100149 memory_layout: Dict[str, Dict[str, int]] = {}
Harrison Mutaid9d5eb12023-02-23 11:30:17 +0000150
151 for symbol in region_symbols:
Chris Kay6acd79a2025-04-17 16:53:32 +0100152 region, _, attr = symbol.lower().strip("__").split("_")
Harrison Mutaid9d5eb12023-02-23 11:30:17 +0000153 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 Kay6acd79a2025-04-17 16:53:32 +0100161 def get_seg_map_as_dict(self) -> List[Dict[str, Any]]:
Harrison Mutaicc60aba2023-02-23 11:30:55 +0000162 """Get a dictionary of segments and their section mappings."""
Chris Kay6acd79a2025-04-17 16:53:32 +0100163 return [asdict(segment) for segment in self._segments.values()]
Harrison Mutaicc60aba2023-02-23 11:30:55 +0000164
Chris Kay5bce4e62025-04-14 17:30:59 +0100165 def get_memory_layout(self) -> Dict[str, Region]:
Harrison Mutaid9d5eb12023-02-23 11:30:17 +0000166 """Get the total memory consumed by this module from the memory
167 configuration.
Harrison Mutaid9d5eb12023-02-23 11:30:17 +0000168 """
Chris Kay5bce4e62025-04-14 17:30:59 +0100169 mem_dict: Dict[str, Region] = {}
Harrison Mutaid9d5eb12023-02-23 11:30:17 +0000170
171 for mem, attrs in self._memory_layout.items():
Chris Kay5bce4e62025-04-14 17:30:59 +0100172 mem_dict[mem] = Region(
173 attrs["start"],
174 attrs["end"],
175 attrs["length"],
176 )
177
Harrison Mutaid9d5eb12023-02-23 11:30:17 +0000178 return mem_dict
Harrison Mutaicc60aba2023-02-23 11:30:55 +0000179
Chris Kaybc74cfc2025-04-15 10:51:00 +0100180 @property
181 def footprint(self) -> Dict[str, Region]:
182 return self._footprint
183
Chris Kay205248e2025-04-28 13:28:00 +0100184 def get_mod_mem_usage_dict(self) -> Dict[str, int]:
Harrison Mutaicc60aba2023-02-23 11:30:55 +0000185 """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 }