Line data Source code
1 : //! This module contains structures related to the ARM64 exception support in PE files
2 :
3 : use lief_ffi as ffi;
4 :
5 : use std::marker::PhantomData;
6 :
7 : use super::ExceptionInfo;
8 :
9 : use crate::common::FromFFI;
10 : use crate::{declare_iterator, to_slice};
11 :
12 : /// This enum represents an entry in the exception table (`.pdata` section) for the AArch64
13 : /// architecture.
14 : ///
15 : /// Since the ARM64 unwinding info can be encoded in a *packed* and *unpacked* format, this enums
16 : /// wraps two entries [`Packed`] and [`Unpacked`].
17 : ///
18 : /// Reference: <https://learn.microsoft.com/en-us/cpp/build/arm64-exception-handling#arm64-exception-handling-information>
19 236090 : #[derive(Debug)]
20 : pub enum RuntimeFunction<'a> {
21 : /// A packed exception entry that fits on 30 bits
22 : Packed(Packed<'a>),
23 :
24 : /// An extended exception entry that has variable length data
25 : Unpacked(Unpacked<'a>),
26 : }
27 :
28 : impl<'a> FromFFI<ffi::PE_RuntimeFunctionAArch64> for RuntimeFunction<'a> {
29 236090 : fn from_ffi(ffi_entry: cxx::UniquePtr<ffi::PE_RuntimeFunctionAArch64>) -> Self {
30 236090 : unsafe {
31 236090 : let obj_ref = ffi_entry.as_ref().unwrap();
32 236090 : if ffi::PE_unwind_aarch64_UnpackedFunction::classof(obj_ref) {
33 133130 : let raw = {
34 133130 : type From = cxx::UniquePtr<ffi::PE_RuntimeFunctionAArch64>;
35 133130 : type To = cxx::UniquePtr<ffi::PE_unwind_aarch64_UnpackedFunction>;
36 133130 : std::mem::transmute::<From, To>(ffi_entry)
37 133130 : };
38 133130 : RuntimeFunction::Unpacked(Unpacked::from_ffi(raw))
39 102960 : } else if ffi::PE_unwind_aarch64_PackedFunction::classof(obj_ref) {
40 102960 : let raw = {
41 102960 : type From = cxx::UniquePtr<ffi::PE_RuntimeFunctionAArch64>;
42 102960 : type To = cxx::UniquePtr<ffi::PE_unwind_aarch64_PackedFunction>;
43 102960 : std::mem::transmute::<From, To>(ffi_entry)
44 102960 : };
45 102960 : RuntimeFunction::Packed(Packed::from_ffi(raw))
46 : } else {
47 0 : panic!("unsupported format: {}", obj_ref.as_ref().to_string());
48 : }
49 : }
50 236090 : }
51 : }
52 :
53 : impl ExceptionInfo for RuntimeFunction<'_> {
54 0 : fn as_generic(&self) -> &ffi::PE_ExceptionInfo {
55 0 : match &self {
56 0 : RuntimeFunction::Packed(f) => f.as_generic(),
57 0 : RuntimeFunction::Unpacked(f) => f.as_generic(),
58 : }
59 0 : }
60 : }
61 :
62 : /// This structure represents a packed AArch64 exception entry.
63 : ///
64 : /// An excepted entry can be packed if the unwind data fit in 30 bits
65 : ///
66 : /// Reference: <https://learn.microsoft.com/en-us/cpp/build/arm64-exception-handling?view=msvc-170#packed-unwind-data>
67 : pub struct Packed<'a> {
68 : ptr: cxx::UniquePtr<ffi::PE_unwind_aarch64_PackedFunction>,
69 : _owner: PhantomData<&'a ffi::PE_Binary>,
70 : }
71 :
72 : impl<'a> FromFFI<ffi::PE_unwind_aarch64_PackedFunction> for Packed<'a> {
73 102960 : fn from_ffi(ptr: cxx::UniquePtr<ffi::PE_unwind_aarch64_PackedFunction>) -> Self {
74 102960 : Self {
75 102960 : ptr,
76 102960 : _owner: PhantomData,
77 102960 : }
78 102960 : }
79 : }
80 :
81 : impl Packed<'_> {
82 : /// Size of the allocated stack
83 102960 : pub fn frame_size(&self) -> u8 {
84 102960 : self.ptr.frame_size()
85 102960 : }
86 :
87 : /// Number of non-volatile INT registers (x19-x28) saved in the canonical stack location.
88 : #[allow(non_snake_case)]
89 102960 : pub fn reg_I(&self) -> u8 {
90 102960 : self.ptr.reg_I()
91 102960 : }
92 :
93 : /// Number of non-volatile FP registers (d8-d15) saved in the canonical stack location
94 : #[allow(non_snake_case)]
95 102960 : pub fn reg_F(&self) -> u8 {
96 102960 : self.ptr.reg_F()
97 102960 : }
98 :
99 : /// 1-bit flag indicating whether the function homes the integer parameter registers (x0-x7) by
100 : /// storing them at the very start of the function.
101 : /// (0 = doesn't home registers, 1 = homes registers).
102 : #[allow(non_snake_case)]
103 102960 : pub fn H(&self) -> u8 {
104 102960 : self.ptr.H()
105 102960 : }
106 :
107 : /// Flag indicating whether the function includes extra instructions to set up a frame chain
108 : /// and return link.
109 : #[allow(non_snake_case)]
110 102960 : pub fn CR(&self) -> u8 {
111 102960 : self.ptr.CR()
112 102960 : }
113 : }
114 :
115 : impl ExceptionInfo for Packed<'_> {
116 0 : fn as_generic(&self) -> &ffi::PE_ExceptionInfo {
117 0 : self.ptr.as_ref().unwrap().as_ref().as_ref()
118 0 : }
119 : }
120 :
121 : impl std::fmt::Debug for Packed<'_> {
122 102960 : fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
123 102960 : f.debug_struct("Packed")
124 102960 : .field("frame_size", &self.frame_size())
125 102960 : .field("RI", &self.reg_I())
126 102960 : .field("RF", &self.reg_F())
127 102960 : .field("H", &self.H())
128 102960 : .field("CR", &self.CR())
129 102960 : .finish()
130 102960 : }
131 : }
132 :
133 : /// This class represents an unpacked AArch64 exception entry
134 : ///
135 : /// Reference: <https://learn.microsoft.com/en-us/cpp/build/arm64-exception-handling?view=msvc-170#xdata-records>
136 : pub struct Unpacked<'a> {
137 : ptr: cxx::UniquePtr<ffi::PE_unwind_aarch64_UnpackedFunction>,
138 : _owner: PhantomData<&'a ffi::PE_Binary>,
139 : }
140 :
141 : impl<'a> FromFFI<ffi::PE_unwind_aarch64_UnpackedFunction> for Unpacked<'a> {
142 133130 : fn from_ffi(ptr: cxx::UniquePtr<ffi::PE_unwind_aarch64_UnpackedFunction>) -> Self {
143 133130 : Self {
144 133130 : ptr,
145 133130 : _owner: PhantomData,
146 133130 : }
147 133130 : }
148 : }
149 :
150 : impl Unpacked<'_> {
151 : /// RVA where this unpacked data is located (usually pointing in `.xdata`)
152 133130 : pub fn xdata_rva(&self) -> u32 {
153 133130 : self.ptr.xdata_rva()
154 133130 : }
155 :
156 : /// Describes the version of the remaining `.xdata`.
157 : ///
158 : /// Currently (2025-01-04), only version 0 is defined, so values of 1-3 aren't
159 : /// permitted.
160 133130 : pub fn version(&self) -> u32 {
161 133130 : self.ptr.version()
162 133130 : }
163 :
164 : /// 1-bit field that indicates the presence (1) or absence (0) of exception
165 : /// data.
166 : #[allow(non_snake_case)]
167 133130 : pub fn X(&self) -> u8 {
168 133130 : self.ptr.X()
169 133130 : }
170 :
171 : /// 1-bit field that indicates that information describing a single epilog is
172 : /// packed into the header (1) rather than requiring more scope words later (0).
173 : #[allow(non_snake_case)]
174 133130 : pub fn E(&self) -> u8 {
175 133130 : self.ptr.E()
176 133130 : }
177 :
178 : /// If [`Unpacked::E`] == 0, specifies the count of the total number of epilog scopes.
179 : /// Otherwise, return 0
180 133130 : pub fn epilog_count(&self) -> u16 {
181 133130 : self.ptr.epilog_count()
182 133130 : }
183 :
184 : /// **If E() == 1**, index of the first unwind code that describes the one and
185 : /// only epilog.
186 133130 : pub fn epilog_offset(&self) -> u16 {
187 133130 : self.ptr.epilog_offset()
188 133130 : }
189 :
190 : /// Number of 32-bit words needed to contain all of the unwind codes
191 133130 : pub fn code_words(&self) -> u32 {
192 133130 : self.ptr.code_words()
193 133130 : }
194 :
195 : /// Exception handler RVA (if any)
196 133130 : pub fn exception_handler(&self) -> u32 {
197 133130 : self.ptr.exception_handler()
198 133130 : }
199 :
200 : /// Slices that contain the unwind codes.
201 0 : pub fn unwind_code(&self) -> &[u8] {
202 0 : to_slice!(self.ptr.unwind_code());
203 0 : }
204 :
205 : /// Iterator over the epilog scopes
206 0 : pub fn epilog_scopes(&self) -> EpilogScopes {
207 0 : EpilogScopes::new(self.ptr.epilog_scopes())
208 0 : }
209 : }
210 :
211 : impl ExceptionInfo for Unpacked<'_> {
212 0 : fn as_generic(&self) -> &ffi::PE_ExceptionInfo {
213 0 : self.ptr.as_ref().unwrap().as_ref().as_ref()
214 0 : }
215 : }
216 :
217 : impl std::fmt::Debug for Unpacked<'_> {
218 133130 : fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
219 133130 : f.debug_struct("Unpacked")
220 133130 : .field("xdata_rva", &self.xdata_rva())
221 133130 : .field("version", &self.version())
222 133130 : .field("X", &self.X())
223 133130 : .field("E", &self.E())
224 133130 : .field("epilog_count", &self.epilog_count())
225 133130 : .field("epilog_offset", &self.epilog_offset())
226 133130 : .field("code_words", &self.code_words())
227 133130 : .field("exception_handler", &self.exception_handler())
228 133130 : .finish()
229 133130 : }
230 : }
231 :
232 : /// This strucure describes an epilog scope.
233 : pub struct EpilogScope<'a> {
234 : ptr: cxx::UniquePtr<ffi::PE_unwind_aarch64_UnpackedFunction_epilog_scope_t>,
235 : _owner: PhantomData<&'a ffi::PE_unwind_aarch64_UnpackedFunction>,
236 : }
237 :
238 : impl<'a> FromFFI<ffi::PE_unwind_aarch64_UnpackedFunction_epilog_scope_t> for EpilogScope<'a> {
239 0 : fn from_ffi(
240 0 : ptr: cxx::UniquePtr<ffi::PE_unwind_aarch64_UnpackedFunction_epilog_scope_t>,
241 0 : ) -> Self {
242 0 : Self {
243 0 : ptr,
244 0 : _owner: PhantomData,
245 0 : }
246 0 : }
247 : }
248 :
249 : impl EpilogScope<'_> {
250 : /// Offset of the epilog relatives to the start of the function
251 0 : pub fn start_offset(&self) -> u32 {
252 0 : self.ptr.start_offset()
253 0 : }
254 :
255 : /// Byte index of the first unwind code that describes this epilog
256 0 : pub fn start_index(&self) -> u16 {
257 0 : self.ptr.start_index()
258 0 : }
259 :
260 : /// Reserved for future expansion. Should be 0.
261 0 : pub fn reserved(&self) -> u8 {
262 0 : self.ptr.reserved()
263 0 : }
264 : }
265 :
266 : impl std::fmt::Debug for EpilogScope<'_> {
267 0 : fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
268 0 : f.debug_struct("EpilogScope")
269 0 : .field("start_offset", &self.start_offset())
270 0 : .field("start_index", &self.start_index())
271 0 : .field("reserved", &self.reserved())
272 0 : .finish()
273 0 : }
274 : }
275 :
276 0 : declare_iterator!(
277 0 : EpilogScopes,
278 0 : EpilogScope<'a>,
279 0 : ffi::PE_unwind_aarch64_UnpackedFunction_epilog_scope_t,
280 0 : ffi::PE_unwind_aarch64_UnpackedFunction,
281 0 : ffi::PE_unwind_aarch64_UnpackedFunction_it_const_epilog_scopes
282 0 : );
|