diff --git a/src/msvc_crt/msvc_crt.c b/src/msvc_crt/msvc_crt.c new file mode 100644 index 00000000..5bb5d1f5 --- /dev/null +++ b/src/msvc_crt/msvc_crt.c @@ -0,0 +1,444 @@ +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +internal U64 +mscrt_parse_func_info(Arena *arena, + String8 raw_data, + U64 section_count, + COFF_SectionHeader *sections, + U64 off, + MSCRT_FuncInfo *func_info) +{ + U64 cursor = off; + + U32 handler_data_voff = 0; + cursor += str8_deserial_read_struct(raw_data, cursor, &handler_data_voff); + + // TODO: what is this? padding? + U32 unknown = 0; + cursor += str8_deserial_read_struct(raw_data, cursor, &unknown); + + // read function info + U64 handler_data_foff = coff_foff_from_voff(sections, section_count, handler_data_voff); + + MSCRT_FuncInfo32 func_info32 = {0}; + str8_deserial_read_struct(raw_data, handler_data_foff, &func_info32); + + // unwind map + MSCRT_UnwindMap32 *unwind_map = push_array(arena, MSCRT_UnwindMap32, func_info32.max_state); + U64 unwind_map_foff = coff_foff_from_voff(sections, section_count, func_info32.unwind_map_voff); + cursor += str8_deserial_read_array(raw_data, unwind_map_foff, &unwind_map[0], func_info32.max_state); + + // read ip states + MSCRT_IPState32 *ip_map = push_array(arena, MSCRT_IPState32, func_info32.ip_map_count); + U64 ip_map_foff = coff_foff_from_voff(sections, section_count, func_info32.ip_map_voff); + str8_deserial_read_array(raw_data, ip_map_foff, &ip_map[0], func_info32.ip_map_count); + + // read try map + MSCRT_TryMapBlock *try_block_map = push_array(arena, MSCRT_TryMapBlock, func_info32.try_block_map_count); + U64 try_map_foff = coff_foff_from_voff(sections, section_count, func_info32.try_block_map_voff); + for (U32 imap = 0; imap < func_info32.try_block_map_count; ++imap) { + MSCRT_TryMapBlock32 map32 = {0}; + str8_deserial_read_struct(raw_data, try_map_foff + imap*sizeof(map32), &map32); + + // convert try map to in-memory version + MSCRT_TryMapBlock *map = &try_block_map[imap]; + map->try_low = map32.try_low; + map->try_high = map32.try_high; + map->catch_high = map32.catch_high; + map->catch_handlers_count = map32.catch_handlers_count; + map->catch_handlers = push_array(arena, MSCRT_EhHandlerType32, map32.catch_handlers_count); + + // read handlers + U64 catch_handlers_foff = coff_foff_from_voff(sections, section_count, map32.catch_handlers_voff); + str8_deserial_read_array(raw_data, catch_handlers_foff, &map->catch_handlers[0], map->catch_handlers_count); + } + + // read exception spec list + MSCRT_ExceptionSpecTypeList es_type_list = {0}; + if (func_info32.es_type_list_voff) { + MSCRT_ExceptionSpecTypeList32 es_list32 = {0}; + U64 es_list_foff = coff_foff_from_voff(sections, section_count, func_info32.es_type_list_voff); + str8_deserial_read_struct(raw_data, es_list_foff, &es_list32); + + es_type_list.count = es_list32.count; + es_type_list.handlers = push_array(arena, MSCRT_EhHandlerType32, es_list32.count); + + U64 handlers_foff = coff_foff_from_voff(sections, section_count, es_list32.handlers_voff); + str8_deserial_read_array(raw_data, handlers_foff, &es_type_list.handlers[0], es_type_list.count); + } + + // pack result + func_info->magic = func_info32.magic; + func_info->max_state = func_info32.max_state; + func_info->unwind_map = unwind_map; + func_info->try_block_map_count = func_info32.try_block_map_count; + func_info->try_block_map = try_block_map; + func_info->ip_map_count = func_info32.ip_map_count; + func_info->ip_map = ip_map; + func_info->frame_offset_unwind_helper = func_info32.frame_offset_unwind_helper; + func_info->es_type_list = es_type_list; + func_info->eh_flags = func_info32.eh_flags; + + U64 parse_size = (cursor - off); + return parse_size; +} + +//////////////////////////////// + +internal U64 +mscrt_parse_handler_type_v4(String8 raw_data, U64 offset, U64 func_voff, MSCRT_EhHandlerTypeV4 *handler) +{ + U64 cursor = offset; + + cursor += str8_deserial_read_struct(raw_data, cursor, &handler->flags); + if (handler->flags & MSCRT_EhHandlerV4Flag_Adjectives) { + cursor += str8_deserial_read_struct(raw_data, cursor, &handler->adjectives); + } + if (handler->flags & MSCRT_EhHandlerV4Flag_DispType) { + cursor += str8_deserial_read_struct(raw_data, cursor, &handler->type_voff); + } + if (handler->flags & MSCRT_EhHandlerV4Flag_DispCatchObj) { + cursor += str8_deserial_read_struct(raw_data, cursor, &handler->catch_obj_voff); + } + cursor += str8_deserial_read_struct(raw_data, cursor, &handler->catch_code_voff); + + U32 cont_type = (handler->flags & MSCRT_EhHandlerV4Flag_ContVOffMask) >> MSCRT_EhHandlerV4Flag_ContVOffShift; + if (handler->flags & MSCRT_EhHandlerV4Flag_ContIsVOff) { + switch (cont_type) { + case MSCRT_ContV4Type_NoMetadata: break; + case MSCRT_ContV4Type_OneFuncRelAddr: { + S32 v = 0; + cursor += str8_deserial_read_struct(raw_data, cursor, &v); + handler->catch_funclet_cont_addr[0] = (U64)v; + handler->catch_funclet_cont_addr_count = 1; + } break; + case MSCRT_ContV4Type_TwoFuncRelAddr: { + S32 v1 = 0, v2 = 0; + cursor += str8_deserial_read_struct(raw_data, cursor, &v1); + cursor += str8_deserial_read_struct(raw_data, cursor, &v2); + handler->catch_funclet_cont_addr[0] = (U64)v1; + handler->catch_funclet_cont_addr[1] = (U64)v2; + handler->catch_funclet_cont_addr_count = 2; + } break; + } + } else { + switch (cont_type) { + case MSCRT_ContV4Type_NoMetadata: { + } break; + case MSCRT_ContV4Type_OneFuncRelAddr: { + U32 v = 0; + cursor += str8_deserial_read_struct(raw_data, cursor, &v); + handler->catch_funclet_cont_addr[0] = func_voff + (U64)v; + handler->catch_funclet_cont_addr_count = 1; + } break; + case MSCRT_ContV4Type_TwoFuncRelAddr: { + U32 v1 = 0, v2 = 0; + cursor += str8_deserial_read_struct(raw_data, cursor, &v1); + cursor += str8_deserial_read_struct(raw_data, cursor, &v2); + handler->catch_funclet_cont_addr[0] = func_voff + (U64)v1; + handler->catch_funclet_cont_addr[1] = func_voff + (U64)v2; + handler->catch_funclet_cont_addr_count = 2; + } break; + } + } + + U64 read_size = cursor - offset; + return read_size; +} + +internal U64 +mscrt_parse_handler_type_v4_array(Arena *arena, + String8 raw_data, + U64 offset, + U64 func_voff, + MSCRT_EhHandlerTypeV4Array *array_out) +{ + U64 cursor = offset; + U32 count = 0; + cursor += str8_deserial_read_struct(raw_data, cursor, &count); + + MSCRT_EhHandlerTypeV4 *handlers = 0; + if (count) { + handlers = push_array(arena, MSCRT_EhHandlerTypeV4, count); + for (U32 i = 0; i < count; ++i) { + cursor += mscrt_parse_handler_type_v4(raw_data, cursor, func_voff, &handlers[i]); + } + } + + array_out->count = count; + array_out->v = handlers; + + U64 read_size = cursor - offset; + return read_size; +} + +internal U64 +mscrt_parse_unwind_v4_entry(String8 raw_data, U64 offset, MSCRT_UnwindEntryV4 *entry_out) +{ + U64 cursor = offset; + + U32 type_and_next_off = 0; + cursor += str8_deserial_read_struct(raw_data, cursor, &type_and_next_off); + + entry_out->type = type_and_next_off & 0x3; + entry_out->next_off = type_and_next_off >> 2; + + switch (entry_out->type) { + case MSCRT_UnwindMapV4Type_DtorWithObj: + case MSCRT_UnwindMapV4Type_DtorWithPtrToObj: { + cursor += str8_deserial_read_struct(raw_data, cursor, &entry_out->action); + cursor += str8_deserial_read_struct(raw_data, cursor, &entry_out->object); + } break; + case MSCRT_UnwindMapV4Type_VOFF: { + cursor += str8_deserial_read_struct(raw_data, cursor, &entry_out->action); + } break; + case MSCRT_UnwindMapV4Type_NoUW: { + // no action and/or object is associated with this type + } break; + default: { + Assert(!"unknown unwind entry type"); + } break; + } + + U64 read_size = cursor - offset; + return read_size; +} + +internal U64 +mscrt_parse_unwind_map_v4(Arena *arena, String8 raw_data, U64 off, MSCRT_UnwindMapV4 *map_out) +{ + U64 cursor = off; + cursor += str8_deserial_read_struct(raw_data, cursor, &map_out->count); + map_out->v = push_array(arena, MSCRT_UnwindEntryV4, map_out->count); + for (U32 i = 0; i < map_out->count; ++i) { + cursor += mscrt_parse_unwind_v4_entry(raw_data, cursor, &map_out->v[i]); + } + U64 read_size = cursor - off; + return read_size; +} + +internal U64 +mscrt_parse_try_block_map_array_v4(Arena *arena, + String8 raw_data, + U64 off, + U64 section_count, + COFF_SectionHeader *sections, + U64 func_voff, + MSCRT_TryBlockMapV4Array *map_out) +{ + U64 cursor = off; + + U32 try_block_map_count = 0; + cursor += str8_deserial_read_struct(raw_data, cursor, &try_block_map_count); + + MSCRT_TryBlockMapV4 *try_block_map = push_array(arena, MSCRT_TryBlockMapV4, try_block_map_count); + for (U32 itry = 0; itry < try_block_map_count; ++itry) { + MSCRT_TryBlockMapV4 *try_block = &try_block_map[itry]; + cursor += str8_deserial_read_struct(raw_data, cursor, &try_block->try_low); + cursor += str8_deserial_read_struct(raw_data, cursor, &try_block->try_high); + cursor += str8_deserial_read_struct(raw_data, cursor, &try_block->catch_high); + + S32 handler_array_voff = 0; + cursor += str8_deserial_read_struct(raw_data, cursor, &handler_array_voff); + + U64 handler_array_foff = coff_foff_from_voff(sections, section_count, (U32)handler_array_voff); + mscrt_parse_handler_type_v4_array(arena, raw_data, handler_array_foff, func_voff, &try_block->handlers); + } + + map_out->count = try_block_map_count; + map_out->v = try_block_map; + + U64 read_size = cursor - off; + return read_size; +} + +internal U64 +mscrt_parse_ip2state_map_v4(Arena *arena, + String8 raw_data, + U64 off, + U64 func_voff, + MSCRT_IP2State32V4 *ip2state_map_out) +{ + U64 cursor = off; + + U32 count = 0; + cursor += str8_deserial_read_struct(raw_data, cursor, &count); + + U32 *voffs = push_array(arena, U32, count); + S32 *states = push_array(arena, S32, count); + + U32 prev_voff = func_voff; + for (U32 i = 0; i < count; ++i) { + // virtual offsets are encoded as deltas + cursor += str8_deserial_read_struct(raw_data, cursor, &voffs[i]); + voffs[i] += prev_voff; + prev_voff = voffs[i]; + + // states are encoded with +1 to avoid encoding negative integers + U32 encoded_state = 0; + cursor += str8_deserial_read_struct(raw_data, cursor, &encoded_state); + states[i] = (S32)encoded_state - 1; + } + + ip2state_map_out->count = count; + ip2state_map_out->voffs = voffs; + ip2state_map_out->states = states; + + U64 read_size = cursor - off; + return read_size; +} + +internal U64 +mscrt_parse_func_info_v4(Arena *arena, + String8 raw_data, + U64 section_count, + COFF_SectionHeader *sections, + U64 off, + U64 func_voff, + MSCRT_ParsedFuncInfoV4 *func_info_out) +{ + U64 cursor = off; + + MSCRT_FuncInfo32V4 func_info = {0}; + cursor += str8_deserial_read_struct(raw_data, cursor, &func_info.header); + if (func_info.header & MSCRT_FuncInfoV4Flag_IsBBT) { + cursor += str8_deserial_read_struct(raw_data, cursor, &func_info.bbt_flags); + } + if (func_info.header & MSCRT_FuncInfoV4Flag_UnwindMap) { + cursor += str8_deserial_read_struct(raw_data, cursor, &func_info.unwind_map_voff); + } + if (func_info.header & MSCRT_FuncInfoV4Flag_TryBlockMap) { + cursor += str8_deserial_read_struct(raw_data, cursor, &func_info.try_block_map_voff); + } + if (func_info.header & MSCRT_FuncInfoV4Flag_IsSeparated) { + // TODO: separted IP state map + NotImplemented; + } else { + cursor += str8_deserial_read_struct(raw_data, cursor, &func_info.ip_to_state_map_voff); + } + if (func_info.header & MSCRT_FuncInfoV4Flag_IsCatch) { + cursor += str8_deserial_read_struct(raw_data, cursor, &func_info.wrt_frame_establisher_voff); + } + + MSCRT_UnwindMapV4 unwind_map = {0}; + if (func_info.header & MSCRT_FuncInfoV4Flag_UnwindMap) { + U64 unwind_map_foff = coff_foff_from_voff(sections, section_count, func_info.unwind_map_voff); + mscrt_parse_unwind_map_v4(arena, raw_data, unwind_map_foff, &unwind_map); + } + + MSCRT_TryBlockMapV4Array try_block_map = {0}; + if (func_info.header & MSCRT_FuncInfoV4Flag_TryBlockMap) { + U64 try_block_map_foff = coff_foff_from_voff(sections, section_count, func_info.try_block_map_voff); + mscrt_parse_try_block_map_array_v4(arena, raw_data, try_block_map_foff, section_count, sections, func_voff, &try_block_map); + } + + MSCRT_IP2State32V4 ip2state_map = {0}; + if (func_info.header & MSCRT_FuncInfoV4Flag_IsSeparated) { + Assert(!"TODO: separated ip2state map"); + } else { + U64 ip_to_state_map_foff = coff_foff_from_voff(sections, section_count, func_info.ip_to_state_map_voff); + mscrt_parse_ip2state_map_v4(arena, raw_data, ip_to_state_map_foff, func_voff, &ip2state_map); + } + + func_info_out->header = func_info.header; + func_info_out->bbt_flags = func_info.bbt_flags; + func_info_out->try_block_map = try_block_map; + func_info_out->unwind_map = unwind_map; + func_info_out->ip2state_map = ip2state_map; + + U64 read_size = cursor - off; + return read_size; +} + +//////////////////////////////// + +internal Rng1U64List +mscrt_catch_blocks_from_data_x8664(Arena *arena, + String8 raw_data, + U64 section_count, + COFF_SectionHeader *sections, + Rng1U64 except_frange) +{ + Temp scratch = scratch_begin(&arena, 1); + + Rng1U64List result = {0}; + + String8 raw_pdata = str8_substr(raw_data, except_frange); + U64 pdata_count = raw_pdata.size / sizeof(PE_IntelPdata); + PE_IntelPdata *src_pdata = (PE_IntelPdata *)raw_pdata.str; + PE_IntelPdata *opl_pdata = src_pdata + pdata_count; + + for (PE_IntelPdata *pdata = src_pdata; pdata < opl_pdata; ++pdata) { + U64 uwinfo_foff = coff_foff_from_voff(sections, section_count, pdata->voff_unwind_info); + PE_UnwindInfo *uwinfo = str8_deserial_get_raw_ptr(raw_data, uwinfo_foff, sizeof(*uwinfo)); + + U8 flags = PE_UnwindInfo_FlagsFromHeader(uwinfo->header); + B32 is_chained = !!(flags & PE_UnwindInfoFlag_CHAINED); + B32 has_handler_data = !is_chained && ((flags & (PE_UnwindInfoFlag_EHANDLER | PE_UnwindInfoFlag_UHANDLER)) != 0); + + if (has_handler_data) { + Temp temp = temp_begin(scratch.arena); + + U32 actual_code_count = PE_UNWIND_INFO_GET_CODE_COUNT(uwinfo->codes_num); + U64 handler_data_foff = uwinfo_foff + sizeof(PE_UnwindInfo) + actual_code_count * sizeof(PE_UnwindCode); + U32 handler_voff = *(U32 *)str8_deserial_get_raw_ptr(raw_data, handler_data_foff, sizeof(handler_voff)); + + String8 handler_name = str8_zero(); + /* TODO: + { + SYMS_UnitID uid = syms_group_uid_from_voff__accelerated(group, handler_voff); + SYMS_UnitAccel *unit = syms_group_unit_from_uid(group, uid); + SYMS_SymbolID sid = syms_group_proc_sid_from_uid_voff__accelerated(group, uid, handler_voff); + handler_name = syms_group_symbol_name_from_sid(temp.arena, group, unit, sid); + } + */ + + B32 is_handler_v3_or_below = str8_match(handler_name, str8_lit("__CxxFrameHandler3"), 0) || + str8_match(handler_name, str8_lit("__GSHandlerCheck_EH"), 0); + if (is_handler_v3_or_below) { + U64 func_info_foff = handler_data_foff + sizeof(handler_voff); + MSCRT_FuncInfo func_info = {0}; + mscrt_parse_func_info(temp.arena, raw_data, section_count, sections, func_info_foff, &func_info); + + for (U32 itry = 0; itry < func_info.try_block_map_count; ++itry) { + MSCRT_TryMapBlock *try_block = &func_info.try_block_map[itry]; + for (U32 icatch = 0; icatch < try_block->catch_handlers_count; ++icatch) { + MSCRT_EhHandlerType32 *catch_block = &try_block->catch_handlers[icatch]; + U64 catch_pdata_off = pe_pdata_off_from_voff__binary_search_x8664(raw_pdata, catch_block->catch_handler_voff); + PE_IntelPdata *catch_pdata = str8_deserial_get_raw_ptr(raw_pdata, catch_pdata_off, sizeof(*catch_pdata)); + rng1u64_list_push(arena, &result, rng_1u64(catch_pdata->voff_first, catch_pdata->voff_one_past_last)); + } + } + goto next; + } + + B32 is_handler_v4 = str8_match(handler_name, str8_lit("__CxxFrameHandler4"), 0) || + str8_match(handler_name, str8_lit("__GSHandlerCheck_EH4"), 0); + if (is_handler_v4) { + U32 func_info_voff = *(U32 *)str8_deserial_get_raw_ptr(raw_data, handler_data_foff + sizeof(handler_voff), sizeof(func_info_voff)); + U64 func_info_foff = coff_foff_from_voff(sections, section_count, func_info_voff); + MSCRT_ParsedFuncInfoV4 func_info = {0}; + mscrt_parse_func_info_v4(temp.arena, raw_data, section_count, sections, func_info_foff, pdata->voff_first, &func_info); + + for (U32 itry = 0; itry < func_info.try_block_map.count; ++itry) { + MSCRT_TryBlockMapV4 *try_block = &func_info.try_block_map.v[itry]; + for (U32 icatch = 0; icatch < try_block->handlers.count; ++icatch) { + MSCRT_EhHandlerTypeV4 *catch_block = &try_block->handlers.v[icatch]; + U64 catch_pdata_off = pe_pdata_off_from_voff__binary_search_x8664(raw_pdata, catch_block->catch_code_voff); + PE_IntelPdata *catch_pdata = str8_deserial_get_raw_ptr(raw_pdata, catch_pdata_off, sizeof(*catch_pdata)); + rng1u64_list_push(arena, &result, rng_1u64(catch_pdata->voff_first, catch_pdata->voff_one_past_last)); + } + } + goto next; + } + + next:; + temp_end(temp); + } + } + + scratch_end(scratch); + return result; +} + diff --git a/src/msvc_crt/msvc_crt.h b/src/msvc_crt/msvc_crt.h new file mode 100644 index 00000000..12ae9938 --- /dev/null +++ b/src/msvc_crt/msvc_crt.h @@ -0,0 +1,307 @@ +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef MSVC_CRT +#define MSVC_CRT + +//////////////////////////////// +// GS Handler + +#define MSCRT_GSHandler_GetFlags(x) (((x) & 0x00000007) >> 0) +#define MSCRT_GSHandler_GetCookieOffset(x) (((x) & 0xFFFFFFF8) >> 3) + +typedef U8 MSCRT_GSHandlerFlags; +enum +{ + MSCRT_GSHandlerFlag_EHandler = (1 << 0), + MSCRT_GSHandlerFlag_UHandler = (1 << 1), + MSCRT_GSHandlerFlag_HasAlignment = (1 << 2) +}; + +//////////////////////////////// +// Exceptions < v4 + +#define MSCRT_MAGIC_GET_CHECK(x) ((x) & 0x1FFFFFFF) +#define MSCRT_MAGIC_GET_FLAGS(x) (((x) & 0xE0000000) >> 29) + +// Magic numbers are incremented by one everytime there is a new version. +// Top 3 bits are reserved for flags. +enum +{ + MSCRT_Magic1 = 0x19930520, + MSCRT_Magic2 = 0x19930521, + MSCRT_Magic3 = 0x19930522, + + // pure magic indicates that exception cannot be caught in native or managed code. + MSCRT_PureMagic1 = 0x1994000, +}; +enum +{ + MSCRT_MagicFlag_EHS = (1 << 0), + MSCRT_MagicFlag_DYNSTALKING = (1 << 1), + MSCRT_MagicFlag_EHNOEXCEPT = (1 << 2) +}; + +typedef U32 MSCRT_Flags; +enum +{ + MSCRT_Flag_SynchronousExceptionOnly = (1 << 0), + MSCRT_Flag_UNKNOWN = (1 << 1), + MSCRT_Flag_StopUnwind = (1 << 2), // When set unwinding can't continue. +}; + +enum +{ + MSCRT_CatchableType_IsSimpleType = (1 << 0), + MSCRT_CatchableType_ByRefOnly = (1 << 1), + MSCRT_CatchableType_HasVirtualBase = (1 << 2), // type is a class with virtual base + MSCRT_CatchableType_IsWinRTHandle = (1 << 3), // type is a WinRT handle + MSCRT_CatchableType_IsStdBadAlloc = (1 << 4) // type is a std::bad_alloc +}; + +enum +{ + MSCRT_ThrowInfo_IsConst = (1 << 0), + MSCRT_ThrowInfo_IsVolatile = (1 << 1), + MSCRT_ThrowInfo_IsUnaligned = (1 << 2), + MSCRT_ThrowInfo_IsPure = (1 << 3), // thrown object is from pure module + MSCRT_ThrowInfo_IsWinRT = (1 << 4) // thrown object is a WinRT exception +}; + +typedef U32 MSCRT_EhHandlerTypeFlags; +enum +{ + MSCRT_EhHandlerTypeFlag_IsConst = (1 << 0), // referenced type is 'const' + MSCRT_EhHandlerTypeFlag_IsVolatile = (1 << 1), // referenced type is 'volatile' + MSCRT_EhHandlerTypeFlag_IsUnaligned = (1 << 2), // referenced type is 'unaligned' + MSCRT_EhHandlerTypeFlag_IsReference = (1 << 3), // catch type is by reference + MSCRT_EhHandlerTypeFlag_IsResumable = (1 << 4), // catch may choose to resume + MSCRT_EhHandlerTypeFlag_IsStdDotDot = (1 << 6), // catch(...) + MSCRT_EhHandlerTypeFlag_IsComplusEH = (1 << 31) // is handling EH in complus +}; + +typedef struct MSCRT_FuncInfo32 +{ + U32 magic; + U32 max_state; + U32 unwind_map_voff; + U32 try_block_map_count; + U32 try_block_map_voff; + U32 ip_map_count; + U32 ip_map_voff; + U32 frame_offset_unwind_helper; + U32 es_type_list_voff; // llvm emits zero, not sure what this supposed to be + MSCRT_Flags eh_flags; +} MSCRT_FuncInfo32; + +typedef struct MSCRT_IPState32 +{ + U32 ip; + S32 state; +} MSCRT_IPState32; + +typedef struct MSCRT_UnwindMap32 +{ + S32 next_state; + U32 action_virt_off; +} MSCRT_UnwindMap32; + +typedef struct MSCRT_EhHandlerType32 +{ + MSCRT_EhHandlerTypeFlags adjectives; + U32 descriptor_voff; + U32 catch_obj_frame_offset; + U32 catch_handler_voff; + U32 fp_distance; +} MSCRT_EhHandlerType32; + +typedef struct MSCRT_TryMapBlock32 +{ + S32 try_low; + S32 try_high; + S32 catch_high; + S32 catch_handlers_count; + U32 catch_handlers_voff; +} MSCRT_TryMapBlock32; + +typedef struct MSCRT_ExceptionSpecTypeList32 +{ + S32 count; + U32 handlers_voff; +} MSCRT_ExceptionSpecTypeList32; + +typedef struct MSCRT_TryMapBlock +{ + S32 try_low; + S32 try_high; + S32 catch_high; + S32 catch_handlers_count; + MSCRT_EhHandlerType32 *catch_handlers; +} MSCRT_TryMapBlock; + +typedef struct MSCRT_ExceptionSpecTypeList +{ + S32 count; + MSCRT_EhHandlerType32 *handlers; +} MSCRT_ExceptionSpecTypeList; + +typedef struct MSCRT_FuncInfo +{ + U32 magic; + U32 max_state; + MSCRT_UnwindMap32 *unwind_map; + U32 try_block_map_count; + MSCRT_TryMapBlock *try_block_map; + U32 ip_map_count; + MSCRT_IPState32 *ip_map; + U32 frame_offset_unwind_helper; + MSCRT_ExceptionSpecTypeList es_type_list; + MSCRT_Flags eh_flags; +} MSCRT_FuncInfo; + +//////////////////////////////// +// C++ Exceptions V4 + +typedef U8 MSCRT_FuncInfoV4Flags; +enum +{ + MSCRT_FuncInfoV4Flag_IsCatch = (1 << 0), // catch funclet + MSCRT_FuncInfoV4Flag_IsSeparated = (1 << 1), // func has separate code segment + MSCRT_FuncInfoV4Flag_IsBBT = (1 << 2), // flags set by basic block trasformations + MSCRT_FuncInfoV4Flag_UnwindMap = (1 << 3), // unwind map is present + MSCRT_FuncInfoV4Flag_TryBlockMap = (1 << 4), // try block map is present + MSCRT_FuncInfoV4Flag_EHs = (1 << 5), + MSCRT_FuncInfoV4Flag_NoExcept = (1 << 6), + MSCRT_FuncInfoV4Flag_Reserved = (1 << 7) +}; + +typedef U32 MSCRT_UnwindMapV4Type; +enum +{ + MSCRT_UnwindMapV4Type_NoUW = 0, // no unwind action associated with this state + MSCRT_UnwindMapV4Type_DtorWithObj = 1, // dtor with an object offset + MSCRT_UnwindMapV4Type_DtorWithPtrToObj = 2, // dtor with an offset that contains a pointer to the object to be destroyed + MSCRT_UnwindMapV4Type_VOFF = 3, // dtor that has a direct function that is called that knows where the object is and can perform more exotic destruction +}; + +enum +{ + MSCRT_ContV4Type_NoMetadata = 1, // no metadata use whatever funclet returns + MSCRT_ContV4Type_OneFuncRelAddr = 2, + MSCRT_ContV4Type_TwoFuncRelAddr = 3 +}; + +#define MSCRT__EH_HANDLER_V4_FLAGS_EXTRACT_CONT_TYPE(x) (((x) & MSCRT_EhHandlerV4Flag_ContVOffMask) >> MSVC_CRTHandlerV4Flag_ContVOffShift) +typedef U8 MSCRT_EhHandlerV4Flags; +enum +{ + MSCRT_EhHandlerV4Flag_Adjectives = (1 << 0), // set if adjectives are present + MSCRT_EhHandlerV4Flag_DispType = (1 << 1), // set if type descriptors are present + MSCRT_EhHandlerV4Flag_DispCatchObj = (1 << 2), // set if catch object object is present + MSCRT_EhHandlerV4Flag_ContIsVOff = (1 << 3), // continuantion addresses are VOFF rather than function relative + + MSCRT_EhHandlerV4Flag_ContVOffMask = 0x30, + MSCRT_EhHandlerV4Flag_ContVOffShift = 4, +}; + +typedef struct MSCRT_EhHandlerTypeV4 +{ + MSCRT_EhHandlerV4Flags flags; + MSCRT_EhHandlerTypeFlags adjectives; + S32 type_voff; + U32 catch_obj_voff; + S32 catch_code_voff; + U64 catch_funclet_cont_addr[2]; + U32 catch_funclet_cont_addr_count; +} MSCRT_EhHandlerTypeV4; + +typedef struct MSCRT_EhHandlerTypeV4Array +{ + U64 count; + MSCRT_EhHandlerTypeV4 *v; +} MSCRT_EhHandlerTypeV4Array; + +typedef struct MSCRT_TryBlockMap32V4 +{ + U32 try_low; + U32 try_high; + U32 catch_high; + S32 handler_array_voff; +} MSCRT_TryBlockMap32V4; + +typedef struct MSCRT_IP2State32V4 +{ + U32 count; + U32 *voffs; + S32 *states; +} MSCRT_IP2State32V4; + +typedef struct MSCRT_SepIPState32V4 +{ + S32 func_start_voff; + S32 ip_map_voff; +} MSCRT_SepIPState32V4; + +typedef struct MSCRT_FuncInfo32V4 +{ + MSCRT_FuncInfoV4Flags header; + U32 bbt_flags; + S32 unwind_map_voff; + S32 try_block_map_voff; + S32 ip_to_state_map_voff; + S32 wrt_frame_establisher_voff; // used only in catch funclets +} MSCRT_FuncInfo32V4; + +typedef struct MSCRT_UnwindEntryV4 +{ + MSCRT_UnwindMapV4Type type; + S32 action; + U32 object; + U32 next_off; +} MSCRT_UnwindEntryV4; + +typedef struct MSCRT_UnwindMapV4 +{ + U32 count; + MSCRT_UnwindEntryV4 *v; +} MSCRT_UnwindMapV4; + +typedef struct MSCRT_TryBlockMapV4 +{ + U32 try_low; + U32 try_high; + U32 catch_high; + MSCRT_EhHandlerTypeV4Array handlers; +} MSCRT_TryBlockMapV4; + +typedef struct MSCRT_TryBlockMapV4Array +{ + U64 count; + MSCRT_TryBlockMapV4 *v; +} MSCRT_TryBlockMapV4Array; + +typedef struct MSCRT_ParsedFuncInfoV4 +{ + MSCRT_FuncInfoV4Flags header; + U32 bbt_flags; + MSCRT_UnwindMapV4 unwind_map; + MSCRT_TryBlockMapV4Array try_block_map; + MSCRT_IP2State32V4 ip2state_map; +} MSCRT_ParsedFuncInfoV4; + +//- Exception info < v4 + +internal U64 mscrt_parse_func_info(Arena *arena, String8 raw_data, U64 section_count, COFF_SectionHeader *sections, U64 off, MSCRT_FuncInfo *func_info); + +//- Exception info v4 + +internal U64 mscrt_parse_handler_type_v4 (String8 raw_data, U64 offset, U64 func_voff, MSCRT_EhHandlerTypeV4 *handler); +internal U64 mscrt_parse_unwind_v4_entry (String8 raw_data, U64 offset, MSCRT_UnwindEntryV4 *entry_out); +internal U64 mscrt_parse_handler_type_v4_array (Arena *arena, String8 raw_data, U64 offset, U64 func_voff, MSCRT_EhHandlerTypeV4Array *array_out); +internal U64 mscrt_parse_unwind_map_v4 (Arena *arena, String8 raw_data, U64 off, MSCRT_UnwindMapV4 *map_out); +internal U64 mscrt_parse_try_block_map_array_v4(Arena *arena, String8 raw_data, U64 off, U64 section_count, COFF_SectionHeader *sections, U64 func_voff, MSCRT_TryBlockMapV4Array *map_out); +internal U64 mscrt_parse_ip2state_map_v4 (Arena *arena, String8 raw_data, U64 off, U64 func_voff, MSCRT_IP2State32V4 *ip2state_map_out); +internal U64 mscrt_parse_func_info_v4 (Arena *arena, String8 raw_data, U64 section_count, COFF_SectionHeader *sections, U64 off, U64 func_voff, MSCRT_ParsedFuncInfoV4 *func_info_out); + +#endif // MSVC_CRT +