mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-25 20:23:11 +01:00
f2ee9eef1a
As the actual windows unwinder doesn't support this case, don't pretend that it is supported when dumping the generated unwind info either, even if it would be possible to interpret it as something sensible. This should reduce the risk of us emitting such a case in code (although it's unlikely as long as the unwind info is generated through the SEH opcodes, as the opcodes can't describe this case). Differential Revision: https://reviews.llvm.org/D91529
1302 lines
47 KiB
C++
1302 lines
47 KiB
C++
//===-- ARMWinEHPrinter.cpp - Windows on ARM EH Data Printer ----*- C++ -*-===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Windows on ARM uses a series of serialised data structures (RuntimeFunction)
|
|
// to create a table of information for unwinding. In order to conserve space,
|
|
// there are two different ways that this data is represented.
|
|
//
|
|
// For functions with canonical forms for the prologue and epilogue, the data
|
|
// can be stored in a "packed" form. In this case, the data is packed into the
|
|
// RuntimeFunction's remaining 30-bits and can fully describe the entire frame.
|
|
//
|
|
// +---------------------------------------+
|
|
// | Function Entry Address |
|
|
// +---------------------------------------+
|
|
// | Packed Form Data |
|
|
// +---------------------------------------+
|
|
//
|
|
// This layout is parsed by Decoder::dumpPackedEntry. No unwind bytecode is
|
|
// associated with such a frame as they can be derived from the provided data.
|
|
// The decoder does not synthesize this data as it is unnecessary for the
|
|
// purposes of validation, with the synthesis being required only by a proper
|
|
// unwinder.
|
|
//
|
|
// For functions that are large or do not match canonical forms, the data is
|
|
// split up into two portions, with the actual data residing in the "exception
|
|
// data" table (.xdata) with a reference to the entry from the "procedure data"
|
|
// (.pdata) entry.
|
|
//
|
|
// The exception data contains information about the frame setup, all of the
|
|
// epilogue scopes (for functions for which there are multiple exit points) and
|
|
// the associated exception handler. Additionally, the entry contains byte-code
|
|
// describing how to unwind the function (c.f. Decoder::decodeOpcodes).
|
|
//
|
|
// +---------------------------------------+
|
|
// | Function Entry Address |
|
|
// +---------------------------------------+
|
|
// | Exception Data Entry Address |
|
|
// +---------------------------------------+
|
|
//
|
|
// This layout is parsed by Decoder::dumpUnpackedEntry. Such an entry must
|
|
// first resolve the exception data entry address. This structure
|
|
// (ExceptionDataRecord) has a variable sized header
|
|
// (c.f. ARM::WinEH::HeaderWords) and encodes most of the same information as
|
|
// the packed form. However, because this information is insufficient to
|
|
// synthesize the unwinding, there are associated unwinding bytecode which make
|
|
// up the bulk of the Decoder.
|
|
//
|
|
// The decoder itself is table-driven, using the first byte to determine the
|
|
// opcode and dispatching to the associated printing routine. The bytecode
|
|
// itself is a variable length instruction encoding that can fully describe the
|
|
// state of the stack and the necessary operations for unwinding to the
|
|
// beginning of the frame.
|
|
//
|
|
// The byte-code maintains a 1-1 instruction mapping, indicating both the width
|
|
// of the instruction (Thumb2 instructions are variable length, 16 or 32 bits
|
|
// wide) allowing the program to unwind from any point in the prologue, body, or
|
|
// epilogue of the function.
|
|
|
|
#include "ARMWinEHPrinter.h"
|
|
#include "llvm/ADT/STLExtras.h"
|
|
#include "llvm/ADT/StringExtras.h"
|
|
#include "llvm/Support/ARMWinEH.h"
|
|
#include "llvm/Support/Format.h"
|
|
|
|
using namespace llvm;
|
|
using namespace llvm::object;
|
|
using namespace llvm::support;
|
|
|
|
namespace llvm {
|
|
raw_ostream &operator<<(raw_ostream &OS, const ARM::WinEH::ReturnType &RT) {
|
|
switch (RT) {
|
|
case ARM::WinEH::ReturnType::RT_POP:
|
|
OS << "pop {pc}";
|
|
break;
|
|
case ARM::WinEH::ReturnType::RT_B:
|
|
OS << "b target";
|
|
break;
|
|
case ARM::WinEH::ReturnType::RT_BW:
|
|
OS << "b.w target";
|
|
break;
|
|
case ARM::WinEH::ReturnType::RT_NoEpilogue:
|
|
OS << "(no epilogue)";
|
|
break;
|
|
}
|
|
return OS;
|
|
}
|
|
}
|
|
|
|
static std::string formatSymbol(StringRef Name, uint64_t Address,
|
|
uint64_t Offset = 0) {
|
|
std::string Buffer;
|
|
raw_string_ostream OS(Buffer);
|
|
|
|
if (!Name.empty())
|
|
OS << Name << " ";
|
|
|
|
if (Offset)
|
|
OS << format("+0x%X (0x%" PRIX64 ")", Offset, Address);
|
|
else if (!Name.empty())
|
|
OS << format("(0x%" PRIX64 ")", Address);
|
|
else
|
|
OS << format("0x%" PRIX64, Address);
|
|
|
|
return OS.str();
|
|
}
|
|
|
|
namespace llvm {
|
|
namespace ARM {
|
|
namespace WinEH {
|
|
const size_t Decoder::PDataEntrySize = sizeof(RuntimeFunction);
|
|
|
|
// TODO name the uops more appropriately
|
|
const Decoder::RingEntry Decoder::Ring[] = {
|
|
{ 0x80, 0x00, 1, &Decoder::opcode_0xxxxxxx }, // UOP_STACK_FREE (16-bit)
|
|
{ 0xc0, 0x80, 2, &Decoder::opcode_10Lxxxxx }, // UOP_POP (32-bit)
|
|
{ 0xf0, 0xc0, 1, &Decoder::opcode_1100xxxx }, // UOP_STACK_SAVE (16-bit)
|
|
{ 0xf8, 0xd0, 1, &Decoder::opcode_11010Lxx }, // UOP_POP (16-bit)
|
|
{ 0xf8, 0xd8, 1, &Decoder::opcode_11011Lxx }, // UOP_POP (32-bit)
|
|
{ 0xf8, 0xe0, 1, &Decoder::opcode_11100xxx }, // UOP_VPOP (32-bit)
|
|
{ 0xfc, 0xe8, 2, &Decoder::opcode_111010xx }, // UOP_STACK_FREE (32-bit)
|
|
{ 0xfe, 0xec, 2, &Decoder::opcode_1110110L }, // UOP_POP (16-bit)
|
|
{ 0xff, 0xee, 2, &Decoder::opcode_11101110 }, // UOP_MICROSOFT_SPECIFIC (16-bit)
|
|
// UOP_PUSH_MACHINE_FRAME
|
|
// UOP_PUSH_CONTEXT
|
|
// UOP_PUSH_TRAP_FRAME
|
|
// UOP_REDZONE_RESTORE_LR
|
|
{ 0xff, 0xef, 2, &Decoder::opcode_11101111 }, // UOP_LDRPC_POSTINC (32-bit)
|
|
{ 0xff, 0xf5, 2, &Decoder::opcode_11110101 }, // UOP_VPOP (32-bit)
|
|
{ 0xff, 0xf6, 2, &Decoder::opcode_11110110 }, // UOP_VPOP (32-bit)
|
|
{ 0xff, 0xf7, 3, &Decoder::opcode_11110111 }, // UOP_STACK_RESTORE (16-bit)
|
|
{ 0xff, 0xf8, 4, &Decoder::opcode_11111000 }, // UOP_STACK_RESTORE (16-bit)
|
|
{ 0xff, 0xf9, 3, &Decoder::opcode_11111001 }, // UOP_STACK_RESTORE (32-bit)
|
|
{ 0xff, 0xfa, 4, &Decoder::opcode_11111010 }, // UOP_STACK_RESTORE (32-bit)
|
|
{ 0xff, 0xfb, 1, &Decoder::opcode_11111011 }, // UOP_NOP (16-bit)
|
|
{ 0xff, 0xfc, 1, &Decoder::opcode_11111100 }, // UOP_NOP (32-bit)
|
|
{ 0xff, 0xfd, 1, &Decoder::opcode_11111101 }, // UOP_NOP (16-bit) / END
|
|
{ 0xff, 0xfe, 1, &Decoder::opcode_11111110 }, // UOP_NOP (32-bit) / END
|
|
{ 0xff, 0xff, 1, &Decoder::opcode_11111111 }, // UOP_END
|
|
};
|
|
|
|
|
|
// Unwind opcodes for ARM64.
|
|
// https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling
|
|
const Decoder::RingEntry Decoder::Ring64[] = {
|
|
{ 0xe0, 0x00, 1, &Decoder::opcode_alloc_s },
|
|
{ 0xe0, 0x20, 1, &Decoder::opcode_save_r19r20_x },
|
|
{ 0xc0, 0x40, 1, &Decoder::opcode_save_fplr },
|
|
{ 0xc0, 0x80, 1, &Decoder::opcode_save_fplr_x },
|
|
{ 0xf8, 0xc0, 2, &Decoder::opcode_alloc_m },
|
|
{ 0xfc, 0xc8, 2, &Decoder::opcode_save_regp },
|
|
{ 0xfc, 0xcc, 2, &Decoder::opcode_save_regp_x },
|
|
{ 0xfc, 0xd0, 2, &Decoder::opcode_save_reg },
|
|
{ 0xfe, 0xd4, 2, &Decoder::opcode_save_reg_x },
|
|
{ 0xfe, 0xd6, 2, &Decoder::opcode_save_lrpair },
|
|
{ 0xfe, 0xd8, 2, &Decoder::opcode_save_fregp },
|
|
{ 0xfe, 0xda, 2, &Decoder::opcode_save_fregp_x },
|
|
{ 0xfe, 0xdc, 2, &Decoder::opcode_save_freg },
|
|
{ 0xff, 0xde, 2, &Decoder::opcode_save_freg_x },
|
|
{ 0xff, 0xe0, 4, &Decoder::opcode_alloc_l },
|
|
{ 0xff, 0xe1, 1, &Decoder::opcode_setfp },
|
|
{ 0xff, 0xe2, 2, &Decoder::opcode_addfp },
|
|
{ 0xff, 0xe3, 1, &Decoder::opcode_nop },
|
|
{ 0xff, 0xe4, 1, &Decoder::opcode_end },
|
|
{ 0xff, 0xe5, 1, &Decoder::opcode_end_c },
|
|
{ 0xff, 0xe6, 1, &Decoder::opcode_save_next },
|
|
{ 0xff, 0xe8, 1, &Decoder::opcode_trap_frame },
|
|
{ 0xff, 0xe9, 1, &Decoder::opcode_machine_frame },
|
|
{ 0xff, 0xea, 1, &Decoder::opcode_context },
|
|
{ 0xff, 0xec, 1, &Decoder::opcode_clear_unwound_to_call },
|
|
};
|
|
|
|
void Decoder::printRegisters(const std::pair<uint16_t, uint32_t> &RegisterMask) {
|
|
static const char * const GPRRegisterNames[16] = {
|
|
"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10",
|
|
"r11", "ip", "sp", "lr", "pc",
|
|
};
|
|
|
|
const uint16_t GPRMask = std::get<0>(RegisterMask);
|
|
const uint16_t VFPMask = std::get<1>(RegisterMask);
|
|
|
|
OS << '{';
|
|
bool Comma = false;
|
|
for (unsigned RI = 0, RE = 11; RI < RE; ++RI) {
|
|
if (GPRMask & (1 << RI)) {
|
|
if (Comma)
|
|
OS << ", ";
|
|
OS << GPRRegisterNames[RI];
|
|
Comma = true;
|
|
}
|
|
}
|
|
for (unsigned RI = 0, RE = 32; RI < RE; ++RI) {
|
|
if (VFPMask & (1 << RI)) {
|
|
if (Comma)
|
|
OS << ", ";
|
|
OS << "d" << unsigned(RI);
|
|
Comma = true;
|
|
}
|
|
}
|
|
for (unsigned RI = 11, RE = 16; RI < RE; ++RI) {
|
|
if (GPRMask & (1 << RI)) {
|
|
if (Comma)
|
|
OS << ", ";
|
|
OS << GPRRegisterNames[RI];
|
|
Comma = true;
|
|
}
|
|
}
|
|
OS << '}';
|
|
}
|
|
|
|
ErrorOr<object::SectionRef>
|
|
Decoder::getSectionContaining(const COFFObjectFile &COFF, uint64_t VA) {
|
|
for (const auto &Section : COFF.sections()) {
|
|
uint64_t Address = Section.getAddress();
|
|
uint64_t Size = Section.getSize();
|
|
|
|
if (VA >= Address && (VA - Address) <= Size)
|
|
return Section;
|
|
}
|
|
return inconvertibleErrorCode();
|
|
}
|
|
|
|
ErrorOr<object::SymbolRef> Decoder::getSymbol(const COFFObjectFile &COFF,
|
|
uint64_t VA, bool FunctionOnly) {
|
|
for (const auto &Symbol : COFF.symbols()) {
|
|
Expected<SymbolRef::Type> Type = Symbol.getType();
|
|
if (!Type)
|
|
return errorToErrorCode(Type.takeError());
|
|
if (FunctionOnly && *Type != SymbolRef::ST_Function)
|
|
continue;
|
|
|
|
Expected<uint64_t> Address = Symbol.getAddress();
|
|
if (!Address)
|
|
return errorToErrorCode(Address.takeError());
|
|
if (*Address == VA)
|
|
return Symbol;
|
|
}
|
|
return inconvertibleErrorCode();
|
|
}
|
|
|
|
ErrorOr<SymbolRef> Decoder::getRelocatedSymbol(const COFFObjectFile &,
|
|
const SectionRef &Section,
|
|
uint64_t Offset) {
|
|
for (const auto &Relocation : Section.relocations()) {
|
|
uint64_t RelocationOffset = Relocation.getOffset();
|
|
if (RelocationOffset == Offset)
|
|
return *Relocation.getSymbol();
|
|
}
|
|
return inconvertibleErrorCode();
|
|
}
|
|
|
|
bool Decoder::opcode_0xxxxxxx(const uint8_t *OC, unsigned &Offset,
|
|
unsigned Length, bool Prologue) {
|
|
uint8_t Imm = OC[Offset] & 0x7f;
|
|
SW.startLine() << format("0x%02x ; %s sp, #(%u * 4)\n",
|
|
OC[Offset],
|
|
static_cast<const char *>(Prologue ? "sub" : "add"),
|
|
Imm);
|
|
++Offset;
|
|
return false;
|
|
}
|
|
|
|
bool Decoder::opcode_10Lxxxxx(const uint8_t *OC, unsigned &Offset,
|
|
unsigned Length, bool Prologue) {
|
|
unsigned Link = (OC[Offset] & 0x20) >> 5;
|
|
uint16_t RegisterMask = (Link << (Prologue ? 14 : 15))
|
|
| ((OC[Offset + 0] & 0x1f) << 8)
|
|
| ((OC[Offset + 1] & 0xff) << 0);
|
|
assert((~RegisterMask & (1 << 13)) && "sp must not be set");
|
|
assert((~RegisterMask & (1 << (Prologue ? 15 : 14))) && "pc must not be set");
|
|
|
|
SW.startLine() << format("0x%02x 0x%02x ; %s.w ",
|
|
OC[Offset + 0], OC[Offset + 1],
|
|
Prologue ? "push" : "pop");
|
|
printRegisters(std::make_pair(RegisterMask, 0));
|
|
OS << '\n';
|
|
|
|
Offset += 2;
|
|
return false;
|
|
}
|
|
|
|
bool Decoder::opcode_1100xxxx(const uint8_t *OC, unsigned &Offset,
|
|
unsigned Length, bool Prologue) {
|
|
if (Prologue)
|
|
SW.startLine() << format("0x%02x ; mov r%u, sp\n",
|
|
OC[Offset], OC[Offset] & 0xf);
|
|
else
|
|
SW.startLine() << format("0x%02x ; mov sp, r%u\n",
|
|
OC[Offset], OC[Offset] & 0xf);
|
|
++Offset;
|
|
return false;
|
|
}
|
|
|
|
bool Decoder::opcode_11010Lxx(const uint8_t *OC, unsigned &Offset,
|
|
unsigned Length, bool Prologue) {
|
|
unsigned Link = (OC[Offset] & 0x4) >> 3;
|
|
unsigned Count = (OC[Offset] & 0x3);
|
|
|
|
uint16_t GPRMask = (Link << (Prologue ? 14 : 15))
|
|
| (((1 << (Count + 1)) - 1) << 4);
|
|
|
|
SW.startLine() << format("0x%02x ; %s ", OC[Offset],
|
|
Prologue ? "push" : "pop");
|
|
printRegisters(std::make_pair(GPRMask, 0));
|
|
OS << '\n';
|
|
|
|
++Offset;
|
|
return false;
|
|
}
|
|
|
|
bool Decoder::opcode_11011Lxx(const uint8_t *OC, unsigned &Offset,
|
|
unsigned Length, bool Prologue) {
|
|
unsigned Link = (OC[Offset] & 0x4) >> 2;
|
|
unsigned Count = (OC[Offset] & 0x3) + 4;
|
|
|
|
uint16_t GPRMask = (Link << (Prologue ? 14 : 15))
|
|
| (((1 << (Count + 1)) - 1) << 4);
|
|
|
|
SW.startLine() << format("0x%02x ; %s.w ", OC[Offset],
|
|
Prologue ? "push" : "pop");
|
|
printRegisters(std::make_pair(GPRMask, 0));
|
|
OS << '\n';
|
|
|
|
++Offset;
|
|
return false;
|
|
}
|
|
|
|
bool Decoder::opcode_11100xxx(const uint8_t *OC, unsigned &Offset,
|
|
unsigned Length, bool Prologue) {
|
|
unsigned High = (OC[Offset] & 0x7);
|
|
uint32_t VFPMask = (((1 << (High + 1)) - 1) << 8);
|
|
|
|
SW.startLine() << format("0x%02x ; %s ", OC[Offset],
|
|
Prologue ? "vpush" : "vpop");
|
|
printRegisters(std::make_pair(0, VFPMask));
|
|
OS << '\n';
|
|
|
|
++Offset;
|
|
return false;
|
|
}
|
|
|
|
bool Decoder::opcode_111010xx(const uint8_t *OC, unsigned &Offset,
|
|
unsigned Length, bool Prologue) {
|
|
uint16_t Imm = ((OC[Offset + 0] & 0x03) << 8) | ((OC[Offset + 1] & 0xff) << 0);
|
|
|
|
SW.startLine() << format("0x%02x 0x%02x ; %s.w sp, #(%u * 4)\n",
|
|
OC[Offset + 0], OC[Offset + 1],
|
|
static_cast<const char *>(Prologue ? "sub" : "add"),
|
|
Imm);
|
|
|
|
Offset += 2;
|
|
return false;
|
|
}
|
|
|
|
bool Decoder::opcode_1110110L(const uint8_t *OC, unsigned &Offset,
|
|
unsigned Length, bool Prologue) {
|
|
uint8_t GPRMask = ((OC[Offset + 0] & 0x01) << (Prologue ? 14 : 15))
|
|
| ((OC[Offset + 1] & 0xff) << 0);
|
|
|
|
SW.startLine() << format("0x%02x 0x%02x ; %s ", OC[Offset + 0],
|
|
OC[Offset + 1], Prologue ? "push" : "pop");
|
|
printRegisters(std::make_pair(GPRMask, 0));
|
|
OS << '\n';
|
|
|
|
Offset += 2;
|
|
return false;
|
|
}
|
|
|
|
bool Decoder::opcode_11101110(const uint8_t *OC, unsigned &Offset,
|
|
unsigned Length, bool Prologue) {
|
|
assert(!Prologue && "may not be used in prologue");
|
|
|
|
if (OC[Offset + 1] & 0xf0)
|
|
SW.startLine() << format("0x%02x 0x%02x ; reserved\n",
|
|
OC[Offset + 0], OC[Offset + 1]);
|
|
else
|
|
SW.startLine()
|
|
<< format("0x%02x 0x%02x ; microsoft-specific (type: %u)\n",
|
|
OC[Offset + 0], OC[Offset + 1], OC[Offset + 1] & 0x0f);
|
|
|
|
Offset += 2;
|
|
return false;
|
|
}
|
|
|
|
bool Decoder::opcode_11101111(const uint8_t *OC, unsigned &Offset,
|
|
unsigned Length, bool Prologue) {
|
|
assert(!Prologue && "may not be used in prologue");
|
|
|
|
if (OC[Offset + 1] & 0xf0)
|
|
SW.startLine() << format("0x%02x 0x%02x ; reserved\n",
|
|
OC[Offset + 0], OC[Offset + 1]);
|
|
else
|
|
SW.startLine()
|
|
<< format("0x%02x 0x%02x ; ldr.w lr, [sp], #%u\n",
|
|
OC[Offset + 0], OC[Offset + 1], OC[Offset + 1] << 2);
|
|
|
|
Offset += 2;
|
|
return false;
|
|
}
|
|
|
|
bool Decoder::opcode_11110101(const uint8_t *OC, unsigned &Offset,
|
|
unsigned Length, bool Prologue) {
|
|
unsigned Start = (OC[Offset + 1] & 0xf0) >> 4;
|
|
unsigned End = (OC[Offset + 1] & 0x0f) >> 0;
|
|
uint32_t VFPMask = ((1 << (End - Start)) - 1) << Start;
|
|
|
|
SW.startLine() << format("0x%02x 0x%02x ; %s ", OC[Offset + 0],
|
|
OC[Offset + 1], Prologue ? "vpush" : "vpop");
|
|
printRegisters(std::make_pair(0, VFPMask));
|
|
OS << '\n';
|
|
|
|
Offset += 2;
|
|
return false;
|
|
}
|
|
|
|
bool Decoder::opcode_11110110(const uint8_t *OC, unsigned &Offset,
|
|
unsigned Length, bool Prologue) {
|
|
unsigned Start = (OC[Offset + 1] & 0xf0) >> 4;
|
|
unsigned End = (OC[Offset + 1] & 0x0f) >> 0;
|
|
uint32_t VFPMask = ((1 << (End - Start)) - 1) << 16;
|
|
|
|
SW.startLine() << format("0x%02x 0x%02x ; %s ", OC[Offset + 0],
|
|
OC[Offset + 1], Prologue ? "vpush" : "vpop");
|
|
printRegisters(std::make_pair(0, VFPMask));
|
|
OS << '\n';
|
|
|
|
Offset += 2;
|
|
return false;
|
|
}
|
|
|
|
bool Decoder::opcode_11110111(const uint8_t *OC, unsigned &Offset,
|
|
unsigned Length, bool Prologue) {
|
|
uint32_t Imm = (OC[Offset + 1] << 8) | (OC[Offset + 2] << 0);
|
|
|
|
SW.startLine() << format("0x%02x 0x%02x 0x%02x ; %s sp, sp, #(%u * 4)\n",
|
|
OC[Offset + 0], OC[Offset + 1], OC[Offset + 2],
|
|
static_cast<const char *>(Prologue ? "sub" : "add"),
|
|
Imm);
|
|
|
|
Offset += 3;
|
|
return false;
|
|
}
|
|
|
|
bool Decoder::opcode_11111000(const uint8_t *OC, unsigned &Offset,
|
|
unsigned Length, bool Prologue) {
|
|
uint32_t Imm = (OC[Offset + 1] << 16)
|
|
| (OC[Offset + 2] << 8)
|
|
| (OC[Offset + 3] << 0);
|
|
|
|
SW.startLine()
|
|
<< format("0x%02x 0x%02x 0x%02x 0x%02x ; %s sp, sp, #(%u * 4)\n",
|
|
OC[Offset + 0], OC[Offset + 1], OC[Offset + 2], OC[Offset + 3],
|
|
static_cast<const char *>(Prologue ? "sub" : "add"), Imm);
|
|
|
|
Offset += 4;
|
|
return false;
|
|
}
|
|
|
|
bool Decoder::opcode_11111001(const uint8_t *OC, unsigned &Offset,
|
|
unsigned Length, bool Prologue) {
|
|
uint32_t Imm = (OC[Offset + 1] << 8) | (OC[Offset + 2] << 0);
|
|
|
|
SW.startLine()
|
|
<< format("0x%02x 0x%02x 0x%02x ; %s.w sp, sp, #(%u * 4)\n",
|
|
OC[Offset + 0], OC[Offset + 1], OC[Offset + 2],
|
|
static_cast<const char *>(Prologue ? "sub" : "add"), Imm);
|
|
|
|
Offset += 3;
|
|
return false;
|
|
}
|
|
|
|
bool Decoder::opcode_11111010(const uint8_t *OC, unsigned &Offset,
|
|
unsigned Length, bool Prologue) {
|
|
uint32_t Imm = (OC[Offset + 1] << 16)
|
|
| (OC[Offset + 2] << 8)
|
|
| (OC[Offset + 3] << 0);
|
|
|
|
SW.startLine()
|
|
<< format("0x%02x 0x%02x 0x%02x 0x%02x ; %s.w sp, sp, #(%u * 4)\n",
|
|
OC[Offset + 0], OC[Offset + 1], OC[Offset + 2], OC[Offset + 3],
|
|
static_cast<const char *>(Prologue ? "sub" : "add"), Imm);
|
|
|
|
Offset += 4;
|
|
return false;
|
|
}
|
|
|
|
bool Decoder::opcode_11111011(const uint8_t *OC, unsigned &Offset,
|
|
unsigned Length, bool Prologue) {
|
|
SW.startLine() << format("0x%02x ; nop\n", OC[Offset]);
|
|
++Offset;
|
|
return false;
|
|
}
|
|
|
|
bool Decoder::opcode_11111100(const uint8_t *OC, unsigned &Offset,
|
|
unsigned Length, bool Prologue) {
|
|
SW.startLine() << format("0x%02x ; nop.w\n", OC[Offset]);
|
|
++Offset;
|
|
return false;
|
|
}
|
|
|
|
bool Decoder::opcode_11111101(const uint8_t *OC, unsigned &Offset,
|
|
unsigned Length, bool Prologue) {
|
|
SW.startLine() << format("0x%02x ; b\n", OC[Offset]);
|
|
++Offset;
|
|
return true;
|
|
}
|
|
|
|
bool Decoder::opcode_11111110(const uint8_t *OC, unsigned &Offset,
|
|
unsigned Length, bool Prologue) {
|
|
SW.startLine() << format("0x%02x ; b.w\n", OC[Offset]);
|
|
++Offset;
|
|
return true;
|
|
}
|
|
|
|
bool Decoder::opcode_11111111(const uint8_t *OC, unsigned &Offset,
|
|
unsigned Length, bool Prologue) {
|
|
++Offset;
|
|
return true;
|
|
}
|
|
|
|
// ARM64 unwind codes start here.
|
|
bool Decoder::opcode_alloc_s(const uint8_t *OC, unsigned &Offset,
|
|
unsigned Length, bool Prologue) {
|
|
uint32_t NumBytes = (OC[Offset] & 0x1F) << 4;
|
|
SW.startLine() << format("0x%02x ; %s sp, #%u\n", OC[Offset],
|
|
static_cast<const char *>(Prologue ? "sub" : "add"),
|
|
NumBytes);
|
|
++Offset;
|
|
return false;
|
|
}
|
|
|
|
bool Decoder::opcode_save_r19r20_x(const uint8_t *OC, unsigned &Offset,
|
|
unsigned Length, bool Prologue) {
|
|
uint32_t Off = (OC[Offset] & 0x1F) << 3;
|
|
if (Prologue)
|
|
SW.startLine() << format(
|
|
"0x%02x ; stp x19, x20, [sp, #-%u]!\n", OC[Offset], Off);
|
|
else
|
|
SW.startLine() << format(
|
|
"0x%02x ; ldp x19, x20, [sp], #%u\n", OC[Offset], Off);
|
|
++Offset;
|
|
return false;
|
|
}
|
|
|
|
bool Decoder::opcode_save_fplr(const uint8_t *OC, unsigned &Offset,
|
|
unsigned Length, bool Prologue) {
|
|
uint32_t Off = (OC[Offset] & 0x3F) << 3;
|
|
SW.startLine() << format(
|
|
"0x%02x ; %s x29, x30, [sp, #%u]\n", OC[Offset],
|
|
static_cast<const char *>(Prologue ? "stp" : "ldp"), Off);
|
|
++Offset;
|
|
return false;
|
|
}
|
|
|
|
bool Decoder::opcode_save_fplr_x(const uint8_t *OC, unsigned &Offset,
|
|
unsigned Length, bool Prologue) {
|
|
uint32_t Off = ((OC[Offset] & 0x3F) + 1) << 3;
|
|
if (Prologue)
|
|
SW.startLine() << format(
|
|
"0x%02x ; stp x29, x30, [sp, #-%u]!\n", OC[Offset], Off);
|
|
else
|
|
SW.startLine() << format(
|
|
"0x%02x ; ldp x29, x30, [sp], #%u\n", OC[Offset], Off);
|
|
++Offset;
|
|
return false;
|
|
}
|
|
|
|
bool Decoder::opcode_alloc_m(const uint8_t *OC, unsigned &Offset,
|
|
unsigned Length, bool Prologue) {
|
|
uint32_t NumBytes = ((OC[Offset] & 0x07) << 8);
|
|
NumBytes |= (OC[Offset + 1] & 0xFF);
|
|
NumBytes <<= 4;
|
|
SW.startLine() << format("0x%02x%02x ; %s sp, #%u\n",
|
|
OC[Offset], OC[Offset + 1],
|
|
static_cast<const char *>(Prologue ? "sub" : "add"),
|
|
NumBytes);
|
|
Offset += 2;
|
|
return false;
|
|
}
|
|
|
|
bool Decoder::opcode_save_regp(const uint8_t *OC, unsigned &Offset,
|
|
unsigned Length, bool Prologue) {
|
|
uint32_t Reg = ((OC[Offset] & 0x03) << 8);
|
|
Reg |= (OC[Offset + 1] & 0xC0);
|
|
Reg >>= 6;
|
|
Reg += 19;
|
|
uint32_t Off = (OC[Offset + 1] & 0x3F) << 3;
|
|
SW.startLine() << format(
|
|
"0x%02x%02x ; %s x%u, x%u, [sp, #%u]\n",
|
|
OC[Offset], OC[Offset + 1],
|
|
static_cast<const char *>(Prologue ? "stp" : "ldp"), Reg, Reg + 1, Off);
|
|
Offset += 2;
|
|
return false;
|
|
}
|
|
|
|
bool Decoder::opcode_save_regp_x(const uint8_t *OC, unsigned &Offset,
|
|
unsigned Length, bool Prologue) {
|
|
uint32_t Reg = ((OC[Offset] & 0x03) << 8);
|
|
Reg |= (OC[Offset + 1] & 0xC0);
|
|
Reg >>= 6;
|
|
Reg += 19;
|
|
uint32_t Off = ((OC[Offset + 1] & 0x3F) + 1) << 3;
|
|
if (Prologue)
|
|
SW.startLine() << format(
|
|
"0x%02x%02x ; stp x%u, x%u, [sp, #-%u]!\n",
|
|
OC[Offset], OC[Offset + 1], Reg,
|
|
Reg + 1, Off);
|
|
else
|
|
SW.startLine() << format(
|
|
"0x%02x%02x ; ldp x%u, x%u, [sp], #%u\n",
|
|
OC[Offset], OC[Offset + 1], Reg,
|
|
Reg + 1, Off);
|
|
Offset += 2;
|
|
return false;
|
|
}
|
|
|
|
bool Decoder::opcode_save_reg(const uint8_t *OC, unsigned &Offset,
|
|
unsigned Length, bool Prologue) {
|
|
uint32_t Reg = (OC[Offset] & 0x03) << 8;
|
|
Reg |= (OC[Offset + 1] & 0xC0);
|
|
Reg >>= 6;
|
|
Reg += 19;
|
|
uint32_t Off = (OC[Offset + 1] & 0x3F) << 3;
|
|
SW.startLine() << format("0x%02x%02x ; %s x%u, [sp, #%u]\n",
|
|
OC[Offset], OC[Offset + 1],
|
|
static_cast<const char *>(Prologue ? "str" : "ldr"),
|
|
Reg, Off);
|
|
Offset += 2;
|
|
return false;
|
|
}
|
|
|
|
bool Decoder::opcode_save_reg_x(const uint8_t *OC, unsigned &Offset,
|
|
unsigned Length, bool Prologue) {
|
|
uint32_t Reg = (OC[Offset] & 0x01) << 8;
|
|
Reg |= (OC[Offset + 1] & 0xE0);
|
|
Reg >>= 5;
|
|
Reg += 19;
|
|
uint32_t Off = ((OC[Offset + 1] & 0x1F) + 1) << 3;
|
|
if (Prologue)
|
|
SW.startLine() << format("0x%02x%02x ; str x%u, [sp, #-%u]!\n",
|
|
OC[Offset], OC[Offset + 1], Reg, Off);
|
|
else
|
|
SW.startLine() << format("0x%02x%02x ; ldr x%u, [sp], #%u\n",
|
|
OC[Offset], OC[Offset + 1], Reg, Off);
|
|
Offset += 2;
|
|
return false;
|
|
}
|
|
|
|
bool Decoder::opcode_save_lrpair(const uint8_t *OC, unsigned &Offset,
|
|
unsigned Length, bool Prologue) {
|
|
uint32_t Reg = (OC[Offset] & 0x01) << 8;
|
|
Reg |= (OC[Offset + 1] & 0xC0);
|
|
Reg >>= 6;
|
|
Reg *= 2;
|
|
Reg += 19;
|
|
uint32_t Off = (OC[Offset + 1] & 0x3F) << 3;
|
|
SW.startLine() << format("0x%02x%02x ; %s x%u, lr, [sp, #%u]\n",
|
|
OC[Offset], OC[Offset + 1],
|
|
static_cast<const char *>(Prologue ? "stp" : "ldp"),
|
|
Reg, Off);
|
|
Offset += 2;
|
|
return false;
|
|
}
|
|
|
|
bool Decoder::opcode_save_fregp(const uint8_t *OC, unsigned &Offset,
|
|
unsigned Length, bool Prologue) {
|
|
uint32_t Reg = (OC[Offset] & 0x01) << 8;
|
|
Reg |= (OC[Offset + 1] & 0xC0);
|
|
Reg >>= 6;
|
|
Reg += 8;
|
|
uint32_t Off = (OC[Offset + 1] & 0x3F) << 3;
|
|
SW.startLine() << format("0x%02x%02x ; %s d%u, d%u, [sp, #%u]\n",
|
|
OC[Offset], OC[Offset + 1],
|
|
static_cast<const char *>(Prologue ? "stp" : "ldp"),
|
|
Reg, Reg + 1, Off);
|
|
Offset += 2;
|
|
return false;
|
|
}
|
|
|
|
bool Decoder::opcode_save_fregp_x(const uint8_t *OC, unsigned &Offset,
|
|
unsigned Length, bool Prologue) {
|
|
uint32_t Reg = (OC[Offset] & 0x01) << 8;
|
|
Reg |= (OC[Offset + 1] & 0xC0);
|
|
Reg >>= 6;
|
|
Reg += 8;
|
|
uint32_t Off = ((OC[Offset + 1] & 0x3F) + 1) << 3;
|
|
if (Prologue)
|
|
SW.startLine() << format(
|
|
"0x%02x%02x ; stp d%u, d%u, [sp, #-%u]!\n", OC[Offset],
|
|
OC[Offset + 1], Reg, Reg + 1, Off);
|
|
else
|
|
SW.startLine() << format(
|
|
"0x%02x%02x ; ldp d%u, d%u, [sp], #%u\n", OC[Offset],
|
|
OC[Offset + 1], Reg, Reg + 1, Off);
|
|
Offset += 2;
|
|
return false;
|
|
}
|
|
|
|
bool Decoder::opcode_save_freg(const uint8_t *OC, unsigned &Offset,
|
|
unsigned Length, bool Prologue) {
|
|
uint32_t Reg = (OC[Offset] & 0x01) << 8;
|
|
Reg |= (OC[Offset + 1] & 0xC0);
|
|
Reg >>= 6;
|
|
Reg += 8;
|
|
uint32_t Off = (OC[Offset + 1] & 0x3F) << 3;
|
|
SW.startLine() << format("0x%02x%02x ; %s d%u, [sp, #%u]\n",
|
|
OC[Offset], OC[Offset + 1],
|
|
static_cast<const char *>(Prologue ? "str" : "ldr"),
|
|
Reg, Off);
|
|
Offset += 2;
|
|
return false;
|
|
}
|
|
|
|
bool Decoder::opcode_save_freg_x(const uint8_t *OC, unsigned &Offset,
|
|
unsigned Length, bool Prologue) {
|
|
uint32_t Reg = ((OC[Offset + 1] & 0xE0) >> 5) + 8;
|
|
uint32_t Off = ((OC[Offset + 1] & 0x1F) + 1) << 3;
|
|
if (Prologue)
|
|
SW.startLine() << format(
|
|
"0x%02x%02x ; str d%u, [sp, #-%u]!\n", OC[Offset],
|
|
OC[Offset + 1], Reg, Off);
|
|
else
|
|
SW.startLine() << format(
|
|
"0x%02x%02x ; ldr d%u, [sp], #%u\n", OC[Offset],
|
|
OC[Offset + 1], Reg, Off);
|
|
Offset += 2;
|
|
return false;
|
|
}
|
|
|
|
bool Decoder::opcode_alloc_l(const uint8_t *OC, unsigned &Offset,
|
|
unsigned Length, bool Prologue) {
|
|
unsigned Off =
|
|
(OC[Offset + 1] << 16) | (OC[Offset + 2] << 8) | (OC[Offset + 3] << 0);
|
|
Off <<= 4;
|
|
SW.startLine() << format(
|
|
"0x%02x%02x%02x%02x ; %s sp, #%u\n", OC[Offset], OC[Offset + 1],
|
|
OC[Offset + 2], OC[Offset + 3],
|
|
static_cast<const char *>(Prologue ? "sub" : "add"), Off);
|
|
Offset += 4;
|
|
return false;
|
|
}
|
|
|
|
bool Decoder::opcode_setfp(const uint8_t *OC, unsigned &Offset, unsigned Length,
|
|
bool Prologue) {
|
|
SW.startLine() << format("0x%02x ; mov %s, %s\n", OC[Offset],
|
|
static_cast<const char *>(Prologue ? "fp" : "sp"),
|
|
static_cast<const char *>(Prologue ? "sp" : "fp"));
|
|
++Offset;
|
|
return false;
|
|
}
|
|
|
|
bool Decoder::opcode_addfp(const uint8_t *OC, unsigned &Offset, unsigned Length,
|
|
bool Prologue) {
|
|
unsigned NumBytes = OC[Offset + 1] << 3;
|
|
SW.startLine() << format(
|
|
"0x%02x%02x ; %s %s, %s, #%u\n", OC[Offset], OC[Offset + 1],
|
|
static_cast<const char *>(Prologue ? "add" : "sub"),
|
|
static_cast<const char *>(Prologue ? "fp" : "sp"),
|
|
static_cast<const char *>(Prologue ? "sp" : "fp"), NumBytes);
|
|
Offset += 2;
|
|
return false;
|
|
}
|
|
|
|
bool Decoder::opcode_nop(const uint8_t *OC, unsigned &Offset, unsigned Length,
|
|
bool Prologue) {
|
|
SW.startLine() << format("0x%02x ; nop\n", OC[Offset]);
|
|
++Offset;
|
|
return false;
|
|
}
|
|
|
|
bool Decoder::opcode_end(const uint8_t *OC, unsigned &Offset, unsigned Length,
|
|
bool Prologue) {
|
|
SW.startLine() << format("0x%02x ; end\n", OC[Offset]);
|
|
++Offset;
|
|
return true;
|
|
}
|
|
|
|
bool Decoder::opcode_end_c(const uint8_t *OC, unsigned &Offset, unsigned Length,
|
|
bool Prologue) {
|
|
SW.startLine() << format("0x%02x ; end_c\n", OC[Offset]);
|
|
++Offset;
|
|
return true;
|
|
}
|
|
|
|
bool Decoder::opcode_save_next(const uint8_t *OC, unsigned &Offset,
|
|
unsigned Length, bool Prologue) {
|
|
if (Prologue)
|
|
SW.startLine() << format("0x%02x ; save next\n", OC[Offset]);
|
|
else
|
|
SW.startLine() << format("0x%02x ; restore next\n",
|
|
OC[Offset]);
|
|
++Offset;
|
|
return false;
|
|
}
|
|
|
|
bool Decoder::opcode_trap_frame(const uint8_t *OC, unsigned &Offset,
|
|
unsigned Length, bool Prologue) {
|
|
SW.startLine() << format("0x%02x ; trap frame\n", OC[Offset]);
|
|
++Offset;
|
|
return false;
|
|
}
|
|
|
|
bool Decoder::opcode_machine_frame(const uint8_t *OC, unsigned &Offset,
|
|
unsigned Length, bool Prologue) {
|
|
SW.startLine() << format("0x%02x ; machine frame\n",
|
|
OC[Offset]);
|
|
++Offset;
|
|
return false;
|
|
}
|
|
|
|
bool Decoder::opcode_context(const uint8_t *OC, unsigned &Offset,
|
|
unsigned Length, bool Prologue) {
|
|
SW.startLine() << format("0x%02x ; context\n", OC[Offset]);
|
|
++Offset;
|
|
return false;
|
|
}
|
|
|
|
bool Decoder::opcode_clear_unwound_to_call(const uint8_t *OC, unsigned &Offset,
|
|
unsigned Length, bool Prologue) {
|
|
SW.startLine() << format("0x%02x ; clear unwound to call\n",
|
|
OC[Offset]);
|
|
++Offset;
|
|
return false;
|
|
}
|
|
|
|
void Decoder::decodeOpcodes(ArrayRef<uint8_t> Opcodes, unsigned Offset,
|
|
bool Prologue) {
|
|
assert((!Prologue || Offset == 0) && "prologue should always use offset 0");
|
|
const RingEntry* DecodeRing = isAArch64 ? Ring64 : Ring;
|
|
bool Terminated = false;
|
|
for (unsigned OI = Offset, OE = Opcodes.size(); !Terminated && OI < OE; ) {
|
|
for (unsigned DI = 0;; ++DI) {
|
|
if ((isAArch64 && (DI >= array_lengthof(Ring64))) ||
|
|
(!isAArch64 && (DI >= array_lengthof(Ring)))) {
|
|
SW.startLine() << format("0x%02x ; Bad opcode!\n",
|
|
Opcodes.data()[OI]);
|
|
++OI;
|
|
break;
|
|
}
|
|
|
|
if ((Opcodes[OI] & DecodeRing[DI].Mask) == DecodeRing[DI].Value) {
|
|
if (OI + DecodeRing[DI].Length > OE) {
|
|
SW.startLine() << format("Opcode 0x%02x goes past the unwind data\n",
|
|
Opcodes[OI]);
|
|
OI += DecodeRing[DI].Length;
|
|
break;
|
|
}
|
|
Terminated =
|
|
(this->*DecodeRing[DI].Routine)(Opcodes.data(), OI, 0, Prologue);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool Decoder::dumpXDataRecord(const COFFObjectFile &COFF,
|
|
const SectionRef &Section,
|
|
uint64_t FunctionAddress, uint64_t VA) {
|
|
ArrayRef<uint8_t> Contents;
|
|
if (COFF.getSectionContents(COFF.getCOFFSection(Section), Contents))
|
|
return false;
|
|
|
|
uint64_t SectionVA = Section.getAddress();
|
|
uint64_t Offset = VA - SectionVA;
|
|
const ulittle32_t *Data =
|
|
reinterpret_cast<const ulittle32_t *>(Contents.data() + Offset);
|
|
|
|
// Sanity check to ensure that the .xdata header is present.
|
|
// A header is one or two words, followed by at least one word to describe
|
|
// the unwind codes. Applicable to both ARM and AArch64.
|
|
if (Contents.size() - Offset < 8)
|
|
report_fatal_error(".xdata must be at least 8 bytes in size");
|
|
|
|
const ExceptionDataRecord XData(Data, isAArch64);
|
|
DictScope XRS(SW, "ExceptionData");
|
|
SW.printNumber("FunctionLength",
|
|
isAArch64 ? XData.FunctionLengthInBytesAArch64() :
|
|
XData.FunctionLengthInBytesARM());
|
|
SW.printNumber("Version", XData.Vers());
|
|
SW.printBoolean("ExceptionData", XData.X());
|
|
SW.printBoolean("EpiloguePacked", XData.E());
|
|
if (!isAArch64)
|
|
SW.printBoolean("Fragment", XData.F());
|
|
SW.printNumber(XData.E() ? "EpilogueOffset" : "EpilogueScopes",
|
|
XData.EpilogueCount());
|
|
uint64_t ByteCodeLength = XData.CodeWords() * sizeof(uint32_t);
|
|
SW.printNumber("ByteCodeLength", ByteCodeLength);
|
|
|
|
if ((int64_t)(Contents.size() - Offset - 4 * HeaderWords(XData) -
|
|
(XData.E() ? 0 : XData.EpilogueCount() * 4) -
|
|
(XData.X() ? 8 : 0)) < (int64_t)ByteCodeLength) {
|
|
SW.flush();
|
|
report_fatal_error("Malformed unwind data");
|
|
}
|
|
|
|
if (XData.E()) {
|
|
ArrayRef<uint8_t> UC = XData.UnwindByteCode();
|
|
if (isAArch64 || !XData.F()) {
|
|
ListScope PS(SW, "Prologue");
|
|
decodeOpcodes(UC, 0, /*Prologue=*/true);
|
|
}
|
|
if (XData.EpilogueCount()) {
|
|
ListScope ES(SW, "Epilogue");
|
|
decodeOpcodes(UC, XData.EpilogueCount(), /*Prologue=*/false);
|
|
}
|
|
} else {
|
|
{
|
|
ListScope PS(SW, "Prologue");
|
|
decodeOpcodes(XData.UnwindByteCode(), 0, /*Prologue=*/true);
|
|
}
|
|
ArrayRef<ulittle32_t> EpilogueScopes = XData.EpilogueScopes();
|
|
ListScope ESS(SW, "EpilogueScopes");
|
|
for (const EpilogueScope ES : EpilogueScopes) {
|
|
DictScope ESES(SW, "EpilogueScope");
|
|
SW.printNumber("StartOffset", ES.EpilogueStartOffset());
|
|
if (!isAArch64)
|
|
SW.printNumber("Condition", ES.Condition());
|
|
SW.printNumber("EpilogueStartIndex",
|
|
isAArch64 ? ES.EpilogueStartIndexAArch64()
|
|
: ES.EpilogueStartIndexARM());
|
|
if (ES.ES & ~0xffc3ffff)
|
|
SW.printNumber("ReservedBits", (ES.ES >> 18) & 0xF);
|
|
|
|
ListScope Opcodes(SW, "Opcodes");
|
|
decodeOpcodes(XData.UnwindByteCode(),
|
|
isAArch64 ? ES.EpilogueStartIndexAArch64()
|
|
: ES.EpilogueStartIndexARM(),
|
|
/*Prologue=*/false);
|
|
}
|
|
}
|
|
|
|
if (XData.X()) {
|
|
const uint64_t Address = COFF.getImageBase() + XData.ExceptionHandlerRVA();
|
|
const uint32_t Parameter = XData.ExceptionHandlerParameter();
|
|
const size_t HandlerOffset = HeaderWords(XData)
|
|
+ (XData.E() ? 0 : XData.EpilogueCount())
|
|
+ XData.CodeWords();
|
|
|
|
ErrorOr<SymbolRef> Symbol = getRelocatedSymbol(
|
|
COFF, Section, Offset + HandlerOffset * sizeof(uint32_t));
|
|
if (!Symbol)
|
|
Symbol = getSymbol(COFF, Address, /*FunctionOnly=*/true);
|
|
if (!Symbol) {
|
|
ListScope EHS(SW, "ExceptionHandler");
|
|
SW.printHex("Routine", Address);
|
|
SW.printHex("Parameter", Parameter);
|
|
return true;
|
|
}
|
|
|
|
Expected<StringRef> Name = Symbol->getName();
|
|
if (!Name) {
|
|
std::string Buf;
|
|
llvm::raw_string_ostream OS(Buf);
|
|
logAllUnhandledErrors(Name.takeError(), OS);
|
|
OS.flush();
|
|
report_fatal_error(Buf);
|
|
}
|
|
|
|
ListScope EHS(SW, "ExceptionHandler");
|
|
SW.printString("Routine", formatSymbol(*Name, Address));
|
|
SW.printHex("Parameter", Parameter);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Decoder::dumpUnpackedEntry(const COFFObjectFile &COFF,
|
|
const SectionRef Section, uint64_t Offset,
|
|
unsigned Index, const RuntimeFunction &RF) {
|
|
assert(RF.Flag() == RuntimeFunctionFlag::RFF_Unpacked &&
|
|
"packed entry cannot be treated as an unpacked entry");
|
|
|
|
ErrorOr<SymbolRef> Function = getRelocatedSymbol(COFF, Section, Offset);
|
|
if (!Function)
|
|
Function = getSymbol(COFF, COFF.getImageBase() + RF.BeginAddress,
|
|
/*FunctionOnly=*/true);
|
|
|
|
ErrorOr<SymbolRef> XDataRecord = getRelocatedSymbol(COFF, Section, Offset + 4);
|
|
if (!XDataRecord)
|
|
XDataRecord = getSymbol(COFF, RF.ExceptionInformationRVA());
|
|
|
|
if (!RF.BeginAddress && !Function)
|
|
return false;
|
|
if (!RF.UnwindData && !XDataRecord)
|
|
return false;
|
|
|
|
StringRef FunctionName;
|
|
uint64_t FunctionAddress;
|
|
if (Function) {
|
|
Expected<StringRef> FunctionNameOrErr = Function->getName();
|
|
if (!FunctionNameOrErr) {
|
|
std::string Buf;
|
|
llvm::raw_string_ostream OS(Buf);
|
|
logAllUnhandledErrors(FunctionNameOrErr.takeError(), OS);
|
|
OS.flush();
|
|
report_fatal_error(Buf);
|
|
}
|
|
FunctionName = *FunctionNameOrErr;
|
|
Expected<uint64_t> FunctionAddressOrErr = Function->getAddress();
|
|
if (!FunctionAddressOrErr) {
|
|
std::string Buf;
|
|
llvm::raw_string_ostream OS(Buf);
|
|
logAllUnhandledErrors(FunctionAddressOrErr.takeError(), OS);
|
|
OS.flush();
|
|
report_fatal_error(Buf);
|
|
}
|
|
FunctionAddress = *FunctionAddressOrErr;
|
|
} else {
|
|
FunctionAddress = COFF.getImageBase() + RF.BeginAddress;
|
|
}
|
|
|
|
SW.printString("Function", formatSymbol(FunctionName, FunctionAddress));
|
|
|
|
if (XDataRecord) {
|
|
Expected<StringRef> Name = XDataRecord->getName();
|
|
if (!Name) {
|
|
std::string Buf;
|
|
llvm::raw_string_ostream OS(Buf);
|
|
logAllUnhandledErrors(Name.takeError(), OS);
|
|
OS.flush();
|
|
report_fatal_error(Buf);
|
|
}
|
|
|
|
Expected<uint64_t> AddressOrErr = XDataRecord->getAddress();
|
|
if (!AddressOrErr) {
|
|
std::string Buf;
|
|
llvm::raw_string_ostream OS(Buf);
|
|
logAllUnhandledErrors(AddressOrErr.takeError(), OS);
|
|
OS.flush();
|
|
report_fatal_error(Buf);
|
|
}
|
|
uint64_t Address = *AddressOrErr;
|
|
|
|
SW.printString("ExceptionRecord", formatSymbol(*Name, Address));
|
|
|
|
Expected<section_iterator> SIOrErr = XDataRecord->getSection();
|
|
if (!SIOrErr) {
|
|
// TODO: Actually report errors helpfully.
|
|
consumeError(SIOrErr.takeError());
|
|
return false;
|
|
}
|
|
section_iterator SI = *SIOrErr;
|
|
|
|
// FIXME: Do we need to add an offset from the relocation?
|
|
return dumpXDataRecord(COFF, *SI, FunctionAddress,
|
|
RF.ExceptionInformationRVA());
|
|
} else {
|
|
uint64_t Address = COFF.getImageBase() + RF.ExceptionInformationRVA();
|
|
SW.printString("ExceptionRecord", formatSymbol("", Address));
|
|
|
|
ErrorOr<SectionRef> Section = getSectionContaining(COFF, Address);
|
|
if (!Section)
|
|
return false;
|
|
|
|
return dumpXDataRecord(COFF, *Section, FunctionAddress, Address);
|
|
}
|
|
}
|
|
|
|
bool Decoder::dumpPackedEntry(const object::COFFObjectFile &COFF,
|
|
const SectionRef Section, uint64_t Offset,
|
|
unsigned Index, const RuntimeFunction &RF) {
|
|
assert((RF.Flag() == RuntimeFunctionFlag::RFF_Packed ||
|
|
RF.Flag() == RuntimeFunctionFlag::RFF_PackedFragment) &&
|
|
"unpacked entry cannot be treated as a packed entry");
|
|
|
|
ErrorOr<SymbolRef> Function = getRelocatedSymbol(COFF, Section, Offset);
|
|
if (!Function)
|
|
Function = getSymbol(COFF, RF.BeginAddress, /*FunctionOnly=*/true);
|
|
|
|
StringRef FunctionName;
|
|
uint64_t FunctionAddress;
|
|
if (Function) {
|
|
Expected<StringRef> FunctionNameOrErr = Function->getName();
|
|
if (!FunctionNameOrErr) {
|
|
std::string Buf;
|
|
llvm::raw_string_ostream OS(Buf);
|
|
logAllUnhandledErrors(FunctionNameOrErr.takeError(), OS);
|
|
OS.flush();
|
|
report_fatal_error(Buf);
|
|
}
|
|
FunctionName = *FunctionNameOrErr;
|
|
Expected<uint64_t> FunctionAddressOrErr = Function->getAddress();
|
|
if (!FunctionAddressOrErr) {
|
|
std::string Buf;
|
|
llvm::raw_string_ostream OS(Buf);
|
|
logAllUnhandledErrors(FunctionAddressOrErr.takeError(), OS);
|
|
OS.flush();
|
|
report_fatal_error(Buf);
|
|
}
|
|
FunctionAddress = *FunctionAddressOrErr;
|
|
} else {
|
|
FunctionAddress = COFF.getPE32Header()->ImageBase + RF.BeginAddress;
|
|
}
|
|
|
|
SW.printString("Function", formatSymbol(FunctionName, FunctionAddress));
|
|
if (!isAArch64)
|
|
SW.printBoolean("Fragment",
|
|
RF.Flag() == RuntimeFunctionFlag::RFF_PackedFragment);
|
|
SW.printNumber("FunctionLength", RF.FunctionLength());
|
|
SW.startLine() << "ReturnType: " << RF.Ret() << '\n';
|
|
SW.printBoolean("HomedParameters", RF.H());
|
|
SW.startLine() << "SavedRegisters: ";
|
|
printRegisters(SavedRegisterMask(RF));
|
|
OS << '\n';
|
|
SW.printNumber("StackAdjustment", StackAdjustment(RF) << 2);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Decoder::dumpPackedARM64Entry(const object::COFFObjectFile &COFF,
|
|
const SectionRef Section, uint64_t Offset,
|
|
unsigned Index,
|
|
const RuntimeFunctionARM64 &RF) {
|
|
assert((RF.Flag() == RuntimeFunctionFlag::RFF_Packed ||
|
|
RF.Flag() == RuntimeFunctionFlag::RFF_PackedFragment) &&
|
|
"unpacked entry cannot be treated as a packed entry");
|
|
|
|
ErrorOr<SymbolRef> Function = getRelocatedSymbol(COFF, Section, Offset);
|
|
if (!Function)
|
|
Function = getSymbol(COFF, RF.BeginAddress, /*FunctionOnly=*/true);
|
|
|
|
StringRef FunctionName;
|
|
uint64_t FunctionAddress;
|
|
if (Function) {
|
|
Expected<StringRef> FunctionNameOrErr = Function->getName();
|
|
if (!FunctionNameOrErr) {
|
|
std::string Buf;
|
|
llvm::raw_string_ostream OS(Buf);
|
|
logAllUnhandledErrors(FunctionNameOrErr.takeError(), OS);
|
|
OS.flush();
|
|
report_fatal_error(Buf);
|
|
}
|
|
FunctionName = *FunctionNameOrErr;
|
|
Expected<uint64_t> FunctionAddressOrErr = Function->getAddress();
|
|
if (!FunctionAddressOrErr) {
|
|
std::string Buf;
|
|
llvm::raw_string_ostream OS(Buf);
|
|
logAllUnhandledErrors(FunctionAddressOrErr.takeError(), OS);
|
|
OS.flush();
|
|
report_fatal_error(Buf);
|
|
}
|
|
FunctionAddress = *FunctionAddressOrErr;
|
|
} else {
|
|
FunctionAddress = COFF.getPE32PlusHeader()->ImageBase + RF.BeginAddress;
|
|
}
|
|
|
|
SW.printString("Function", formatSymbol(FunctionName, FunctionAddress));
|
|
SW.printBoolean("Fragment",
|
|
RF.Flag() == RuntimeFunctionFlag::RFF_PackedFragment);
|
|
SW.printNumber("FunctionLength", RF.FunctionLength());
|
|
SW.printNumber("RegF", RF.RegF());
|
|
SW.printNumber("RegI", RF.RegI());
|
|
SW.printBoolean("HomedParameters", RF.H());
|
|
SW.printNumber("CR", RF.CR());
|
|
SW.printNumber("FrameSize", RF.FrameSize() << 4);
|
|
ListScope PS(SW, "Prologue");
|
|
|
|
// Synthesize the equivalent prologue according to the documentation
|
|
// at https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling,
|
|
// printed in reverse order compared to the docs, to match how prologues
|
|
// are printed for the non-packed case.
|
|
int IntSZ = 8 * RF.RegI();
|
|
if (RF.CR() == 1)
|
|
IntSZ += 8;
|
|
int FpSZ = 8 * RF.RegF();
|
|
if (RF.RegF())
|
|
FpSZ += 8;
|
|
int SavSZ = (IntSZ + FpSZ + 8 * 8 * RF.H() + 0xf) & ~0xf;
|
|
int LocSZ = (RF.FrameSize() << 4) - SavSZ;
|
|
|
|
if (RF.CR() == 3) {
|
|
SW.startLine() << "mov x29, sp\n";
|
|
if (LocSZ <= 512) {
|
|
SW.startLine() << format("stp x29, lr, [sp, #-%d]!\n", LocSZ);
|
|
} else {
|
|
SW.startLine() << "stp x29, lr, [sp, #0]\n";
|
|
}
|
|
}
|
|
if (LocSZ > 4080) {
|
|
SW.startLine() << format("sub sp, sp, #%d\n", LocSZ - 4080);
|
|
SW.startLine() << "sub sp, sp, #4080\n";
|
|
} else if ((RF.CR() != 3 && LocSZ > 0) || LocSZ > 512) {
|
|
SW.startLine() << format("sub sp, sp, #%d\n", LocSZ);
|
|
}
|
|
if (RF.H()) {
|
|
SW.startLine() << format("stp x6, x7, [sp, #%d]\n", IntSZ + FpSZ + 48);
|
|
SW.startLine() << format("stp x4, x5, [sp, #%d]\n", IntSZ + FpSZ + 32);
|
|
SW.startLine() << format("stp x2, x3, [sp, #%d]\n", IntSZ + FpSZ + 16);
|
|
if (RF.RegI() > 0 || RF.RegF() > 0 || RF.CR() == 1) {
|
|
SW.startLine() << format("stp x0, x1, [sp, #%d]\n", IntSZ + FpSZ);
|
|
} else {
|
|
// This case isn't documented; if neither RegI nor RegF nor CR=1
|
|
// have decremented the stack pointer by SavSZ, we need to do it here
|
|
// (as the final stack adjustment of LocSZ excludes SavSZ).
|
|
SW.startLine() << format("stp x0, x1, [sp, #-%d]!\n", SavSZ);
|
|
}
|
|
}
|
|
int FloatRegs = RF.RegF() > 0 ? RF.RegF() + 1 : 0;
|
|
for (int I = (FloatRegs + 1) / 2 - 1; I >= 0; I--) {
|
|
if (I == (FloatRegs + 1) / 2 - 1 && FloatRegs % 2 == 1) {
|
|
// The last register, an odd register without a pair
|
|
SW.startLine() << format("str d%d, [sp, #%d]\n", 8 + 2 * I,
|
|
IntSZ + 16 * I);
|
|
} else if (I == 0 && RF.RegI() == 0 && RF.CR() != 1) {
|
|
SW.startLine() << format("stp d%d, d%d, [sp, #-%d]!\n", 8 + 2 * I,
|
|
8 + 2 * I + 1, SavSZ);
|
|
} else {
|
|
SW.startLine() << format("stp d%d, d%d, [sp, #%d]\n", 8 + 2 * I,
|
|
8 + 2 * I + 1, IntSZ + 16 * I);
|
|
}
|
|
}
|
|
if (RF.CR() == 1 && (RF.RegI() % 2) == 0) {
|
|
if (RF.RegI() == 0)
|
|
SW.startLine() << format("str lr, [sp, #-%d]!\n", SavSZ);
|
|
else
|
|
SW.startLine() << format("str lr, [sp, #%d]\n", IntSZ - 8);
|
|
}
|
|
for (int I = (RF.RegI() + 1) / 2 - 1; I >= 0; I--) {
|
|
if (I == (RF.RegI() + 1) / 2 - 1 && RF.RegI() % 2 == 1) {
|
|
// The last register, an odd register without a pair
|
|
if (RF.CR() == 1) {
|
|
if (I == 0) { // If this is the only register pair
|
|
// CR=1 combined with RegI=1 doesn't map to a documented case;
|
|
// it doesn't map to any regular unwind info opcode, and the
|
|
// actual unwinder doesn't support it.
|
|
SW.startLine() << "INVALID!\n";
|
|
} else
|
|
SW.startLine() << format("stp x%d, lr, [sp, #%d]\n", 19 + 2 * I,
|
|
16 * I);
|
|
} else {
|
|
if (I == 0)
|
|
SW.startLine() << format("str x%d, [sp, #-%d]!\n", 19 + 2 * I, SavSZ);
|
|
else
|
|
SW.startLine() << format("str x%d, [sp, #%d]\n", 19 + 2 * I, 16 * I);
|
|
}
|
|
} else if (I == 0) {
|
|
// The first register pair
|
|
SW.startLine() << format("stp x19, x20, [sp, #-%d]!\n", SavSZ);
|
|
} else {
|
|
SW.startLine() << format("stp x%d, x%d, [sp, #%d]\n", 19 + 2 * I,
|
|
19 + 2 * I + 1, 16 * I);
|
|
}
|
|
}
|
|
SW.startLine() << "end\n";
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Decoder::dumpProcedureDataEntry(const COFFObjectFile &COFF,
|
|
const SectionRef Section, unsigned Index,
|
|
ArrayRef<uint8_t> Contents) {
|
|
uint64_t Offset = PDataEntrySize * Index;
|
|
const ulittle32_t *Data =
|
|
reinterpret_cast<const ulittle32_t *>(Contents.data() + Offset);
|
|
|
|
const RuntimeFunction Entry(Data);
|
|
DictScope RFS(SW, "RuntimeFunction");
|
|
if (Entry.Flag() == RuntimeFunctionFlag::RFF_Unpacked)
|
|
return dumpUnpackedEntry(COFF, Section, Offset, Index, Entry);
|
|
if (isAArch64) {
|
|
const RuntimeFunctionARM64 EntryARM64(Data);
|
|
return dumpPackedARM64Entry(COFF, Section, Offset, Index, EntryARM64);
|
|
}
|
|
return dumpPackedEntry(COFF, Section, Offset, Index, Entry);
|
|
}
|
|
|
|
void Decoder::dumpProcedureData(const COFFObjectFile &COFF,
|
|
const SectionRef Section) {
|
|
ArrayRef<uint8_t> Contents;
|
|
if (COFF.getSectionContents(COFF.getCOFFSection(Section), Contents))
|
|
return;
|
|
|
|
if (Contents.size() % PDataEntrySize) {
|
|
errs() << ".pdata content is not " << PDataEntrySize << "-byte aligned\n";
|
|
return;
|
|
}
|
|
|
|
for (unsigned EI = 0, EE = Contents.size() / PDataEntrySize; EI < EE; ++EI)
|
|
if (!dumpProcedureDataEntry(COFF, Section, EI, Contents))
|
|
break;
|
|
}
|
|
|
|
Error Decoder::dumpProcedureData(const COFFObjectFile &COFF) {
|
|
for (const auto &Section : COFF.sections()) {
|
|
Expected<StringRef> NameOrErr =
|
|
COFF.getSectionName(COFF.getCOFFSection(Section));
|
|
if (!NameOrErr)
|
|
return NameOrErr.takeError();
|
|
|
|
if (NameOrErr->startswith(".pdata"))
|
|
dumpProcedureData(COFF, Section);
|
|
}
|
|
return Error::success();
|
|
}
|
|
}
|
|
}
|
|
}
|