LLVM 23.0.0git
Win64EH.cpp
Go to the documentation of this file.
1//===-- Win64EH.cpp - Win64 EH V3 Support -----------------------*- 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// This file implements decoding helpers for V3 unwind information on Win64.
10//
11//===----------------------------------------------------------------------===//
12
14#include "llvm/Support/Endian.h"
15#include "llvm/Support/Error.h"
17
18using namespace llvm;
19using namespace llvm::Win64EH;
20
22 static const char *const Names[] = {
23 "RAX", "RCX", "RDX", "RBX", "RSP", "RBP", "RSI", "RDI",
24 "R8", "R9", "R10", "R11", "R12", "R13", "R14", "R15",
25 "R16", "R17", "R18", "R19", "R20", "R21", "R22", "R23",
26 "R24", "R25", "R26", "R27", "R28", "R29", "R30", "R31",
27 };
28 if (Reg >= std::size(Names))
29 return "<invalid>";
30 return Names[Reg];
31}
32
34 unsigned Offset) {
35 if (Offset >= Pool.size())
36 return createStringError("WOD pool overflow at offset %u", Offset);
37
38 uint8_t FirstByte = Pool[Offset];
39 DecodedWOD W = {};
40
41 // Determine opcode from variable-width prefix encoding.
42 // The dispatch order matters: check shorter prefixes first since they
43 // occupy the lowest bits, then fall through to longer prefixes.
44 // 3-bit prefix (bits [2:0] >= 4): opcodes 4-7
45 // 4-bit prefix (bits [3:0] >= 8): opcodes 8-10
46 // 6-bit prefix (bits [5:0] == 0x20): opcode 32 (PUSH2)
47 // 8-bit prefix (full byte 0-3): opcodes 0-3
48 uint8_t Low3 = FirstByte & 0x07;
49
50 // 3-bit opcode: bits [2:0] in {4, 5, 6, 7}
51 if (Low3 >= 4) {
52 switch (Low3) {
53 case WOD_PUSH: {
54 W.Opcode = WOD_PUSH;
55 W.ByteSize = 1;
56 W.Register = (FirstByte >> 3) & 0x1F; // 5-bit register
57 return W;
58 }
60 W.Opcode = WOD_SAVE_NONVOL_FAR;
61 W.ByteSize = 5;
62 if (Offset + 5 > Pool.size())
63 return createStringError("WOD_SAVE_NONVOL_FAR truncated at offset %u",
64 Offset);
65 W.Register = (FirstByte >> 3) & 0x1F;
66 W.Displacement = support::endian::read32le(&Pool[Offset + 1]);
67 return W;
68 }
69 case WOD_SAVE_NONVOL: {
70 W.Opcode = WOD_SAVE_NONVOL;
71 W.ByteSize = 3;
72 if (Offset + 3 > Pool.size())
73 return createStringError("WOD_SAVE_NONVOL truncated at offset %u",
74 Offset);
75 W.Register = (FirstByte >> 3) & 0x1F;
76 W.Displacement =
78 return W;
79 }
81 W.Opcode = WOD_PUSH_CONSECUTIVE_2;
82 W.ByteSize = 1;
83 W.Register = (FirstByte >> 3) & 0x1F;
84 if (W.Register > 30)
85 return createStringError(
86 "WOD_PUSH_CONSECUTIVE_2 Register=%u out of range [0,30] at pool "
87 "offset %u",
88 W.Register, Offset);
89 return W;
90 }
91 default:
92 return createStringError("unknown WOD opcode 0x%02X at pool offset %u",
93 FirstByte, Offset);
94 }
95 }
96
97 // 4-bit opcode: bits [3:0] in {8, 9, 10, ...}
98 uint8_t Low4 = FirstByte & 0x0F;
99 if (Low4 >= 8) {
100 switch (Low4) {
101 case WOD_ALLOC_SMALL: {
102 W.Opcode = WOD_ALLOC_SMALL;
103 W.ByteSize = 1;
104 W.Size = (unsigned)(((FirstByte >> 4) & 0x0F) + 1) * 8;
105 return W;
106 }
107 case WOD_SAVE_XMM128_FAR: {
108 W.Opcode = WOD_SAVE_XMM128_FAR;
109 W.ByteSize = 5;
110 if (Offset + 5 > Pool.size())
111 return createStringError("WOD_SAVE_XMM128_FAR truncated at offset %u",
112 Offset);
113 W.Register = (FirstByte >> 4) & 0x0F;
114 W.Displacement = support::endian::read32le(&Pool[Offset + 1]);
115 return W;
116 }
117 case WOD_SAVE_XMM128: {
118 W.Opcode = WOD_SAVE_XMM128;
119 W.ByteSize = 3;
120 if (Offset + 3 > Pool.size())
121 return createStringError("WOD_SAVE_XMM128 truncated at offset %u",
122 Offset);
123 W.Register = (FirstByte >> 4) & 0x0F;
124 W.Displacement =
125 (uint32_t)support::endian::read16le(&Pool[Offset + 1]) * 16;
126 return W;
127 }
128 default:
129 return createStringError("unknown WOD opcode 0x%02X at pool offset %u",
130 FirstByte, Offset);
131 }
132 }
133
134 // 6-bit opcode: bits [5:0] == 0x20 (WOD_PUSH2)
135 uint8_t Low6 = FirstByte & 0x3F;
136 if (Low6 == WOD_PUSH2) {
137 W.Opcode = WOD_PUSH2;
138 W.ByteSize = 2;
139 if (Offset + 2 > Pool.size())
140 return createStringError("WOD_PUSH2 truncated at offset %u", Offset);
141 uint8_t SecondByte = Pool[Offset + 1];
142 // First reg from bits [7:6] of first byte (2 bits) and bits [2:0] of second
143 // (3 bits)
144 W.Register = ((FirstByte >> 6) & 0x03) | ((SecondByte & 0x07) << 2);
145 W.Register2 = (SecondByte >> 3) & 0x1F;
146 return W;
147 }
148
149 // 8-bit opcode: full byte is opcode (values 0-3)
150 switch (FirstByte) {
151 case WOD_SET_FPREG: {
152 W.Opcode = WOD_SET_FPREG;
153 W.ByteSize = 2;
154 if (Offset + 2 > Pool.size())
155 return createStringError("WOD_SET_FPREG truncated at offset %u", Offset);
156 uint8_t SecondByte = Pool[Offset + 1];
157 W.Register = SecondByte & 0x0F; // 4-bit register
158 W.Displacement = (unsigned)((SecondByte >> 4) & 0x0F) * 16;
159 return W;
160 }
161 case WOD_ALLOC_HUGE: {
162 W.Opcode = WOD_ALLOC_HUGE;
163 W.ByteSize = 5;
164 if (Offset + 5 > Pool.size())
165 return createStringError("WOD_ALLOC_HUGE truncated at offset %u", Offset);
166 W.Size = support::endian::read32le(&Pool[Offset + 1]);
167 return W;
168 }
169 case WOD_ALLOC_LARGE: {
170 W.Opcode = WOD_ALLOC_LARGE;
171 W.ByteSize = 3;
172 if (Offset + 3 > Pool.size())
173 return createStringError("WOD_ALLOC_LARGE truncated at offset %u",
174 Offset);
175 W.Size = (uint32_t)support::endian::read16le(&Pool[Offset + 1]) * 8;
176 return W;
177 }
179 W.Opcode = WOD_PUSH_CANONICAL_FRAME;
180 W.ByteSize = 2;
181 if (Offset + 2 > Pool.size())
182 return createStringError(
183 "WOD_PUSH_CANONICAL_FRAME truncated at offset %u", Offset);
184 W.Type = Pool[Offset + 1];
185 return W;
186 }
187 default:
188 return createStringError("unknown WOD opcode 0x%02X at pool offset %u",
189 FirstByte, Offset);
190 }
191}
192
195 if (Data.size() < 4)
196 return createStringError("V3 unwind info too short: %zu bytes",
197 Data.size());
198
200 Info.Version = Data[0] & 0x07;
201 Info.Flags = (Data[0] >> 3) & 0x1F;
202 Info.SizeOfProlog = Data[1];
203 Info.PayloadWords = Data[2];
204 Info.NumberOfOps = Data[3] & 0x1F;
205 Info.NumberOfEpilogs = (Data[3] >> 5) & 0x07;
206
207 // The fixed header is always 4 bytes. When UNW_FlagLarge is set, the first
208 // byte of the payload is the UNWIND_INFO_LARGE_V3 extension byte (which
209 // extends SizeOfProlog to 16 bits and widens prolog IP offset entries to
210 // 16 bits). That byte IS counted in PayloadWords.
211 unsigned Offset = 4; // Start of payload
212
213 // Compute the end of the payload area declared by PayloadWords. All
214 // subsequent reads of payload structures (the optional UNWIND_INFO_LARGE_V3
215 // byte, prolog IP offsets, epilog descriptors) must stay within this region;
216 // reading past it would either overflow the buffer or cross into the
217 // trailing handler/chain data, both of which indicate a malformed record.
218 unsigned PayloadEnd = 4 + Info.PayloadWords * 2;
219 if (PayloadEnd > Data.size())
220 return createStringError(
221 "V3 unwind info PayloadWords (%u) extends past end of buffer",
222 Info.PayloadWords);
223
224 bool IsLarge = Info.isLarge();
225 if (IsLarge) {
226 if (Offset >= PayloadEnd)
227 return createStringError(
228 "V3 unwind info with UNW_FlagLarge too short: PayloadWords (%u) "
229 "leaves no room for UNWIND_INFO_LARGE_V3",
230 Info.PayloadWords);
231 Info.SizeOfProlog |= static_cast<uint16_t>(Data[Offset]) << 8;
232 Offset += 1;
233 }
234
235 // Read prolog IP offsets (8-bit each, or 16-bit when LARGE)
236 for (unsigned I = 0; I < Info.NumberOfOps; ++I) {
237 if (IsLarge) {
238 if (Offset + 2 > PayloadEnd)
239 return createStringError(
240 "V3 payload truncated reading prolog IP offset %u", I);
241 Info.PrologIpOffsets.push_back(support::endian::read16le(&Data[Offset]));
242 Offset += 2;
243 } else {
244 if (Offset >= PayloadEnd)
245 return createStringError(
246 "V3 payload truncated reading prolog IP offset %u", I);
247 Info.PrologIpOffsets.push_back(Data[Offset++]);
248 }
249 }
250
251 // Read epilog descriptors
252 int32_t PrevResolvedOffset = 0;
253 for (unsigned I = 0; I < Info.NumberOfEpilogs; ++I) {
254 DecodedEpilogV3 Epi;
255 if (Offset >= PayloadEnd)
256 return createStringError(
257 "V3 payload truncated reading epilog %u FlagsAndNumOps", I);
258 uint8_t FlagsAndNumOps = Data[Offset++];
259 Epi.Flags = FlagsAndNumOps & 0x07;
260 Epi.NumberOfOps = (FlagsAndNumOps >> 3) & 0x1F;
261
262 if (Offset + 2 > PayloadEnd)
263 return createStringError(
264 "V3 payload truncated reading epilog %u EpilogOffset", I);
265 int16_t RawOffset =
266 static_cast<int16_t>(support::endian::read16le(&Data[Offset]));
267 Offset += 2;
268
269 // The first epilog's EpilogOffset is absolute (from fragment start or
270 // tail). Subsequent epilogs store a delta from the previous epilog's
271 // resolved position. Accumulate to resolve all to absolute.
272 if (I == 0)
273 Epi.EpilogOffset = RawOffset;
274 else
275 Epi.EpilogOffset = PrevResolvedOffset + RawOffset;
276 PrevResolvedOffset = Epi.EpilogOffset;
277
278 // Inherited descriptors (NumberOfOps == 0) are only 3 bytes:
279 // FlagsAndNumOps(1) + EpilogOffset(2). They have no FirstOp,
280 // IpOffsetOfLastInstruction, or IP offset fields appended; instead,
281 // the previous epilog's Flags bits 0 and 1, FirstOp,
282 // IpOffsetOfLastInstruction, and IP offset array are inherited.
283 //
284 // If this is the first epilog there is no previous descriptor to
285 // inherit from — the record is malformed. We leave the extended fields
286 // zero-initialized so callers can still see the (broken) header and
287 // EpilogOffset; downstream consumers (e.g. the dumpers) surface a
288 // warning when they encounter NumberOfOps == 0 at index 0.
289 if (Epi.NumberOfOps == 0) {
290 if (!Info.Epilogs.empty()) {
291 const DecodedEpilogV3 &Prev = Info.Epilogs.back();
292 // Flags bits 0 (EPILOG_INFO_PARENT_FRAGMENT_TRANSFER) and 1
293 // (EPILOG_INFO_LARGE) are inherited from the previous epilog; any
294 // bits present in this descriptor's own flags byte at those
295 // positions are ignored. Bit 2 (reserved) keeps its raw read value.
296 Epi.Flags = (Epi.Flags & uint8_t{0xFC}) | (Prev.Flags & uint8_t{0x03});
297 Epi.FirstOp = Prev.FirstOp;
299 Epi.IpOffsets = Prev.IpOffsets;
300 } else {
301 Epi.FirstOp = 0;
303 }
304 Info.Epilogs.push_back(std::move(Epi));
305 continue;
306 }
307
308 bool EpiLarge = Epi.isLarge();
309
310 if (Offset + 2 > PayloadEnd)
311 return createStringError("V3 payload truncated reading epilog %u FirstOp",
312 I);
314 Offset += 2;
315
316 // IpOffsetOfLastInstruction: 8-bit normally, 16-bit when EPILOG_INFO_LARGE
317 if (EpiLarge) {
318 if (Offset + 2 > PayloadEnd)
319 return createStringError(
320 "V3 payload truncated reading epilog %u IpOffsetOfLastInstruction",
321 I);
323 Offset += 2;
324 } else {
325 if (Offset >= PayloadEnd)
326 return createStringError(
327 "V3 payload truncated reading epilog %u IpOffsetOfLastInstruction",
328 I);
330 }
331
332 // Read epilog IP offsets (8-bit each, or 16-bit when EPILOG_INFO_LARGE)
333 for (unsigned J = 0; J < Epi.NumberOfOps; ++J) {
334 if (EpiLarge) {
335 if (Offset + 2 > PayloadEnd)
336 return createStringError(
337 "V3 payload truncated reading epilog %u IP offset %u", I, J);
339 Offset += 2;
340 } else {
341 if (Offset >= PayloadEnd)
342 return createStringError(
343 "V3 payload truncated reading epilog %u IP offset %u", I, J);
345 }
346 }
347
348 Info.Epilogs.push_back(std::move(Epi));
349 }
350
351 // Identify WOD pool: everything from current offset until the end of
352 // the payload area declared by PayloadWords.
353 if (Offset < PayloadEnd)
354 Info.WODPool = Data.slice(Offset, PayloadEnd - Offset);
355 else
356 Info.WODPool = ArrayRef<uint8_t>();
357
358 // When PayloadWords is odd, the encoder emits 2 trailing zero bytes inside
359 // the payload region as padding before the handler/chain. Report the
360 // aligned offset so consumers locate the next field correctly.
361 Info.PayloadSize = alignTo(PayloadEnd, 4);
362
363 return Info;
364}
#define I(x, y, z)
Definition MD5.cpp:57
Represent a constant reference to an array (0 or more elements consecutively in memory),...
Definition ArrayRef.h:40
size_t size() const
Get the array size.
Definition ArrayRef.h:141
Tagged union holding either a T or a Error.
Definition Error.h:485
void push_back(const T &Elt)
Represent a constant reference to a string, i.e.
Definition StringRef.h:56
StringRef getRegisterNameV3(unsigned Reg)
Return the register name for a 5-bit AMD64 integer register number.
Definition Win64EH.cpp:21
Expected< DecodedWOD > decodeWOD(ArrayRef< uint8_t > Pool, unsigned Offset)
Decode one WOD from the pool at the given byte offset.
Definition Win64EH.cpp:33
@ WOD_SAVE_XMM128_FAR
Definition Win64EH.h:251
@ WOD_PUSH_CANONICAL_FRAME
Definition Win64EH.h:245
@ WOD_PUSH_CONSECUTIVE_2
Definition Win64EH.h:249
@ WOD_SAVE_NONVOL_FAR
Definition Win64EH.h:247
Expected< DecodedUnwindInfoV3 > decodeUnwindInfoV3(ArrayRef< uint8_t > Data)
Parse a V3 UNWIND_INFO from raw bytes.
Definition Win64EH.cpp:194
uint16_t read16le(const void *P)
Definition Endian.h:429
uint32_t read32le(const void *P)
Definition Endian.h:432
This is an optimization pass for GlobalISel generic memory operations.
@ Offset
Definition DWP.cpp:558
Error createStringError(std::error_code EC, char const *Fmt, const Ts &... Vals)
Create formatted StringError object.
Definition Error.h:1321
constexpr uint64_t alignTo(uint64_t Size, Align A)
Returns a multiple of A needed to store Size bytes.
Definition Alignment.h:144
FunctionAddr VTableAddr uintptr_t uintptr_t Data
Definition InstrProf.h:221
Decoded V3 epilog descriptor.
Definition Win64EH.h:279
bool isLarge() const
Whether the EPILOG_INFO_LARGE flag is set.
Definition Win64EH.h:288
SmallVector< uint16_t, 8 > IpOffsets
Definition Win64EH.h:285
Decoded V3 UNWIND_INFO.
Definition Win64EH.h:292
Decoded V3 Winding Operation Descriptor.
Definition Win64EH.h:265