#include "EhTable.h"
#include "Base/LogFile.h"
#include "Exception.h"
#include "ObjectModel/MObject.inline.h"
namespace MapleRuntime {
void* EHTable::ReadAbsPtr(const uint8_t* p) const
{
#ifdef __arm__
int32_t offset = *reinterpret_cast<const int32_t*>(p);
const uint8_t* newAddr = p + offset;
return reinterpret_cast<void*>(*reinterpret_cast<const uint32_t*>(newAddr));
#else
auto value = *reinterpret_cast<uint64_t*>(const_cast<uint8_t*>(p));
return reinterpret_cast<void*>(value);
#endif
}
void* EHTable::ReadUData4(const uint8_t* p) const
{
uint32_t value = *reinterpret_cast<uint32_t*>(reinterpret_cast<uintptr_t>(const_cast<uint8_t*>(p)));
return reinterpret_cast<void*>(value);
}
void* EHTable::ReadIndirPcRelSData4(const uint8_t* p) const
{
uint32_t offset = *reinterpret_cast<uint32_t*>(reinterpret_cast<uintptr_t>(const_cast<uint8_t*>(p)));
if (offset == 0) {
return nullptr;
}
uint64_t value = *reinterpret_cast<uint64_t*>(reinterpret_cast<uintptr_t>(const_cast<uint8_t*>(p + offset)));
return reinterpret_cast<void*>(value);
}
void* EHTable::ReadIndirPcRelSData8(const uint8_t* p) const
{
uint64_t offset = *reinterpret_cast<uint64_t*>(reinterpret_cast<uintptr_t>(const_cast<uint8_t*>(p)));
uint64_t value = *reinterpret_cast<uint64_t*>(reinterpret_cast<uintptr_t>(const_cast<uint8_t*>(p + offset)));
return reinterpret_cast<void*>(value);
}
uint64_t EHTable::ReadULEB128(const uint8_t** data)
{
uint64_t value = 0;
uint64_t shift = 0;
uint8_t byte = 0;
const uint8_t* p = *data;
do {
byte = *p++;
value |= (byte & VALUE_MASK) << shift;
shift += SHIFT_LENGTH;
} while ((byte & BYTE_MAX_BIT) != 0);
*data = p;
return value;
}
int64_t EHTable::ReadSLEB128(const uint8_t** data)
{
int64_t value = 0;
uint64_t shift = 0;
uint8_t byte = 0;
const uint8_t* p = *data;
do {
byte = *p++;
value |= (static_cast<uint64_t>(byte) & VALUE_MASK) << shift;
shift += SHIFT_LENGTH;
} while ((byte & BYTE_MAX_BIT) != 0);
constexpr uint8_t decodeBitsLen = 64;
constexpr uint8_t signedFlag = 0x40;
if ((shift < decodeBitsLen) && ((byte & signedFlag) != 0)) {
#if defined(__APPLE__)
value |= LLONG_MAX << shift;
#else
value |= ULLONG_MAX << shift;
#endif
}
return value;
}
void EHTable::BuildEHTable(const uint8_t* lsda)
{
const uint8_t* curPtr = lsda;
uint8_t lpStartEncoding = *curPtr++;
ttypeEncoding = curPtr++;
#ifdef _WIN64
MRT_ASSERT(*ttypeEncoding == static_cast<uint8_t>(TTypeEncoding::ABS_PTR), "Unsupported ttype enconding!");
#else
MRT_ASSERT(*ttypeEncoding == static_cast<uint8_t>(TTypeEncoding::ABS_PTR) ||
*ttypeEncoding == static_cast<uint8_t>(TTypeEncoding::U_DATA_4) ||
*ttypeEncoding == static_cast<uint8_t>(TTypeEncoding::INDIR_PC_REL_S_DATA_8) ||
*ttypeEncoding == static_cast<uint8_t>(TTypeEncoding::INDIR_PC_REL_S_DATA_4),
"Unsupported ttype enconding!");
#endif
uint64_t classInfoOffset = EHTable::ReadULEB128(&curPtr);
exceptionTypeStart = curPtr + classInfoOffset;
uint8_t callSiteEncoding = *curPtr++;
(void)lpStartEncoding;
(void)callSiteEncoding;
DLOG(EXCEPTION, "creat eh table lpStartEncoding : %u ttypeEncoding : %u callSiteEncoding : %u",
static_cast<uint32_t>(lpStartEncoding), static_cast<uint32_t>(*ttypeEncoding),
static_cast<uint32_t>(callSiteEncoding));
uint64_t callSiteTableLength = EHTable::ReadULEB128(&curPtr);
callSiteTableStart = curPtr;
callSiteTableEnd = callSiteTableStart + callSiteTableLength;
actionTableStart = callSiteTableEnd;
}
void EHTable::ScanEHTable(const uint32_t* pc, const ExceptionWrapper& eWrapper, const uint32_t* startIp,
ScanResult& result) const
{
ExceptionRef exceptionRef = eWrapper.GetExceptionRef();
uint64_t ip = reinterpret_cast<uint64_t>(pc) - 1;
uint64_t funcstart = reinterpret_cast<uint64_t>(startIp);
uint64_t ipOffset = ip - funcstart;
const uint8_t* curPtr = callSiteTableStart;
while (curPtr < callSiteTableEnd) {
uint64_t start = ReadULEB128(&curPtr);
uint64_t length = ReadULEB128(&curPtr);
uint64_t landingPad = ReadULEB128(&curPtr);
uint64_t actionEntry = ReadULEB128(&curPtr);
if ((start <= ipOffset) && (ipOffset < (start + length))) {
if (landingPad == 0) {
continue;
}
landingPad = reinterpret_cast<uintptr_t>(startIp) + landingPad;
if (actionEntry == 0) {
result.landingPad = landingPad;
return;
}
uint8_t exceptionTypeIndex = MatchActionTable(actionEntry, exceptionRef, *ttypeEncoding);
if (exceptionTypeIndex > 0) {
result.typeIndex = exceptionTypeIndex;
result.landingPad = landingPad;
result.isCaught = true;
return;
}
}
}
}
uint8_t EHTable::MatchActionTable(uint64_t actionEntryIdx, const ExceptionRef& exceptionRef, uint8_t flag) const
{
const uint8_t* actionPtr = actionTableStart + (actionEntryIdx - 1);
for (;;) {
uint64_t typeIndex = *actionPtr++;
if (typeIndex > 0) {
void* catchType = nullptr;
switch (flag) {
case static_cast<int>(TTypeEncoding::ABS_PTR):
catchType = ReadAbsPtr(exceptionTypeStart - typeIndex * sizeof(uintptr_t));
break;
case static_cast<int>(TTypeEncoding::U_DATA_4):
catchType = ReadUData4(exceptionTypeStart - typeIndex * sizeof(int));
break;
case static_cast<int>(TTypeEncoding::INDIR_PC_REL_S_DATA_4):
catchType = ReadIndirPcRelSData4(exceptionTypeStart - typeIndex * sizeof(uint32_t));
break;
case static_cast<int>(TTypeEncoding::INDIR_PC_REL_S_DATA_8):
catchType = ReadIndirPcRelSData8(exceptionTypeStart - typeIndex * sizeof(uint64_t));
break;
default:
LOG(RTLOG_FATAL, "Unsupported ttype enconding!");
break;
}
if (catchType == nullptr || exceptionRef->IsSubType(*(reinterpret_cast<TypeInfo*>(catchType)))) {
return static_cast<uint8_t>(typeIndex);
}
}
if (*actionPtr == 0) {
return 0;
} else {
int64_t offset = EHTable::ReadSLEB128(&actionPtr);
actionPtr += offset;
}
}
}
}