1 | //===-- ARMWinEHPrinter.cpp - Windows on ARM EH Data Printer ----*- C++ -*-===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | // Windows on ARM uses a series of serialised data structures (RuntimeFunction) |
10 | // to create a table of information for unwinding. In order to conserve space, |
11 | // there are two different ways that this data is represented. |
12 | // |
13 | // For functions with canonical forms for the prologue and epilogue, the data |
14 | // can be stored in a "packed" form. In this case, the data is packed into the |
15 | // RuntimeFunction's remaining 30-bits and can fully describe the entire frame. |
16 | // |
17 | // +---------------------------------------+ |
18 | // | Function Entry Address | |
19 | // +---------------------------------------+ |
20 | // | Packed Form Data | |
21 | // +---------------------------------------+ |
22 | // |
23 | // This layout is parsed by Decoder::dumpPackedEntry. No unwind bytecode is |
24 | // associated with such a frame as they can be derived from the provided data. |
25 | // The decoder does not synthesize this data as it is unnecessary for the |
26 | // purposes of validation, with the synthesis being required only by a proper |
27 | // unwinder. |
28 | // |
29 | // For functions that are large or do not match canonical forms, the data is |
30 | // split up into two portions, with the actual data residing in the "exception |
31 | // data" table (.xdata) with a reference to the entry from the "procedure data" |
32 | // (.pdata) entry. |
33 | // |
34 | // The exception data contains information about the frame setup, all of the |
35 | // epilogue scopes (for functions for which there are multiple exit points) and |
36 | // the associated exception handler. Additionally, the entry contains byte-code |
37 | // describing how to unwind the function (c.f. Decoder::decodeOpcodes). |
38 | // |
39 | // +---------------------------------------+ |
40 | // | Function Entry Address | |
41 | // +---------------------------------------+ |
42 | // | Exception Data Entry Address | |
43 | // +---------------------------------------+ |
44 | // |
45 | // This layout is parsed by Decoder::dumpUnpackedEntry. Such an entry must |
46 | // first resolve the exception data entry address. This structure |
47 | // (ExceptionDataRecord) has a variable sized header |
48 | // (c.f. ARM::WinEH::HeaderWords) and encodes most of the same information as |
49 | // the packed form. However, because this information is insufficient to |
50 | // synthesize the unwinding, there are associated unwinding bytecode which make |
51 | // up the bulk of the Decoder. |
52 | // |
53 | // The decoder itself is table-driven, using the first byte to determine the |
54 | // opcode and dispatching to the associated printing routine. The bytecode |
55 | // itself is a variable length instruction encoding that can fully describe the |
56 | // state of the stack and the necessary operations for unwinding to the |
57 | // beginning of the frame. |
58 | // |
59 | // The byte-code maintains a 1-1 instruction mapping, indicating both the width |
60 | // of the instruction (Thumb2 instructions are variable length, 16 or 32 bits |
61 | // wide) allowing the program to unwind from any point in the prologue, body, or |
62 | // epilogue of the function. |
63 | |
64 | #include "ARMWinEHPrinter.h" |
65 | #include "llvm/ADT/STLExtras.h" |
66 | #include "llvm/ADT/StringExtras.h" |
67 | #include "llvm/Support/ARMWinEH.h" |
68 | #include "llvm/Support/Format.h" |
69 | |
70 | using namespace llvm; |
71 | using namespace llvm::object; |
72 | using namespace llvm::support; |
73 | |
74 | namespace llvm { |
75 | raw_ostream &operator<<(raw_ostream &OS, const ARM::WinEH::ReturnType &RT) { |
76 | switch (RT) { |
77 | case ARM::WinEH::ReturnType::RT_POP: |
78 | OS << "pop {pc}" ; |
79 | break; |
80 | case ARM::WinEH::ReturnType::RT_B: |
81 | OS << "bx <reg>" ; |
82 | break; |
83 | case ARM::WinEH::ReturnType::RT_BW: |
84 | OS << "b.w <target>" ; |
85 | break; |
86 | case ARM::WinEH::ReturnType::RT_NoEpilogue: |
87 | OS << "(no epilogue)" ; |
88 | break; |
89 | } |
90 | return OS; |
91 | } |
92 | } |
93 | |
94 | static std::string formatSymbol(StringRef Name, uint64_t Address, |
95 | uint64_t Offset = 0) { |
96 | std::string Buffer; |
97 | raw_string_ostream OS(Buffer); |
98 | |
99 | if (!Name.empty()) |
100 | OS << Name << " " ; |
101 | |
102 | if (Offset) |
103 | OS << format(Fmt: "+0x%" PRIX64 " (0x%" PRIX64 ")" , Vals: Offset, Vals: Address); |
104 | else if (!Name.empty()) |
105 | OS << format(Fmt: "(0x%" PRIX64 ")" , Vals: Address); |
106 | else |
107 | OS << format(Fmt: "0x%" PRIX64, Vals: Address); |
108 | |
109 | return Buffer; |
110 | } |
111 | |
112 | namespace llvm { |
113 | namespace ARM { |
114 | namespace WinEH { |
115 | const size_t Decoder::PDataEntrySize = sizeof(RuntimeFunction); |
116 | |
117 | // TODO name the uops more appropriately |
118 | const Decoder::RingEntry Decoder::Ring[] = { |
119 | { .Mask: 0x80, .Value: 0x00, .Length: 1, .Routine: &Decoder::opcode_0xxxxxxx }, // UOP_STACK_FREE (16-bit) |
120 | { .Mask: 0xc0, .Value: 0x80, .Length: 2, .Routine: &Decoder::opcode_10Lxxxxx }, // UOP_POP (32-bit) |
121 | { .Mask: 0xf0, .Value: 0xc0, .Length: 1, .Routine: &Decoder::opcode_1100xxxx }, // UOP_STACK_SAVE (16-bit) |
122 | { .Mask: 0xf8, .Value: 0xd0, .Length: 1, .Routine: &Decoder::opcode_11010Lxx }, // UOP_POP (16-bit) |
123 | { .Mask: 0xf8, .Value: 0xd8, .Length: 1, .Routine: &Decoder::opcode_11011Lxx }, // UOP_POP (32-bit) |
124 | { .Mask: 0xf8, .Value: 0xe0, .Length: 1, .Routine: &Decoder::opcode_11100xxx }, // UOP_VPOP (32-bit) |
125 | { .Mask: 0xfc, .Value: 0xe8, .Length: 2, .Routine: &Decoder::opcode_111010xx }, // UOP_STACK_FREE (32-bit) |
126 | { .Mask: 0xfe, .Value: 0xec, .Length: 2, .Routine: &Decoder::opcode_1110110L }, // UOP_POP (16-bit) |
127 | { .Mask: 0xff, .Value: 0xee, .Length: 2, .Routine: &Decoder::opcode_11101110 }, // UOP_MICROSOFT_SPECIFIC (16-bit) |
128 | // UOP_PUSH_MACHINE_FRAME |
129 | // UOP_PUSH_CONTEXT |
130 | // UOP_PUSH_TRAP_FRAME |
131 | // UOP_REDZONE_RESTORE_LR |
132 | { .Mask: 0xff, .Value: 0xef, .Length: 2, .Routine: &Decoder::opcode_11101111 }, // UOP_LDRPC_POSTINC (32-bit) |
133 | { .Mask: 0xff, .Value: 0xf5, .Length: 2, .Routine: &Decoder::opcode_11110101 }, // UOP_VPOP (32-bit) |
134 | { .Mask: 0xff, .Value: 0xf6, .Length: 2, .Routine: &Decoder::opcode_11110110 }, // UOP_VPOP (32-bit) |
135 | { .Mask: 0xff, .Value: 0xf7, .Length: 3, .Routine: &Decoder::opcode_11110111 }, // UOP_STACK_RESTORE (16-bit) |
136 | { .Mask: 0xff, .Value: 0xf8, .Length: 4, .Routine: &Decoder::opcode_11111000 }, // UOP_STACK_RESTORE (16-bit) |
137 | { .Mask: 0xff, .Value: 0xf9, .Length: 3, .Routine: &Decoder::opcode_11111001 }, // UOP_STACK_RESTORE (32-bit) |
138 | { .Mask: 0xff, .Value: 0xfa, .Length: 4, .Routine: &Decoder::opcode_11111010 }, // UOP_STACK_RESTORE (32-bit) |
139 | { .Mask: 0xff, .Value: 0xfb, .Length: 1, .Routine: &Decoder::opcode_11111011 }, // UOP_NOP (16-bit) |
140 | { .Mask: 0xff, .Value: 0xfc, .Length: 1, .Routine: &Decoder::opcode_11111100 }, // UOP_NOP (32-bit) |
141 | { .Mask: 0xff, .Value: 0xfd, .Length: 1, .Routine: &Decoder::opcode_11111101 }, // UOP_NOP (16-bit) / END |
142 | { .Mask: 0xff, .Value: 0xfe, .Length: 1, .Routine: &Decoder::opcode_11111110 }, // UOP_NOP (32-bit) / END |
143 | { .Mask: 0xff, .Value: 0xff, .Length: 1, .Routine: &Decoder::opcode_11111111 }, // UOP_END |
144 | }; |
145 | |
146 | // Unwind opcodes for ARM64. |
147 | // https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling |
148 | const Decoder::RingEntry Decoder::Ring64[] = { |
149 | {.Mask: 0xe0, .Value: 0x00, .Length: 1, .Routine: &Decoder::opcode_alloc_s}, |
150 | {.Mask: 0xe0, .Value: 0x20, .Length: 1, .Routine: &Decoder::opcode_save_r19r20_x}, |
151 | {.Mask: 0xc0, .Value: 0x40, .Length: 1, .Routine: &Decoder::opcode_save_fplr}, |
152 | {.Mask: 0xc0, .Value: 0x80, .Length: 1, .Routine: &Decoder::opcode_save_fplr_x}, |
153 | {.Mask: 0xf8, .Value: 0xc0, .Length: 2, .Routine: &Decoder::opcode_alloc_m}, |
154 | {.Mask: 0xfc, .Value: 0xc8, .Length: 2, .Routine: &Decoder::opcode_save_regp}, |
155 | {.Mask: 0xfc, .Value: 0xcc, .Length: 2, .Routine: &Decoder::opcode_save_regp_x}, |
156 | {.Mask: 0xfc, .Value: 0xd0, .Length: 2, .Routine: &Decoder::opcode_save_reg}, |
157 | {.Mask: 0xfe, .Value: 0xd4, .Length: 2, .Routine: &Decoder::opcode_save_reg_x}, |
158 | {.Mask: 0xfe, .Value: 0xd6, .Length: 2, .Routine: &Decoder::opcode_save_lrpair}, |
159 | {.Mask: 0xfe, .Value: 0xd8, .Length: 2, .Routine: &Decoder::opcode_save_fregp}, |
160 | {.Mask: 0xfe, .Value: 0xda, .Length: 2, .Routine: &Decoder::opcode_save_fregp_x}, |
161 | {.Mask: 0xfe, .Value: 0xdc, .Length: 2, .Routine: &Decoder::opcode_save_freg}, |
162 | {.Mask: 0xff, .Value: 0xde, .Length: 2, .Routine: &Decoder::opcode_save_freg_x}, |
163 | {.Mask: 0xff, .Value: 0xdf, .Length: 2, .Routine: &Decoder::opcode_alloc_z}, |
164 | {.Mask: 0xff, .Value: 0xe0, .Length: 4, .Routine: &Decoder::opcode_alloc_l}, |
165 | {.Mask: 0xff, .Value: 0xe1, .Length: 1, .Routine: &Decoder::opcode_setfp}, |
166 | {.Mask: 0xff, .Value: 0xe2, .Length: 2, .Routine: &Decoder::opcode_addfp}, |
167 | {.Mask: 0xff, .Value: 0xe3, .Length: 1, .Routine: &Decoder::opcode_nop}, |
168 | {.Mask: 0xff, .Value: 0xe4, .Length: 1, .Routine: &Decoder::opcode_end}, |
169 | {.Mask: 0xff, .Value: 0xe5, .Length: 1, .Routine: &Decoder::opcode_end_c}, |
170 | {.Mask: 0xff, .Value: 0xe6, .Length: 1, .Routine: &Decoder::opcode_save_next}, |
171 | {.Mask: 0xff, .Value: 0xe7, .Length: 3, .Routine: &Decoder::opcode_e7}, |
172 | {.Mask: 0xff, .Value: 0xe8, .Length: 1, .Routine: &Decoder::opcode_trap_frame}, |
173 | {.Mask: 0xff, .Value: 0xe9, .Length: 1, .Routine: &Decoder::opcode_machine_frame}, |
174 | {.Mask: 0xff, .Value: 0xea, .Length: 1, .Routine: &Decoder::opcode_context}, |
175 | {.Mask: 0xff, .Value: 0xeb, .Length: 1, .Routine: &Decoder::opcode_ec_context}, |
176 | {.Mask: 0xff, .Value: 0xec, .Length: 1, .Routine: &Decoder::opcode_clear_unwound_to_call}, |
177 | {.Mask: 0xff, .Value: 0xfc, .Length: 1, .Routine: &Decoder::opcode_pac_sign_lr}, |
178 | }; |
179 | |
180 | static void printRange(raw_ostream &OS, ListSeparator &LS, unsigned First, |
181 | unsigned Last, char Letter) { |
182 | if (First == Last) |
183 | OS << LS << Letter << First; |
184 | else |
185 | OS << LS << Letter << First << "-" << Letter << Last; |
186 | } |
187 | |
188 | static void printRange(raw_ostream &OS, uint32_t Mask, ListSeparator &LS, |
189 | unsigned Start, unsigned End, char Letter) { |
190 | int First = -1; |
191 | for (unsigned RI = Start; RI <= End; ++RI) { |
192 | if (Mask & (1 << RI)) { |
193 | if (First < 0) |
194 | First = RI; |
195 | } else { |
196 | if (First >= 0) { |
197 | printRange(OS, LS, First, Last: RI - 1, Letter); |
198 | First = -1; |
199 | } |
200 | } |
201 | } |
202 | if (First >= 0) |
203 | printRange(OS, LS, First, Last: End, Letter); |
204 | } |
205 | |
206 | void Decoder::printGPRMask(uint16_t GPRMask) { |
207 | OS << '{'; |
208 | ListSeparator LS; |
209 | printRange(OS, Mask: GPRMask, LS, Start: 0, End: 12, Letter: 'r'); |
210 | if (GPRMask & (1 << 14)) |
211 | OS << LS << "lr" ; |
212 | if (GPRMask & (1 << 15)) |
213 | OS << LS << "pc" ; |
214 | OS << '}'; |
215 | } |
216 | |
217 | void Decoder::printVFPMask(uint32_t VFPMask) { |
218 | OS << '{'; |
219 | ListSeparator LS; |
220 | printRange(OS, Mask: VFPMask, LS, Start: 0, End: 31, Letter: 'd'); |
221 | OS << '}'; |
222 | } |
223 | |
224 | ErrorOr<object::SectionRef> |
225 | Decoder::getSectionContaining(const COFFObjectFile &COFF, uint64_t VA) { |
226 | for (const auto &Section : COFF.sections()) { |
227 | uint64_t Address = Section.getAddress(); |
228 | uint64_t Size = Section.getSize(); |
229 | |
230 | if (VA >= Address && (VA - Address) <= Size) |
231 | return Section; |
232 | } |
233 | return inconvertibleErrorCode(); |
234 | } |
235 | |
236 | ErrorOr<object::SymbolRef> Decoder::getSymbol(const COFFObjectFile &COFF, |
237 | uint64_t VA, bool FunctionOnly) { |
238 | for (const auto &Symbol : COFF.symbols()) { |
239 | Expected<SymbolRef::Type> Type = Symbol.getType(); |
240 | if (!Type) |
241 | return errorToErrorCode(Err: Type.takeError()); |
242 | if (FunctionOnly && *Type != SymbolRef::ST_Function) |
243 | continue; |
244 | |
245 | Expected<uint64_t> Address = Symbol.getAddress(); |
246 | if (!Address) |
247 | return errorToErrorCode(Err: Address.takeError()); |
248 | if (*Address == VA) |
249 | return Symbol; |
250 | } |
251 | return inconvertibleErrorCode(); |
252 | } |
253 | |
254 | ErrorOr<SymbolRef> Decoder::getRelocatedSymbol(const COFFObjectFile &, |
255 | const SectionRef &Section, |
256 | uint64_t Offset) { |
257 | for (const auto &Relocation : Section.relocations()) { |
258 | uint64_t RelocationOffset = Relocation.getOffset(); |
259 | if (RelocationOffset == Offset) |
260 | return *Relocation.getSymbol(); |
261 | } |
262 | return inconvertibleErrorCode(); |
263 | } |
264 | |
265 | SymbolRef Decoder::getPreferredSymbol(const COFFObjectFile &COFF, SymbolRef Sym, |
266 | uint64_t &SymbolOffset) { |
267 | // The symbol resolved by getRelocatedSymbol can be any internal |
268 | // nondescriptive symbol; try to resolve a more descriptive one. |
269 | COFFSymbolRef CoffSym = COFF.getCOFFSymbol(Symbol: Sym); |
270 | if (CoffSym.getStorageClass() != COFF::IMAGE_SYM_CLASS_LABEL && |
271 | CoffSym.getSectionDefinition() == nullptr) |
272 | return Sym; |
273 | for (const auto &S : COFF.symbols()) { |
274 | COFFSymbolRef CS = COFF.getCOFFSymbol(Symbol: S); |
275 | if (CS.getSectionNumber() == CoffSym.getSectionNumber() && |
276 | CS.getValue() <= CoffSym.getValue() + SymbolOffset && |
277 | CS.getStorageClass() != COFF::IMAGE_SYM_CLASS_LABEL && |
278 | CS.getSectionDefinition() == nullptr) { |
279 | uint32_t Offset = CoffSym.getValue() + SymbolOffset - CS.getValue(); |
280 | if (Offset <= SymbolOffset) { |
281 | SymbolOffset = Offset; |
282 | Sym = S; |
283 | CoffSym = CS; |
284 | if (CS.isExternal() && SymbolOffset == 0) |
285 | return Sym; |
286 | } |
287 | } |
288 | } |
289 | return Sym; |
290 | } |
291 | |
292 | ErrorOr<SymbolRef> Decoder::getSymbolForLocation( |
293 | const COFFObjectFile &COFF, const SectionRef &Section, |
294 | uint64_t OffsetInSection, uint64_t ImmediateOffset, uint64_t &SymbolAddress, |
295 | uint64_t &SymbolOffset, bool FunctionOnly) { |
296 | // Try to locate a relocation that points at the offset in the section |
297 | ErrorOr<SymbolRef> SymOrErr = |
298 | getRelocatedSymbol(COFF, Section, Offset: OffsetInSection); |
299 | if (SymOrErr) { |
300 | // We found a relocation symbol; the immediate offset needs to be added |
301 | // to the symbol address. |
302 | SymbolOffset = ImmediateOffset; |
303 | |
304 | Expected<uint64_t> AddressOrErr = SymOrErr->getAddress(); |
305 | if (!AddressOrErr) { |
306 | std::string Buf; |
307 | llvm::raw_string_ostream OS(Buf); |
308 | logAllUnhandledErrors(E: AddressOrErr.takeError(), OS); |
309 | reportFatalUsageError(reason: Twine(Buf)); |
310 | } |
311 | // We apply SymbolOffset here directly. We return it separately to allow |
312 | // the caller to print it as an offset on the symbol name. |
313 | SymbolAddress = *AddressOrErr + SymbolOffset; |
314 | |
315 | if (FunctionOnly) // Resolve label/section symbols into function names. |
316 | SymOrErr = getPreferredSymbol(COFF, Sym: *SymOrErr, SymbolOffset); |
317 | } else { |
318 | // No matching relocation found; operating on a linked image. Try to |
319 | // find a descriptive symbol if possible. The immediate offset contains |
320 | // the image relative address, and we shouldn't add any offset to the |
321 | // symbol. |
322 | SymbolAddress = COFF.getImageBase() + ImmediateOffset; |
323 | SymbolOffset = 0; |
324 | SymOrErr = getSymbol(COFF, VA: SymbolAddress, FunctionOnly); |
325 | } |
326 | return SymOrErr; |
327 | } |
328 | |
329 | bool Decoder::opcode_0xxxxxxx(const uint8_t *OC, unsigned &Offset, |
330 | unsigned Length, bool Prologue) { |
331 | uint8_t Imm = OC[Offset] & 0x7f; |
332 | SW.startLine() << format(Fmt: "0x%02x ; %s sp, #(%u * 4)\n" , |
333 | Vals: OC[Offset], |
334 | Vals: static_cast<const char *>(Prologue ? "sub" : "add" ), |
335 | Vals: Imm); |
336 | ++Offset; |
337 | return false; |
338 | } |
339 | |
340 | bool Decoder::opcode_10Lxxxxx(const uint8_t *OC, unsigned &Offset, |
341 | unsigned Length, bool Prologue) { |
342 | unsigned Link = (OC[Offset] & 0x20) >> 5; |
343 | uint16_t RegisterMask = (Link << (Prologue ? 14 : 15)) |
344 | | ((OC[Offset + 0] & 0x1f) << 8) |
345 | | ((OC[Offset + 1] & 0xff) << 0); |
346 | assert((~RegisterMask & (1 << 13)) && "sp must not be set" ); |
347 | assert((~RegisterMask & (1 << (Prologue ? 15 : 14))) && "pc must not be set" ); |
348 | |
349 | SW.startLine() << format(Fmt: "0x%02x 0x%02x ; %s.w " , |
350 | Vals: OC[Offset + 0], Vals: OC[Offset + 1], |
351 | Vals: Prologue ? "push" : "pop" ); |
352 | printGPRMask(GPRMask: RegisterMask); |
353 | OS << '\n'; |
354 | |
355 | Offset += 2; |
356 | return false; |
357 | } |
358 | |
359 | bool Decoder::opcode_1100xxxx(const uint8_t *OC, unsigned &Offset, |
360 | unsigned Length, bool Prologue) { |
361 | if (Prologue) |
362 | SW.startLine() << format(Fmt: "0x%02x ; mov r%u, sp\n" , |
363 | Vals: OC[Offset], Vals: OC[Offset] & 0xf); |
364 | else |
365 | SW.startLine() << format(Fmt: "0x%02x ; mov sp, r%u\n" , |
366 | Vals: OC[Offset], Vals: OC[Offset] & 0xf); |
367 | ++Offset; |
368 | return false; |
369 | } |
370 | |
371 | bool Decoder::opcode_11010Lxx(const uint8_t *OC, unsigned &Offset, |
372 | unsigned Length, bool Prologue) { |
373 | unsigned Link = (OC[Offset] & 0x4) >> 2; |
374 | unsigned Count = (OC[Offset] & 0x3); |
375 | |
376 | uint16_t GPRMask = (Link << (Prologue ? 14 : 15)) |
377 | | (((1 << (Count + 1)) - 1) << 4); |
378 | |
379 | SW.startLine() << format(Fmt: "0x%02x ; %s " , Vals: OC[Offset], |
380 | Vals: Prologue ? "push" : "pop" ); |
381 | printGPRMask(GPRMask); |
382 | OS << '\n'; |
383 | |
384 | ++Offset; |
385 | return false; |
386 | } |
387 | |
388 | bool Decoder::opcode_11011Lxx(const uint8_t *OC, unsigned &Offset, |
389 | unsigned Length, bool Prologue) { |
390 | unsigned Link = (OC[Offset] & 0x4) >> 2; |
391 | unsigned Count = (OC[Offset] & 0x3) + 4; |
392 | |
393 | uint16_t GPRMask = (Link << (Prologue ? 14 : 15)) |
394 | | (((1 << (Count + 1)) - 1) << 4); |
395 | |
396 | SW.startLine() << format(Fmt: "0x%02x ; %s.w " , Vals: OC[Offset], |
397 | Vals: Prologue ? "push" : "pop" ); |
398 | printGPRMask(GPRMask); |
399 | OS << '\n'; |
400 | |
401 | ++Offset; |
402 | return false; |
403 | } |
404 | |
405 | bool Decoder::opcode_11100xxx(const uint8_t *OC, unsigned &Offset, |
406 | unsigned Length, bool Prologue) { |
407 | unsigned High = (OC[Offset] & 0x7); |
408 | uint32_t VFPMask = (((1 << (High + 1)) - 1) << 8); |
409 | |
410 | SW.startLine() << format(Fmt: "0x%02x ; %s " , Vals: OC[Offset], |
411 | Vals: Prologue ? "vpush" : "vpop" ); |
412 | printVFPMask(VFPMask); |
413 | OS << '\n'; |
414 | |
415 | ++Offset; |
416 | return false; |
417 | } |
418 | |
419 | bool Decoder::opcode_111010xx(const uint8_t *OC, unsigned &Offset, |
420 | unsigned Length, bool Prologue) { |
421 | uint16_t Imm = ((OC[Offset + 0] & 0x03) << 8) | ((OC[Offset + 1] & 0xff) << 0); |
422 | |
423 | SW.startLine() << format(Fmt: "0x%02x 0x%02x ; %s.w sp, #(%u * 4)\n" , |
424 | Vals: OC[Offset + 0], Vals: OC[Offset + 1], |
425 | Vals: static_cast<const char *>(Prologue ? "sub" : "add" ), |
426 | Vals: Imm); |
427 | |
428 | Offset += 2; |
429 | return false; |
430 | } |
431 | |
432 | bool Decoder::opcode_1110110L(const uint8_t *OC, unsigned &Offset, |
433 | unsigned Length, bool Prologue) { |
434 | uint16_t GPRMask = ((OC[Offset + 0] & 0x01) << (Prologue ? 14 : 15)) |
435 | | ((OC[Offset + 1] & 0xff) << 0); |
436 | |
437 | SW.startLine() << format(Fmt: "0x%02x 0x%02x ; %s " , Vals: OC[Offset + 0], |
438 | Vals: OC[Offset + 1], Vals: Prologue ? "push" : "pop" ); |
439 | printGPRMask(GPRMask); |
440 | OS << '\n'; |
441 | |
442 | Offset += 2; |
443 | return false; |
444 | } |
445 | |
446 | bool Decoder::opcode_11101110(const uint8_t *OC, unsigned &Offset, |
447 | unsigned Length, bool Prologue) { |
448 | assert(!Prologue && "may not be used in prologue" ); |
449 | |
450 | if (OC[Offset + 1] & 0xf0) |
451 | SW.startLine() << format(Fmt: "0x%02x 0x%02x ; reserved\n" , |
452 | Vals: OC[Offset + 0], Vals: OC[Offset + 1]); |
453 | else |
454 | SW.startLine() |
455 | << format(Fmt: "0x%02x 0x%02x ; microsoft-specific (type: %u)\n" , |
456 | Vals: OC[Offset + 0], Vals: OC[Offset + 1], Vals: OC[Offset + 1] & 0x0f); |
457 | |
458 | Offset += 2; |
459 | return false; |
460 | } |
461 | |
462 | bool Decoder::opcode_11101111(const uint8_t *OC, unsigned &Offset, |
463 | unsigned Length, bool Prologue) { |
464 | if (OC[Offset + 1] & 0xf0) |
465 | SW.startLine() << format(Fmt: "0x%02x 0x%02x ; reserved\n" , |
466 | Vals: OC[Offset + 0], Vals: OC[Offset + 1]); |
467 | else if (Prologue) |
468 | SW.startLine() |
469 | << format(Fmt: "0x%02x 0x%02x ; str.w lr, [sp, #-%u]!\n" , |
470 | Vals: OC[Offset + 0], Vals: OC[Offset + 1], Vals: OC[Offset + 1] << 2); |
471 | else |
472 | SW.startLine() |
473 | << format(Fmt: "0x%02x 0x%02x ; ldr.w lr, [sp], #%u\n" , |
474 | Vals: OC[Offset + 0], Vals: OC[Offset + 1], Vals: OC[Offset + 1] << 2); |
475 | |
476 | Offset += 2; |
477 | return false; |
478 | } |
479 | |
480 | bool Decoder::opcode_11110101(const uint8_t *OC, unsigned &Offset, |
481 | unsigned Length, bool Prologue) { |
482 | unsigned Start = (OC[Offset + 1] & 0xf0) >> 4; |
483 | unsigned End = (OC[Offset + 1] & 0x0f) >> 0; |
484 | uint32_t VFPMask = ((1 << (End + 1 - Start)) - 1) << Start; |
485 | |
486 | SW.startLine() << format(Fmt: "0x%02x 0x%02x ; %s " , Vals: OC[Offset + 0], |
487 | Vals: OC[Offset + 1], Vals: Prologue ? "vpush" : "vpop" ); |
488 | printVFPMask(VFPMask); |
489 | OS << '\n'; |
490 | |
491 | Offset += 2; |
492 | return false; |
493 | } |
494 | |
495 | bool Decoder::opcode_11110110(const uint8_t *OC, unsigned &Offset, |
496 | unsigned Length, bool Prologue) { |
497 | unsigned Start = (OC[Offset + 1] & 0xf0) >> 4; |
498 | unsigned End = (OC[Offset + 1] & 0x0f) >> 0; |
499 | uint32_t VFPMask = ((1 << (End + 1 - Start)) - 1) << (16 + Start); |
500 | |
501 | SW.startLine() << format(Fmt: "0x%02x 0x%02x ; %s " , Vals: OC[Offset + 0], |
502 | Vals: OC[Offset + 1], Vals: Prologue ? "vpush" : "vpop" ); |
503 | printVFPMask(VFPMask); |
504 | OS << '\n'; |
505 | |
506 | Offset += 2; |
507 | return false; |
508 | } |
509 | |
510 | bool Decoder::opcode_11110111(const uint8_t *OC, unsigned &Offset, |
511 | unsigned Length, bool Prologue) { |
512 | uint32_t Imm = (OC[Offset + 1] << 8) | (OC[Offset + 2] << 0); |
513 | |
514 | SW.startLine() << format(Fmt: "0x%02x 0x%02x 0x%02x ; %s sp, sp, #(%u * 4)\n" , |
515 | Vals: OC[Offset + 0], Vals: OC[Offset + 1], Vals: OC[Offset + 2], |
516 | Vals: static_cast<const char *>(Prologue ? "sub" : "add" ), |
517 | Vals: Imm); |
518 | |
519 | Offset += 3; |
520 | return false; |
521 | } |
522 | |
523 | bool Decoder::opcode_11111000(const uint8_t *OC, unsigned &Offset, |
524 | unsigned Length, bool Prologue) { |
525 | uint32_t Imm = (OC[Offset + 1] << 16) |
526 | | (OC[Offset + 2] << 8) |
527 | | (OC[Offset + 3] << 0); |
528 | |
529 | SW.startLine() |
530 | << format(Fmt: "0x%02x 0x%02x 0x%02x 0x%02x ; %s sp, sp, #(%u * 4)\n" , |
531 | Vals: OC[Offset + 0], Vals: OC[Offset + 1], Vals: OC[Offset + 2], Vals: OC[Offset + 3], |
532 | Vals: static_cast<const char *>(Prologue ? "sub" : "add" ), Vals: Imm); |
533 | |
534 | Offset += 4; |
535 | return false; |
536 | } |
537 | |
538 | bool Decoder::opcode_11111001(const uint8_t *OC, unsigned &Offset, |
539 | unsigned Length, bool Prologue) { |
540 | uint32_t Imm = (OC[Offset + 1] << 8) | (OC[Offset + 2] << 0); |
541 | |
542 | SW.startLine() |
543 | << format(Fmt: "0x%02x 0x%02x 0x%02x ; %s.w sp, sp, #(%u * 4)\n" , |
544 | Vals: OC[Offset + 0], Vals: OC[Offset + 1], Vals: OC[Offset + 2], |
545 | Vals: static_cast<const char *>(Prologue ? "sub" : "add" ), Vals: Imm); |
546 | |
547 | Offset += 3; |
548 | return false; |
549 | } |
550 | |
551 | bool Decoder::opcode_11111010(const uint8_t *OC, unsigned &Offset, |
552 | unsigned Length, bool Prologue) { |
553 | uint32_t Imm = (OC[Offset + 1] << 16) |
554 | | (OC[Offset + 2] << 8) |
555 | | (OC[Offset + 3] << 0); |
556 | |
557 | SW.startLine() |
558 | << format(Fmt: "0x%02x 0x%02x 0x%02x 0x%02x ; %s.w sp, sp, #(%u * 4)\n" , |
559 | Vals: OC[Offset + 0], Vals: OC[Offset + 1], Vals: OC[Offset + 2], Vals: OC[Offset + 3], |
560 | Vals: static_cast<const char *>(Prologue ? "sub" : "add" ), Vals: Imm); |
561 | |
562 | Offset += 4; |
563 | return false; |
564 | } |
565 | |
566 | bool Decoder::opcode_11111011(const uint8_t *OC, unsigned &Offset, |
567 | unsigned Length, bool Prologue) { |
568 | SW.startLine() << format(Fmt: "0x%02x ; nop\n" , Vals: OC[Offset]); |
569 | ++Offset; |
570 | return false; |
571 | } |
572 | |
573 | bool Decoder::opcode_11111100(const uint8_t *OC, unsigned &Offset, |
574 | unsigned Length, bool Prologue) { |
575 | SW.startLine() << format(Fmt: "0x%02x ; nop.w\n" , Vals: OC[Offset]); |
576 | ++Offset; |
577 | return false; |
578 | } |
579 | |
580 | bool Decoder::opcode_11111101(const uint8_t *OC, unsigned &Offset, |
581 | unsigned Length, bool Prologue) { |
582 | SW.startLine() << format(Fmt: "0x%02x ; bx <reg>\n" , Vals: OC[Offset]); |
583 | ++Offset; |
584 | return true; |
585 | } |
586 | |
587 | bool Decoder::opcode_11111110(const uint8_t *OC, unsigned &Offset, |
588 | unsigned Length, bool Prologue) { |
589 | SW.startLine() << format(Fmt: "0x%02x ; b.w <target>\n" , Vals: OC[Offset]); |
590 | ++Offset; |
591 | return true; |
592 | } |
593 | |
594 | bool Decoder::opcode_11111111(const uint8_t *OC, unsigned &Offset, |
595 | unsigned Length, bool Prologue) { |
596 | ++Offset; |
597 | return true; |
598 | } |
599 | |
600 | // ARM64 unwind codes start here. |
601 | bool Decoder::opcode_alloc_s(const uint8_t *OC, unsigned &Offset, |
602 | unsigned Length, bool Prologue) { |
603 | uint32_t NumBytes = (OC[Offset] & 0x1F) << 4; |
604 | SW.startLine() << format(Fmt: "0x%02x ; %s sp, #%u\n" , Vals: OC[Offset], |
605 | Vals: static_cast<const char *>(Prologue ? "sub" : "add" ), |
606 | Vals: NumBytes); |
607 | ++Offset; |
608 | return false; |
609 | } |
610 | |
611 | bool Decoder::opcode_save_r19r20_x(const uint8_t *OC, unsigned &Offset, |
612 | unsigned Length, bool Prologue) { |
613 | uint32_t Off = (OC[Offset] & 0x1F) << 3; |
614 | if (Prologue) |
615 | SW.startLine() << format( |
616 | Fmt: "0x%02x ; stp x19, x20, [sp, #-%u]!\n" , Vals: OC[Offset], Vals: Off); |
617 | else |
618 | SW.startLine() << format( |
619 | Fmt: "0x%02x ; ldp x19, x20, [sp], #%u\n" , Vals: OC[Offset], Vals: Off); |
620 | ++Offset; |
621 | return false; |
622 | } |
623 | |
624 | bool Decoder::opcode_save_fplr(const uint8_t *OC, unsigned &Offset, |
625 | unsigned Length, bool Prologue) { |
626 | uint32_t Off = (OC[Offset] & 0x3F) << 3; |
627 | SW.startLine() << format( |
628 | Fmt: "0x%02x ; %s x29, x30, [sp, #%u]\n" , Vals: OC[Offset], |
629 | Vals: static_cast<const char *>(Prologue ? "stp" : "ldp" ), Vals: Off); |
630 | ++Offset; |
631 | return false; |
632 | } |
633 | |
634 | bool Decoder::opcode_save_fplr_x(const uint8_t *OC, unsigned &Offset, |
635 | unsigned Length, bool Prologue) { |
636 | uint32_t Off = ((OC[Offset] & 0x3F) + 1) << 3; |
637 | if (Prologue) |
638 | SW.startLine() << format( |
639 | Fmt: "0x%02x ; stp x29, x30, [sp, #-%u]!\n" , Vals: OC[Offset], Vals: Off); |
640 | else |
641 | SW.startLine() << format( |
642 | Fmt: "0x%02x ; ldp x29, x30, [sp], #%u\n" , Vals: OC[Offset], Vals: Off); |
643 | ++Offset; |
644 | return false; |
645 | } |
646 | |
647 | bool Decoder::opcode_alloc_m(const uint8_t *OC, unsigned &Offset, |
648 | unsigned Length, bool Prologue) { |
649 | uint32_t NumBytes = ((OC[Offset] & 0x07) << 8); |
650 | NumBytes |= (OC[Offset + 1] & 0xFF); |
651 | NumBytes <<= 4; |
652 | SW.startLine() << format(Fmt: "0x%02x%02x ; %s sp, #%u\n" , |
653 | Vals: OC[Offset], Vals: OC[Offset + 1], |
654 | Vals: static_cast<const char *>(Prologue ? "sub" : "add" ), |
655 | Vals: NumBytes); |
656 | Offset += 2; |
657 | return false; |
658 | } |
659 | |
660 | bool Decoder::opcode_save_regp(const uint8_t *OC, unsigned &Offset, |
661 | unsigned Length, bool Prologue) { |
662 | uint32_t Reg = ((OC[Offset] & 0x03) << 8); |
663 | Reg |= (OC[Offset + 1] & 0xC0); |
664 | Reg >>= 6; |
665 | Reg += 19; |
666 | uint32_t Off = (OC[Offset + 1] & 0x3F) << 3; |
667 | SW.startLine() << format( |
668 | Fmt: "0x%02x%02x ; %s x%u, x%u, [sp, #%u]\n" , |
669 | Vals: OC[Offset], Vals: OC[Offset + 1], |
670 | Vals: static_cast<const char *>(Prologue ? "stp" : "ldp" ), Vals: Reg, Vals: Reg + 1, Vals: Off); |
671 | Offset += 2; |
672 | return false; |
673 | } |
674 | |
675 | bool Decoder::opcode_save_regp_x(const uint8_t *OC, unsigned &Offset, |
676 | unsigned Length, bool Prologue) { |
677 | uint32_t Reg = ((OC[Offset] & 0x03) << 8); |
678 | Reg |= (OC[Offset + 1] & 0xC0); |
679 | Reg >>= 6; |
680 | Reg += 19; |
681 | uint32_t Off = ((OC[Offset + 1] & 0x3F) + 1) << 3; |
682 | if (Prologue) |
683 | SW.startLine() << format( |
684 | Fmt: "0x%02x%02x ; stp x%u, x%u, [sp, #-%u]!\n" , |
685 | Vals: OC[Offset], Vals: OC[Offset + 1], Vals: Reg, |
686 | Vals: Reg + 1, Vals: Off); |
687 | else |
688 | SW.startLine() << format( |
689 | Fmt: "0x%02x%02x ; ldp x%u, x%u, [sp], #%u\n" , |
690 | Vals: OC[Offset], Vals: OC[Offset + 1], Vals: Reg, |
691 | Vals: Reg + 1, Vals: Off); |
692 | Offset += 2; |
693 | return false; |
694 | } |
695 | |
696 | bool Decoder::opcode_save_reg(const uint8_t *OC, unsigned &Offset, |
697 | unsigned Length, bool Prologue) { |
698 | uint32_t Reg = (OC[Offset] & 0x03) << 8; |
699 | Reg |= (OC[Offset + 1] & 0xC0); |
700 | Reg >>= 6; |
701 | Reg += 19; |
702 | uint32_t Off = (OC[Offset + 1] & 0x3F) << 3; |
703 | SW.startLine() << format(Fmt: "0x%02x%02x ; %s x%u, [sp, #%u]\n" , |
704 | Vals: OC[Offset], Vals: OC[Offset + 1], |
705 | Vals: static_cast<const char *>(Prologue ? "str" : "ldr" ), |
706 | Vals: Reg, Vals: Off); |
707 | Offset += 2; |
708 | return false; |
709 | } |
710 | |
711 | bool Decoder::opcode_save_reg_x(const uint8_t *OC, unsigned &Offset, |
712 | unsigned Length, bool Prologue) { |
713 | uint32_t Reg = (OC[Offset] & 0x01) << 8; |
714 | Reg |= (OC[Offset + 1] & 0xE0); |
715 | Reg >>= 5; |
716 | Reg += 19; |
717 | uint32_t Off = ((OC[Offset + 1] & 0x1F) + 1) << 3; |
718 | if (Prologue) |
719 | SW.startLine() << format(Fmt: "0x%02x%02x ; str x%u, [sp, #-%u]!\n" , |
720 | Vals: OC[Offset], Vals: OC[Offset + 1], Vals: Reg, Vals: Off); |
721 | else |
722 | SW.startLine() << format(Fmt: "0x%02x%02x ; ldr x%u, [sp], #%u\n" , |
723 | Vals: OC[Offset], Vals: OC[Offset + 1], Vals: Reg, Vals: Off); |
724 | Offset += 2; |
725 | return false; |
726 | } |
727 | |
728 | bool Decoder::opcode_save_lrpair(const uint8_t *OC, unsigned &Offset, |
729 | unsigned Length, bool Prologue) { |
730 | uint32_t Reg = (OC[Offset] & 0x01) << 8; |
731 | Reg |= (OC[Offset + 1] & 0xC0); |
732 | Reg >>= 6; |
733 | Reg *= 2; |
734 | Reg += 19; |
735 | uint32_t Off = (OC[Offset + 1] & 0x3F) << 3; |
736 | SW.startLine() << format(Fmt: "0x%02x%02x ; %s x%u, lr, [sp, #%u]\n" , |
737 | Vals: OC[Offset], Vals: OC[Offset + 1], |
738 | Vals: static_cast<const char *>(Prologue ? "stp" : "ldp" ), |
739 | Vals: Reg, Vals: Off); |
740 | Offset += 2; |
741 | return false; |
742 | } |
743 | |
744 | bool Decoder::opcode_save_fregp(const uint8_t *OC, unsigned &Offset, |
745 | unsigned Length, bool Prologue) { |
746 | uint32_t Reg = (OC[Offset] & 0x01) << 8; |
747 | Reg |= (OC[Offset + 1] & 0xC0); |
748 | Reg >>= 6; |
749 | Reg += 8; |
750 | uint32_t Off = (OC[Offset + 1] & 0x3F) << 3; |
751 | SW.startLine() << format(Fmt: "0x%02x%02x ; %s d%u, d%u, [sp, #%u]\n" , |
752 | Vals: OC[Offset], Vals: OC[Offset + 1], |
753 | Vals: static_cast<const char *>(Prologue ? "stp" : "ldp" ), |
754 | Vals: Reg, Vals: Reg + 1, Vals: Off); |
755 | Offset += 2; |
756 | return false; |
757 | } |
758 | |
759 | bool Decoder::opcode_save_fregp_x(const uint8_t *OC, unsigned &Offset, |
760 | unsigned Length, bool Prologue) { |
761 | uint32_t Reg = (OC[Offset] & 0x01) << 8; |
762 | Reg |= (OC[Offset + 1] & 0xC0); |
763 | Reg >>= 6; |
764 | Reg += 8; |
765 | uint32_t Off = ((OC[Offset + 1] & 0x3F) + 1) << 3; |
766 | if (Prologue) |
767 | SW.startLine() << format( |
768 | Fmt: "0x%02x%02x ; stp d%u, d%u, [sp, #-%u]!\n" , Vals: OC[Offset], |
769 | Vals: OC[Offset + 1], Vals: Reg, Vals: Reg + 1, Vals: Off); |
770 | else |
771 | SW.startLine() << format( |
772 | Fmt: "0x%02x%02x ; ldp d%u, d%u, [sp], #%u\n" , Vals: OC[Offset], |
773 | Vals: OC[Offset + 1], Vals: Reg, Vals: Reg + 1, Vals: Off); |
774 | Offset += 2; |
775 | return false; |
776 | } |
777 | |
778 | bool Decoder::opcode_save_freg(const uint8_t *OC, unsigned &Offset, |
779 | unsigned Length, bool Prologue) { |
780 | uint32_t Reg = (OC[Offset] & 0x01) << 8; |
781 | Reg |= (OC[Offset + 1] & 0xC0); |
782 | Reg >>= 6; |
783 | Reg += 8; |
784 | uint32_t Off = (OC[Offset + 1] & 0x3F) << 3; |
785 | SW.startLine() << format(Fmt: "0x%02x%02x ; %s d%u, [sp, #%u]\n" , |
786 | Vals: OC[Offset], Vals: OC[Offset + 1], |
787 | Vals: static_cast<const char *>(Prologue ? "str" : "ldr" ), |
788 | Vals: Reg, Vals: Off); |
789 | Offset += 2; |
790 | return false; |
791 | } |
792 | |
793 | bool Decoder::opcode_save_freg_x(const uint8_t *OC, unsigned &Offset, |
794 | unsigned Length, bool Prologue) { |
795 | uint32_t Reg = ((OC[Offset + 1] & 0xE0) >> 5) + 8; |
796 | uint32_t Off = ((OC[Offset + 1] & 0x1F) + 1) << 3; |
797 | if (Prologue) |
798 | SW.startLine() << format( |
799 | Fmt: "0x%02x%02x ; str d%u, [sp, #-%u]!\n" , Vals: OC[Offset], |
800 | Vals: OC[Offset + 1], Vals: Reg, Vals: Off); |
801 | else |
802 | SW.startLine() << format( |
803 | Fmt: "0x%02x%02x ; ldr d%u, [sp], #%u\n" , Vals: OC[Offset], |
804 | Vals: OC[Offset + 1], Vals: Reg, Vals: Off); |
805 | Offset += 2; |
806 | return false; |
807 | } |
808 | |
809 | bool Decoder::opcode_alloc_z(const uint8_t *OC, unsigned &Offset, |
810 | unsigned Length, bool Prologue) { |
811 | unsigned Off = OC[Offset + 1]; |
812 | SW.startLine() << format(Fmt: "0x%02x%02x ; addvl sp, #%d\n" , |
813 | Vals: OC[Offset], Vals: OC[Offset + 1], |
814 | Vals: Prologue ? -(int)Off : (int)Off); |
815 | Offset += 2; |
816 | return false; |
817 | } |
818 | |
819 | bool Decoder::opcode_alloc_l(const uint8_t *OC, unsigned &Offset, |
820 | unsigned Length, bool Prologue) { |
821 | unsigned Off = |
822 | (OC[Offset + 1] << 16) | (OC[Offset + 2] << 8) | (OC[Offset + 3] << 0); |
823 | Off <<= 4; |
824 | SW.startLine() << format( |
825 | Fmt: "0x%02x%02x%02x%02x ; %s sp, #%u\n" , Vals: OC[Offset], Vals: OC[Offset + 1], |
826 | Vals: OC[Offset + 2], Vals: OC[Offset + 3], |
827 | Vals: static_cast<const char *>(Prologue ? "sub" : "add" ), Vals: Off); |
828 | Offset += 4; |
829 | return false; |
830 | } |
831 | |
832 | bool Decoder::opcode_setfp(const uint8_t *OC, unsigned &Offset, unsigned Length, |
833 | bool Prologue) { |
834 | SW.startLine() << format(Fmt: "0x%02x ; mov %s, %s\n" , Vals: OC[Offset], |
835 | Vals: static_cast<const char *>(Prologue ? "fp" : "sp" ), |
836 | Vals: static_cast<const char *>(Prologue ? "sp" : "fp" )); |
837 | ++Offset; |
838 | return false; |
839 | } |
840 | |
841 | bool Decoder::opcode_addfp(const uint8_t *OC, unsigned &Offset, unsigned Length, |
842 | bool Prologue) { |
843 | unsigned NumBytes = OC[Offset + 1] << 3; |
844 | SW.startLine() << format( |
845 | Fmt: "0x%02x%02x ; %s %s, %s, #%u\n" , Vals: OC[Offset], Vals: OC[Offset + 1], |
846 | Vals: static_cast<const char *>(Prologue ? "add" : "sub" ), |
847 | Vals: static_cast<const char *>(Prologue ? "fp" : "sp" ), |
848 | Vals: static_cast<const char *>(Prologue ? "sp" : "fp" ), Vals: NumBytes); |
849 | Offset += 2; |
850 | return false; |
851 | } |
852 | |
853 | bool Decoder::opcode_nop(const uint8_t *OC, unsigned &Offset, unsigned Length, |
854 | bool Prologue) { |
855 | SW.startLine() << format(Fmt: "0x%02x ; nop\n" , Vals: OC[Offset]); |
856 | ++Offset; |
857 | return false; |
858 | } |
859 | |
860 | bool Decoder::opcode_end(const uint8_t *OC, unsigned &Offset, unsigned Length, |
861 | bool Prologue) { |
862 | SW.startLine() << format(Fmt: "0x%02x ; end\n" , Vals: OC[Offset]); |
863 | ++Offset; |
864 | return true; |
865 | } |
866 | |
867 | bool Decoder::opcode_end_c(const uint8_t *OC, unsigned &Offset, unsigned Length, |
868 | bool Prologue) { |
869 | SW.startLine() << format(Fmt: "0x%02x ; end_c\n" , Vals: OC[Offset]); |
870 | ++Offset; |
871 | return false; |
872 | } |
873 | |
874 | bool Decoder::opcode_save_next(const uint8_t *OC, unsigned &Offset, |
875 | unsigned Length, bool Prologue) { |
876 | if (Prologue) |
877 | SW.startLine() << format(Fmt: "0x%02x ; save next\n" , Vals: OC[Offset]); |
878 | else |
879 | SW.startLine() << format(Fmt: "0x%02x ; restore next\n" , |
880 | Vals: OC[Offset]); |
881 | ++Offset; |
882 | return false; |
883 | } |
884 | |
885 | bool Decoder::opcode_e7(const uint8_t *OC, unsigned &Offset, unsigned Length, |
886 | bool Prologue) { |
887 | // The e7 opcode has unusual decoding rules; write out the logic. |
888 | if ((OC[Offset + 1] & 0x80) == 0x80) { |
889 | SW.getOStream() << "reserved encoding\n" ; |
890 | Offset += 3; |
891 | return false; |
892 | } |
893 | |
894 | if ((OC[Offset + 2] & 0xC0) == 0xC0) { |
895 | if ((OC[Offset + 1] & 0x10) == 0) |
896 | return opcode_save_zreg(Opcodes: OC, Offset, Length, Prologue); |
897 | return opcode_save_preg(Opcodes: OC, Offset, Length, Prologue); |
898 | } |
899 | |
900 | return opcode_save_any_reg(Opcodes: OC, Offset, Length, Prologue); |
901 | } |
902 | |
903 | bool Decoder::opcode_save_any_reg(const uint8_t *OC, unsigned &Offset, |
904 | unsigned Length, bool Prologue) { |
905 | // Whether the instruction has writeback |
906 | bool Writeback = (OC[Offset + 1] & 0x20) == 0x20; |
907 | // Whether the instruction is paired. (Paired instructions are required |
908 | // to save/restore adjacent registers.) |
909 | bool Paired = (OC[Offset + 1] & 0x40) == 0x40; |
910 | // The kind of register saved: |
911 | // - 0 is an x register |
912 | // - 1 is the low half of a q register |
913 | // - 2 is a whole q register |
914 | int RegKind = (OC[Offset + 2] & 0xC0) >> 6; |
915 | // Encoded register name (0 -> x0/q0, 1 -> x1/q1, etc.) |
916 | int Reg = OC[Offset + 1] & 0x1F; |
917 | // Encoded stack offset of load/store instruction; decoding varies by mode. |
918 | int StackOffset = OC[Offset + 2] & 0x3F; |
919 | if (Writeback) |
920 | StackOffset++; |
921 | if (!Writeback && !Paired && RegKind != 2) |
922 | StackOffset *= 8; |
923 | else |
924 | StackOffset *= 16; |
925 | |
926 | SW.startLine() << format(Fmt: "0x%02x%02x%02x ; " , Vals: OC[Offset], |
927 | Vals: OC[Offset + 1], Vals: OC[Offset + 2]); |
928 | |
929 | // Verify the encoding is in a form we understand. The high bit of the first |
930 | // byte, and mode 3 for the register kind are apparently reserved. The |
931 | // encoded register must refer to a valid register. |
932 | int MaxReg = 0x1F; |
933 | if (Paired) |
934 | --MaxReg; |
935 | if (RegKind == 0) |
936 | --MaxReg; |
937 | if ((OC[Offset + 1] & 0x80) == 0x80 || RegKind == 3 || Reg > MaxReg) { |
938 | SW.getOStream() << "invalid save_any_reg encoding\n" ; |
939 | Offset += 3; |
940 | return false; |
941 | } |
942 | |
943 | if (Paired) { |
944 | if (Prologue) |
945 | SW.getOStream() << "stp " ; |
946 | else |
947 | SW.getOStream() << "ldp " ; |
948 | } else { |
949 | if (Prologue) |
950 | SW.getOStream() << "str " ; |
951 | else |
952 | SW.getOStream() << "ldr " ; |
953 | } |
954 | |
955 | char RegChar = 'x'; |
956 | if (RegKind == 1) { |
957 | RegChar = 'd'; |
958 | } else if (RegKind == 2) { |
959 | RegChar = 'q'; |
960 | } |
961 | |
962 | if (Paired) |
963 | SW.getOStream() << format(Fmt: "%c%d, %c%d, " , Vals: RegChar, Vals: Reg, Vals: RegChar, Vals: Reg + 1); |
964 | else |
965 | SW.getOStream() << format(Fmt: "%c%d, " , Vals: RegChar, Vals: Reg); |
966 | |
967 | if (Writeback) { |
968 | if (Prologue) |
969 | SW.getOStream() << format(Fmt: "[sp, #-%d]!\n" , Vals: StackOffset); |
970 | else |
971 | SW.getOStream() << format(Fmt: "[sp], #%d\n" , Vals: StackOffset); |
972 | } else { |
973 | SW.getOStream() << format(Fmt: "[sp, #%d]\n" , Vals: StackOffset); |
974 | } |
975 | |
976 | Offset += 3; |
977 | return false; |
978 | } |
979 | |
980 | bool Decoder::opcode_save_zreg(const uint8_t *OC, unsigned &Offset, |
981 | unsigned Length, bool Prologue) { |
982 | uint32_t Reg = (OC[Offset + 1] & 0x0F) + 8; |
983 | uint32_t Off = ((OC[Offset + 1] & 0x60) << 1) | (OC[Offset + 2] & 0x3F); |
984 | SW.startLine() << format( |
985 | Fmt: "0x%02x%02x%02x ; %s z%u, [sp, #%u, mul vl]\n" , Vals: OC[Offset], |
986 | Vals: OC[Offset + 1], Vals: OC[Offset + 2], |
987 | Vals: static_cast<const char *>(Prologue ? "str" : "ldr" ), Vals: Reg, Vals: Off); |
988 | Offset += 3; |
989 | return false; |
990 | } |
991 | |
992 | bool Decoder::opcode_save_preg(const uint8_t *OC, unsigned &Offset, |
993 | unsigned Length, bool Prologue) { |
994 | uint32_t Reg = (OC[Offset + 1] & 0x0F); |
995 | uint32_t Off = ((OC[Offset + 1] & 0x60) << 1) | (OC[Offset + 2] & 0x3F); |
996 | SW.startLine() << format( |
997 | Fmt: "0x%02x%02x%02x ; %s p%u, [sp, #%u, mul vl]\n" , Vals: OC[Offset], |
998 | Vals: OC[Offset + 1], Vals: OC[Offset + 2], |
999 | Vals: static_cast<const char *>(Prologue ? "str" : "ldr" ), Vals: Reg, Vals: Off); |
1000 | Offset += 3; |
1001 | return false; |
1002 | } |
1003 | |
1004 | bool Decoder::opcode_trap_frame(const uint8_t *OC, unsigned &Offset, |
1005 | unsigned Length, bool Prologue) { |
1006 | SW.startLine() << format(Fmt: "0x%02x ; trap frame\n" , Vals: OC[Offset]); |
1007 | ++Offset; |
1008 | return false; |
1009 | } |
1010 | |
1011 | bool Decoder::opcode_machine_frame(const uint8_t *OC, unsigned &Offset, |
1012 | unsigned Length, bool Prologue) { |
1013 | SW.startLine() << format(Fmt: "0x%02x ; machine frame\n" , |
1014 | Vals: OC[Offset]); |
1015 | ++Offset; |
1016 | return false; |
1017 | } |
1018 | |
1019 | bool Decoder::opcode_context(const uint8_t *OC, unsigned &Offset, |
1020 | unsigned Length, bool Prologue) { |
1021 | SW.startLine() << format(Fmt: "0x%02x ; context\n" , Vals: OC[Offset]); |
1022 | ++Offset; |
1023 | return false; |
1024 | } |
1025 | |
1026 | bool Decoder::opcode_ec_context(const uint8_t *OC, unsigned &Offset, |
1027 | unsigned Length, bool Prologue) { |
1028 | SW.startLine() << format(Fmt: "0x%02x ; EC context\n" , Vals: OC[Offset]); |
1029 | ++Offset; |
1030 | return false; |
1031 | } |
1032 | |
1033 | bool Decoder::opcode_clear_unwound_to_call(const uint8_t *OC, unsigned &Offset, |
1034 | unsigned Length, bool Prologue) { |
1035 | SW.startLine() << format(Fmt: "0x%02x ; clear unwound to call\n" , |
1036 | Vals: OC[Offset]); |
1037 | ++Offset; |
1038 | return false; |
1039 | } |
1040 | |
1041 | bool Decoder::opcode_pac_sign_lr(const uint8_t *OC, unsigned &Offset, |
1042 | unsigned Length, bool Prologue) { |
1043 | if (Prologue) |
1044 | SW.startLine() << format(Fmt: "0x%02x ; pacibsp\n" , Vals: OC[Offset]); |
1045 | else |
1046 | SW.startLine() << format(Fmt: "0x%02x ; autibsp\n" , Vals: OC[Offset]); |
1047 | ++Offset; |
1048 | return false; |
1049 | } |
1050 | |
1051 | void Decoder::decodeOpcodes(ArrayRef<uint8_t> Opcodes, unsigned Offset, |
1052 | bool Prologue) { |
1053 | assert((!Prologue || Offset == 0) && "prologue should always use offset 0" ); |
1054 | const RingEntry* DecodeRing = isAArch64 ? Ring64 : Ring; |
1055 | bool Terminated = false; |
1056 | for (unsigned OI = Offset, OE = Opcodes.size(); !Terminated && OI < OE; ) { |
1057 | for (unsigned DI = 0;; ++DI) { |
1058 | if ((isAArch64 && (DI >= std::size(Ring64))) || |
1059 | (!isAArch64 && (DI >= std::size(Ring)))) { |
1060 | SW.startLine() << format(Fmt: "0x%02x ; Bad opcode!\n" , |
1061 | Vals: Opcodes.data()[OI]); |
1062 | ++OI; |
1063 | break; |
1064 | } |
1065 | |
1066 | if ((Opcodes[OI] & DecodeRing[DI].Mask) == DecodeRing[DI].Value) { |
1067 | if (OI + DecodeRing[DI].Length > OE) { |
1068 | SW.startLine() << format(Fmt: "Opcode 0x%02x goes past the unwind data\n" , |
1069 | Vals: Opcodes[OI]); |
1070 | OI += DecodeRing[DI].Length; |
1071 | break; |
1072 | } |
1073 | Terminated = |
1074 | (this->*DecodeRing[DI].Routine)(Opcodes.data(), OI, 0, Prologue); |
1075 | break; |
1076 | } |
1077 | } |
1078 | } |
1079 | } |
1080 | |
1081 | bool Decoder::dumpXDataRecord(const COFFObjectFile &COFF, |
1082 | const SectionRef &Section, |
1083 | uint64_t FunctionAddress, uint64_t VA) { |
1084 | ArrayRef<uint8_t> Contents; |
1085 | if (COFF.getSectionContents(Sec: COFF.getCOFFSection(Section), Res&: Contents)) |
1086 | return false; |
1087 | |
1088 | uint64_t SectionVA = Section.getAddress(); |
1089 | uint64_t Offset = VA - SectionVA; |
1090 | const ulittle32_t *Data = |
1091 | reinterpret_cast<const ulittle32_t *>(Contents.data() + Offset); |
1092 | |
1093 | // Sanity check to ensure that the .xdata header is present. |
1094 | // A header is one or two words, followed by at least one word to describe |
1095 | // the unwind codes. Applicable to both ARM and AArch64. |
1096 | if (Contents.size() - Offset < 8) |
1097 | reportFatalUsageError(reason: ".xdata must be at least 8 bytes in size" ); |
1098 | |
1099 | const ExceptionDataRecord XData(Data, isAArch64); |
1100 | DictScope XRS(SW, "ExceptionData" ); |
1101 | SW.printNumber(Label: "FunctionLength" , |
1102 | Value: isAArch64 ? XData.FunctionLengthInBytesAArch64() : |
1103 | XData.FunctionLengthInBytesARM()); |
1104 | SW.printNumber(Label: "Version" , Value: XData.Vers()); |
1105 | SW.printBoolean(Label: "ExceptionData" , Value: XData.X()); |
1106 | SW.printBoolean(Label: "EpiloguePacked" , Value: XData.E()); |
1107 | if (!isAArch64) |
1108 | SW.printBoolean(Label: "Fragment" , Value: XData.F()); |
1109 | SW.printNumber(Label: XData.E() ? "EpilogueOffset" : "EpilogueScopes" , |
1110 | Value: XData.EpilogueCount()); |
1111 | uint64_t ByteCodeLength = XData.CodeWords() * sizeof(uint32_t); |
1112 | SW.printNumber(Label: "ByteCodeLength" , Value: ByteCodeLength); |
1113 | |
1114 | if ((int64_t)(Contents.size() - Offset - 4 * HeaderWords(XR: XData) - |
1115 | (XData.E() ? 0 : XData.EpilogueCount() * 4) - |
1116 | (XData.X() ? 8 : 0)) < (int64_t)ByteCodeLength) { |
1117 | SW.flush(); |
1118 | reportFatalUsageError(reason: "Malformed unwind data" ); |
1119 | } |
1120 | |
1121 | if (XData.E()) { |
1122 | ArrayRef<uint8_t> UC = XData.UnwindByteCode(); |
1123 | { |
1124 | ListScope PS(SW, "Prologue" ); |
1125 | decodeOpcodes(Opcodes: UC, Offset: 0, /*Prologue=*/true); |
1126 | } |
1127 | if (XData.EpilogueCount()) { |
1128 | ListScope ES(SW, "Epilogue" ); |
1129 | decodeOpcodes(Opcodes: UC, Offset: XData.EpilogueCount(), /*Prologue=*/false); |
1130 | } |
1131 | } else { |
1132 | { |
1133 | ListScope PS(SW, "Prologue" ); |
1134 | decodeOpcodes(Opcodes: XData.UnwindByteCode(), Offset: 0, /*Prologue=*/true); |
1135 | } |
1136 | ArrayRef<ulittle32_t> EpilogueScopes = XData.EpilogueScopes(); |
1137 | ListScope ESS(SW, "EpilogueScopes" ); |
1138 | for (const EpilogueScope ES : EpilogueScopes) { |
1139 | DictScope ESES(SW, "EpilogueScope" ); |
1140 | SW.printNumber(Label: "StartOffset" , Value: ES.EpilogueStartOffset()); |
1141 | if (!isAArch64) |
1142 | SW.printNumber(Label: "Condition" , Value: ES.Condition()); |
1143 | SW.printNumber(Label: "EpilogueStartIndex" , |
1144 | Value: isAArch64 ? ES.EpilogueStartIndexAArch64() |
1145 | : ES.EpilogueStartIndexARM()); |
1146 | unsigned ReservedMask = isAArch64 ? 0xF : 0x3; |
1147 | if ((ES.ES >> 18) & ReservedMask) |
1148 | SW.printNumber(Label: "ReservedBits" , Value: (ES.ES >> 18) & ReservedMask); |
1149 | |
1150 | ListScope Opcodes(SW, "Opcodes" ); |
1151 | decodeOpcodes(Opcodes: XData.UnwindByteCode(), |
1152 | Offset: isAArch64 ? ES.EpilogueStartIndexAArch64() |
1153 | : ES.EpilogueStartIndexARM(), |
1154 | /*Prologue=*/false); |
1155 | } |
1156 | } |
1157 | |
1158 | if (XData.X()) { |
1159 | const uint32_t Parameter = XData.ExceptionHandlerParameter(); |
1160 | const size_t HandlerOffset = HeaderWords(XR: XData) + |
1161 | (XData.E() ? 0 : XData.EpilogueCount()) + |
1162 | XData.CodeWords(); |
1163 | |
1164 | uint64_t Address, SymbolOffset; |
1165 | ErrorOr<SymbolRef> Symbol = getSymbolForLocation( |
1166 | COFF, Section, OffsetInSection: Offset + HandlerOffset * sizeof(uint32_t), |
1167 | ImmediateOffset: XData.ExceptionHandlerRVA(), SymbolAddress&: Address, SymbolOffset, |
1168 | /*FunctionOnly=*/true); |
1169 | if (!Symbol) { |
1170 | ListScope EHS(SW, "ExceptionHandler" ); |
1171 | SW.printHex(Label: "Routine" , Value: Address); |
1172 | SW.printHex(Label: "Parameter" , Value: Parameter); |
1173 | return true; |
1174 | } |
1175 | |
1176 | Expected<StringRef> Name = Symbol->getName(); |
1177 | if (!Name) { |
1178 | std::string Buf; |
1179 | llvm::raw_string_ostream OS(Buf); |
1180 | logAllUnhandledErrors(E: Name.takeError(), OS); |
1181 | reportFatalUsageError(reason: Twine(Buf)); |
1182 | } |
1183 | |
1184 | ListScope EHS(SW, "ExceptionHandler" ); |
1185 | SW.printString(Label: "Routine" , Value: formatSymbol(Name: *Name, Address, Offset: SymbolOffset)); |
1186 | SW.printHex(Label: "Parameter" , Value: Parameter); |
1187 | } |
1188 | |
1189 | return true; |
1190 | } |
1191 | |
1192 | bool Decoder::dumpUnpackedEntry(const COFFObjectFile &COFF, |
1193 | const SectionRef Section, uint64_t Offset, |
1194 | unsigned Index, const RuntimeFunction &RF) { |
1195 | assert(RF.Flag() == RuntimeFunctionFlag::RFF_Unpacked && |
1196 | "packed entry cannot be treated as an unpacked entry" ); |
1197 | |
1198 | uint64_t FunctionAddress, FunctionOffset; |
1199 | ErrorOr<SymbolRef> Function = getSymbolForLocation( |
1200 | COFF, Section, OffsetInSection: Offset, ImmediateOffset: RF.BeginAddress, SymbolAddress&: FunctionAddress, SymbolOffset&: FunctionOffset, |
1201 | /*FunctionOnly=*/true); |
1202 | |
1203 | uint64_t XDataAddress, XDataOffset; |
1204 | ErrorOr<SymbolRef> XDataRecord = getSymbolForLocation( |
1205 | COFF, Section, OffsetInSection: Offset + 4, ImmediateOffset: RF.ExceptionInformationRVA(), SymbolAddress&: XDataAddress, |
1206 | SymbolOffset&: XDataOffset); |
1207 | |
1208 | if (!RF.BeginAddress && !Function) |
1209 | return false; |
1210 | if (!RF.UnwindData && !XDataRecord) |
1211 | return false; |
1212 | |
1213 | StringRef FunctionName; |
1214 | if (Function) { |
1215 | Expected<StringRef> FunctionNameOrErr = Function->getName(); |
1216 | if (!FunctionNameOrErr) { |
1217 | std::string Buf; |
1218 | llvm::raw_string_ostream OS(Buf); |
1219 | logAllUnhandledErrors(E: FunctionNameOrErr.takeError(), OS); |
1220 | reportFatalUsageError(reason: Twine(Buf)); |
1221 | } |
1222 | FunctionName = *FunctionNameOrErr; |
1223 | } |
1224 | |
1225 | SW.printString(Label: "Function" , |
1226 | Value: formatSymbol(Name: FunctionName, Address: FunctionAddress, Offset: FunctionOffset)); |
1227 | |
1228 | if (XDataRecord) { |
1229 | Expected<StringRef> Name = XDataRecord->getName(); |
1230 | if (!Name) { |
1231 | std::string Buf; |
1232 | llvm::raw_string_ostream OS(Buf); |
1233 | logAllUnhandledErrors(E: Name.takeError(), OS); |
1234 | reportFatalUsageError(reason: Twine(Buf)); |
1235 | } |
1236 | |
1237 | SW.printString(Label: "ExceptionRecord" , |
1238 | Value: formatSymbol(Name: *Name, Address: XDataAddress, Offset: XDataOffset)); |
1239 | |
1240 | Expected<section_iterator> SIOrErr = XDataRecord->getSection(); |
1241 | if (!SIOrErr) { |
1242 | // TODO: Actually report errors helpfully. |
1243 | consumeError(Err: SIOrErr.takeError()); |
1244 | return false; |
1245 | } |
1246 | section_iterator SI = *SIOrErr; |
1247 | |
1248 | return dumpXDataRecord(COFF, Section: *SI, FunctionAddress, VA: XDataAddress); |
1249 | } else { |
1250 | SW.printString(Label: "ExceptionRecord" , Value: formatSymbol(Name: "" , Address: XDataAddress)); |
1251 | |
1252 | ErrorOr<SectionRef> Section = getSectionContaining(COFF, VA: XDataAddress); |
1253 | if (!Section) |
1254 | return false; |
1255 | |
1256 | return dumpXDataRecord(COFF, Section: *Section, FunctionAddress, VA: XDataAddress); |
1257 | } |
1258 | } |
1259 | |
1260 | bool Decoder::dumpPackedEntry(const object::COFFObjectFile &COFF, |
1261 | const SectionRef Section, uint64_t Offset, |
1262 | unsigned Index, const RuntimeFunction &RF) { |
1263 | assert((RF.Flag() == RuntimeFunctionFlag::RFF_Packed || |
1264 | RF.Flag() == RuntimeFunctionFlag::RFF_PackedFragment) && |
1265 | "unpacked entry cannot be treated as a packed entry" ); |
1266 | |
1267 | uint64_t FunctionAddress, FunctionOffset; |
1268 | ErrorOr<SymbolRef> Function = getSymbolForLocation( |
1269 | COFF, Section, OffsetInSection: Offset, ImmediateOffset: RF.BeginAddress, SymbolAddress&: FunctionAddress, SymbolOffset&: FunctionOffset, |
1270 | /*FunctionOnly=*/true); |
1271 | |
1272 | StringRef FunctionName; |
1273 | if (Function) { |
1274 | Expected<StringRef> FunctionNameOrErr = Function->getName(); |
1275 | if (!FunctionNameOrErr) { |
1276 | std::string Buf; |
1277 | llvm::raw_string_ostream OS(Buf); |
1278 | logAllUnhandledErrors(E: FunctionNameOrErr.takeError(), OS); |
1279 | reportFatalUsageError(reason: Twine(Buf)); |
1280 | } |
1281 | FunctionName = *FunctionNameOrErr; |
1282 | } |
1283 | |
1284 | SW.printString(Label: "Function" , |
1285 | Value: formatSymbol(Name: FunctionName, Address: FunctionAddress, Offset: FunctionOffset)); |
1286 | SW.printBoolean(Label: "Fragment" , |
1287 | Value: RF.Flag() == RuntimeFunctionFlag::RFF_PackedFragment); |
1288 | SW.printNumber(Label: "FunctionLength" , Value: RF.FunctionLength()); |
1289 | SW.startLine() << "ReturnType: " << RF.Ret() << '\n'; |
1290 | SW.printBoolean(Label: "HomedParameters" , Value: RF.H()); |
1291 | SW.printNumber(Label: "Reg" , Value: RF.Reg()); |
1292 | SW.printNumber(Label: "R" , Value: RF.R()); |
1293 | SW.printBoolean(Label: "LinkRegister" , Value: RF.L()); |
1294 | SW.printBoolean(Label: "Chaining" , Value: RF.C()); |
1295 | SW.printNumber(Label: "StackAdjustment" , Value: StackAdjustment(RF) << 2); |
1296 | |
1297 | { |
1298 | ListScope PS(SW, "Prologue" ); |
1299 | |
1300 | uint16_t GPRMask, VFPMask; |
1301 | std::tie(args&: GPRMask, args&: VFPMask) = SavedRegisterMask(RF, /*Prologue=*/true); |
1302 | |
1303 | if (StackAdjustment(RF) && !PrologueFolding(RF)) |
1304 | SW.startLine() << "sub sp, sp, #" << StackAdjustment(RF) * 4 << "\n" ; |
1305 | if (VFPMask) { |
1306 | SW.startLine() << "vpush " ; |
1307 | printVFPMask(VFPMask); |
1308 | OS << "\n" ; |
1309 | } |
1310 | if (RF.C()) { |
1311 | // Count the number of registers pushed below R11 |
1312 | int FpOffset = 4 * llvm::popcount(Value: GPRMask & ((1U << 11) - 1)); |
1313 | if (FpOffset) |
1314 | SW.startLine() << "add.w r11, sp, #" << FpOffset << "\n" ; |
1315 | else |
1316 | SW.startLine() << "mov r11, sp\n" ; |
1317 | } |
1318 | if (GPRMask) { |
1319 | SW.startLine() << "push " ; |
1320 | printGPRMask(GPRMask); |
1321 | OS << "\n" ; |
1322 | } |
1323 | if (RF.H()) |
1324 | SW.startLine() << "push {r0-r3}\n" ; |
1325 | } |
1326 | |
1327 | if (RF.Ret() != ReturnType::RT_NoEpilogue) { |
1328 | ListScope PS(SW, "Epilogue" ); |
1329 | |
1330 | uint16_t GPRMask, VFPMask; |
1331 | std::tie(args&: GPRMask, args&: VFPMask) = SavedRegisterMask(RF, /*Prologue=*/false); |
1332 | |
1333 | if (StackAdjustment(RF) && !EpilogueFolding(RF)) |
1334 | SW.startLine() << "add sp, sp, #" << StackAdjustment(RF) * 4 << "\n" ; |
1335 | if (VFPMask) { |
1336 | SW.startLine() << "vpop " ; |
1337 | printVFPMask(VFPMask); |
1338 | OS << "\n" ; |
1339 | } |
1340 | if (GPRMask) { |
1341 | SW.startLine() << "pop " ; |
1342 | printGPRMask(GPRMask); |
1343 | OS << "\n" ; |
1344 | } |
1345 | if (RF.H()) { |
1346 | if (RF.L() == 0 || RF.Ret() != ReturnType::RT_POP) |
1347 | SW.startLine() << "add sp, sp, #16\n" ; |
1348 | else |
1349 | SW.startLine() << "ldr pc, [sp], #20\n" ; |
1350 | } |
1351 | if (RF.Ret() != ReturnType::RT_POP) |
1352 | SW.startLine() << RF.Ret() << '\n'; |
1353 | } |
1354 | |
1355 | return true; |
1356 | } |
1357 | |
1358 | bool Decoder::dumpPackedARM64Entry(const object::COFFObjectFile &COFF, |
1359 | const SectionRef Section, uint64_t Offset, |
1360 | unsigned Index, |
1361 | const RuntimeFunctionARM64 &RF) { |
1362 | assert((RF.Flag() == RuntimeFunctionFlag::RFF_Packed || |
1363 | RF.Flag() == RuntimeFunctionFlag::RFF_PackedFragment) && |
1364 | "unpacked entry cannot be treated as a packed entry" ); |
1365 | |
1366 | uint64_t FunctionAddress, FunctionOffset; |
1367 | ErrorOr<SymbolRef> Function = getSymbolForLocation( |
1368 | COFF, Section, OffsetInSection: Offset, ImmediateOffset: RF.BeginAddress, SymbolAddress&: FunctionAddress, SymbolOffset&: FunctionOffset, |
1369 | /*FunctionOnly=*/true); |
1370 | |
1371 | StringRef FunctionName; |
1372 | if (Function) { |
1373 | Expected<StringRef> FunctionNameOrErr = Function->getName(); |
1374 | if (!FunctionNameOrErr) { |
1375 | std::string Buf; |
1376 | llvm::raw_string_ostream OS(Buf); |
1377 | logAllUnhandledErrors(E: FunctionNameOrErr.takeError(), OS); |
1378 | reportFatalUsageError(reason: Twine(Buf)); |
1379 | } |
1380 | FunctionName = *FunctionNameOrErr; |
1381 | } |
1382 | |
1383 | SW.printString(Label: "Function" , |
1384 | Value: formatSymbol(Name: FunctionName, Address: FunctionAddress, Offset: FunctionOffset)); |
1385 | SW.printBoolean(Label: "Fragment" , |
1386 | Value: RF.Flag() == RuntimeFunctionFlag::RFF_PackedFragment); |
1387 | SW.printNumber(Label: "FunctionLength" , Value: RF.FunctionLength()); |
1388 | SW.printNumber(Label: "RegF" , Value: RF.RegF()); |
1389 | SW.printNumber(Label: "RegI" , Value: RF.RegI()); |
1390 | SW.printBoolean(Label: "HomedParameters" , Value: RF.H()); |
1391 | SW.printNumber(Label: "CR" , Value: RF.CR()); |
1392 | SW.printNumber(Label: "FrameSize" , Value: RF.FrameSize() << 4); |
1393 | ListScope PS(SW, "Prologue" ); |
1394 | |
1395 | // Synthesize the equivalent prologue according to the documentation |
1396 | // at https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling, |
1397 | // printed in reverse order compared to the docs, to match how prologues |
1398 | // are printed for the non-packed case. |
1399 | int IntSZ = 8 * RF.RegI(); |
1400 | if (RF.CR() == 1) |
1401 | IntSZ += 8; |
1402 | int FpSZ = 8 * RF.RegF(); |
1403 | if (RF.RegF()) |
1404 | FpSZ += 8; |
1405 | int SavSZ = (IntSZ + FpSZ + 8 * 8 * RF.H() + 0xf) & ~0xf; |
1406 | int LocSZ = (RF.FrameSize() << 4) - SavSZ; |
1407 | |
1408 | if (RF.CR() == 2 || RF.CR() == 3) { |
1409 | SW.startLine() << "mov x29, sp\n" ; |
1410 | if (LocSZ <= 512) { |
1411 | SW.startLine() << format(Fmt: "stp x29, lr, [sp, #-%d]!\n" , Vals: LocSZ); |
1412 | } else { |
1413 | SW.startLine() << "stp x29, lr, [sp, #0]\n" ; |
1414 | } |
1415 | } |
1416 | if (LocSZ > 4080) { |
1417 | SW.startLine() << format(Fmt: "sub sp, sp, #%d\n" , Vals: LocSZ - 4080); |
1418 | SW.startLine() << "sub sp, sp, #4080\n" ; |
1419 | } else if ((RF.CR() != 3 && RF.CR() != 2 && LocSZ > 0) || LocSZ > 512) { |
1420 | SW.startLine() << format(Fmt: "sub sp, sp, #%d\n" , Vals: LocSZ); |
1421 | } |
1422 | if (RF.H()) { |
1423 | SW.startLine() << format(Fmt: "stp x6, x7, [sp, #%d]\n" , Vals: SavSZ - 16); |
1424 | SW.startLine() << format(Fmt: "stp x4, x5, [sp, #%d]\n" , Vals: SavSZ - 32); |
1425 | SW.startLine() << format(Fmt: "stp x2, x3, [sp, #%d]\n" , Vals: SavSZ - 48); |
1426 | if (RF.RegI() > 0 || RF.RegF() > 0 || RF.CR() == 1) { |
1427 | SW.startLine() << format(Fmt: "stp x0, x1, [sp, #%d]\n" , Vals: SavSZ - 64); |
1428 | } else { |
1429 | // This case isn't documented; if neither RegI nor RegF nor CR=1 |
1430 | // have decremented the stack pointer by SavSZ, we need to do it here |
1431 | // (as the final stack adjustment of LocSZ excludes SavSZ). |
1432 | SW.startLine() << format(Fmt: "stp x0, x1, [sp, #-%d]!\n" , Vals: SavSZ); |
1433 | } |
1434 | } |
1435 | int FloatRegs = RF.RegF() > 0 ? RF.RegF() + 1 : 0; |
1436 | for (int I = (FloatRegs + 1) / 2 - 1; I >= 0; I--) { |
1437 | if (I == (FloatRegs + 1) / 2 - 1 && FloatRegs % 2 == 1) { |
1438 | // The last register, an odd register without a pair |
1439 | SW.startLine() << format(Fmt: "str d%d, [sp, #%d]\n" , Vals: 8 + 2 * I, |
1440 | Vals: IntSZ + 16 * I); |
1441 | } else if (I == 0 && RF.RegI() == 0 && RF.CR() != 1) { |
1442 | SW.startLine() << format(Fmt: "stp d%d, d%d, [sp, #-%d]!\n" , Vals: 8 + 2 * I, |
1443 | Vals: 8 + 2 * I + 1, Vals: SavSZ); |
1444 | } else { |
1445 | SW.startLine() << format(Fmt: "stp d%d, d%d, [sp, #%d]\n" , Vals: 8 + 2 * I, |
1446 | Vals: 8 + 2 * I + 1, Vals: IntSZ + 16 * I); |
1447 | } |
1448 | } |
1449 | if (RF.CR() == 1 && (RF.RegI() % 2) == 0) { |
1450 | if (RF.RegI() == 0) |
1451 | SW.startLine() << format(Fmt: "str lr, [sp, #-%d]!\n" , Vals: SavSZ); |
1452 | else |
1453 | SW.startLine() << format(Fmt: "str lr, [sp, #%d]\n" , Vals: IntSZ - 8); |
1454 | } |
1455 | for (int I = (RF.RegI() + 1) / 2 - 1; I >= 0; I--) { |
1456 | if (I == (RF.RegI() + 1) / 2 - 1 && RF.RegI() % 2 == 1) { |
1457 | // The last register, an odd register without a pair |
1458 | if (RF.CR() == 1) { |
1459 | if (I == 0) { // If this is the only register pair |
1460 | // CR=1 combined with RegI=1 doesn't map to a documented case; |
1461 | // it doesn't map to any regular unwind info opcode, and the |
1462 | // actual unwinder doesn't support it. |
1463 | SW.startLine() << "INVALID!\n" ; |
1464 | } else |
1465 | SW.startLine() << format(Fmt: "stp x%d, lr, [sp, #%d]\n" , Vals: 19 + 2 * I, |
1466 | Vals: 16 * I); |
1467 | } else { |
1468 | if (I == 0) |
1469 | SW.startLine() << format(Fmt: "str x%d, [sp, #-%d]!\n" , Vals: 19 + 2 * I, Vals: SavSZ); |
1470 | else |
1471 | SW.startLine() << format(Fmt: "str x%d, [sp, #%d]\n" , Vals: 19 + 2 * I, Vals: 16 * I); |
1472 | } |
1473 | } else if (I == 0) { |
1474 | // The first register pair |
1475 | SW.startLine() << format(Fmt: "stp x19, x20, [sp, #-%d]!\n" , Vals: SavSZ); |
1476 | } else { |
1477 | SW.startLine() << format(Fmt: "stp x%d, x%d, [sp, #%d]\n" , Vals: 19 + 2 * I, |
1478 | Vals: 19 + 2 * I + 1, Vals: 16 * I); |
1479 | } |
1480 | } |
1481 | // CR=2 is yet undocumented, see |
1482 | // https://github.com/MicrosoftDocs/cpp-docs/pull/4202 for upstream |
1483 | // progress on getting it documented. |
1484 | if (RF.CR() == 2) |
1485 | SW.startLine() << "pacibsp\n" ; |
1486 | SW.startLine() << "end\n" ; |
1487 | |
1488 | return true; |
1489 | } |
1490 | |
1491 | bool Decoder::dumpProcedureDataEntry(const COFFObjectFile &COFF, |
1492 | const SectionRef Section, unsigned Index, |
1493 | ArrayRef<uint8_t> Contents) { |
1494 | uint64_t Offset = PDataEntrySize * Index; |
1495 | const ulittle32_t *Data = |
1496 | reinterpret_cast<const ulittle32_t *>(Contents.data() + Offset); |
1497 | |
1498 | const RuntimeFunction Entry(Data); |
1499 | DictScope RFS(SW, "RuntimeFunction" ); |
1500 | if (Entry.Flag() == RuntimeFunctionFlag::RFF_Unpacked) |
1501 | return dumpUnpackedEntry(COFF, Section, Offset, Index, RF: Entry); |
1502 | if (isAArch64) { |
1503 | const RuntimeFunctionARM64 EntryARM64(Data); |
1504 | return dumpPackedARM64Entry(COFF, Section, Offset, Index, RF: EntryARM64); |
1505 | } |
1506 | return dumpPackedEntry(COFF, Section, Offset, Index, RF: Entry); |
1507 | } |
1508 | |
1509 | void Decoder::dumpProcedureData(const COFFObjectFile &COFF, |
1510 | const SectionRef Section) { |
1511 | ArrayRef<uint8_t> Contents; |
1512 | if (COFF.getSectionContents(Sec: COFF.getCOFFSection(Section), Res&: Contents)) |
1513 | return; |
1514 | |
1515 | if (Contents.size() % PDataEntrySize) { |
1516 | errs() << ".pdata content is not " << PDataEntrySize << "-byte aligned\n" ; |
1517 | return; |
1518 | } |
1519 | |
1520 | for (unsigned EI = 0, EE = Contents.size() / PDataEntrySize; EI < EE; ++EI) |
1521 | if (!dumpProcedureDataEntry(COFF, Section, Index: EI, Contents)) |
1522 | break; |
1523 | } |
1524 | |
1525 | Error Decoder::dumpProcedureData(const COFFObjectFile &COFF) { |
1526 | for (const auto &Section : COFF.sections()) { |
1527 | Expected<StringRef> NameOrErr = |
1528 | COFF.getSectionName(Sec: COFF.getCOFFSection(Section)); |
1529 | if (!NameOrErr) |
1530 | return NameOrErr.takeError(); |
1531 | |
1532 | if (NameOrErr->starts_with(Prefix: ".pdata" )) |
1533 | dumpProcedureData(COFF, Section); |
1534 | } |
1535 | return Error::success(); |
1536 | } |
1537 | } |
1538 | } |
1539 | } |
1540 | |