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 // Index of the most recent full descriptor (NumberOfOps != 0). Per the V3
254 // spec, a descriptor with NumberOfOps == 0 inherits its effective fields
255 // from the first *preceding* descriptor with NumberOfOps != 0, which is not
256 // necessarily the immediately preceding descriptor.
257 int BaseEpilogIdx = -1;
258 for (unsigned I = 0; I < Info.NumberOfEpilogs; ++I) {
259 DecodedEpilogV3 Epi;
260 if (Offset >= PayloadEnd)
261 return createStringError(
262 "V3 payload truncated reading epilog %u FlagsAndNumOps", I);
263 uint8_t FlagsAndNumOps = Data[Offset++];
264 Epi.Flags = FlagsAndNumOps & 0x07;
265 Epi.NumberOfOps = (FlagsAndNumOps >> 3) & 0x1F;
266
267 if (Offset + 2 > PayloadEnd)
268 return createStringError(
269 "V3 payload truncated reading epilog %u EpilogOffset", I);
270 int16_t RawOffset =
271 static_cast<int16_t>(support::endian::read16le(&Data[Offset]));
272 Offset += 2;
273
274 // The first epilog's EpilogOffset is absolute (from fragment start or
275 // tail). Subsequent epilogs store a delta from the previous epilog's
276 // resolved position. Accumulate to resolve all to absolute.
277 if (I == 0)
278 Epi.EpilogOffset = RawOffset;
279 else
280 Epi.EpilogOffset = PrevResolvedOffset + RawOffset;
281 PrevResolvedOffset = Epi.EpilogOffset;
282
283 // Inherited descriptors (NumberOfOps == 0) are only 3 bytes:
284 // FlagsAndNumOps(1) + EpilogOffset(2). They have no FirstOp,
285 // IpOffsetOfLastInstruction, or IP offset fields appended; instead, the
286 // effective NumberOfOps, FirstOp, IpOffsetOfLastInstruction, and IP offset
287 // array are inherited from the first preceding descriptor with
288 // NumberOfOps != 0 (the "base"). Per the V3 spec, Flags bits 0 and 1 are
289 // NOT inherited: the producer is required to replicate them into this
290 // record, so we keep the value read from this descriptor's own flags byte.
291 //
292 // If there is no preceding base descriptor to inherit from — the record is
293 // malformed. We leave the extended fields zero-initialized so callers can
294 // still see the (broken) header and EpilogOffset; downstream consumers
295 // (e.g. the dumpers) surface a warning when they encounter NumberOfOps == 0
296 // at index 0.
297 if (Epi.NumberOfOps == 0) {
298 if (BaseEpilogIdx >= 0) {
299 const DecodedEpilogV3 &Base = Info.Epilogs[BaseEpilogIdx];
300 // Flags bits 0 and 1 are producer-replicated, not inherited: a
301 // compliant producer must have written the base's values into this
302 // descriptor's own flags byte, so they should already match the base.
303 // We intentionally keep this record's own bits (rather than copying
304 // the base's) so a non-compliant producer is not silently masked. This
305 // decoder runs on potentially-malformed object files, so a mismatch is
306 // not asserted here; downstream consumers (the dumpers) surface a
307 // warning when the replicated bits disagree with the base.
308 Epi.FirstOp = Base.FirstOp;
309 Epi.IpOffsetOfLastInstruction = Base.IpOffsetOfLastInstruction;
310 Epi.IpOffsets = Base.IpOffsets;
311 } else {
312 Epi.FirstOp = 0;
314 }
315 Info.Epilogs.push_back(std::move(Epi));
316 continue;
317 }
318
319 bool EpiLarge = Epi.isLarge();
320
321 if (Offset + 2 > PayloadEnd)
322 return createStringError("V3 payload truncated reading epilog %u FirstOp",
323 I);
325 Offset += 2;
326
327 // IpOffsetOfLastInstruction: 8-bit normally, 16-bit when EPILOG_INFO_LARGE
328 if (EpiLarge) {
329 if (Offset + 2 > PayloadEnd)
330 return createStringError(
331 "V3 payload truncated reading epilog %u IpOffsetOfLastInstruction",
332 I);
334 Offset += 2;
335 } else {
336 if (Offset >= PayloadEnd)
337 return createStringError(
338 "V3 payload truncated reading epilog %u IpOffsetOfLastInstruction",
339 I);
341 }
342
343 // Read epilog IP offsets (8-bit each, or 16-bit when EPILOG_INFO_LARGE)
344 for (unsigned J = 0; J < Epi.NumberOfOps; ++J) {
345 if (EpiLarge) {
346 if (Offset + 2 > PayloadEnd)
347 return createStringError(
348 "V3 payload truncated reading epilog %u IP offset %u", I, J);
350 Offset += 2;
351 } else {
352 if (Offset >= PayloadEnd)
353 return createStringError(
354 "V3 payload truncated reading epilog %u IP offset %u", I, J);
356 }
357 }
358
359 // This is a full descriptor (NumberOfOps != 0); it becomes the base that
360 // subsequent inherited descriptors reference.
361 BaseEpilogIdx = Info.Epilogs.size();
362 Info.Epilogs.push_back(std::move(Epi));
363 }
364
365 // Identify WOD pool: everything from current offset until the end of
366 // the payload area declared by PayloadWords.
367 if (Offset < PayloadEnd)
368 Info.WODPool = Data.slice(Offset, PayloadEnd - Offset);
369 else
370 Info.WODPool = ArrayRef<uint8_t>();
371
372 // When PayloadWords is odd, the encoder emits 2 trailing zero bytes inside
373 // the payload region as padding before the handler/chain. Report the
374 // aligned offset so consumers locate the next field correctly.
375 Info.PayloadSize = alignTo(PayloadEnd, 4);
376
377 return Info;
378}
#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
LLVM_ABI StringRef getRegisterNameV3(unsigned Reg)
Return the register name for a 5-bit AMD64 integer register number.
Definition Win64EH.cpp:21
LLVM_ABI 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
LLVM_ABI 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