mirror of
https://github.com/Bareflank/hypervisor
synced 2026-08-17 06:23:04 -04:00
This patch addresses several bugs with the build system: - Lack of Windows support - Clang Tidy was not working properly - Making modifications to the source code would compile, but would not re-link resulting in issues - Removed excess file copying - Make clean now works - Removed unused or confusing folders in the build folder as part of the build process. It is now much easier to traverse the build folder for dependencies - The targets didn't work with extensions. This has been fixed. In addition, the following was also done under this patch - Intrinsics was moved to the top level and out of the VMM - Platform files are now in a folder called "platform" instead of "arch" - All subprojects have the same include/src/tests files structure - All intel specific code is properly organized to match intended namespacing - Removed dead code - A small part of the VMCS was converted to use the delegate pattern - All dependencies are now downloaded at configure time instead of compile time. - The cache directory is now cached by Travis CI - CMake output better matches old build system - Added make rebuild and separate clean targets to remove various parts of the build depending on needs. - Added targets for Clean, Tidy, Rebuild and Format for each subproject - Re-organized the cmake logic so that macros are not spread out - New validation removes unneeded complexity - Removed the need for the compiler wrapper, and in doing so, we now provide a simpilar set of toolchain files - Removed the need for Git repos. All external dependencies are downloaded using a zip or tarball - Libcxx and Libcxxabi are now in their own files. Much similar logic - Unit test CMake files have been greatly simplified - Each subproject is unaware of it's prefix and no long use VMM, USERSPACE or TEST variables in their cmake files - Fixed bugs with the flags - Renamed the varbiables in the default.cmake config to be more consistent and easier to follow in the rest of the code Signed-off-by: “rianquinn” <“rianquinn@gmail.com”>
617 lines
20 KiB
C++
617 lines
20 KiB
C++
//
|
|
// Bareflank Unwind Library
|
|
// Copyright (C) 2015 Assured Information Security, Inc.
|
|
//
|
|
// This library is free software; you can redistribute it and/or
|
|
// modify it under the terms of the GNU Lesser General Public
|
|
// License as published by the Free Software Foundation; either
|
|
// version 2.1 of the License, or (at your option) any later version.
|
|
//
|
|
// This library is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
// Lesser General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Lesser General Public
|
|
// License along with this library; if not, write to the Free Software
|
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
#ifndef EH_FRAME_H
|
|
#define EH_FRAME_H
|
|
|
|
#include <stdint.h>
|
|
#include <bfconstants.h>
|
|
#include <bfehframelist.h>
|
|
|
|
#include <registers_intel_x64.h>
|
|
|
|
class common_entry;
|
|
class ci_entry;
|
|
class fd_entry;
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Overview
|
|
// -----------------------------------------------------------------------------
|
|
//
|
|
// Exception Handling Framework (eh_frame) is based on the DWARF specification.
|
|
// This implementation uses the DWARF 4 specification, but the actual format
|
|
// is defined here (as there are some differences):
|
|
//
|
|
// https://refspecs.linuxfoundation.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic.pdf
|
|
//
|
|
// In addition to the above specification, Ian Lance Taylor has a great
|
|
// explanation of this section on his blog:
|
|
//
|
|
// http://www.airs.com/blog/archives/460
|
|
// http://www.airs.com/blog/archives/462
|
|
// http://www.airs.com/blog/archives/464
|
|
//
|
|
// and there is another good explanation of this information here:
|
|
//
|
|
// http://www.deadp0rk.com/2013/09/22/base_abi/
|
|
//
|
|
// Notes:
|
|
//
|
|
// - This implementation is not optimized for performance. Exception handling
|
|
// in bareflank should not be used for flow control, but rather for error
|
|
// handling (which should not happen often)
|
|
//
|
|
// - The specification is written for 32bit and 64bit. This implementation
|
|
// only supports 64bit.
|
|
//
|
|
// - readelf can be used to parse the .eh_frame section, and provide all of
|
|
// the information that this code should be capable of parsing. Use the
|
|
// --debug-dump=frames flag with a binary or shared module.
|
|
//
|
|
// - If you see a function that takes a char **addr, it will use the addr and
|
|
// then advance the addr based on the operation performed. This is because
|
|
// the way this spec is written, decoding everything is done in a linear
|
|
// fashion, and each operation is compressed, so you don't know how much to
|
|
// advance the pointer until you decode the operation.
|
|
//
|
|
// In addition to this implementation, there are two other implementations that
|
|
// could provide useful information:
|
|
//
|
|
// http://www.nongnu.org/libunwind/
|
|
// https://github.com/llvm-mirror/libunwind
|
|
//
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// DWARF Extensions (section 10.5)
|
|
// -----------------------------------------------------------------------------
|
|
//
|
|
// The Exception Handler Framework in our version of the LSB uses the DWARF 4
|
|
// spec, but does so by applying the following extensions
|
|
//
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// DWARF Exception Header Encoding (section 10.5.1)
|
|
// -----------------------------------------------------------------------------
|
|
//
|
|
// An EH encoding is a byte, with the lower 4 bits describing the format of the
|
|
// pointer, and the upper 4 bits describing how the pointer should be applied.
|
|
//
|
|
|
|
// Data Format
|
|
#define DW_EH_PE_absptr 0x00
|
|
#define DW_EH_PE_uleb128 0x01
|
|
#define DW_EH_PE_udata2 0x02
|
|
#define DW_EH_PE_udata4 0x03
|
|
#define DW_EH_PE_udata8 0x04
|
|
#define DW_EH_PE_sleb128 0x09
|
|
#define DW_EH_PE_sdata2 0x0A
|
|
#define DW_EH_PE_sdata4 0x0B
|
|
#define DW_EH_PE_sdata8 0x0C
|
|
|
|
// Data Application
|
|
#define DW_EH_PE_pcrel 0x10
|
|
#define DW_EH_PE_textrel 0x20
|
|
#define DW_EH_PE_datarel 0x30
|
|
#define DW_EH_PE_funcrel 0x40
|
|
#define DW_EH_PE_aligned 0x50
|
|
|
|
// Special
|
|
#define DW_EH_PE_omit 0xFF
|
|
|
|
/// Decode Pointer
|
|
///
|
|
/// Decodes a pointer located at addr, given the provided encoding scheme.
|
|
/// The whole reason this function exists is that for most pointers in an
|
|
/// executable, not all of the address bits are needed. For example, on a 64bit
|
|
/// system, most executables are well under 1GB, and thus, the vast majority
|
|
/// of address bits are not needed to relay an address. To save space, pointers
|
|
/// are encoded, and this function provides the decoding logic.
|
|
///
|
|
/// Note that for 64bit, you are only likely to PC relative addressing. This
|
|
/// is because in 64bit, all of the code is relocatable.
|
|
///
|
|
/// @param addr the location of the encoded pointer. Note that this takes a
|
|
/// double pointer. This is because the total size of the pointer is
|
|
/// not know until it is decoded. Therefore, this function not only decodes
|
|
/// but it also advances the address based on the size of the pointer.
|
|
/// @param encoding the scheme by which the pointer is encoded.
|
|
/// @return the resulting pointer
|
|
///
|
|
uint64_t decode_pointer(char **addr, uint64_t encoding);
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// DWARF Call Frame Instruction (CFI) Extensions (section 10.5.2)
|
|
// -----------------------------------------------------------------------------
|
|
//
|
|
// These extend the call frame instructions that are defined in the DWARF 4
|
|
// specification.
|
|
//
|
|
|
|
#define DW_CFA_GNU_args_size 0x2E
|
|
#define DW_CFA_GNU_negative_offset_extended 0x2F
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// .eh_frame Section (section 10.6.1)
|
|
// -----------------------------------------------------------------------------
|
|
//
|
|
// The .eh_frame section is located in each compiled unit (binary or shared
|
|
// module). It contains one or more Call Frame Information structures (not
|
|
// to be confusd with the Call Frame Instructions which are defined in the
|
|
// DWARF portion of this code). The CFI structures have the following format:
|
|
//
|
|
// CFI
|
|
// ---------------------
|
|
// - CIE -
|
|
// - FDE -
|
|
// - FDE -
|
|
// ---------------------
|
|
// - CIE -
|
|
// - FDE -
|
|
// - ... -
|
|
// ---------------------
|
|
//
|
|
// The size of the .eh_frame section dictates the number of CIEs in the CFI,
|
|
// which means that both the starting address of the .eh_frame section, and
|
|
// it's size must be available. The best way to view the difference between
|
|
// the CIE and the FDE is, the FDE contains the information needed to unwind
|
|
// the stack for a given program counter (PC), and the CIE contains the
|
|
// information for an FDE that just do happens to be shared with other FDEs.
|
|
// For this reason, when looking for the FDE associated with your PC, you
|
|
// basically scan looking for FDEs, and just ignore an entry if it happens to
|
|
// be a CIE instead of an FDE. Once you find the FDE you care about, you can
|
|
// use the CIE offset stored in the FDE to find the CIE associated with that
|
|
// FDE. It probably would have made sense for GCC to place the all of the CIEs
|
|
// at the end of the list to make scanning easier, but that's not the case.
|
|
//
|
|
// Both the CIE and the FDE's have some fields that are encoded using both
|
|
// signed and unsigned LEB128 format. What this means is the value could be
|
|
// 8bits, or even 64bits, it all depends on how many bits are needed to encode
|
|
// the value, which provides a means to compress the information in each CIE
|
|
// and FDE. For example, a length field my contain the value 0x40, which only
|
|
// takes up a byte. The length could also be 0x4000000. The problem is, in a
|
|
// lot of cases, len might end up being small most of the time, but because
|
|
// it could be larger, 64bits would need to be allocated every time. To
|
|
// prevent this, LEB128 breaks up the number in a way that allows it to be
|
|
// encoded in a compressed form, thus taking up less space. In our code, when
|
|
// you see a LEB128, we store the decoded values as 64bits. There is a really
|
|
// good explanation of this here:
|
|
//
|
|
// https://en.wikipedia.org/wiki/LEB128
|
|
|
|
/// Exception Header Framework
|
|
///
|
|
/// This is a pretty simple class. The entire .eh_frame ELF section exists
|
|
/// to provide a list of FDEs that describe a specific call frame for
|
|
/// unwinding. This class provides a means to lookup an FDE for any PC that
|
|
/// the code might be executing from.
|
|
///
|
|
class eh_frame
|
|
{
|
|
public:
|
|
static fd_entry find_fde(register_state *state);
|
|
};
|
|
|
|
/// Common Entry
|
|
///
|
|
/// Both the CIE and the FDE share the length field. The length field can have
|
|
/// two different sizes, so this class provides a simple way to describe the
|
|
/// CIE/FDE itself (entry start / end), the portion of the CIE/FDE that does
|
|
/// not include the length field (the payload), as well as some other
|
|
/// functions for convenience.
|
|
///
|
|
class common_entry
|
|
{
|
|
public:
|
|
|
|
/// Default Constructor
|
|
///
|
|
/// Creates an invalid CIE/FDE
|
|
///
|
|
common_entry();
|
|
|
|
/// Constructor
|
|
///
|
|
/// Creates an invalid CIE/FDE, but stores the location of the beginning
|
|
/// of the .eh_frame section.
|
|
///
|
|
explicit common_entry(const eh_frame_t &eh_frame);
|
|
|
|
/// Destructor
|
|
///
|
|
virtual ~common_entry() = default;
|
|
|
|
/// Default Move Constructor
|
|
///
|
|
common_entry(common_entry &&) noexcept = default;
|
|
|
|
/// Default Copy Constructor
|
|
///
|
|
common_entry(const common_entry &) = default;
|
|
|
|
/// Default Move Assignment Operator
|
|
///
|
|
common_entry &operator=(common_entry &&) noexcept = default;
|
|
|
|
/// Default Copy Assignment Operator
|
|
///
|
|
common_entry &operator=(const common_entry &) = default;
|
|
|
|
/// Next CIE/FDE
|
|
///
|
|
/// Moves to the next CIE/FDE in the list. If the CIE/FDE is invalid, this
|
|
/// function does nothing. If is possible that this function could result
|
|
/// in an invalid CIE/FDE, which can be used in a for loop to determine
|
|
/// when the end of the list has been approached.
|
|
///
|
|
/// @return next CIE/FDE
|
|
///
|
|
common_entry &operator++();
|
|
|
|
/// Valid
|
|
///
|
|
/// @return returns true if the CIE/FDE is valid
|
|
///
|
|
operator bool() const
|
|
{ return m_entry_start != nullptr; }
|
|
|
|
/// Is CIE
|
|
///
|
|
/// @return returns true if this is a CIE
|
|
///
|
|
bool is_cie() const
|
|
{ return m_is_cie; }
|
|
|
|
/// Is FDE
|
|
///
|
|
/// @return returns true is this is an FDE
|
|
///
|
|
bool is_fde() const
|
|
{ return !m_is_cie; }
|
|
|
|
/// Entry Start
|
|
///
|
|
/// @return returns the start of the CIE/FDE in memory
|
|
///
|
|
char *entry_start() const
|
|
{ return m_entry_start; }
|
|
|
|
/// Entry End
|
|
///
|
|
/// @return returns the end of the CIE/FDE in memory
|
|
///
|
|
char *entry_end() const
|
|
{ return m_entry_end; }
|
|
|
|
/// Payload Start
|
|
///
|
|
/// @return returns the start of the CIE/FDE's payload in memory, which
|
|
/// is the portion of the CIE/FDE that does not contain the length field
|
|
///
|
|
char *payload_start() const
|
|
{ return m_payload_start; }
|
|
|
|
/// Payload End
|
|
///
|
|
/// @return returns the end of the CIE/FDE's payload in memory, which
|
|
/// is the portion of the CIE/FDE that does not contain the length field.
|
|
/// Note that this should be the same as entry_end
|
|
///
|
|
char *payload_end() const
|
|
{ return m_payload_end; }
|
|
|
|
/// EH Framework
|
|
///
|
|
/// @return returns the .eh_frame associated with this CIE/FDE
|
|
///
|
|
eh_frame_t eh_frame() const
|
|
{ return m_eh_frame; }
|
|
|
|
protected:
|
|
virtual void parse(char *addr) = 0;
|
|
void non_virtual_parse(char *addr);
|
|
|
|
protected:
|
|
bool m_is_cie;
|
|
|
|
char *m_entry_start;
|
|
char *m_entry_end;
|
|
|
|
char *m_payload_start;
|
|
char *m_payload_end;
|
|
|
|
eh_frame_t m_eh_frame;
|
|
};
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Common Information Entry (CIE) Format (section 10.6.1.1)
|
|
// -----------------------------------------------------------------------------
|
|
//
|
|
|
|
/// Common Information Entry
|
|
///
|
|
/// The goal of the CIE is to provide a set of DWARF instructions that are the
|
|
/// same for all FDEs (at least this is how it is documented). What this really
|
|
/// means is each CIE defines a different function prolog that the compiler
|
|
/// has, and there are not many of them. The CIE also contains other information
|
|
/// that is shared by all of the FDEs, like the personality function, and the
|
|
/// location of the LSDA, and the different encoding types.
|
|
///
|
|
/// When parsing the .eh_frame section, it's best to actually skip over the CIEs
|
|
/// and only look for FDEs. Once you have the FDE you want, you can use the
|
|
/// pointer in the FDE to locate the CIE associated with that FDE.
|
|
///
|
|
class ci_entry : public common_entry
|
|
{
|
|
public:
|
|
|
|
/// Default Constructor
|
|
///
|
|
/// Creates an invalid CIE
|
|
///
|
|
ci_entry();
|
|
|
|
/// Constructor
|
|
///
|
|
/// Creates an invalid CIE, but stores the location of the beginning
|
|
/// of the .eh_frame section.
|
|
///
|
|
explicit ci_entry(const eh_frame_t &eh_frame);
|
|
|
|
/// Constructor
|
|
///
|
|
/// Creates a valid CIE if the addr that is provided points to a valid
|
|
/// CIE in the .eh_frame provided
|
|
///
|
|
explicit ci_entry(const eh_frame_t &eh_frame, void *addr);
|
|
|
|
/// Destructor
|
|
///
|
|
~ci_entry() override = default;
|
|
|
|
/// Default Move Constructor
|
|
///
|
|
ci_entry(ci_entry &&) noexcept = default;
|
|
|
|
/// Default Copy Constructor
|
|
///
|
|
ci_entry(const ci_entry &) = default;
|
|
|
|
/// Default Move Assignment Operator
|
|
///
|
|
ci_entry &operator=(ci_entry &&) noexcept = default;
|
|
|
|
/// Default Copy Assignment Operator
|
|
///
|
|
ci_entry &operator=(const ci_entry &) = default;
|
|
|
|
/// Augmentation String
|
|
///
|
|
/// Each CIE can provide different types of information, and to provide a
|
|
/// means to compress this information, the DWARF spec defines which
|
|
/// fields each CIE/FDE actually provides using the augmentation string.
|
|
/// Each character in the string defines what the next thing in the
|
|
/// augmentation data portion of the CIE/FDE is. For more information on
|
|
/// this, please see the specification.
|
|
///
|
|
/// @return pointer to the augmentation string
|
|
///
|
|
char augmentation_string(uint64_t index) const
|
|
{ return m_augmentation_string[index]; }
|
|
|
|
/// Code Alignment
|
|
///
|
|
/// @return returns how the code is aligned. On x86_64, this is usually
|
|
/// just 1, which means it's pointless.
|
|
///
|
|
uint64_t code_alignment() const
|
|
{ return m_code_alignment; }
|
|
|
|
/// Data Alignment
|
|
///
|
|
/// @return returns how the data is aligned. On x86_64, this is usually
|
|
/// -8 bytes, which means that each register is 8 bytes, growing down
|
|
/// from the CFA
|
|
///
|
|
int64_t data_alignment() const
|
|
{ return m_data_alignment; }
|
|
|
|
/// Return Address Register
|
|
///
|
|
/// @return returns the instruction pointer register index. The System
|
|
/// V 64bit ABI defines this as 16 (rip)
|
|
///
|
|
uint64_t return_address_reg() const
|
|
{ return m_return_address_reg; }
|
|
|
|
/// Pointer Encoding
|
|
///
|
|
/// @return returns how each pointer is encoded. This is defined by
|
|
/// the eh_frame spec (not the DWARF spec) and is usually a PC relative
|
|
/// encoding, as x86_64 code is relocatable.
|
|
///
|
|
uint64_t pointer_encoding() const
|
|
{ return m_pointer_encoding; }
|
|
|
|
/// LSDA Encoding
|
|
///
|
|
/// @return returns how the LSDA is encoded
|
|
///
|
|
uint64_t lsda_encoding() const
|
|
{ return m_lsda_encoding; }
|
|
|
|
/// Personality Encoding
|
|
///
|
|
/// @return returns how the personality function's pointer is encoded.
|
|
///
|
|
uint64_t personality_encoding() const
|
|
{ return m_personality_encoding; }
|
|
|
|
/// Personality Function
|
|
///
|
|
/// @return returns a pointer to the personality function. The personality
|
|
/// function tells the unwinder when to stop searching for the catch
|
|
/// blocks
|
|
///
|
|
uint64_t personality_function() const
|
|
{ return m_personality_function; }
|
|
|
|
/// Initial Instructions
|
|
///
|
|
/// @return returns a pointer to the initial DWARF instructions that
|
|
/// usually define the function prologs that the compiler creates
|
|
///
|
|
char *initial_instructions() const
|
|
{ return m_initial_instructions; }
|
|
|
|
protected:
|
|
void parse(char *addr) override;
|
|
void non_virtual_parse(char *addr);
|
|
|
|
public:
|
|
const char *m_augmentation_string;
|
|
uint64_t m_code_alignment;
|
|
int64_t m_data_alignment;
|
|
uint64_t m_return_address_reg;
|
|
uint64_t m_pointer_encoding;
|
|
uint64_t m_lsda_encoding;
|
|
uint64_t m_personality_encoding;
|
|
uint64_t m_personality_function;
|
|
char *m_initial_instructions;
|
|
};
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Frame Description Entry (FDE) (section 10.6.1.2)
|
|
// -----------------------------------------------------------------------------
|
|
|
|
/// Frame Description Entry
|
|
///
|
|
/// The FDE provides all of the instructions for restoring the state of the
|
|
/// registers when unwinding the stack. There is one FDE for each call frame
|
|
/// that is created by the compiler (which is usually a function, but can
|
|
/// include more than just functions in practice).
|
|
///
|
|
class fd_entry : public common_entry
|
|
{
|
|
public:
|
|
|
|
/// Default Constructor
|
|
///
|
|
/// Creates an invalid FDE
|
|
///
|
|
fd_entry();
|
|
|
|
/// Constructor
|
|
///
|
|
/// Creates an invalid FDE, but stores the location of the beginning
|
|
/// of the .eh_frame section.
|
|
///
|
|
explicit fd_entry(const eh_frame_t &eh_frame);
|
|
|
|
/// Constructor
|
|
///
|
|
/// Creates a valid FDE if the addr that is provided points to a valid
|
|
/// FDE in the .eh_frame provided
|
|
///
|
|
explicit fd_entry(const eh_frame_t &eh_frame, void *addr);
|
|
|
|
/// Destructor
|
|
///
|
|
~fd_entry() override = default;
|
|
|
|
/// Default Move Constructor
|
|
///
|
|
fd_entry(fd_entry &&) noexcept = default;
|
|
|
|
/// Default Copy Constructor
|
|
///
|
|
fd_entry(const fd_entry &) = default;
|
|
|
|
/// Default Move Assignment Operator
|
|
///
|
|
fd_entry &operator=(fd_entry &&) noexcept = default;
|
|
|
|
/// Default Copy Assignment Operator
|
|
///
|
|
fd_entry &operator=(const fd_entry &) = default;
|
|
|
|
/// Is PC In Range
|
|
///
|
|
/// Note: the range for the PC is not 0 indexed (fails if you attempt
|
|
/// to code this as >= && < instead of > && <=). The test case is a
|
|
/// function that does nothing but throws. The compiler will emit code
|
|
/// without an epilogue, and the range will include the address of the
|
|
/// next instruction which is the start of another function.
|
|
///
|
|
/// @param pc the program counter (on x86_64 this is rip) to test
|
|
/// @return returns true if this FDE contains the instructions for the
|
|
/// PC provided.
|
|
///
|
|
bool is_in_range(uint64_t pc) const
|
|
{ return (pc > m_pc_begin) && (pc <= m_pc_begin + m_pc_range); }
|
|
|
|
/// PC Begin
|
|
///
|
|
/// @return returns the beginning of the FDE's range
|
|
///
|
|
uint64_t pc_begin() const
|
|
{ return m_pc_begin; }
|
|
|
|
/// PC Range
|
|
///
|
|
/// @return returns the range of the FDE
|
|
///
|
|
uint64_t pc_range() const
|
|
{ return m_pc_range; }
|
|
|
|
/// LSDA Location
|
|
///
|
|
/// @return returns the location of the LSDA given the encoding defined
|
|
/// in the CIE
|
|
///
|
|
uint64_t lsda() const
|
|
{ return m_lsda; }
|
|
|
|
/// Instructions
|
|
///
|
|
/// @return returns the location of the DWARF instructions that define how
|
|
/// to unwind the CFA that this FDE defines.
|
|
///
|
|
char *instructions() const
|
|
{ return m_instructions; }
|
|
|
|
/// CIE
|
|
///
|
|
/// @return returns the CIE associated with this FDE.
|
|
///
|
|
const ci_entry &cie() const
|
|
{ return m_cie; }
|
|
|
|
protected:
|
|
void parse(char *addr) override;
|
|
void non_virtual_parse(char *addr);
|
|
|
|
private:
|
|
uint64_t m_pc_begin;
|
|
uint64_t m_pc_range;
|
|
uint64_t m_lsda;
|
|
char *m_instructions;
|
|
|
|
ci_entry m_cie;
|
|
};
|
|
|
|
#endif
|