You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
Allows fast iteration of C++ changes without restarting the application. To use, select the "Live Coding (Experimental)" mode from the drop down menu next to the editor's compile button, or type "LiveCoding" into the console for a monolithic build. Press Ctrl+Alt+F11 to find changes and compile.
Changes vs standalone Live++ version:
* UBT is used to execute builds. This allows standard UE4 adaptive unity mode, allows us to reuse object files when we do regular builds, supports using any build executor allowed by UBT (XGE, SNDBS, etc..).
* Adding new source files is supported.
* Custom visualizer for FNames is supported via a weakly linked symbol in a static library (Engine/Extras/NatvisHelpers).
* Settings are exposed in the editor's project settings dialog.
* Standalone application has been rewritten as a Slate app ("LiveCodingConsole"). There is an additional option to start the program as hidden, where it will not be visible until Ctrl+Alt+F11 is hit. Similarly, closing the window will hide it instead of closing the application.
* Does not require a standalone licensed version of Live++.
Known issues:
* Does not currently support class layout changes / object reinstancing
#rb none
[FYI] Marc.Audy, Stefan.Boberg, Nick.Penwarden
#jira
#ROBOMERGE-OWNER: ben.marsh
#ROBOMERGE-AUTHOR: ben.marsh
#ROBOMERGE-SOURCE: CL 5304722 in //UE4/Release-4.22/... via CL 5309051
#ROBOMERGE-BOT: BUILD (Main -> Dev-Build)
[CL 5325555 by ben marsh in Dev-Build branch]
486 lines
15 KiB
C++
486 lines
15 KiB
C++
// Copyright 2011-2019 Molecular Matters GmbH, all rights reserved.
|
|
|
|
#pragma once
|
|
|
|
#include "CoreTypes.h"
|
|
#include "LC_ImmutableString.h"
|
|
#include "LC_MemoryFile.h"
|
|
#include "LC_Types.h"
|
|
#include "LC_Logging.h"
|
|
#include "Windows/WindowsHWrapper.h"
|
|
|
|
#undef RELATIVE
|
|
#undef OPTIONAL
|
|
|
|
struct PoolAllocatorSingleThreadPolicy;
|
|
template <typename T> class PoolAllocator;
|
|
|
|
namespace coff
|
|
{
|
|
struct ObjFile
|
|
{
|
|
ImmutableString filename;
|
|
file::MemoryFile* memoryFile;
|
|
|
|
LC_DISABLE_ASSIGNMENT(ObjFile);
|
|
};
|
|
|
|
struct LibFile
|
|
{
|
|
ImmutableString filename;
|
|
file::MemoryFile* memoryFile;
|
|
|
|
LC_DISABLE_ASSIGNMENT(LibFile);
|
|
};
|
|
|
|
struct SymbolType
|
|
{
|
|
enum Enum : uint8_t
|
|
{
|
|
EXTERNAL_DATA,
|
|
EXTERNAL_FUNCTION,
|
|
STATIC_DATA,
|
|
STATIC_FUNCTION,
|
|
UNKNOWN_DATA,
|
|
UNKNOWN_FUNCTION
|
|
};
|
|
};
|
|
|
|
struct Relocation
|
|
{
|
|
struct Type
|
|
{
|
|
enum Enum : uint16_t
|
|
{
|
|
#if LC_64_BIT
|
|
/*
|
|
From the COFF spec: "5.2.1. Type Indicators"
|
|
The following relocation types are defined for x64 and compatible processors.
|
|
Constant Description
|
|
IMAGE_REL_AMD64_ABSOLUTE The relocation is ignored.
|
|
IMAGE_REL_AMD64_ADDR64 The 64-bit VA of the relocation target.
|
|
IMAGE_REL_AMD64_ADDR32 The 32-bit VA of the relocation target.
|
|
IMAGE_REL_AMD64_ADDR32NB The 32-bit address without an image base (RVA).
|
|
IMAGE_REL_AMD64_REL32 The 32-bit relative address from the byte following the relocation.
|
|
IMAGE_REL_AMD64_REL32_1 The 32-bit address relative to byte distance 1 from the relocation.
|
|
IMAGE_REL_AMD64_REL32_2 The 32-bit address relative to byte distance 2 from the relocation.
|
|
IMAGE_REL_AMD64_REL32_3 The 32-bit address relative to byte distance 3 from the relocation.
|
|
IMAGE_REL_AMD64_REL32_4 The 32-bit address relative to byte distance 4 from the relocation.
|
|
IMAGE_REL_AMD64_REL32_5 The 32-bit address relative to byte distance 5 from the relocation.
|
|
IMAGE_REL_AMD64_SECTION The 16-bit section index of the section that contains the target.
|
|
This is used to support debugging information.
|
|
IMAGE_REL_AMD64_SECREL The 32-bit offset of the target from the beginning of its section.
|
|
This is used to support debugging information and static thread local storage.
|
|
IMAGE_REL_AMD64_SECREL7 A 7-bit unsigned offset from the base of the section that contains the target.
|
|
IMAGE_REL_AMD64_TOKEN CLR tokens.
|
|
IMAGE_REL_AMD64_SREL32 A 32-bit signed span-dependent value emitted into the object.
|
|
IMAGE_REL_AMD64_PAIR A pair that must immediately follow every span-dependent value.
|
|
IMAGE_REL_AMD64_SSPAN32 A 32-bit signed span-dependent value that is applied at link time.
|
|
|
|
This means that only a handful of relocation types need to be supported.
|
|
*/
|
|
|
|
// also used in 32-bit
|
|
RELATIVE = IMAGE_REL_AMD64_REL32,
|
|
SECTION_RELATIVE = IMAGE_REL_AMD64_SECREL,
|
|
VA_32 = IMAGE_REL_AMD64_ADDR32,
|
|
RVA_32 = IMAGE_REL_AMD64_ADDR32NB,
|
|
|
|
// 64-bit only
|
|
RELATIVE_OFFSET_1 = IMAGE_REL_AMD64_REL32_1,
|
|
RELATIVE_OFFSET_2 = IMAGE_REL_AMD64_REL32_2,
|
|
RELATIVE_OFFSET_3 = IMAGE_REL_AMD64_REL32_3,
|
|
RELATIVE_OFFSET_4 = IMAGE_REL_AMD64_REL32_4,
|
|
RELATIVE_OFFSET_5 = IMAGE_REL_AMD64_REL32_5,
|
|
VA_64 = IMAGE_REL_AMD64_ADDR64,
|
|
#else
|
|
/*
|
|
From the COFF spec: "5.2.1. Type Indicators"
|
|
The following relocation type indicators are defined for Intel 386 and compatible
|
|
processors.
|
|
Constant Description
|
|
IMAGE_REL_I386_ABSOLUTE The relocation is ignored.
|
|
IMAGE_REL_I386_DIR16 Not supported.
|
|
IMAGE_REL_I386_REL16 Not supported.
|
|
IMAGE_REL_I386_DIR32 The target's 32-bit VA.
|
|
IMAGE_REL_I386_DIR32NB The target's 32-bit RVA.
|
|
IMAGE_REL_I386_SEG12 Not supported.
|
|
IMAGE_REL_I386_SECTION The 16-bit section index of the section that contains the target.
|
|
This is used to support debugging information.
|
|
IMAGE_REL_I386_SECREL The 32-bit offset of the target from the beginning of its section.
|
|
This is used to support debugging information and static thread local storage.
|
|
IMAGE_REL_I386_TOKEN The CLR token.
|
|
IMAGE_REL_I386_SECREL7 A 7-bit offset from the base of the section that contains the target.
|
|
IMAGE_REL_I386_REL32 The 32-bit relative displacement to the target.
|
|
This supports the x86 relative branch and call instructions.
|
|
|
|
This means that only a handful of relocation types need to be supported.
|
|
*/
|
|
|
|
RELATIVE = IMAGE_REL_I386_REL32,
|
|
SECTION_RELATIVE = IMAGE_REL_I386_SECREL,
|
|
VA_32 = IMAGE_REL_I386_DIR32,
|
|
RVA_32 = IMAGE_REL_I386_DIR32NB,
|
|
#endif
|
|
UNKNOWN = 0xFFFFu
|
|
};
|
|
|
|
static const char* ToString(Enum value)
|
|
{
|
|
switch (value)
|
|
{
|
|
case Type::RELATIVE: return "RELATIVE";
|
|
case Type::SECTION_RELATIVE: return "SECTION_RELATIVE";
|
|
case Type::VA_32: return "VA_32";
|
|
case Type::RVA_32: return "RVA_32";
|
|
|
|
#if LC_64_BIT
|
|
case Type::RELATIVE_OFFSET_1: return "RELATIVE_OFFSET_1";
|
|
case Type::RELATIVE_OFFSET_2: return "RELATIVE_OFFSET_2";
|
|
case Type::RELATIVE_OFFSET_3: return "RELATIVE_OFFSET_3";
|
|
case Type::RELATIVE_OFFSET_4: return "RELATIVE_OFFSET_4";
|
|
case Type::RELATIVE_OFFSET_5: return "RELATIVE_OFFSET_5";
|
|
case Type::VA_64: return "VA_64";
|
|
#endif
|
|
|
|
case Type::UNKNOWN: return "UNKNOWN";
|
|
default: return "Invalid type";
|
|
}
|
|
}
|
|
|
|
// returns the byte distance to the position of where the relocation should be applied
|
|
static uint32_t GetByteDistance(Enum type)
|
|
{
|
|
switch (type)
|
|
{
|
|
case Type::RELATIVE:
|
|
return 0u;
|
|
|
|
#if LC_64_BIT
|
|
case Type::RELATIVE_OFFSET_1:
|
|
return 1u;
|
|
|
|
case Type::RELATIVE_OFFSET_2:
|
|
return 2u;
|
|
|
|
case Type::RELATIVE_OFFSET_3:
|
|
return 3u;
|
|
|
|
case Type::RELATIVE_OFFSET_4:
|
|
return 4u;
|
|
|
|
case Type::RELATIVE_OFFSET_5:
|
|
return 5u;
|
|
|
|
case Type::VA_64:
|
|
#endif
|
|
case Type::SECTION_RELATIVE:
|
|
case Type::VA_32:
|
|
case Type::RVA_32:
|
|
case Type::UNKNOWN:
|
|
default:
|
|
LC_ERROR_DEV("Unexpected relocation type %s (%d)", ToString(type), type);
|
|
return 0u;
|
|
}
|
|
}
|
|
};
|
|
|
|
uint32_t dstSymbolNameIndex; // name of the symbol that the relocation points to
|
|
uint32_t srcRva; // relative to start of source symbol
|
|
uint32_t dstOffset; // the offset to the destination symbol to which the relocation is applied
|
|
// e.g. a write to a 64-bit integer has two relocations: both to the same symbol, but one at offset 0, the other at offset 4
|
|
int32_t dstSectionIndex; // index of the section the destination symbol belongs to
|
|
Type::Enum type; // type of the relocation
|
|
SymbolType::Enum srcSymbolType; // symbol type of the source symbol, cached (does not increase struct size)
|
|
SymbolType::Enum dstSymbolType; // symbol type of the destination symbol, cached (does not increase struct size)
|
|
};
|
|
|
|
struct Symbol
|
|
{
|
|
uint32_t nameIndex;
|
|
uint32_t rva;
|
|
uint32_t sectionIndex;
|
|
SymbolType::Enum type;
|
|
types::vector<Relocation*> relocations;
|
|
};
|
|
|
|
struct Section
|
|
{
|
|
ImmutableString name;
|
|
uint32_t rawDataSize;
|
|
uint32_t rawDataRva;
|
|
uint32_t characteristics; // TODO: can save memory by putting characteristics and selection into bitfields
|
|
uint8_t comdatSelection; // COMDAT selection specification, if any (0 means this is not a COMDAT section)
|
|
|
|
LC_DISABLE_ASSIGNMENT(Section);
|
|
};
|
|
|
|
struct CrtSection
|
|
{
|
|
ImmutableString name;
|
|
uint32_t rawDataSize;
|
|
uint32_t rawDataRva;
|
|
types::vector<Symbol*> symbols; // no symbol ownership
|
|
|
|
LC_DISABLE_ASSIGNMENT(CrtSection);
|
|
};
|
|
|
|
struct CoffDB
|
|
{
|
|
// the string table
|
|
types::vector<ImmutableString> stringTable;
|
|
|
|
// an array of all sections
|
|
types::vector<Section> sections;
|
|
|
|
// an array of all symbols.
|
|
// symbol ownership.
|
|
types::vector<Symbol*> symbols;
|
|
|
|
// lookup-table from name index to corresponding Symbol*.
|
|
// no symbol ownership.
|
|
types::vector<Symbol*> indexToSymbol;
|
|
|
|
// C/C++ runtime sections
|
|
types::vector<CrtSection> crtSections;
|
|
|
|
// allocators
|
|
PoolAllocator<PoolAllocatorSingleThreadPolicy>* symbolAllocator;
|
|
PoolAllocator<PoolAllocatorSingleThreadPolicy>* relocationAllocator;
|
|
};
|
|
|
|
struct LibEntry
|
|
{
|
|
ImmutableString objPath; // path of the .obj file stored in the archive
|
|
uint64_t offset; // offset into the file at which the COFF is stored
|
|
|
|
LC_DISABLE_ASSIGNMENT(LibEntry);
|
|
};
|
|
|
|
struct LibDB
|
|
{
|
|
types::vector<ImmutableString> exportedSymbols; // all symbols exported by the library, alphabetically sorted
|
|
types::vector<LibEntry> libEntries;
|
|
};
|
|
|
|
struct UnresolvedSymbolDB
|
|
{
|
|
types::vector<ImmutableString> symbols;
|
|
types::vector<uint32_t> symbolIndex;
|
|
};
|
|
|
|
struct ExternalSymbolDB
|
|
{
|
|
types::vector<ImmutableString> symbols;
|
|
types::vector<SymbolType::Enum> types;
|
|
};
|
|
|
|
|
|
|
|
struct RawSection
|
|
{
|
|
IMAGE_SECTION_HEADER header;
|
|
void* data;
|
|
types::vector<IMAGE_RELOCATION> relocations;
|
|
types::vector<IMAGE_LINENUMBER> lineNumbers;
|
|
bool wasRemoved;
|
|
bool isSelectAnyComdat;
|
|
|
|
RawSection(void)
|
|
: header()
|
|
, data(nullptr)
|
|
, relocations()
|
|
, lineNumbers()
|
|
, wasRemoved(false)
|
|
, isSelectAnyComdat(false)
|
|
{
|
|
}
|
|
|
|
// move constructor
|
|
RawSection(RawSection&& other)
|
|
: header(std::move(other.header))
|
|
, data(std::move(other.data))
|
|
, relocations(std::move(other.relocations))
|
|
, lineNumbers(std::move(other.lineNumbers))
|
|
, wasRemoved(std::move(other.wasRemoved))
|
|
, isSelectAnyComdat(std::move(other.isSelectAnyComdat))
|
|
{
|
|
other.data = nullptr;
|
|
}
|
|
|
|
LC_DISABLE_COPY(RawSection);
|
|
LC_DISABLE_ASSIGNMENT(RawSection);
|
|
LC_DISABLE_MOVE_ASSIGNMENT(RawSection);
|
|
};
|
|
|
|
struct RawStringTable
|
|
{
|
|
char* data;
|
|
uint32_t size;
|
|
};
|
|
|
|
struct RawCoff
|
|
{
|
|
types::vector<RawSection> sections;
|
|
types::vector<ImmutableString> stringTable;
|
|
|
|
// indexed by section index, gives all section indices of COMDAT sections which are associated with this section
|
|
types::unordered_map<uint32_t, types::vector<uint32_t>> associatedComdatSections;
|
|
|
|
RawStringTable rawStringTable;
|
|
uint64_t size;
|
|
uint32_t type;
|
|
};
|
|
|
|
struct RawCoffRegular : public RawCoff
|
|
{
|
|
static const uint32_t TYPE = 0u;
|
|
|
|
IMAGE_FILE_HEADER header;
|
|
types::vector<IMAGE_SYMBOL> symbols;
|
|
};
|
|
|
|
struct RawCoffBigObj : public RawCoff
|
|
{
|
|
static const uint32_t TYPE = 1u;
|
|
|
|
ANON_OBJECT_HEADER_BIGOBJ header;
|
|
types::vector<IMAGE_SYMBOL_EX> symbols;
|
|
};
|
|
|
|
|
|
struct ReadFlags
|
|
{
|
|
enum Enum
|
|
{
|
|
NONE = 0,
|
|
GENERATE_ANS_NAME_FROM_UNIQUE_ID = 1
|
|
};
|
|
};
|
|
|
|
|
|
struct SymbolRemovalStrategy
|
|
{
|
|
enum Enum : SHORT
|
|
{
|
|
MSVC_COMPATIBLE = IMAGE_SYM_DEBUG,
|
|
LLD_COMPATIBLE = IMAGE_SYM_ABSOLUTE
|
|
};
|
|
};
|
|
|
|
|
|
// LIFETIME
|
|
ObjFile* OpenObj(const wchar_t* filename);
|
|
void CloseObj(ObjFile*& file);
|
|
|
|
LibFile* OpenLib(const wchar_t* filename);
|
|
void CloseLib(LibFile*& file);
|
|
|
|
|
|
// UPDATE
|
|
RawCoff* ReadRaw(ObjFile* file, uint32_t uniqueId, ReadFlags::Enum readFlags);
|
|
void WriteRaw(const wchar_t* filename, const RawCoff* rawCoff, SymbolRemovalStrategy::Enum removalStrategy);
|
|
void DestroyRaw(RawCoff* rawCoff);
|
|
|
|
|
|
size_t GetSymbolCount(const RawCoff* rawCoff);
|
|
size_t GetSectionCount(const RawCoff* rawCoff);
|
|
|
|
size_t GetAuxSymbolCount(const RawCoff* rawCoff, size_t symbolIndex);
|
|
SymbolType::Enum GetSymbolType(const RawCoff* rawCoff, size_t index);
|
|
const ImmutableString& GetSymbolName(const RawCoff* rawCoff, size_t index);
|
|
uint32_t GetSymbolSectionIndex(const RawCoff* rawCoff, size_t index);
|
|
|
|
bool IsAbsoluteSymbol(const RawCoff* rawCoff, size_t index);
|
|
bool IsDebugSymbol(const RawCoff* rawCoff, size_t index);
|
|
bool IsSectionSymbol(const RawCoff* rawCoff, size_t index);
|
|
bool IsUndefinedSymbol(const RawCoff* rawCoff, size_t index);
|
|
bool IsRemovedSymbol(const RawCoff* rawCoff, size_t index, SymbolRemovalStrategy::Enum removalStrategy);
|
|
|
|
bool IsSelectAnyComdatSection(const RawCoff* rawCoff, size_t sectionIndex);
|
|
|
|
|
|
// extracts directives from a COFF file
|
|
types::vector<std::string> ExtractLinkerDirectives(const ObjFile* file);
|
|
|
|
// extracts directives from a raw COFF file
|
|
types::vector<std::string> ExtractLinkerDirectives(const RawCoff* rawCoff);
|
|
|
|
// replaces linker directives in the raw COFF file
|
|
void ReplaceLinkerDirectives(RawCoff* rawCoff, const types::vector<std::string>& directives);
|
|
|
|
|
|
// removes a symbol
|
|
void RemoveSymbol(RawCoff* rawCoff, size_t symbolIndex, SymbolRemovalStrategy::Enum removalStrategy);
|
|
|
|
// remove all relocations to the symbol with the given index
|
|
void RemoveRelocations(RawCoff* rawCoff, size_t symbolIndex);
|
|
|
|
// removes a section with the given index
|
|
void RemoveSection(RawCoff* rawCoff, size_t sectionIndex);
|
|
|
|
// removes all COMDAT sections that have the given section index as their associated section.
|
|
// (e.g. a COMDAT section with associative section 0x5 only needs to become part of the image if
|
|
// section 5 is also part of the image).
|
|
void RemoveAssociatedComdatSections(RawCoff* rawCoff, size_t sectionIndex);
|
|
|
|
|
|
// unique ID must uniquely identify this ObjFile. each obj file with a unique name must have a unique ID
|
|
UnresolvedSymbolDB* GatherUnresolvedSymbolDatabase(ObjFile* file, uint32_t uniqueId, coff::ReadFlags::Enum readFlags);
|
|
void DestroyDatabase(UnresolvedSymbolDB* db);
|
|
|
|
// unique ID must uniquely identify this ObjFile. each obj file with a unique name must have a unique ID
|
|
ExternalSymbolDB* GatherExternalSymbolDatabase(ObjFile* file, uint32_t uniqueId, coff::ReadFlags::Enum readFlags);
|
|
void DestroyDatabase(ExternalSymbolDB* db);
|
|
|
|
|
|
// unique ID must uniquely identify this ObjFile. each obj file with a unique name must have a unique ID
|
|
CoffDB* GatherDatabase(ObjFile* file, uint32_t uniqueId, ReadFlags::Enum readFlags);
|
|
void DestroyDatabase(CoffDB* db);
|
|
|
|
LibDB* GatherDatabase(LibFile* file);
|
|
void DestroyDatabase(LibDB* db);
|
|
|
|
// loads the COFF database from an .obj contained in a .lib
|
|
CoffDB* GatherDatabase(LibFile* file, const LibDB* libDb, const ImmutableString& objPath);
|
|
|
|
|
|
size_t GetIndexCount(const CoffDB* coffDb);
|
|
const Symbol* GetSymbolByIndex(const CoffDB* coffDb, size_t index);
|
|
|
|
const ImmutableString& GetSymbolName(const CoffDB* coffDb, const Symbol* symbol);
|
|
const ImmutableString& GetRelocationDstSymbolName(const CoffDB* coffDb, const Relocation* relocation);
|
|
const ImmutableString& GetUnresolvedSymbolName(const CoffDB* coffDb, size_t unresolvedSymbolIndex);
|
|
|
|
SymbolType::Enum GetRelocationSrcSymbolType(const Relocation* relocation);
|
|
SymbolType::Enum GetRelocationDstSymbolType(const Relocation* relocation);
|
|
|
|
|
|
// finds a CRT section with the given name and size. returns nullptr if not found or ambiguous
|
|
const CrtSection* FindCrtSection(const CoffDB* coffDb, const ImmutableString& sectionName, uint32_t sectionSize);
|
|
|
|
// finds all matching CRT sections
|
|
std::vector<const CrtSection*> FindMatchingCrtSections(const CoffDB* coffDb, const ImmutableString& sectionName, uint32_t sectionSize);
|
|
|
|
|
|
uint32_t FindCoffSuffix(const ImmutableString& symbolName);
|
|
|
|
|
|
// returns 0 if the relocation destination section is invalid
|
|
uint32_t GetRelocationDestinationSectionCharacteristics(const coff::CoffDB* coffDb, const coff::Relocation* relocation);
|
|
|
|
|
|
// ACCESS
|
|
const void* GetBaseAddress(const ObjFile* file);
|
|
|
|
bool IsFunctionSymbol(SymbolType::Enum type);
|
|
|
|
char GetCoffSuffix(void);
|
|
wchar_t GetWideCoffSuffix(void);
|
|
|
|
const ImmutableString& GetTlsSectionName(void);
|
|
|
|
|
|
bool IsInterestingSymbol(const ImmutableString& name);
|
|
}
|