From d73ce364b94f0901104bd99a0f274b6709e55b8d Mon Sep 17 00:00:00 2001 From: Ryan Fleury Date: Tue, 2 Jun 2026 11:20:49 -0700 Subject: [PATCH] major clean-up pass over unwinding/module-info abstraction, + usage in the debug engine. simplified / cleaned up DWARF helpers for unwind info & expressions. decoupling unwinding / DWARF evaluation from memory read callbacks. light module info now prepared by demon layer - debug engine no longer duplicates per-backend parsing. unwind abstraction layer now does thin abstraction over unwinding backends, debug engine only calls abstraction. clean up demon to have fewer ad-hoc 'queries' on handles, and prefer sending info over debug events when possible / when it fits the usage pattern. move DWARF enum specifications to metacode, generate enums/xlists/tables/etc. decouple raddbg/radbin completely from old parsing helpers. replace portion of DWARF dumping; pull out some new parsing helpers from converter / dumper. --- project.4coder | 2 +- project.raddbg | 13 +- src/arch/arch.c | 2 +- src/arch/arch.h | 2 +- src/artifact_cache/artifact_cache.c | 47 +- src/artifact_cache/artifact_cache.h | 19 +- src/base/base_inc.c | 1 + src/base/base_inc.h | 1 + src/base/base_memory.h | 6 - src/base/base_memory_map.c | 36 + src/base/base_memory_map.h | 38 + src/base/base_shared_memory.h | 8 +- src/base/base_strings.c | 99 +- src/base/base_strings.h | 6 +- src/dbg_engine/dbg_engine_core.c | 12 +- src/dbg_engine/dbg_engine_core.h | 1 + src/dbg_engine/dbg_engine_ctrl.c | 446 ++-- src/dbg_engine/dbg_engine_ctrl.h | 70 +- src/demon/demon_core.h | 7 +- src/dwarf/dwarf.c | 1212 ++++------- src/dwarf/dwarf.h | 1852 +---------------- src/dwarf/dwarf.mdesk | 1354 +++++++++++- src/dwarf/dwarf_coff.c | 62 - src/dwarf/dwarf_dump.c | 8 +- src/dwarf/dwarf_dump_2.c | 718 +++++++ src/dwarf/dwarf_dump_2.h | 17 + src/dwarf/dwarf_expr.c | 108 - src/dwarf/dwarf_expr.h | 3 - src/dwarf/dwarf_expr_2.c | 393 ++++ src/dwarf/dwarf_expr_2.h | 66 + src/dwarf/dwarf_help.c | 66 - src/dwarf/dwarf_help.h | 18 - src/dwarf/dwarf_inc.c | 28 +- src/dwarf/dwarf_inc.h | 28 +- src/dwarf/dwarf_notes.txt | 42 - src/dwarf/dwarf_parse.c | 14 +- src/dwarf/dwarf_parse.h | 18 - src/dwarf/dwarf_parse_2.c | 733 +++++-- src/dwarf/dwarf_parse_2.h | 179 +- src/dwarf/dwarf_parse_coff.c | 45 + .../{dwarf_coff.h => dwarf_parse_coff.h} | 6 +- src/dwarf/{dwarf_elf.c => dwarf_parse_elf.c} | 17 +- src/dwarf/{dwarf_elf.h => dwarf_parse_elf.h} | 8 +- src/dwarf/dwarf_unwind_2.c | 2 + src/dwarf/dwarf_unwind_2.h | 44 + src/dwarf/dwarf_writer.c | 232 ++- src/dwarf/dwarf_writer.h | 20 + src/dwarf/eh_dump.c | 141 -- src/dwarf/eh_frame.c | 470 ----- src/dwarf/generated/dwarf.meta.c | 69 + src/dwarf/generated/dwarf.meta.h | 1713 ++++++++++++++- src/dwarf/tests/dwarf_tests.c | 84 +- src/eh_frame/eh_frame.c | 650 ++++++ src/{dwarf => eh_frame}/eh_frame.h | 18 +- src/eh_frame/eh_frame_dump.c | 152 ++ .../eh_dump.h => eh_frame/eh_frame_dump.h} | 4 +- src/eh_frame/unwind/eh_frame_unwind.c | 696 +++++++ src/eh_frame/unwind/eh_frame_unwind.h | 22 + src/linux/base/linux_base.c | 2 +- src/linux/demon/linux_demon.c | 102 +- src/linux/demon/linux_demon.h | 2 + .../window_manager/linux_window_manager.c | 402 ++-- src/metagen/metagen_main.c | 18 +- src/pe/x64/unwind/pe_x64_unwind.c | 936 +++++++++ src/pe/x64/unwind/pe_x64_unwind.h | 28 + src/radbin/radbin.c | 11 +- src/radbin/radbin_main.c | 16 +- src/raddbg/raddbg_core.c | 11 +- src/raddbg/raddbg_main.c | 6 + src/raddbg/tests/raddbg_tests.c | 4 +- src/rdi/rdi_local.c | 1 + src/rdi_from_dwarf/rdi_from_dwarf.c | 14 +- src/rdi_from_dwarf/rdi_from_dwarf_2.c | 496 +---- .../tests/rdi_from_dwarf_tests.c | 56 +- src/torture/torture_main.c | 8 +- src/unwind/generated/unwind.meta.c | 5 + src/unwind/generated/unwind.meta.h | 21 + src/unwind/unwind.c | 24 + src/unwind/unwind.h | 45 + src/unwind/unwind.mdesk | 21 + src/unwind/unwind_inc.c | 10 + src/unwind/unwind_inc.h | 15 + src/win32/base/win32_base.c | 4 +- src/win32/demon/win32_demon.c | 173 +- src/win32/demon/win32_demon.h | 3 + .../window_manager/win32_window_manager.c | 20 +- src/window_manager/window_manager.h | 20 +- src/window_manager/window_manager_stub.c | 20 +- 88 files changed, 9899 insertions(+), 4723 deletions(-) create mode 100644 src/base/base_memory_map.c create mode 100644 src/base/base_memory_map.h delete mode 100644 src/dwarf/dwarf_coff.c create mode 100644 src/dwarf/dwarf_dump_2.c create mode 100644 src/dwarf/dwarf_dump_2.h create mode 100644 src/dwarf/dwarf_expr_2.c create mode 100644 src/dwarf/dwarf_expr_2.h delete mode 100644 src/dwarf/dwarf_help.c delete mode 100644 src/dwarf/dwarf_help.h delete mode 100644 src/dwarf/dwarf_notes.txt create mode 100644 src/dwarf/dwarf_parse_coff.c rename src/dwarf/{dwarf_coff.h => dwarf_parse_coff.h} (82%) rename src/dwarf/{dwarf_elf.c => dwarf_parse_elf.c} (87%) rename src/dwarf/{dwarf_elf.h => dwarf_parse_elf.h} (53%) create mode 100644 src/dwarf/dwarf_unwind_2.c create mode 100644 src/dwarf/dwarf_unwind_2.h delete mode 100644 src/dwarf/eh_dump.c delete mode 100644 src/dwarf/eh_frame.c create mode 100644 src/eh_frame/eh_frame.c rename src/{dwarf => eh_frame}/eh_frame.h (85%) create mode 100644 src/eh_frame/eh_frame_dump.c rename src/{dwarf/eh_dump.h => eh_frame/eh_frame_dump.h} (92%) create mode 100644 src/eh_frame/unwind/eh_frame_unwind.c create mode 100644 src/eh_frame/unwind/eh_frame_unwind.h create mode 100644 src/pe/x64/unwind/pe_x64_unwind.c create mode 100644 src/pe/x64/unwind/pe_x64_unwind.h create mode 100644 src/unwind/generated/unwind.meta.c create mode 100644 src/unwind/generated/unwind.meta.h create mode 100644 src/unwind/unwind.c create mode 100644 src/unwind/unwind.h create mode 100644 src/unwind/unwind.mdesk create mode 100644 src/unwind/unwind_inc.c create mode 100644 src/unwind/unwind_inc.h diff --git a/project.4coder b/project.4coder index 14cc2147..2f198477 100644 --- a/project.4coder +++ b/project.4coder @@ -49,7 +49,7 @@ commands = // .f1 = { .win = "raddbg_stable --ipc kill_all && build raddbg", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, // .f1 = { .win = "raddbg_stable --ipc kill_all && build raddbg debug telemetry", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, // .f1 = { .win = "raddbg_stable --ipc kill_all && build radbin", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, - .f1 = { .win = "build raddbg", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, + .f1 = { .win = "build torture", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, //- rjf: [raddbg wsl] // .f1 = { .win = "wsl ./build.sh raddbg", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, diff --git a/project.raddbg b/project.raddbg index 59545fc2..d0bef4ce 100644 --- a/project.raddbg +++ b/project.raddbg @@ -7,7 +7,6 @@ target: working_directory: build arguments: "--user:raddbg_test.user --project:raddbg_test.project" debug_subprocesses: 0 - enabled: 1 } target: { @@ -19,6 +18,7 @@ target: { executable: "build/mule_main.exe" working_directory: build + enabled: 1 } target: { @@ -44,3 +44,14 @@ target: working_directory: build arguments: "rdi_from_pdb/* --gui" } +target: +{ + executable: "build/metagen.exe" + working_directory: build +} +target: +{ + executable: "build/radbin.exe" + working_directory: build + arguments: "--rdi raddbg" +} diff --git a/src/arch/arch.c b/src/arch/arch.c index f8521b21..25e07963 100644 --- a/src/arch/arch.c +++ b/src/arch/arch.c @@ -69,7 +69,7 @@ arch_reg_code_from_rdi(Arch arch, RDI_RegCode code) } internal ARCH_RegCode -arch_reg_code_from_dw(Arch arch, DW_Reg code) +arch_reg_code_from_dw(Arch arch, DW_RegCode code) { ARCH_RegCode result = 0; { diff --git a/src/arch/arch.h b/src/arch/arch.h index fa1b1e64..dbdb1b28 100644 --- a/src/arch/arch.h +++ b/src/arch/arch.h @@ -38,7 +38,7 @@ global read_only ARCH_Info arch_info_nil = {0, 0, 0, 0, {0}, &arch_reg_code_rng_ internal ARCH_Info *arch_info_from_arch(Arch arch); internal ARCH_RegCode arch_reg_code_from_name(ARCH_Info *arch_info, String8 name); internal ARCH_RegCode arch_reg_code_from_rdi(Arch arch, RDI_RegCode code); -internal ARCH_RegCode arch_reg_code_from_dw(Arch arch, DW_Reg code); +internal ARCH_RegCode arch_reg_code_from_dw(Arch arch, DW_RegCode code); internal B32 arch_reg_block_read_range(ARCH_Info *arch_info, void *block, Rng1U16 range, void *dst); internal B32 arch_reg_block_write_range(ARCH_Info *arch_info, void *block, Rng1U16 range, void *src); internal U64 arch_ip_from_reg_block(ARCH_Info *arch_info, void *block); diff --git a/src/artifact_cache/artifact_cache.c b/src/artifact_cache/artifact_cache.c index 7ba72310..54db8c31 100644 --- a/src/artifact_cache/artifact_cache.c +++ b/src/artifact_cache/artifact_cache.c @@ -58,6 +58,7 @@ ac_artifact_from_key_(Access *access, String8 key, AC_ArtifactParams *params, U6 cache->slots_count = Max(256, params->slots_count); cache->slots = push_array(cache_stripe->arena, AC_Slot, cache->slots_count); cache->stripes = stripe_array_alloc(cache_stripe->arena); + cache->stripe_free_node_ptrs = push_array(cache_stripe->arena, AC_NodePtr *, cache->stripes.count); } } if(cache != 0) @@ -73,6 +74,9 @@ ac_artifact_from_key_(Access *access, String8 key, AC_ArtifactParams *params, U6 AC_Slot *slot = &cache->slots[slot_idx]; Stripe *stripe = stripe_from_slot_idx(&cache->stripes, slot_idx); + //- rjf: unpack thread context + B32 is_node_work_active = (ac_tctx != 0); + //- rjf: cache * key -> existing artifact B32 artifact_is_stale = 1; B32 got_artifact = 0; @@ -92,11 +96,16 @@ ac_artifact_from_key_(Access *access, String8 key, AC_ArtifactParams *params, U6 artifact_is_stale = is_stale; artifact = n->val; access_touch(access, &n->access_pt, stripe->cv); + ins_atomic_u64_eval_assign(&n->last_touched_ac_request_gen, ins_atomic_u64_eval(&ac_shared->request_gen)); } if(is_stale) { B32 got_task = (ins_atomic_u64_eval_cond_assign(&n->working_count, 1, 0) == 0); need_request = got_task; + if(is_node_work_active) + { + ins_atomic_u32_eval_assign(&n->other_nodes_depend_on_me, 1); + } } break; } @@ -140,6 +149,10 @@ ac_artifact_from_key_(Access *access, String8 key, AC_ArtifactParams *params, U6 node->key = str8_copy(stripe->arena, key); node->working_count = 1; node->evict_threshold_us = params->evict_threshold_us; + if(is_node_work_active) + { + node->other_nodes_depend_on_me = 1; + } } node->access_pt.last_time_touched_us = now_time_us(); node->access_pt.last_update_idx_touched = update_tick_idx(); @@ -181,6 +194,7 @@ ac_artifact_from_key_(Access *access, String8 key, AC_ArtifactParams *params, U6 artifact_is_stale = (node->last_completed_gen == params->gen); artifact = node->val; access_touch(access, &node->access_pt, stripe->cv); + ins_atomic_u64_eval_assign(&node->last_touched_ac_request_gen, ins_atomic_u64_eval(&ac_shared->request_gen)); } // rjf: abort if needed @@ -243,7 +257,9 @@ ac_async_tick(void) for(AC_Node *n = slot->first, *next = 0; n != 0; n = next) { next = n->next; - if(access_pt_is_expired(&n->access_pt, .time = n->evict_threshold_us) && ins_atomic_u64_eval(&n->working_count) == 0) + if(access_pt_is_expired(&n->access_pt, .time = n->evict_threshold_us) && + ins_atomic_u64_eval(&n->working_count) == 0 && + (ins_atomic_u32_eval(&n->other_nodes_depend_on_me) == 0 || ac_shared->request_gen > n->last_touched_ac_request_gen)) { slot_has_work = 1; if(!write_mode) @@ -327,6 +343,11 @@ ac_async_tick(void) lane_sync_u64(&tasks_count, 0); lane_sync(); + ////////////////////////////// + //- rjf: set up artifact cache thread context + // + ac_tctx = push_array(scratch.arena, AC_TCTX, 1); + ////////////////////////////// //- rjf: do all requests // @@ -610,6 +631,30 @@ ac_async_tick(void) } lane_sync(); + ////////////////////////////// + //- rjf: increment the request gen, only if all requests are empty + // + if(lane_idx() == 0) + { + B32 have_live_requests = 0; + for EachElement(idx, ac_shared->req_batches) + { + MutexScope(ac_shared->req_batches[idx].mutex) + { + have_live_requests = (ac_shared->req_batches[idx].wide_count != 0 || ac_shared->req_batches[idx].thin_count != 0); + } + } + if(!have_live_requests) + { + ac_shared->request_gen += 1; + } + } + + ////////////////////////////// + //- rjf: reset thread context + // + ac_tctx = 0; + ////////////////////////////// //- rjf: disable cancellation scanning // diff --git a/src/artifact_cache/artifact_cache.h b/src/artifact_cache/artifact_cache.h index 04953201..2bd082bb 100644 --- a/src/artifact_cache/artifact_cache.h +++ b/src/artifact_cache/artifact_cache.h @@ -59,6 +59,13 @@ struct AC_RequestNode AC_Request v; }; +typedef struct AC_NodePtr AC_NodePtr; +struct AC_NodePtr +{ + AC_NodePtr *next; + struct AC_Node *node; +}; + typedef struct AC_Node AC_Node; struct AC_Node { @@ -76,8 +83,9 @@ struct AC_Node U64 working_count; U64 completion_count; U64 evict_threshold_us; + U64 last_touched_ac_request_gen; B32 cancelled; - U64 _unused_; + B32 other_nodes_depend_on_me; }; typedef struct AC_Slot AC_Slot; @@ -99,6 +107,7 @@ struct AC_Cache U64 slots_count; AC_Slot *slots; StripeArray stripes; + AC_NodePtr **stripe_free_node_ptrs; }; typedef struct AC_RequestBatch AC_RequestBatch; @@ -114,10 +123,17 @@ struct AC_RequestBatch U64 thin_count; }; +typedef struct AC_TCTX AC_TCTX; +struct AC_TCTX +{ + U64 _unused_; +}; + typedef struct AC_Shared AC_Shared; struct AC_Shared { Arena *arena; + U64 request_gen; // rjf: cache cache U64 cache_slots_count; @@ -136,6 +152,7 @@ struct AC_Shared //~ rjf: Globals global AC_Shared *ac_shared = 0; +thread_static AC_TCTX *ac_tctx = 0; //////////////////////////////// //~ rjf: Layer Initialization diff --git a/src/base/base_inc.c b/src/base/base_inc.c index ab242f81..90467069 100644 --- a/src/base/base_inc.c +++ b/src/base/base_inc.c @@ -13,6 +13,7 @@ #include "base_arena.c" #include "base_math.c" #include "base_strings.c" +#include "base_memory_map.c" #include "base_hash.c" #include "base_system.c" #include "base_threads.c" diff --git a/src/base/base_inc.h b/src/base/base_inc.h index bc8d568d..3a378ec0 100644 --- a/src/base/base_inc.h +++ b/src/base/base_inc.h @@ -15,6 +15,7 @@ #include "base_arena.h" #include "base_math.h" #include "base_strings.h" +#include "base_memory_map.h" #include "base_hash.h" #include "base_system.h" #include "base_threads.h" diff --git a/src/base/base_memory.h b/src/base/base_memory.h index e6f58271..e73c7afd 100644 --- a/src/base/base_memory.h +++ b/src/base/base_memory.h @@ -4,12 +4,6 @@ #ifndef BASE_MEMORY_H #define BASE_MEMORY_H -typedef struct SharedMemory SharedMemory; -struct SharedMemory -{ - U64 u64[1]; -}; - //////////////////////////////// //~ rjf: @per_os_impl Platform Memory Allocation diff --git a/src/base/base_memory_map.c b/src/base/base_memory_map.c new file mode 100644 index 00000000..23e37ea7 --- /dev/null +++ b/src/base/base_memory_map.c @@ -0,0 +1,36 @@ +// Copyright (c) Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +//////////////////////////////// +//~ rjf: Memory Map Functions + +internal void +memory_map_push(Arena *arena, MemoryMap *map, Rng1U64 vaddr_range, void *data) +{ + MemoryMapRangeNode *n = push_array(arena, MemoryMapRangeNode, 1); + n->v.vaddr_range = vaddr_range; + n->v.base = data; + SLLQueuePush(map->first_range, map->last_range, n); +} + +internal U64 +memory_map_read(MemoryMap *map, Rng1U64 range, void *dst) +{ + U64 dst_vaddr = range.min; + { + for(MemoryMapRangeNode *n = map->first_range; n != 0; n = n->next) + { + if(contains_1u64(n->v.vaddr_range, dst_vaddr)) + { + U64 src_off = dst_vaddr - n->v.vaddr_range.min; + U64 num_bytes_possible = n->v.vaddr_range.max - dst_vaddr; + U64 num_bytes_needed = range.max - dst_vaddr; + U64 num_bytes_to_read = Min(num_bytes_needed, num_bytes_possible); + MemoryCopy((U8 *)dst + (dst_vaddr - range.min), (U8 *)n->v.base + src_off, num_bytes_to_read); + dst_vaddr += num_bytes_to_read; + } + } + } + U64 bytes_read = (dst_vaddr - range.min); + return bytes_read; +} diff --git a/src/base/base_memory_map.h b/src/base/base_memory_map.h new file mode 100644 index 00000000..40a5e4fd --- /dev/null +++ b/src/base/base_memory_map.h @@ -0,0 +1,38 @@ +// Copyright (c) Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef BASE_MEMORY_MAP_H +#define BASE_MEMORY_MAP_H + +//////////////////////////////// +//~ rjf: Memory Map Types + +typedef struct MemoryMapRange MemoryMapRange; +struct MemoryMapRange +{ + Rng1U64 vaddr_range; + void *base; +}; + +typedef struct MemoryMapRangeNode MemoryMapRangeNode; +struct MemoryMapRangeNode +{ + MemoryMapRangeNode *next; + MemoryMapRange v; +}; + +typedef struct MemoryMap MemoryMap; +struct MemoryMap +{ + MemoryMapRangeNode *first_range; + MemoryMapRangeNode *last_range; +}; + +//////////////////////////////// +//~ rjf: Memory Map Functions + +internal void memory_map_push(Arena *arena, MemoryMap *map, Rng1U64 vaddr_range, void *data); +internal U64 memory_map_read(MemoryMap *map, Rng1U64 range, void *dst); +#define memory_map_read_struct(map, vaddr, ptr) memory_map_read((map), r1u64((vaddr), (vaddr)+sizeof(*(ptr))), (ptr)) + +#endif // BASE_MEMORY_MAP_H diff --git a/src/base/base_shared_memory.h b/src/base/base_shared_memory.h index 7d9f1498..ec288d86 100644 --- a/src/base/base_shared_memory.h +++ b/src/base/base_shared_memory.h @@ -4,8 +4,14 @@ #ifndef BASE_SHARED_MEMORY_H #define BASE_SHARED_MEMORY_H +typedef struct SharedMemory SharedMemory; +struct SharedMemory +{ + U64 u64[1]; +}; + //////////////////////////////// -//~ rjf: @os_hooks Shared Memory +//~ rjf: @per_os_impl Shared Memory internal SharedMemory shared_memory_alloc(U64 size, String8 name); internal SharedMemory shared_memory_open(String8 name); diff --git a/src/base/base_strings.c b/src/base/base_strings.c index 5373f087..8825d479 100644 --- a/src/base/base_strings.c +++ b/src/base/base_strings.c @@ -1476,7 +1476,7 @@ str8_from_version(Arena *arena, U64 version) //~ rjf: String Path Helpers internal String8 -default_exe_name_suffix_from_os(OperatingSystem os) +program_ext_postfix_from_os(OperatingSystem os, B32 require_ext) { String8 result = {0}; switch(os) @@ -1486,36 +1486,15 @@ default_exe_name_suffix_from_os(OperatingSystem os) { result = s(".exe"); }break; - } - return result; -} - -internal String8 -program_data_folder_prefix_from_os(OperatingSystem os) -{ - String8 result = {0}; - switch(os) - { - default:{}break; case OperatingSystem_Linux: - case OperatingSystem_Mac: + if(require_ext) { - result = s("."); + result = s(".elf"); }break; - } - return result; -} - -internal String8 -program_ext_postfix_from_os(OperatingSystem os) -{ - String8 result = {0}; - switch(os) - { - default:{}break; - case OperatingSystem_Windows: + case OperatingSystem_Mac: + if(require_ext) { - result = s(".exe"); + result = s(".macho"); }break; } return result; @@ -3038,6 +3017,72 @@ str8_deserial_read_block(String8 string, U64 off, U64 size, String8 *block_out) return block_out->size; } +internal U64 +str8_deserial_read_uleb128(String8 string, U64 off, U64 *value_out) +{ + U64 value = 0; + U64 shift = 0; + U64 cursor = off; + for(;;) + { + U8 byte = 0; + U64 bytes_read = str8_deserial_read_struct(string, cursor, &byte); + if(bytes_read != sizeof(byte)) + { + break; + } + U8 val = byte & 0x7fu; + value |= ((U64)val) << shift; + cursor += bytes_read; + shift += 7u; + if((byte & 0x80u) == 0) + { + break; + } + } + if(value_out != 0) + { + *value_out = value; + } + U64 bytes_read = cursor - off; + return bytes_read; +} + +internal U64 +str8_deserial_read_sleb128(String8 string, U64 off, S64 *value_out) +{ + U64 value = 0; + U64 shift = 0; + U64 cursor = off; + for(;;) + { + U8 byte; + U64 bytes_read = str8_deserial_read_struct(string, cursor, &byte); + if(bytes_read != sizeof(byte)) + { + break; + } + U8 val = byte & 0x7fu; + value |= ((U64)val) << shift; + cursor += bytes_read; + shift += 7u; + if((byte & 0x80u) == 0) + { + if(shift < sizeof(value) * 8 && (byte & 0x40u) != 0) + { + value |= -(S64)(1ull << shift); + } + break; + } + } + if(value_out != 0) + { + *value_out = value; + } + U64 bytes_read = cursor - off; + return bytes_read; +} + //////////////////////////////// internal int diff --git a/src/base/base_strings.h b/src/base/base_strings.h index dadcf5ac..6c1796a0 100644 --- a/src/base/base_strings.h +++ b/src/base/base_strings.h @@ -323,9 +323,7 @@ g_path_style_map[] = { str8_lit_comp("system"), PathStyle_SystemAbsolute }, }; -internal String8 default_exe_name_suffix_from_os(OperatingSystem os); -internal String8 program_data_folder_prefix_from_os(OperatingSystem os); -internal String8 program_ext_postfix_from_os(OperatingSystem os); +internal String8 program_ext_postfix_from_os(OperatingSystem os, B32 require_ext); internal String8 str8_chop_last_slash(String8 string); internal String8 str8_skip_last_slash(String8 string); @@ -457,6 +455,8 @@ internal U64 str8_deserial_read_windows_utf16_string16(String8 string, U64 of internal U64 str8_deserial_read_block(String8 string, U64 off, U64 size, String8 *block_out); #define str8_deserial_read_array(string, off, ptr, count) str8_deserial_read((string), (off), (ptr), sizeof(*(ptr))*(count), sizeof(*(ptr))) #define str8_deserial_read_struct(string, off, ptr) str8_deserial_read_array(string, off, ptr, 1) +internal U64 str8_deserial_read_uleb128(String8 string, U64 off, U64 *value_out); +internal U64 str8_deserial_read_sleb128(String8 string, U64 off, S64 *value_out); //////////////////////////////// //~ string buffer diff --git a/src/dbg_engine/dbg_engine_core.c b/src/dbg_engine/dbg_engine_core.c index dbf192bb..02a05907 100644 --- a/src/dbg_engine/dbg_engine_core.c +++ b/src/dbg_engine/dbg_engine_core.c @@ -37,15 +37,9 @@ d_init(void) d_ctrl_state->thread_reg_cache.stripes[idx].arena = arena_alloc(); d_ctrl_state->thread_reg_cache.stripes[idx].rw_mutex = rw_mutex_alloc(); } - d_ctrl_state->module_image_info_cache.slots_count = 1024; - d_ctrl_state->module_image_info_cache.slots = push_array(arena, D_ModuleImageInfoCacheSlot, d_ctrl_state->module_image_info_cache.slots_count); - d_ctrl_state->module_image_info_cache.stripes_count = get_system_info()->logical_processor_count; - d_ctrl_state->module_image_info_cache.stripes = push_array(arena, D_ModuleImageInfoCacheStripe, d_ctrl_state->module_image_info_cache.stripes_count); - for(U64 idx = 0; idx < d_ctrl_state->module_image_info_cache.stripes_count; idx += 1) - { - d_ctrl_state->module_image_info_cache.stripes[idx].arena = arena_alloc(); - d_ctrl_state->module_image_info_cache.stripes[idx].rw_mutex = rw_mutex_alloc(); - } + d_ctrl_state->module_info_cache.slots_count = 1024; + d_ctrl_state->module_info_cache.slots = push_array(arena, D_ModuleInfoCacheSlot, d_ctrl_state->module_info_cache.slots_count); + d_ctrl_state->module_info_cache.stripes = stripe_array_alloc(arena); d_ctrl_state->u2c_ring_size = KB(64); d_ctrl_state->u2c_ring_base = push_array_no_zero(arena, U8, d_ctrl_state->u2c_ring_size); d_ctrl_state->u2c_ring_mutex = mutex_alloc(); diff --git a/src/dbg_engine/dbg_engine_core.h b/src/dbg_engine/dbg_engine_core.h index d9676383..26a90c6e 100644 --- a/src/dbg_engine/dbg_engine_core.h +++ b/src/dbg_engine/dbg_engine_core.h @@ -139,6 +139,7 @@ struct D_Entity D_TlsModel tls_model; U64 tls_index; U64 tls_offset; + U64 tls_root_vaddr; OperatingSystem target_os; D_MsgID src_msg_id; }; diff --git a/src/dbg_engine/dbg_engine_ctrl.c b/src/dbg_engine/dbg_engine_ctrl.c index dcdf9a50..cf494e69 100644 --- a/src/dbg_engine/dbg_engine_ctrl.c +++ b/src/dbg_engine/dbg_engine_ctrl.c @@ -1358,6 +1358,7 @@ d_entity_store_apply_events(D_EntityCtxRWStore *store, D_EventList *list) } } thread->stack_base = event->stack_base; + thread->tls_root_vaddr = event->tls_root; } //d_cached_ip_from_thread(&store->ctx, event->entity); }break; @@ -1633,6 +1634,25 @@ d_sp_from_thread(D_Handle handle) return result; } +internal B32 +d_thread_get_module_tls_vaddr(D_Handle thread, D_Handle module, U64 *vaddr_out) +{ + B32 result = 0; + + // rjf: resolve to demon + DMN_Handle thread_dmn = d_dmn_from_handle(thread); + DMN_Handle module_dmn = d_dmn_from_handle(module); + + // rjf: compute vaddr + if(!dmn_handle_match(thread_dmn, dmn_handle_zero()) && + !dmn_handle_match(module_dmn, dmn_handle_zero())) + { + result = dmn_thread_get_module_tls_vaddr(thread_dmn, module_dmn, vaddr_out); + } + + return result; +} + //- rjf: thread register cache reading internal void * @@ -1695,14 +1715,6 @@ d_cached_reg_block_from_thread(Arena *arena, D_Handle handle) return result; } -internal U64 -d_cached_tls_root_vaddr_from_thread(D_Handle handle) -{ - DMN_Handle handle_dmn = d_dmn_from_handle(handle); - U64 result = dmn_tls_root_vaddr_from_thread(handle_dmn); - return result; -} - internal U64 d_cached_ip_from_thread(D_Handle handle) { @@ -1734,17 +1746,18 @@ d_cached_sp_from_thread(D_Handle handle) //- rjf: cache lookups +#if 0 // TODO(rjf): @unwind internal PE_IntelPdata * d_intel_pdata_from_module_voff(Arena *arena, D_Handle module_handle, U64 voff) { PE_IntelPdata *first_pdata = 0; { U64 hash = d_hash_from_handle(module_handle); - U64 slot_idx = hash%d_ctrl_state->module_image_info_cache.slots_count; - U64 stripe_idx = slot_idx%d_ctrl_state->module_image_info_cache.stripes_count; - D_ModuleImageInfoCacheSlot *slot = &d_ctrl_state->module_image_info_cache.slots[slot_idx]; - D_ModuleImageInfoCacheStripe *stripe = &d_ctrl_state->module_image_info_cache.stripes[stripe_idx]; - MutexScopeR(stripe->rw_mutex) for(D_ModuleImageInfoCacheNode *n = slot->first; n != 0; n = n->next) + U64 slot_idx = hash%d_ctrl_state->module_info_cache.slots_count; + U64 stripe_idx = slot_idx%d_ctrl_state->module_info_cache.stripes_count; + D_ModuleInfoCacheSlot *slot = &d_ctrl_state->module_info_cache.slots[slot_idx]; + D_ModuleInfoCacheStripe *stripe = &d_ctrl_state->module_info_cache.stripes[stripe_idx]; + MutexScopeR(stripe->rw_mutex) for(D_ModuleInfoCacheNode *n = slot->first; n != 0; n = n->next) { if(d_handle_match(n->module, module_handle)) { @@ -1800,70 +1813,54 @@ d_intel_pdata_from_module_voff(Arena *arena, D_Handle module_handle, U64 voff) } return first_pdata; } +#endif + +internal D_ModuleInfo * +d_info_from_module(Access *access, D_Handle module) +{ + D_ModuleInfo *result = &d_module_info_nil; + { + D_ModuleInfoCache *cache = &d_ctrl_state->module_info_cache; + U64 hash = d_hash_from_handle(module); + U64 slot_idx = hash%cache->slots_count; + D_ModuleInfoCacheSlot *slot = &cache->slots[slot_idx]; + Stripe *stripe = stripe_from_slot_idx(&cache->stripes, slot_idx); + MutexScopeR(stripe->rw_mutex) for(D_ModuleInfoCacheNode *n = slot->first; n != 0; n = n->next) + { + if(d_handle_match(n->module, module)) + { + access_touch(access, &n->access_pt, stripe->cv); + result = &n->v; + break; + } + } + } + return result; +} internal U64 d_entry_point_voff_from_module(D_Handle module_handle) { - U64 result = 0; - U64 hash = d_hash_from_handle(module_handle); - U64 slot_idx = hash%d_ctrl_state->module_image_info_cache.slots_count; - U64 stripe_idx = slot_idx%d_ctrl_state->module_image_info_cache.stripes_count; - D_ModuleImageInfoCacheSlot *slot = &d_ctrl_state->module_image_info_cache.slots[slot_idx]; - D_ModuleImageInfoCacheStripe *stripe = &d_ctrl_state->module_image_info_cache.stripes[stripe_idx]; - MutexScopeR(stripe->rw_mutex) for(D_ModuleImageInfoCacheNode *n = slot->first; n != 0; n = n->next) - { - if(d_handle_match(n->module, module_handle)) - { - result = n->entry_point_voff; - break; - } - } + Access *access = access_open(); + U64 result = d_info_from_module(access, module_handle)->entry_point_voff; + access_close(access); return result; } internal String8 d_initial_debug_info_path_from_module(Arena *arena, D_Handle module_handle) { - String8 result = {0}; - U64 hash = d_hash_from_handle(module_handle); - U64 slot_idx = hash%d_ctrl_state->module_image_info_cache.slots_count; - U64 stripe_idx = slot_idx%d_ctrl_state->module_image_info_cache.stripes_count; - D_ModuleImageInfoCacheSlot *slot = &d_ctrl_state->module_image_info_cache.slots[slot_idx]; - D_ModuleImageInfoCacheStripe *stripe = &d_ctrl_state->module_image_info_cache.stripes[stripe_idx]; - MutexScopeR(stripe->rw_mutex) for(D_ModuleImageInfoCacheNode *n = slot->first; n != 0; n = n->next) - { - if(d_handle_match(n->module, module_handle)) - { - result = str8_copy(arena, n->local_debug_info_path); - break; - } - } - return result; -} - -internal String8 -d_raddbg_data_from_module(Arena *arena, D_Handle module_handle) -{ - String8 result = {0}; - U64 hash = d_hash_from_handle(module_handle); - U64 slot_idx = hash%d_ctrl_state->module_image_info_cache.slots_count; - U64 stripe_idx = slot_idx%d_ctrl_state->module_image_info_cache.stripes_count; - D_ModuleImageInfoCacheSlot *slot = &d_ctrl_state->module_image_info_cache.slots[slot_idx]; - D_ModuleImageInfoCacheStripe *stripe = &d_ctrl_state->module_image_info_cache.stripes[stripe_idx]; - MutexScopeR(stripe->rw_mutex) for(D_ModuleImageInfoCacheNode *n = slot->first; n != 0; n = n->next) - { - if(d_handle_match(n->module, module_handle)) - { - result = push_str8_copy(arena, n->raddbg_data); - break; - } - } + Access *access = access_open(); + String8 result = str8_copy(arena, d_info_from_module(access, module_handle)->local_debug_info_path); + access_close(access); return result; } //////////////////////////////// //~ rjf: Unwinding Functions +#if 0 // TODO(rjf): @unwind + //- rjf: [dwarf] typedef struct D_MemoryReadContextDwarfX64 D_MemoryReadContextDwarfX64; @@ -1930,11 +1927,11 @@ d_establish_frame_unwind_context__dwarf(Arena *arena, D_Handle process_handle, D EH_PtrCtx eh_ptr_ctx = {0}; { U64 hash = d_hash_from_handle(module_handle); - U64 slot_idx = hash%d_ctrl_state->module_image_info_cache.slots_count; - U64 stripe_idx = slot_idx%d_ctrl_state->module_image_info_cache.stripes_count; - D_ModuleImageInfoCacheSlot *slot = &d_ctrl_state->module_image_info_cache.slots[slot_idx]; - D_ModuleImageInfoCacheStripe *stripe = &d_ctrl_state->module_image_info_cache.stripes[stripe_idx]; - MutexScopeR(stripe->rw_mutex) for EachNode(n, D_ModuleImageInfoCacheNode, slot->first) + U64 slot_idx = hash%d_ctrl_state->module_info_cache.slots_count; + U64 stripe_idx = slot_idx%d_ctrl_state->module_info_cache.stripes_count; + D_ModuleInfoCacheSlot *slot = &d_ctrl_state->module_info_cache.slots[slot_idx]; + D_ModuleInfoCacheStripe *stripe = &d_ctrl_state->module_info_cache.stripes[stripe_idx]; + MutexScopeR(stripe->rw_mutex) for EachNode(n, D_ModuleInfoCacheNode, slot->first) { if(d_handle_match(n->module, module_handle)) { @@ -2998,6 +2995,7 @@ d_unwind_step__pe_x64(D_Handle process_handle, D_Handle module_handle, U64 modul if(is_stale) {result.flags |= D_UnwindFlag_Stale;} return result; } +#endif //- rjf: abstracted full unwind @@ -3008,35 +3006,149 @@ d_unwind_from_thread(Arena *arena, D_Handle thread, U64 endt_us) Temp scratch = scratch_begin(&arena, 1); D_Unwind unwind = { .flags = D_UnwindFlag_Error }; + ////////////////////////////// + //- rjf: grab run state pre-unwind computing + // + U64 run_gen = d_run_gen(); + + ////////////////////////////// //- rjf: unpack args + // D_Entity *thread_entity = d_entity_from_handle(thread); D_Entity *process_entity = thread_entity->parent; Arch arch = thread_entity->arch; ARCH_Info *arch_info = arch_info_from_arch(arch); U64 arch_reg_block_size = arch_info->reg_block_size; + ////////////////////////////// //- rjf: grab initial register block + // void *regs_block = d_cached_reg_block_from_thread(scratch.arena, thread); B32 regs_block_good = (arch != Arch_Null && regs_block != 0); - //- rjf: loop & unwind + ////////////////////////////// + //- rjf: grab initial memory + // + MemoryMap memory_map = {0}; + { + // NOTE(rjf): we can pre-fill memory that we can expect to load here - + // otherwise we will lazily evaluate it via the unwinding backends below + } + + ////////////////////////////// + //- rjf: push one frame for register block we already have + // D_UnwindFrameNode *first_frame_node = 0; D_UnwindFrameNode *last_frame_node = 0; U64 frame_node_count = 0; if(regs_block_good) { - for(unwind.flags = 0;;) + D_UnwindFrameNode *frame_node = push_array(scratch.arena, D_UnwindFrameNode, 1); + D_UnwindFrame *f = &frame_node->v; + f->regs = push_array_no_zero(arena, U8, arch_reg_block_size); + MemoryCopy(f->regs, regs_block, arch_reg_block_size); + DLLPushBack(first_frame_node, last_frame_node, frame_node); + frame_node_count += 1; + } + + ////////////////////////////// + //- rjf: loop & unwind + // + if(regs_block_good) + { + unwind.flags = 0; + for(;!unwind.flags;) { ARCH_Info *arch_info = arch_info_from_arch(arch); - U64 rip = arch_ip_from_reg_block(arch_info, regs_block); - U64 rsp = arch_sp_from_reg_block(arch_info, regs_block); + U64 start_ip = arch_ip_from_reg_block(arch_info, regs_block); + U64 start_sp = arch_sp_from_reg_block(arch_info, regs_block); - // rjf: cancel on zero rip (except the top frame) - if(rip == 0 && frame_node_count > 0) + //- rjf: cancel on zero rip (except the top frame) + if(start_ip == 0 && frame_node_count > 0) { break; } + //- rjf: open access for this step + Access *access = access_open(); + + //- rjf: ip -> module / unpack + D_Entity *module_entity = d_module_from_process_vaddr(process_entity, start_ip); + D_ModuleInfo *module_info = d_info_from_module(access, module_entity->handle); + UWND_Unwinder unwinder = module_info->unwinder; + UWND_ModuleInfo unwinder_module_info = {module_entity->vaddr_range.min, module_info->unwind_info}; + + //- rjf: thread * module -> tls vaddr + U64 tls_vaddr = 0; + d_thread_get_module_tls_vaddr(thread_entity->handle, module_entity->handle, &tls_vaddr); + + //- rjf: do one unwind step + B32 step_is_good = 0; + for(;!unwind.flags;) + { + // rjf: try step + U64 cfa = 0; + UWND_StepResult step = uwnd_step(unwinder, arch, &memory_map, &unwinder_module_info, tls_vaddr, regs_block, &cfa); + + // rjf: if we failed to read memory, try to read that memory, equip to memory map. + // if it is stale and we run out of time, we will need to mark the whole unwind as + // stale. if it can't be read, the unwind fails. + if(step.status == UWND_StepStatus_FailedMemoryRead) + { + D_ProcessMemorySlice slice = d_process_memory_slice_from_vaddr_range(scratch.arena, process_entity->handle, step.missed_read_vaddr_range, 1, 0); + String8 data = slice.data; + if(slice.stale) + { + unwind.flags |= D_UnwindFlag_Stale; + } + else if(data.size < dim_1u64(step.missed_read_vaddr_range)) + { + unwind.flags |= D_UnwindFlag_Error; + } + else + { + memory_map_push(scratch.arena, &memory_map, step.missed_read_vaddr_range, data.str); + } + } + + // rjf: if the step itself failed, we just fail out. + else if(step.status == UWND_StepStatus_Error) + { + unwind.flags |= D_UnwindFlag_Error; + } + + // rjf: if the step worked, we're good. equip this step's CFA to the previously added frame, + // then exit the step loop. + else if(step.status == UWND_StepStatus_Good) + { + last_frame_node->v.cfa = cfa; + step_is_good = 1; + break; + } + } + + //- rjf: push successful steps to frame list + if(step_is_good && arch_ip_from_reg_block(arch_info, regs_block) != 0 && arch_ip_from_reg_block(arch_info, regs_block) != start_ip) + { + D_UnwindFrameNode *frame_node = push_array(scratch.arena, D_UnwindFrameNode, 1); + D_UnwindFrame *f = &frame_node->v; + f->regs = push_array_no_zero(arena, U8, arch_reg_block_size); + MemoryCopy(f->regs, regs_block, arch_reg_block_size); + DLLPushBack(first_frame_node, last_frame_node, frame_node); + frame_node_count += 1; + } + + //- rjf: close access + access_close(access); + + //- rjf: exit if we made no progress on the unwind + if(arch_ip_from_reg_block(arch_info, regs_block) == start_ip || + arch_sp_from_reg_block(arch_info, regs_block) == start_sp) + { + break; + } + +#if 0 // TODO(rjf): @unwind // rip -> module D_Entity *module_entity = d_module_from_process_vaddr(process_entity, rip); @@ -3097,13 +3209,14 @@ d_unwind_from_thread(Arena *arena, D_Handle thread, U64 endt_us) }break; } - // stop unwinding on errors or stale data + // rjf: stop unwinding on errors or stale data unwind.flags |= step_result.flags; if(unwind.flags & (D_UnwindFlag_Stale|D_UnwindFlag_Error) || (arch_sp_from_reg_block(arch_info, regs_block) == rsp && arch_ip_from_reg_block(arch_info, regs_block) == rip)) { break; } +#endif } } @@ -3758,23 +3871,47 @@ d_ctrl_thread__module_open(D_Handle process, D_Handle module, U64 base_vaddr, DM { Temp scratch = scratch_begin(0,0); - //- rjf: set up per-module info + ////////////////////////////// + //- rjf: allocate / set up basic per-module info + // Arena *arena = arena_alloc(); String8 raddbg_data = d_data_from_process_vaddr_range(arena, process, shift_1u64(module_info->raddbg_info_voff_range, base_vaddr), 0); - U64 pdatas_count = dim_1u64(module_info->pe_intel_pdatas_vaddr_range) / sizeof(PE_IntelPdata); - String8 pdatas_data = d_data_from_process_vaddr_range(arena, process, module_info->pe_intel_pdatas_vaddr_range, 0); - pdatas_count = Min(pdatas_count, pdatas_data.size / sizeof(PE_IntelPdata)); - PE_IntelPdata *pdatas = (PE_IntelPdata *)pdatas_data.str; - EH_PtrCtx eh_ptr_ctx = {.pc_vaddr = max_U64, .text_vaddr = max_U64, .data_vaddr = max_U64, .func_vaddr = max_U64, .ptr_align = 0}; - EH_FrameHdr eh_frame_hdr = {0}; - B32 is_unwind_eh = 0; - if(module_info->eh_frame_header_vaddr_range.min != 0) + + ////////////////////////////// + //- rjf: prepare unwind info + // + UWND_Unwinder unwinder = UWND_Unwinder_Null; + void *unwind_info_opaque = 0; { - String8 eh_frame_hdr_data = d_data_from_process_vaddr_range(arena, process, module_info->eh_frame_header_vaddr_range, 0); - eh_ptr_ctx.pc_vaddr = module_info->eh_frame_header_vaddr_range.min; - eh_ptr_ctx.data_vaddr = module_info->eh_frame_header_vaddr_range.min; - eh_frame_hdr = eh_parse_frame_hdr(eh_frame_hdr_data, byte_size_from_arch(module_info->arch), &eh_ptr_ctx); - is_unwind_eh = 1; + //- rjf: PE/x64 unwinder + if(dim_1u64(module_info->pe_intel_pdatas_vaddr_range) != 0) + { + unwinder = UWND_Unwinder_PEx64; + PE_X64_UWND_ModuleUnwindInfo *unwind_info = push_array(arena, PE_X64_UWND_ModuleUnwindInfo, 1); + { + U64 pdatas_count = dim_1u64(module_info->pe_intel_pdatas_vaddr_range) / sizeof(PE_IntelPdata); + String8 pdatas_data = d_data_from_process_vaddr_range(arena, process, module_info->pe_intel_pdatas_vaddr_range, 0); + pdatas_count = Min(pdatas_count, pdatas_data.size / sizeof(PE_IntelPdata)); + PE_IntelPdata *pdatas = (PE_IntelPdata *)pdatas_data.str; + unwind_info->pdatas = pdatas; + unwind_info->pdatas_count = pdatas_count; + } + unwind_info_opaque = unwind_info; + } + + //- rjf: .eh_frame unwinder + else if(dim_1u64(module_info->eh_frame_header_vaddr_range) != 0) + { + unwinder = UWND_Unwinder_EHFrame; + EH_UWND_ModuleUnwindInfo *unwind_info = push_array(arena, EH_UWND_ModuleUnwindInfo, 1); + { + String8 eh_frame_hdr_data = d_data_from_process_vaddr_range(arena, process, module_info->eh_frame_header_vaddr_range, 0); + unwind_info->ptr_ctx.pc_vaddr = module_info->eh_frame_header_vaddr_range.min; + unwind_info->ptr_ctx.data_vaddr = module_info->eh_frame_header_vaddr_range.min; + unwind_info->header = eh_parse_frame_hdr(eh_frame_hdr_data, byte_size_from_arch(module_info->arch), &unwind_info->ptr_ctx); + } + unwind_info_opaque = unwind_info; + } } ////////////////////////////// @@ -3853,19 +3990,33 @@ d_ctrl_thread__module_open(D_Handle process, D_Handle module, U64 base_vaddr, DM d_process_write_struct(process, base_vaddr + module_info->raddbg_is_attached_marker_voff, &new_value); } + ////////////////////////////// + //- rjf: fill info + // + D_ModuleInfo info = {0}; + { + info.entry_point_voff = module_info->entry_point_voff; + info.unwinder = unwinder; + info.unwind_info = unwind_info_opaque; + info.local_debug_info_path = initial_debug_info_path; + info.debug_info_unique_identifier = debug_info_unique_identifier; + info.raddbg_attached_marker_voff = module_info->raddbg_is_attached_marker_voff; + info.raddbg_data = raddbg_data; + } + ////////////////////////////// //- rjf: insert info into cache // { + D_ModuleInfoCache *cache = &d_ctrl_state->module_info_cache; U64 hash = d_hash_from_handle(module); - U64 slot_idx = hash%d_ctrl_state->module_image_info_cache.slots_count; - U64 stripe_idx = slot_idx%d_ctrl_state->module_image_info_cache.stripes_count; - D_ModuleImageInfoCacheSlot *slot = &d_ctrl_state->module_image_info_cache.slots[slot_idx]; - D_ModuleImageInfoCacheStripe *stripe = &d_ctrl_state->module_image_info_cache.stripes[stripe_idx]; + U64 slot_idx = hash%cache->slots_count; + D_ModuleInfoCacheSlot *slot = &cache->slots[slot_idx]; + Stripe *stripe = stripe_from_slot_idx(&cache->stripes, slot_idx); MutexScopeW(stripe->rw_mutex) { - D_ModuleImageInfoCacheNode *node = 0; - for EachNode(n, D_ModuleImageInfoCacheNode, slot->first) + D_ModuleInfoCacheNode *node = 0; + for EachNode(n, D_ModuleInfoCacheNode, slot->first) { if(d_handle_match(n->module, module)) { @@ -3875,21 +4026,11 @@ d_ctrl_thread__module_open(D_Handle process, D_Handle module, U64 base_vaddr, DM } if(!node) { - node = push_array(arena, D_ModuleImageInfoCacheNode, 1); + node = push_array(arena, D_ModuleInfoCacheNode, 1); DLLPushBack(slot->first, slot->last, node); - node->module = module; - node->arena = arena; - node->pdatas = pdatas; - node->pdatas_count = pdatas_count; - node->cfi_rebase = module_info->cfi_rebase; - node->is_unwind_eh = is_unwind_eh; - node->eh_frame_hdr = eh_frame_hdr; - node->eh_ptr_ctx = eh_ptr_ctx; - node->entry_point_voff = module_info->entry_point_voff; - node->local_debug_info_path = initial_debug_info_path; - node->debug_info_unique_identifier= debug_info_unique_identifier; - node->raddbg_attached_marker_voff = module_info->raddbg_is_attached_marker_voff; - node->raddbg_data = str8_copy(arena, raddbg_data); + node->module = module; + node->arena = arena; + node->v = info; } } } @@ -3903,19 +4044,19 @@ d_ctrl_thread__module_close(D_Handle process, D_Handle module, U64 base_vaddr) DMN_Handle process_dmn = d_dmn_from_handle(process); ////////////////////////////// - //- rjf: evict module image info from cache + //- rjf: evict module info from cache // U64 raddbg_attached_marker_voff = 0; { + D_ModuleInfoCache *cache = &d_ctrl_state->module_info_cache; U64 hash = d_hash_from_handle(module); - U64 slot_idx = hash%d_ctrl_state->module_image_info_cache.slots_count; - U64 stripe_idx = slot_idx%d_ctrl_state->module_image_info_cache.stripes_count; - D_ModuleImageInfoCacheSlot *slot = &d_ctrl_state->module_image_info_cache.slots[slot_idx]; - D_ModuleImageInfoCacheStripe *stripe = &d_ctrl_state->module_image_info_cache.stripes[stripe_idx]; - MutexScopeW(stripe->rw_mutex) + U64 slot_idx = hash%cache->slots_count; + Stripe *stripe = stripe_from_slot_idx(&cache->stripes, slot_idx); + D_ModuleInfoCacheSlot *slot = &cache->slots[slot_idx]; + MutexScopeW(stripe->rw_mutex) for(;;) { - D_ModuleImageInfoCacheNode *node = 0; - for(D_ModuleImageInfoCacheNode *n = slot->first; n != 0; n = n->next) + D_ModuleInfoCacheNode *node = 0; + for(D_ModuleInfoCacheNode *n = slot->first; n != 0; n = n->next) { if(d_handle_match(n->module, module)) { @@ -3923,12 +4064,14 @@ d_ctrl_thread__module_close(D_Handle process, D_Handle module, U64 base_vaddr) break; } } - if(node) + if(node && access_pt_is_expired(&node->access_pt, .time = 0, .update_idxs = 0)) { - raddbg_attached_marker_voff = node->raddbg_attached_marker_voff; + raddbg_attached_marker_voff = node->v.raddbg_attached_marker_voff; DLLRemove(slot->first, slot->last, node); arena_release(node->arena); + break; } + cond_var_wait_rw_w(stripe->cv, stripe->rw_mutex, max_U64); } } @@ -4314,8 +4457,8 @@ d_ctrl_thread__next_dmn_event(Arena *arena, DMN_CtrlCtx *ctrl_ctx, D_Msg *msg, D out_evt->parent = d_handle_from_dmn(D_MachineID_Local, event->process); out_evt->arch = event->arch; out_evt->entity_id = event->code; - out_evt->stack_base = dmn_stack_base_vaddr_from_thread(event->thread); - out_evt->tls_root = dmn_tls_root_vaddr_from_thread(event->thread); + out_evt->stack_base = event->stack_pointer; + out_evt->tls_root = event->tls_root_vaddr; out_evt->rip_vaddr = event->instruction_pointer; out_evt->string = event->string; }break; @@ -5902,7 +6045,9 @@ d_ctrl_thread__run(DMN_CtrlCtx *ctrl_ctx, D_Msg *msg) //- rjf: add traps for module-baked entry points, if specified if(!entries_found) { - String8 raddbg_data = d_raddbg_data_from_module(scratch.arena, module->handle); + Access *access = access_open(); + D_ModuleInfo *module_info = d_info_from_module(access, module->handle); + String8 raddbg_data = module_info->raddbg_data; U8 split_char = 0; String8List raddbg_data_text_parts = str8_split(scratch.arena, raddbg_data, &split_char, 1, 0); for(String8Node *text_n = raddbg_data_text_parts.first; text_n != 0; text_n = text_n->next) @@ -5932,6 +6077,7 @@ d_ctrl_thread__run(DMN_CtrlCtx *ctrl_ctx, D_Msg *msg) } } } + access_close(access); } //- rjf: add traps for PID-correllated entry points @@ -7091,6 +7237,64 @@ d_process_memory_read(D_Handle process, Rng1U64 range, B32 *is_stale_out, void * return good; } +//////////////////////////////// +//~ rjf: TLS Address Artifact Cache Hooks / Lookups + +internal AC_Artifact +d_tls_vaddr_artifact_create(String8 key, B32 *cancel_signal, B32 *retry_out, U64 *gen_out) +{ + // rjf: unpack key + D_Handle thread = {0}; + D_Handle module = {0}; + { + U64 off = 0; + off += str8_deserial_read_struct(key, off, &thread); + off += str8_deserial_read_struct(key, off, &module); + } + + // rjf: resolve to demon + DMN_Handle thread_dmn = d_dmn_from_handle(thread); + DMN_Handle module_dmn = d_dmn_from_handle(module); + + // rjf: compute vaddr + U64 tls_vaddr = 0; + B32 success = 1; + if(!dmn_handle_match(thread_dmn, dmn_handle_zero()) && + !dmn_handle_match(module_dmn, dmn_handle_zero())) + { + success = dmn_thread_get_module_tls_vaddr(thread_dmn, module_dmn, &tls_vaddr); + } + + // rjf: if not successful -> retry + if(!success) + { + retry_out[0] = 1; + } + + // rjf: package as artifact + AC_Artifact artifact = {0}; + artifact.u64[0] = tls_vaddr; + return artifact; +} + +internal void d_tls_vaddr_artifact_destroy(AC_Artifact artifact) +{ + // NOTE(rjf): no-op +} + +internal U64 +d_cached_tls_vaddr_from_thread_module(D_Handle thread_handle, D_Handle module_handle, U64 endt_us, B32 *stale_out) +{ + U64 result = 0; + Access *access = access_open(); + D_Handle key_data[] = {thread_handle, module_handle}; + String8 key = str8((U8 *)&key_data[0], sizeof(key_data)); + AC_Artifact artifact = ac_artifact_from_key(access, key, d_tls_vaddr_artifact_create, d_tls_vaddr_artifact_destroy, endt_us, .stale_out = stale_out); + result = artifact.u64[0]; + access_close(access); + return result; +} + //////////////////////////////// //~ rjf: Call Stack Artifact Cache Hooks / Lookups @@ -7226,7 +7430,7 @@ d_call_stack_artifact_create(String8 key, B32 *cancel_signal, B32 *retry_out, U6 { pre_reg_gen = d_reg_gen(); pre_mem_gen = d_mem_gen(); - unwind = d_unwind_from_thread(arena, thread_handle, now_time_us()+5000); + unwind = d_unwind_from_thread(arena, thread_handle, 0); if(!(unwind.flags & D_UnwindFlag_Stale)) { good = 1; diff --git a/src/dbg_engine/dbg_engine_ctrl.h b/src/dbg_engine/dbg_engine_ctrl.h index 9df2083e..13d0e926 100644 --- a/src/dbg_engine/dbg_engine_ctrl.h +++ b/src/dbg_engine/dbg_engine_ctrl.h @@ -49,6 +49,7 @@ struct D_Unwind D_UnwindFlags flags; }; +#if 0 // TODO(rjf): @unwind typedef struct D_FrameUnwindContext D_FrameUnwindContext; struct D_FrameUnwindContext { @@ -57,6 +58,7 @@ struct D_FrameUnwindContext DW_CFI_Row *cfi_row; U64 ret_addr_reg; }; +#endif //////////////////////////////// //~ rjf: Call Stack Types @@ -171,21 +173,12 @@ struct D_ThreadRegCache //////////////////////////////// //~ rjf: Module Image Info Cache Types -typedef struct D_ModuleImageInfoCacheNode D_ModuleImageInfoCacheNode; -struct D_ModuleImageInfoCacheNode +typedef struct D_ModuleInfo D_ModuleInfo; +struct D_ModuleInfo { - D_ModuleImageInfoCacheNode *next; - D_ModuleImageInfoCacheNode *prev; - D_Handle module; - Arena *arena; - PE_IntelPdata *pdatas; - U64 pdatas_count; - U64 cfi_rebase; - String8 dwarf_unwind_data; - B32 is_unwind_eh; - EH_FrameHdr eh_frame_hdr; - EH_PtrCtx eh_ptr_ctx; U64 entry_point_voff; + UWND_Unwinder unwinder; + void *unwind_info; String8 local_debug_info_path; String8 local_server_cache_debug_info_path; String8 debug_info_unique_identifier; @@ -193,27 +186,30 @@ struct D_ModuleImageInfoCacheNode String8 raddbg_data; }; -typedef struct D_ModuleImageInfoCacheSlot D_ModuleImageInfoCacheSlot; -struct D_ModuleImageInfoCacheSlot -{ - D_ModuleImageInfoCacheNode *first; - D_ModuleImageInfoCacheNode *last; -}; - -typedef struct D_ModuleImageInfoCacheStripe D_ModuleImageInfoCacheStripe; -struct D_ModuleImageInfoCacheStripe +typedef struct D_ModuleInfoCacheNode D_ModuleInfoCacheNode; +struct D_ModuleInfoCacheNode { + D_ModuleInfoCacheNode *next; + D_ModuleInfoCacheNode *prev; + AccessPt access_pt; + D_Handle module; Arena *arena; - RWMutex rw_mutex; + D_ModuleInfo v; }; -typedef struct D_ModuleImageInfoCache D_ModuleImageInfoCache; -struct D_ModuleImageInfoCache +typedef struct D_ModuleInfoCacheSlot D_ModuleInfoCacheSlot; +struct D_ModuleInfoCacheSlot +{ + D_ModuleInfoCacheNode *first; + D_ModuleInfoCacheNode *last; +}; + +typedef struct D_ModuleInfoCache D_ModuleInfoCache; +struct D_ModuleInfoCache { U64 slots_count; - D_ModuleImageInfoCacheSlot *slots; - U64 stripes_count; - D_ModuleImageInfoCacheStripe *stripes; + D_ModuleInfoCacheSlot *slots; + StripeArray stripes; }; //////////////////////////////// @@ -340,7 +336,7 @@ struct D_CtrlState // rjf: caches D_ThreadRegCache thread_reg_cache; - D_ModuleImageInfoCache module_image_info_cache; + D_ModuleInfoCache module_info_cache; D_DumpCache dump_cache; // rjf: generations @@ -413,6 +409,7 @@ read_only global D_CallStackTreeNode d_call_stack_tree_node_nil = &d_call_stack_tree_node_nil, }; thread_static D_EntityCtxLookupAccel *d_entity_ctx_lookup_accel = 0; +read_only global D_ModuleInfo d_module_info_nil = {0}; //////////////////////////////// //~ rjf: Basic Type Functions @@ -550,10 +547,10 @@ internal B32 d_thread_read_reg_block(D_Handle handle, void *reg_block); internal B32 d_thread_write_reg_block(D_Handle thread, void *reg_block); internal U64 d_ip_from_thread(D_Handle handle); internal U64 d_sp_from_thread(D_Handle handle); +internal B32 d_thread_get_module_tls_vaddr(D_Handle thread, D_Handle module, U64 *vaddr_out); //- rjf: thread register cache reading internal void *d_cached_reg_block_from_thread(Arena *arena, D_Handle handle); -internal U64 d_cached_tls_root_vaddr_from_thread(D_Handle handle); internal U64 d_cached_ip_from_thread(D_Handle handle); internal U64 d_cached_sp_from_thread(D_Handle handle); @@ -561,14 +558,17 @@ internal U64 d_cached_sp_from_thread(D_Handle handle); //~ rjf: Module Image Info Functions //- rjf: cache lookups +#if 0 // TODO(rjf): @unwind internal PE_IntelPdata *d_intel_pdata_from_module_voff(Arena *arena, D_Handle module_handle, U64 voff); +#endif +internal D_ModuleInfo *d_info_from_module(Access *access, D_Handle module); internal U64 d_entry_point_voff_from_module(D_Handle module_handle); internal String8 d_initial_debug_info_path_from_module(Arena *arena, D_Handle module_handle); -internal String8 d_raddbg_data_from_module(Arena *arena, D_Handle module_handle); //////////////////////////////// //~ rjf: Unwinding Functions +#if 0 // TODO(rjf): @unwind //- rjf: [dwarf] internal D_UnwindStepResult d_unwind_step_result_from_machine_op_result(MachineOpResult s); internal D_UnwindStepResult d_establish_frame_unwind_context__dwarf(Arena *arena, D_Handle process_handle, D_Handle module_handle, Arch arch, void *regs, U64 endt_us, D_FrameUnwindContext *ctx_out); @@ -577,6 +577,7 @@ internal D_UnwindStepResult d_unwind_step__dwarf(D_Handle process_handle, Arch a //- rjf: [x64] internal U64 *d_unwind_reg_from_pe_gpr_reg__pe_x64(X64_RegBlock *regs, PE_UnwindGprRegX64 gpr_reg); internal D_UnwindStepResult d_unwind_step__pe_x64(D_Handle process_handle, D_Handle module_handle, U64 module_base_vaddr, X64_RegBlock *regs, U64 endt_us); +#endif //- rjf: abstracted full unwind internal D_Unwind d_unwind_from_thread(Arena *arena, D_Handle thread, U64 endt_us); @@ -673,6 +674,13 @@ internal D_ProcessMemorySlice d_process_memory_slice_from_vaddr_range(Arena *are internal B32 d_process_memory_read(D_Handle process, Rng1U64 range, B32 *is_stale_out, void *out, U64 endt_us); #define d_process_memory_read_struct(process, vaddr, is_stale_out, ptr, endt_us) d_process_memory_read((process), r1u64((vaddr), (vaddr)+(sizeof(*(ptr)))), (is_stale_out), (ptr), (endt_us)) +//////////////////////////////// +//~ rjf: TLS Address Artifact Cache Hooks / Lookups + +internal AC_Artifact d_tls_vaddr_artifact_create(String8 key, B32 *cancel_signal, B32 *retry_out, U64 *gen_out); +internal void d_tls_vaddr_artifact_destroy(AC_Artifact artifact); +internal U64 d_cached_tls_vaddr_from_thread_module(D_Handle thread_handle, D_Handle module_handle, U64 endt_us, B32 *stale_out); + //////////////////////////////// //~ rjf: Call Stack Artifact Cache Hooks / Lookups diff --git a/src/demon/demon_core.h b/src/demon/demon_core.h index 76a16ab8..eba11daf 100644 --- a/src/demon/demon_core.h +++ b/src/demon/demon_core.h @@ -97,7 +97,6 @@ struct DMN_ModuleInfo // rjf: unwinding info Rng1U64 pe_intel_pdatas_vaddr_range; Rng1U64 eh_frame_header_vaddr_range; - U64 cfi_rebase; // rjf: special raddbg data Rng1U64 raddbg_info_voff_range; @@ -135,6 +134,9 @@ struct DMN_Event // rjf: module info DMN_ModuleInfo *module_info; + + // rjf: thread info + U64 tls_root_vaddr; }; typedef struct DMN_EventNode DMN_EventNode; @@ -291,10 +293,9 @@ internal U64 dmn_process_read(DMN_Handle process, Rng1U64 range, void *dst); internal B32 dmn_process_write(DMN_Handle process, Rng1U64 range, void *src); //- rjf: threads -internal U64 dmn_stack_base_vaddr_from_thread(DMN_Handle handle); -internal U64 dmn_tls_root_vaddr_from_thread(DMN_Handle handle); internal B32 dmn_thread_read_reg_block(DMN_Handle handle, void *reg_block); internal B32 dmn_thread_write_reg_block(DMN_Handle handle, void *reg_block); +internal B32 dmn_thread_get_module_tls_vaddr(DMN_Handle thread, DMN_Handle module, U64 *vaddr_out); //- rjf: system process listing internal void dmn_process_iter_begin(DMN_ProcessIter *iter); diff --git a/src/dwarf/dwarf.c b/src/dwarf/dwarf.c index 3bbdc22d..5d73309e 100644 --- a/src/dwarf/dwarf.c +++ b/src/dwarf/dwarf.c @@ -2,235 +2,471 @@ // Licensed under the MIT license (https://opensource.org/license/mit/) //////////////////////////////// -//~ rjf: Generated Code +//~ rjf: Includes #include "dwarf/generated/dwarf.meta.c" +#if defined(X64_H) +# include "dwarf/x64/dwarf_x64.c" +#endif -internal DW_AttribClass -dw_attrib_class_from_attrib_v2(DW_AttribKind k) +//////////////////////////////// +//~ rjf: X-List Helpers + +//- rjf: format address sizes + +internal U64 +dw_addr_size_from_format(DW_Format fmt) { - switch (k) { -#define X(_N,_C) case DW_AttribKind_##_N: return _C; - DW_AttribKind_ClassFlags_V2_XList + U64 result = 0; + switch(fmt) + { + default:{}break; +#define X(name, string, addr_size) case DW_Format_##name:{result = (addr_size);}break; + DW_Format_XList #undef X } - return DW_AttribClass_Null; -} - -internal DW_AttribClass -dw_attrib_class_from_attrib_v3(DW_AttribKind k) -{ - switch (k) { -#define X(_N,_C) case DW_AttribKind_##_N: return _C; - DW_AttribKind_ClassFlags_V3_XList -#undef X - } - return DW_AttribClass_Null; -} - -internal DW_AttribClass -dw_attrib_class_from_attrib_v4(DW_AttribKind k) -{ - switch (k) { -#define X(_N,_C) case DW_AttribKind_##_N: return _C; - DW_AttribKind_ClassFlags_V4_XList -#undef X - } - return DW_AttribClass_Null; -} - -internal DW_AttribClass -dw_attrib_class_from_attrib_v5(DW_AttribKind k) -{ - switch (k) { -#define X(_N,_C) case DW_AttribKind_##_N: return _C; - DW_AttribKind_ClassFlags_V5_XList -#undef X - } - return DW_AttribClass_Null; -} - -internal DW_AttribClass -dw_attrib_class_from_attrib_gnu(DW_AttribKind k) -{ - switch (k) { -#define X(_N,_C) case DW_AttribKind_##_N: return _C; - DW_AttribKind_ClassFlags_GNU_XList -#undef X - } - return DW_AttribClass_Null; -} - -internal DW_AttribClass -dw_attrib_class_from_attrib_llvm(DW_AttribKind k) -{ - switch (k) { -#define X(_N,_C) case DW_AttribKind_##_N: return _C; - DW_AttribKind_ClassFlags_LLVM_XList -#undef X - } - return DW_AttribClass_Null; -} - -internal DW_AttribClass -dw_attrib_class_from_attrib_apple(DW_AttribKind k) -{ - switch (k) { -#define X(_N,_C) case DW_AttribKind_##_N: return _C; - DW_AttribKind_ClassFlags_APPLE_XList -#undef X - } - return DW_AttribClass_Null; -} - -internal DW_AttribClass -dw_attrib_class_from_attrib_mips(DW_AttribKind k) -{ - switch (k) { -#define X(_N,_C) case DW_AttribKind_##_N: return _C; - DW_AttribKind_ClassFlags_MIPS_XList -#undef X - } - return DW_AttribClass_Null; -} - -internal DW_AttribClass -dw_attrib_class_from_attrib(DW_Version ver, DW_Ext ext, DW_AttribKind k) -{ - DW_AttribClass result = DW_AttribClass_Null; - - while (ext) { - U64 z = 64-clz64(ext); - if (z == 0) { - break; - } - U64 flag = 1 << (z-1); - ext &= ~flag; - - switch (flag) { - case DW_Ext_Null: break; - case DW_Ext_GNU: result = dw_attrib_class_from_attrib_gnu(k); break; - case DW_Ext_LLVM: result = dw_attrib_class_from_attrib_llvm(k); break; - case DW_Ext_APPLE: result = dw_attrib_class_from_attrib_apple(k); break; - case DW_Ext_MIPS: result = dw_attrib_class_from_attrib_mips(k); break; - default: InvalidPath; break; - } - - if (result != DW_AttribClass_Null) { - break; - } - } - - if (result == DW_AttribClass_Null) { - switch (ver) { - case DW_Version_Null: break; - case DW_Version_1: AssertAlways(!"DWARF V1 is not supported"); break; - case DW_Version_2: result = dw_attrib_class_from_attrib_v2(k); break; - case DW_Version_3: result = dw_attrib_class_from_attrib_v3(k); break; - case DW_Version_4: result = dw_attrib_class_from_attrib_v4(k); break; - case DW_Version_5: result = dw_attrib_class_from_attrib_v5(k); break; - default: InvalidPath; break; - } - } - return result; } +//- rjf: attribute classes + +internal DW_AttribClass +dw_attrib_class_from_kind(DW_Version version, DW_ExtFlags exts, DW_AttribKind k) +{ + DW_AttribClass result = 0; + if(result == 0) switch(k) + { + default:{}break; +#define X(name, code, version, classes) case DW_AttribKind_##name:{result = (classes);}break; + DW_AttribKind_XList +#undef X + } + for(B32 retry = 0; !result && retry <= 1; retry += 1) + { + if(result == 0 && exts & DW_ExtFlag_GNU) switch(k) + { + default:{}break; +#define X(name, code, classes) case DW_GNU_AttribKind_##name:{result = (classes);}break; + DW_GNU_AttribKind_XList +#undef X + } + if(result == 0 && exts & DW_ExtFlag_LLVM) switch(k) + { + default:{}break; +#define X(name, code, classes) case DW_LLVM_AttribKind_##name:{result = (classes);}break; + DW_LLVM_AttribKind_XList +#undef X + } + if(result == 0 && exts & DW_ExtFlag_Apple) switch(k) + { + default:{}break; +#define X(name, code, classes) case DW_Apple_AttribKind_##name:{result = (classes);}break; + DW_Apple_AttribKind_XList +#undef X + } + if(result == 0 && exts & DW_ExtFlag_MIPS) switch(k) + { + default:{}break; +#define X(name, code, classes) case DW_MIPS_AttribKind_##name:{result = (classes);}break; + DW_MIPS_AttribKind_XList +#undef X + } + exts = DW_ExtFlag_All; + } + return result; +} + +internal DW_AttribClass +dw_attrib_class_from_form_kind(DW_Version version, DW_ExtFlags exts, DW_FormKind k) +{ + DW_AttribClass result = 0; + if(result == 0) switch(k) + { + default:{}break; +#define X(name, code, version, classes) case DW_FormKind_##name:{result = (classes);}break; + DW_FormKind_XList +#undef X + } + for(B32 retry = 0; !result && retry <= 1; retry += 1) + { + if(result == 0 && exts & DW_ExtFlag_GNU) switch(k) + { + default:{}break; +#define X(name, code, classes) case DW_GNU_FormKind_##name:{result = (classes);}break; + DW_GNU_FormKind_XList +#undef X + } + exts = DW_ExtFlag_All; + } + return result; +} + +//- rjf: expr op metadata + +internal DW_ExprOpInfo * +dw_info_from_expr_op(DW_Version version, DW_ExtFlags exts, DW_ExprOp op) +{ + DW_ExprOpInfo *result = &dw_expr_op_info_nil; + if(result == &dw_expr_op_info_nil) switch(op) + { + default:{}break; +#define X(name_, code, version_, operand_count_, pop_count_, push_count_, operand_kind_0, operand_kind_1, valid_for_cfa_exprs_) case DW_ExprOp_##name_:{local_persist DW_ExprOpInfo info = {.push_count = (push_count_), .pop_count = (pop_count_), .operand_count = (operand_count_), .valid_for_cfa_exprs = (valid_for_cfa_exprs_), .operand_kinds = {DW_ExprOperandKind_##operand_kind_0, DW_ExprOperandKind_##operand_kind_1}, .name = str8_lit_comp(#name_), .version = DW_Version_##version_}; result = &info;}break; + DW_ExprOp_XList +#undef X + } + for(B32 retry = 0; (result == &dw_expr_op_info_nil) && retry <= 1; retry += 1) + { + if((result == &dw_expr_op_info_nil) && exts & DW_ExtFlag_GNU) switch(op) + { + default:{}break; +#define X(name_, code, operand_count_, pop_count_, push_count_, operand_kind_0, operand_kind_1, valid_for_cfa_exprs_) case DW_GNU_ExprOp_##name_:{local_persist DW_ExprOpInfo info = {.push_count = (push_count_), .pop_count = (pop_count_), .operand_count = (operand_count_), .valid_for_cfa_exprs = (valid_for_cfa_exprs_), .operand_kinds = {DW_ExprOperandKind_##operand_kind_0, DW_ExprOperandKind_##operand_kind_1}, .name = str8_lit_comp(#name_)}; result = &info;}break; + DW_GNU_ExprOp_XList +#undef X + } + exts = DW_ExtFlag_All; + } + return result; +} + +//- rjf: enum -> string + +internal String8 +dw_string_from_format(DW_Format fmt) +{ + String8 result = {0}; + switch(fmt) + { + default:{}break; +#define X(name, string, addr_size) case DW_Format_##name:{result = str8_lit(string);}break; + DW_Format_XList +#undef X + } + return result; +} + +internal String8 +dw_string_from_tag_kind(DW_Version version, DW_ExtFlags exts, DW_TagKind k) +{ + String8 result = {0}; + if(result.size == 0) switch(k) + { + default:{}break; +#define X(name, code, version) case DW_TagKind_##name:{result = s(#name);}break; + DW_TagKind_XList +#undef X + } + for(B32 retry = 0; !result.size && retry <= 1; retry += 1) + { + if(result.size == 0 && exts & DW_ExtFlag_GNU) switch(k) + { +#define X(name, code) case DW_GNU_TagKind_##name:{result = s(#name);}break; + DW_GNU_TagKind_XList +#undef X + } + exts = DW_ExtFlag_All; + } + return result; +} + +internal String8 +dw_string_from_attrib_kind(DW_Version version, DW_ExtFlags exts, DW_AttribKind k) +{ + String8 result = {0}; + if(result.size == 0) switch(k) + { + default:{}break; +#define X(name, code, version, classes) case DW_AttribKind_##name:{result = s(#name);}break; + DW_AttribKind_XList +#undef X + } + for(B32 retry = 0; !result.size && retry <= 1; retry += 1) + { + if(result.size == 0 && exts & DW_ExtFlag_GNU) switch(k) + { + default:{}break; +#define X(name, code, classes) case DW_GNU_AttribKind_##name:{result = s(#name);}break; + DW_GNU_AttribKind_XList +#undef X + } + if(result.size == 0 && exts & DW_ExtFlag_LLVM) switch(k) + { + default:{}break; +#define X(name, code, classes) case DW_LLVM_AttribKind_##name:{result = s(#name);}break; + DW_LLVM_AttribKind_XList +#undef X + } + if(result.size == 0 && exts & DW_ExtFlag_Apple) switch(k) + { + default:{}break; +#define X(name, code, classes) case DW_Apple_AttribKind_##name:{result = s(#name);}break; + DW_Apple_AttribKind_XList +#undef X + } + if(result.size == 0 && exts & DW_ExtFlag_MIPS) switch(k) + { + default:{}break; +#define X(name, code, classes) case DW_MIPS_AttribKind_##name:{result = s(#name);}break; + DW_MIPS_AttribKind_XList +#undef X + } + exts = DW_ExtFlag_All; + } + return result; +} + +internal String8 +dw_string_from_form_kind(DW_Version version, DW_ExtFlags exts, DW_FormKind k) +{ + String8 result = {0}; + if(result.size == 0) switch(k) + { + default:{}break; +#define X(name, code, version, classes) case DW_FormKind_##name:{result = s(#name);}break; + DW_FormKind_XList +#undef X + } + for(B32 retry = 0; !result.size && retry <= 1; retry += 1) + { + if(result.size == 0 && exts & DW_ExtFlag_GNU) switch(k) + { + default:{}break; +#define X(name, code, classes) case DW_GNU_FormKind_##name:{result = s(#name);}break; + DW_GNU_FormKind_XList +#undef X + } + exts = DW_ExtFlag_All; + } + return result; +} + +internal String8 +dw_string_from_language(DW_Language k) +{ + String8 result = {0}; + switch(k) + { + default:{}break; +#define X(name, ...) case DW_Language_##name:{result = s(#name);}break; + DW_Language_XList +#undef X + } + return result; +} + +internal String8 +dw_string_from_comp_unit_kind(DW_CompUnitKind k) +{ + String8 result = {0}; + switch(k) + { + default:{}break; +#define X(name, ...) case DW_CompUnitKind_##name:{result = s(#name);}break; + DW_CompUnitKind_XList +#undef X + } + return result; +} + +internal String8 +dw_string_from_inl_kind(DW_InlKind k) +{ + String8 result = {0}; + switch(k) + { + default:{}break; +#define X(name, ...) case DW_InlKind_##name:{result = s(#name);}break; + DW_InlKind_XList +#undef X + } + return result; +} + +internal String8 +dw_string_from_access_kind(DW_AccessKind k) +{ + String8 result = {0}; + switch(k) + { + default:{}break; +#define X(name, ...) case DW_AccessKind_##name:{result = s(#name);}break; + DW_AccessKind_XList +#undef X + } + return result; +} + +internal String8 +dw_string_from_calling_convention_kind(DW_CallingConventionKind k) +{ + String8 result = {0}; + switch(k) + { + default:{}break; +#define X(name, ...) case DW_CallingConventionKind_##name:{result = s(#name);}break; + DW_CallingConventionKind_XList +#undef X + } + return result; +} + +internal String8 +dw_string_from_ate(DW_ATE k) +{ + String8 result = {0}; + switch(k) + { + default:{}break; +#define X(name, ...) case DW_ATE_##name:{result = s(#name);}break; + DW_ATE_XList +#undef X + } + return result; +} + +internal String8 +dw_string_from_visibility_kind(DW_VisibilityKind k) +{ + String8 result = {0}; + switch(k) + { + default:{}break; +#define X(name, ...) case DW_VisibilityKind_##name:{result = s(#name);}break; + DW_VisibilityKind_XList +#undef X + } + return result; +} + +internal String8 +dw_string_from_std_opcode(DW_StdOpcode opcode) +{ + String8 result = {0}; + switch(opcode) + { + default:{}break; +#define X(name, ...) case DW_StdOpcode_##name:{result = s(#name);}break; + DW_StdOpcode_XList +#undef X + } + return result; +} + +internal String8 +dw_string_from_ext_opcode(DW_ExtOpcode opcode) +{ + String8 result = {0}; + switch(opcode) + { + default:{}break; +#define X(name, ...) case DW_ExtOpcode_##name:{result = s(#name);}break; + DW_ExtOpcode_XList +#undef X + } + return result; +} + +internal String8 +dw_string_from_lle(DW_LLE lle) +{ + String8 result = {0}; + switch(lle) + { + default:{}break; +#define X(name, ...) case DW_LLE_##name:{result = s(#name);}break; + DW_LLE_XList +#undef X + } + return result; +} + +internal String8 +dw_string_from_rle(DW_RLE rle) +{ + String8 result = {0}; + switch(rle) + { + default:{}break; +#define X(name, ...) case DW_RLE_##name:{result = s(#name);}break; + DW_RLE_XList +#undef X + } + return result; +} + +internal String8 +dw_regular_name_from_section_kind(DW_SectionKind k) +{ + String8 result = {0}; + switch(k) + { + default:{}break; +#define X(name, regular_name, ...) case DW_SectionKind_##name:{result = str8_lit(regular_name);}break; + DW_SectionKind_XList +#undef X + } + return result; +} + +internal String8 +dw_macho_name_from_section_kind(DW_SectionKind k) +{ + String8 result = {0}; + switch(k) + { + default:{}break; +#define X(name, regular_name, macho_name, ...) case DW_SectionKind_##name:{result = str8_lit(macho_name);}break; + DW_SectionKind_XList +#undef X + } + return result; +} + +internal String8 +dw_dwo_name_from_section_kind(DW_SectionKind k) +{ + String8 result = {0}; + switch(k) + { + default:{}break; +#define X(name, regular_name, macho_name, dwo_name, ...) case DW_SectionKind_##name:{result = str8_lit(dwo_name);}break; + DW_SectionKind_XList +#undef X + } + return result; +} + +//- rjf: architecture info + +internal U64 +dw_reg_count_from_arch(Arch arch) +{ + U64 result = 0; + switch(arch) + { + default:{}break; + case Arch_x64:{result = DW_RegCodeX64_LAST;}break; + } + return result; +} + +//////////////////////////////// +//~ TODO(rjf): OLD vvvvvvvvvvv + +#if 0 internal DW_AttribClass dw_attrib_class_from_form_kind(DW_Version ver, DW_FormKind k) { - DW_AttribClass result = DW_AttribClass_Null; -#define X(_N,_C) case DW_Form_##_N:{result = _C;}break; + DW_AttribClass result = 0; switch(k) { - DW_Form_AttribClass_GNU_XList - } - switch(ver) - { - case DW_Version_5: - { - switch (k) - { - DW_Form_AttribClass_V5_XList - } - }break; - case DW_Version_4: - { - switch (k) - { - DW_Form_AttribClass_V4_XList - } - }break; - case DW_Version_3: - { - switch (k) - { - DW_Form_AttribClass_V2_XList - } - }break; - case DW_Version_2: - { - switch(k) - { - DW_Form_AttribClass_V2_XList - } - }break; - case DW_Version_1:{} break; - case DW_Version_Null:{}break; - } + default:{}break; +#define X(name, code, version, ext, class) case DW_FormKind_##name:{result = (class);}break; + DW_FormKind_XList #undef X + } return result; } -internal B32 -dw_are_attrib_class_and_form_kind_compatible(DW_Version ver, DW_AttribClass attrib_class, DW_FormKind form_kind) -{ - DW_AttribClass compat_flags = dw_attrib_class_from_form_kind(ver, form_kind); - B32 are_compat = (attrib_class & compat_flags) != 0; - return are_compat; -} - -internal B32 -dw_is_form_kind_ref(DW_Version ver, DW_Ext ext, DW_FormKind form_kind) -{ - B32 is_ref = 0; - if (form_kind == DW_Form_RefAddr || - form_kind == DW_Form_Ref1 || - form_kind == DW_Form_Ref2 || - form_kind == DW_Form_Ref4 || - form_kind == DW_Form_Ref8 || - form_kind == DW_Form_RefUData) { - is_ref = 1; - } else { - if (ext == DW_Ext_GNU) { - is_ref = DW_Form_GNU_RefAlt; - } - } - return is_ref; -} - -internal U64 -dw_reg_size_from_code_x64(DW_Reg reg_code) -{ - switch (reg_code) { -#define X(reg_name_dw, reg_code_dw, reg_name_rdi, reg_pos, reg_size) case DW_RegX64_##reg_name_dw: return reg_size; - DW_Regs_X64_XList -#undef X - } - return 0; -} - -internal U64 -dw_reg_pos_from_code_x64(DW_Reg reg_code) -{ - switch (reg_code) { -#define X(reg_name_dw, reg_code_dw, reg_name_rdi, reg_pos, reg_size) case DW_RegX64_##reg_name_dw: return reg_pos; - DW_Regs_X64_XList -#undef X - } - return max_U64; -} - internal U64 dw_reg_size_from_code(Arch arch, DW_Reg reg_code) { @@ -277,194 +513,6 @@ dw_reg_max_size_from_arch(Arch arch) return max_size; } -internal U64 -dw_sp_from_arch(Arch arch) -{ - switch (arch) { - default: NotImplemented; - case Arch_Null: return 0; - case Arch_x64: return DW_RegX64_Rsp; - } -} - - -internal U64 -dw_size_from_format(DW_Format format) -{ - U64 result = 0; - switch(format) - { - case DW_Format_Null:{}break; - case DW_Format_32Bit:{result = 4;}break; - case DW_Format_64Bit:{result = 8;}break; - } - return result; -} - -internal DW_AttribClass -dw_pick_attrib_value_class(DW_Version ver, DW_Ext ext, B32 relaxed, DW_AttribKind attrib_kind, DW_FormKind form_kind) -{ - // NOTE(rjf): DWARF's spec specifies two mappings: - // (DW_AttribKind) => List(DW_AttribClass) - // (DW_FormKind) => List(DW_AttribClass) - // - // This function's purpose is to find the overlapping class between an - // DW_AttribKind and DW_FormKind. - - DW_AttribClass attrib_class = dw_attrib_class_from_attrib(ver, ext, attrib_kind); - DW_AttribClass form_class = dw_attrib_class_from_form_kind(ver, form_kind); - - if(relaxed) - { - if(attrib_class == DW_AttribClass_Null || form_class == DW_AttribClass_Null) - { - attrib_class = dw_attrib_class_from_attrib(DW_Version_Last, ext, attrib_kind); - form_class = dw_attrib_class_from_form_kind(DW_Version_Last, form_kind); - } - } - - DW_AttribClass result = DW_AttribClass_Null; - if(attrib_class != DW_AttribClass_Null && form_class != DW_AttribClass_Null) - { - result = DW_AttribClass_Undefined; - - for(U32 i = 0; i < 32; ++i) - { - U32 n = 1u << i; - if((attrib_class & n) != 0 && (form_class & n) != 0) - { - result = ((DW_AttribClass) n); - break; - } - } - } - - return result; -} - -internal U64 -dw_pick_default_lower_bound(DW_Language lang) -{ - U64 lower_bound = max_U64; - switch(lang) - { - case DW_Language_Null: break; - case DW_Language_C89: - case DW_Language_C: - case DW_Language_CPlusPlus: - case DW_Language_C99: - case DW_Language_CPlusPlus03: - case DW_Language_CPlusPlus11: - case DW_Language_C11: - case DW_Language_CPlusPlus14: - case DW_Language_Java: - case DW_Language_ObjC: - case DW_Language_ObjCPlusPlus: - case DW_Language_UPC: - case DW_Language_D: - case DW_Language_Python: - case DW_Language_OpenCL: - case DW_Language_Go: - case DW_Language_Haskell: - case DW_Language_OCaml: - case DW_Language_Rust: - case DW_Language_Swift: - case DW_Language_Dylan: - case DW_Language_RenderScript: - case DW_Language_BLISS: - { - lower_bound = 0; - }break; - case DW_Language_Ada83: - case DW_Language_Cobol74: - case DW_Language_Cobol85: - case DW_Language_Fortran77: - case DW_Language_Fortran90: - case DW_Language_Pascal83: - case DW_Language_Modula2: - case DW_Language_Ada95: - case DW_Language_Fortran95: - case DW_Language_PLI: - case DW_Language_Modula3: - case DW_Language_Julia: - case DW_Language_Fortran03: - case DW_Language_Fortran08: - { - lower_bound = 1; - }break; - default:{}break; - } - return lower_bound; -} - -internal U64 -dw_operand_count_from_expr_op(DW_ExprOp op) -{ - U64 result = 0; - switch(op) - { - default:{}break; -#define X(_N, _ID, _OPER_COUNT, _POP_COUNT, _PUSH_COUNT, ...) case _ID:{result = _OPER_COUNT;}break; - DW_Expr_V3_XList - DW_Expr_V4_XList - DW_Expr_V5_XList - DW_Expr_GNU_XList -#undef X - } - return result; -} - -internal U64 -dw_pop_count_from_expr_op(DW_ExprOp op) -{ - U64 result = 0; - switch(op) - { - default:{}break; -#define X(_N, _ID, _OPER_COUNT, _POP_COUNT, _PUSH_COUNT, ...) case _ID:{result = _POP_COUNT;}break; - DW_Expr_V3_XList - DW_Expr_V4_XList - DW_Expr_V5_XList - DW_Expr_GNU_XList -#undef X - } - return result; -} - -internal U64 -dw_push_count_from_expr_op(DW_ExprOp op) -{ - U64 result = 0; - switch (op) { - default:{}break; -#define X(_N, _ID, _OPER_COUNT, _POP_COUNT, _PUSH_COUNT, ...) case _ID:{result = _PUSH_COUNT;}break; - DW_Expr_V3_XList - DW_Expr_V4_XList - DW_Expr_V5_XList - DW_Expr_GNU_XList -#undef X - } - return result; -} - -internal DW_ExprOperandType * -dw_operand_types_from_expr_opcode(DW_ExprOp op) -{ - local_persist DW_ExprOperandType nil_t[] = {DW_ExprOperandType_Null}; - DW_ExprOperandType *result = nil_t; - switch(op) - { - default:{}break; -#define X(_N, _ID, _OPER_COUNT, _POP_COUNT, _PUSH_COUNT, _OPER_TYPE0, _OPER_TYPE1) case _ID: { local_persist DW_ExprOperandType t[] = { DW_ExprOperandType_##_OPER_TYPE0, DW_ExprOperandType_##_OPER_TYPE1 }; result = t; }break; - DW_Expr_V3_XList - DW_Expr_V4_XList - DW_Expr_V5_XList - DW_Expr_GNU_XList -#undef X - } - return result; -} - internal U64 dw_operand_count_from_cfa_opcode(DW_CFA_Opcode opcode) { @@ -534,156 +582,6 @@ dw_operand_types_from_cfa_op(DW_CFA_Opcode opcode) return 0; } -internal String8 -dw_string_from_format(DW_Format format) -{ - switch (format) { - case DW_Format_Null: return str8_lit("NULL"); - case DW_Format_32Bit: return str8_lit("DWARF32"); - case DW_Format_64Bit: return str8_lit("DWARF64"); - } - return str8_zero(); -} - -internal String8 -dw_string_from_expr_op(Arena *arena, DW_Version ver, DW_Ext ext, DW_ExprOp op) -{ - String8 result = {0}; - -#define X(_N,...) case DW_ExprOp_##_N: result = str8_lit(Stringify(_N)); goto exit; - if (ext & DW_Ext_GNU) { - switch (op) { - DW_Expr_GNU_XList; - } - } - - switch (ver) { - case DW_Version_5: { - switch (op) { - DW_Expr_V5_XList - } - } // fall-through - case DW_Version_4: { - switch (op) { - DW_Expr_V4_XList - } - } // fall-through - case DW_Version_3: - case DW_Version_2: - case DW_Version_1: { - switch (op) { - DW_Expr_V3_XList - } - } // fall-through - case DW_Version_Null: - break; - } -#undef X - - result = push_str8f(arena, "%x", op); - - exit:; - return result; -} - -internal String8 -dw_string_from_tag_kind(Arena *arena, DW_TagKind kind) -{ - switch (kind) { - case DW_TagKind_Null: return str8_lit("Null"); -#define X(_N,_ID) case DW_TagKind_##_N: return str8_lit(Stringify(_N)); - DW_TagKind_V3_XList - DW_TagKind_V5_XList - DW_TagKind_GNU_XList -#undef X - } - return push_str8f(arena, "%llx", kind); -} - -internal String8 -dw_string_from_attrib_kind(Arena *arena, DW_Version ver, DW_Ext ext, DW_AttribKind kind) -{ -#define X(_N,...) case DW_AttribKind_##_N:{result = str8_lit(Stringify(_N));}break; - String8 result = {0}; - - //- rjf: try extensions - while(ext) - { - U64 z = 64-clz64(ext); - if(z == 0) - { - break; - } - U64 flag = 1 << (z-1); - ext &= ~flag; - switch(flag) - { - default:{}break; - case DW_Ext_Null: break; - case DW_Ext_GNU: switch (kind) { DW_AttribKind_GNU_XList } break; - case DW_Ext_LLVM: switch (kind) { DW_AttribKind_LLVM_XList } break; - case DW_Ext_APPLE: switch (kind) { DW_AttribKind_APPLE_XList } break; - case DW_Ext_MIPS: switch (kind) { DW_AttribKind_MIPS_XList } break; - } - } - - //- rjf: try version - if(result.size == 0) - { - for(U64 retry = 0; retry < 2; retry += 1) - { - DW_Version version = retry ? DW_Version_5 : ver; - switch(version) - { - case DW_Version_5: { switch(kind) { DW_AttribKind_V5_XList } } // fall-through - case DW_Version_4: { switch(kind) { DW_AttribKind_V4_XList } } // fall-through - case DW_Version_3: { switch(kind) { DW_AttribKind_V3_XList } } // fall-through - case DW_Version_2: { switch(kind) { DW_AttribKind_V2_XList } } // fall-through - case DW_Version_1: {}break; - case DW_Version_Null:{}break; - default:{}break; - } - } - } - - //- rjf: fallback - if(result.size == 0) - { - result = push_str8f(arena, "#%u", kind); - } - -#undef X - return result; -} - -internal String8 -dw_string_from_form_kind(Arena *arena, DW_Version ver, DW_FormKind kind) -{ -#define X(_N,...) case DW_Form_##_N: return str8_lit(Stringify(_N)); - switch (ver) { - case DW_Version_5: { - switch (kind) { - DW_Form_V5_XList - } - } // fall-through - case DW_Version_4: { - switch (kind) { - DW_Form_V4_XList - } - } // fall-through - case DW_Version_3: - case DW_Version_2: { - switch (kind) { - DW_Form_V2_XList - } - } // fall-through - case DW_Version_Null: break; - } -#undef X - String8 result = push_str8f(arena, "%x", kind); - return result; -} - internal String8 dw_string_from_language(Arena *arena, DW_Language kind) { @@ -873,218 +771,4 @@ dw_dwo_name_string_from_section_kind(DW_SectionKind k) } return str8_zero(); } - -internal U64 -dw_write_to_buffer_uleb128(U8 buffer[10], U64 v) -{ - U64 buffer_size = 0; - U64 value = v; - do { - U8 byte = value & 0x7f; - value >>= 7; - if (value != 0) { - byte |= 0x80; - } - Assert(buffer_size < 10); - buffer[buffer_size++] = byte; - } while (value > 0); - return buffer_size; -} - -internal U64 -dw_write_to_buffer_sleb128(U8 buffer[10], U64 v) -{ - U64 buffer_size = 0; - for (S64 value = v, more = 1; more != 0; ) { - U8 byte = value & 0x7f; - value >>= 7; - U8 sign_bit = byte & 0x40; - if ((value == 0 && sign_bit == 0) || (value == -1 && sign_bit != 0)) { - more = 0; - } else { - byte |= 0x80; - } - Assert(buffer_size < 10); - buffer[buffer_size++] = byte; - } - return buffer_size; -} - -internal String8 -dw_write_uleb128(Arena *arena, U64 v) -{ - U8 buffer[10]; - U64 buffer_size = dw_write_to_buffer_uleb128(buffer, v); - return str8_copy(arena, str8(buffer, buffer_size)); -} - -internal String8 -dw_write_sleb128(Arena *arena, S64 v) -{ - U8 buffer[10]; - U64 buffer_size = dw_write_to_buffer_sleb128(buffer, v); - return str8_copy(arena, str8(buffer, buffer_size)); -} - -internal U64 -dw_size_from_uleb128(U64 v) -{ - U8 buffer[10]; - return dw_write_to_buffer_uleb128(buffer, v); -} - -internal U64 -dw_size_from_sleb128(S64 v) -{ - U8 buffer[10]; - return dw_write_to_buffer_sleb128(buffer, v); -} - -internal void * -dw_serial_push_length(Arena *arena, String8List *srl, DW_Format format, U64 length) -{ - void *result; - if (format == DW_Format_64Bit) { - U8 buffer[sizeof(U32) + sizeof(U64)]; - MemorySet(&buffer[0], 0xff, sizeof(U32)); - MemoryCopy(buffer + sizeof(U32), &length, sizeof(length)); - result = str8_serial_push_string(arena, srl, str8_array_fixed(buffer)); - } else { - result = str8_serial_push_string(arena, srl, str8((U8 *)&length, sizeof(U32))); - } - return result; -} - -internal void * -dw_serial_push_uint(Arena *arena, String8List *srl, DW_Format format, U64 v) -{ - String8 uint; - if (format == DW_Format_64Bit) { - U64 *ptr = push_array(arena, U64, 1); - *ptr = v; - uint = str8((U8 *)ptr, sizeof(*ptr)); - } else { - U32 *ptr = push_array(arena, U32, 1); - *ptr = safe_cast_u32(v); - uint = str8((U8 *)ptr, sizeof(*ptr)); - } - return str8_serial_push_string(arena, srl, uint); -} - -internal void * -dw_serial_push_uleb128(Arena *arena, String8List *srl, U64 v) -{ - U8 buffer[10]; - U64 buffer_size = dw_write_to_buffer_uleb128(buffer, v); - return str8_serial_push_string(arena, srl, str8(buffer, buffer_size)); -} - -internal void * -dw_serial_push_sleb128(Arena *arena, String8List *srl, S64 v) -{ - U8 buffer[10]; - U64 buffer_size = dw_write_to_buffer_sleb128(buffer, v); - return str8_serial_push_string(arena, srl, str8(buffer, buffer_size)); -} - -internal B32 -dw_form_match(DW_Form a, DW_Form b) -{ - B32 is_match = 0; - - if (a.kind == b.kind) { - switch (a.kind) { - case DW_Form_Null: {} break; - - case DW_Form_Addr: { is_match = str8_match(a.addr, b.addr, 0); } break; - case DW_Form_String: { is_match = str8_match(a.string, b.string, 0); } break; - case DW_Form_ExprLoc: { is_match = str8_match(a.exprloc, b.exprloc, 0); } break; - - case DW_Form_Block: - case DW_Form_Block1: - case DW_Form_Block2: - case DW_Form_Block4: { - is_match = str8_match(a.block, b.block, 0); - } break; - - case DW_Form_Data1: - case DW_Form_Data2: - case DW_Form_Data4: - case DW_Form_Data8: - case DW_Form_Data16: { - is_match = str8_match(a.data, b.data, 0); - } break; - - case DW_Form_Flag: { is_match = a.flag == b.flag; } break; - case DW_Form_SData: { is_match = a.sdata == b.sdata; } break; - case DW_Form_UData: { is_match = a.udata == b.udata; } break; - - case DW_Form_RefAddr: - case DW_Form_Ref1: - case DW_Form_Ref2: - case DW_Form_Ref4: - case DW_Form_Ref8: - case DW_Form_RefUData: - case DW_Form_GNU_RefAlt: { - is_match = a.ref == b.ref; - } break; - - case DW_Form_Indirect: { NotImplemented; } break; - - case DW_Form_SecOffset: - case DW_Form_LineStrp: - case DW_Form_GNU_StrpAlt: { - is_match = a.sec_offset == b.sec_offset; - } break; - - case DW_Form_ImplicitConst: { is_match = a.implicit_const, b.implicit_const; } break; - - case DW_Form_Strx: - case DW_Form_Strx1: - case DW_Form_Strx2: - case DW_Form_Strx3: - case DW_Form_Strx4: - case DW_Form_Addrx: - case DW_Form_Addrx1: - case DW_Form_Addrx2: - case DW_Form_Addrx3: - case DW_Form_Addrx4: - case DW_Form_RngListx: - case DW_Form_LocListx: { - is_match = a.xval == b.xval; - } break; - - case DW_Form_StrpSup: { is_match = a.strp_sup == b.strp_sup; } break; - - case DW_Form_RefSup4: { NotImplemented; } break; - case DW_Form_RefSup8: { NotImplemented; } break; - - default: { InvalidPath; } break; - } - } - - return is_match; -} - -internal U64 -dw_length_from_std_opcode(DW_StdOpcode opcode) -{ - switch (opcode) { - case DW_StdOpcode_ExtendedOpcode: return 0; - case DW_StdOpcode_Copy: return 0; - case DW_StdOpcode_AdvancePc: return 1; - case DW_StdOpcode_AdvanceLine: return 1; - case DW_StdOpcode_SetFile: return 1; - case DW_StdOpcode_SetColumn: return 1; - case DW_StdOpcode_NegateStmt: return 0; - case DW_StdOpcode_SetBasicBlock: return 0; - case DW_StdOpcode_ConstAddPc: return 0; - case DW_StdOpcode_FixedAdvancePc: return 1; - case DW_StdOpcode_SetPrologueEnd: return 0; - case DW_StdOpcode_SetEpilogueBegin: return 0; - case DW_StdOpcode_SetIsa: return 1; - default: InvalidPath; break; - } - return 0; -} - +#endif diff --git a/src/dwarf/dwarf.h b/src/dwarf/dwarf.h index 804deee1..c8bedf7f 100644 --- a/src/dwarf/dwarf.h +++ b/src/dwarf/dwarf.h @@ -4,1795 +4,81 @@ #ifndef DWARF_H #define DWARF_H -typedef U16 DW_Version; -typedef enum DW_VersionEnum -{ - DW_Version_Null, - DW_Version_1, - DW_Version_2, - DW_Version_3, - DW_Version_4, - DW_Version_5, - DW_Version_Last = DW_Version_5 -} DW_VersionEnum; - -typedef U64 DW_Ext; -typedef enum DW_ExtEnum -{ - DW_Ext_Null, - - DW_Ext_GNU = (1 << 0), - DW_Ext_LLVM = (1 << 1), - DW_Ext_APPLE = (1 << 2), - DW_Ext_MIPS = (1 << 3), - - DW_Ext_All = DW_Ext_GNU|DW_Ext_LLVM|DW_Ext_APPLE|DW_Ext_MIPS, -} DW_ExtEnum; - -typedef enum DW_Format -{ - DW_Format_Null, - DW_Format_32Bit, - DW_Format_64Bit -} DW_Format; - -#define DW_SentinelFromSize(address_size) ((address_size) == 4 ? max_U32 : (address_size) == 8 ? max_U64 : 0) - -#define DW_SectionKind_XList \ -X(Null, "", "", "" ) \ -X(Abbrev, ".debug_abbrev", "__debug_abbrev", ".debug_abbrev.dwo" ) \ -X(ARanges, ".debug_aranges", "__debug_aranges", ".debug_aranges.dwo" ) \ -X(Frame, ".debug_frame", "__debug_frame", ".debug_frame.dwo" ) \ -X(Info, ".debug_info", "__debug_info", ".debug_info.dwo" ) \ -X(Line, ".debug_line", "__debug_line", ".debug_line.dwo" ) \ -X(Loc, ".debug_loc", "__debug_loc", ".debug_loc.dwo" ) \ -X(MacInfo, ".debug_macinfo", "__debug_macinfo", ".debug_macinfo.dwo" ) \ -X(PubNames, ".debug_pubnames", "__debug_pubnames", ".debug_pubnames.dwo" ) \ -X(PubTypes, ".debug_pubtypes", "__debug_pubtypes", ".debug_pubtypes.dwo" ) \ -X(Ranges, ".debug_ranges", "__debug_ranges", ".debug_ranges.dwo" ) \ -X(Str, ".debug_str", "__debug_str", ".debug_str.dwo" ) \ -X(Addr, ".debug_addr", "__debug_addr", ".debug_addr.dwo" ) \ -X(LocLists, ".debug_loclists", "__debug_loclists", ".debug_loclists.dwo" ) \ -X(RngLists, ".debug_rnglists", "__debug_rnglists", ".debug_rnglists.dwo" ) \ -X(StrOffsets, ".debug_str_offsets", "__debug_str_offsets", ".debug_str_offsets.dwo") \ -X(LineStr, ".debug_line_str", "__debug_line_str", ".debug_line_str.dwo" ) \ -X(Names, ".debug_names", "__debug_names", ".debug_names.dwo" ) - -typedef U64 DW_SectionKind; -typedef enum DW_SectionKindEnum -{ -#define X(_N,...) DW_Section_##_N, - DW_SectionKind_XList -#undef X - DW_Section_COUNT -} DW_SectionKindEnum; - -typedef U32 DW_SectionFlags; -enum -{ -#define X(_N, ...) DW_SectionFlag_##_N = (1 << DW_Section_##_N), - DW_SectionKind_XList -#undef X - DW_SectionFlag_All = 0xffffffffu, -}; - -#define DW_Language_XList \ -X(Null, 0x00) \ -X(C89, 0x01) \ -X(C, 0x02) \ -X(Ada83, 0x03) \ -X(CPlusPlus, 0x04) \ -X(Cobol74, 0x05) \ -X(Cobol85, 0x06) \ -X(Fortran77, 0x07) \ -X(Fortran90, 0x08) \ -X(Pascal83, 0x09) \ -X(Modula2, 0x0A) \ -X(Java, 0x0B) \ -X(C99, 0x0C) \ -X(Ada95, 0x0D) \ -X(Fortran95, 0x0E) \ -X(PLI, 0x0F) \ -X(ObjC, 0x10) \ -X(ObjCPlusPlus, 0x11) \ -X(UPC, 0x12) \ -X(D, 0x13) \ -X(Python, 0x14) \ -X(OpenCL, 0x15) \ -X(Go, 0x16) \ -X(Modula3, 0x17) \ -X(Haskell, 0x18) \ -X(CPlusPlus03, 0x19) \ -X(CPlusPlus11, 0x1a) \ -X(OCaml, 0x1b) \ -X(Rust, 0x1c) \ -X(C11, 0x1d) \ -X(Swift, 0x1e) \ -X(Julia, 0x1f) \ -X(Dylan, 0x20) \ -X(CPlusPlus14, 0x21) \ -X(Fortran03, 0x22) \ -X(Fortran08, 0x23) \ -X(RenderScript, 0x24) \ -X(BLISS, 0x25) \ -X(MipsAssembler, 0x8001) \ -X(GoogleRenderScript, 0x8E57) \ -X(SunAssembler, 0x9001) \ -X(BorlandDelphi, 0xB000) - -typedef U32 DW_Language; -typedef enum DW_LanguageEnum -{ -#define X(_N, _ID) DW_Language_##_N = _ID, - DW_Language_XList -#undef X - DW_Language_UserLo = 0x8000, - DW_Language_UserHi = 0xffff, -} DW_LanguageEnum; - -#define DW_Inl_XList \ -X(NotInlined, 0x00) \ -X(Inlined, 0x01) \ -X(DeclaredNotInlined, 0x02) \ -X(DeclaredInlined, 0x03) - -typedef U32 DW_InlKind; -typedef enum DW_InlKindEnum -{ -#define X(_N,_ID) DW_Inl_##_N = _ID, - DW_Inl_XList -#undef X -} DW_InlKindEnum; - -#define DW_StdOpcode_XList \ -X(ExtendedOpcode, 0x00) \ -X(Copy, 0x01) \ -X(AdvancePc, 0x02) \ -X(AdvanceLine, 0x03) \ -X(SetFile, 0x04) \ -X(SetColumn, 0x05) \ -X(NegateStmt, 0x06) \ -X(SetBasicBlock, 0x07) \ -X(ConstAddPc, 0x08) \ -X(FixedAdvancePc, 0x09) \ -X(SetPrologueEnd, 0x0A) \ -X(SetEpilogueBegin, 0x0B) \ -X(SetIsa, 0x0C) - -typedef U8 DW_StdOpcode; -typedef enum DW_StdOpcodeEnum -{ -#define X(_N,_ID) DW_StdOpcode_##_N = _ID, - DW_StdOpcode_XList -#undef X - DW_StdOpcode_Count, -} DW_StdOpcodeEnum; - -#define DW_ExtOpcode_XList \ -X(Undefined, 0x00) \ -X(EndSequence, 0x01) \ -X(SetAddress, 0x02) \ -X(DefineFile, 0x03) \ -X(SetDiscriminator, 0x04) \ -X(UserLo, 0x80) \ -X(UserHi, 0xff) - -typedef U8 DW_ExtOpcode; -typedef enum DW_ExtOpcodeEnum -{ -#define X(_N,_ID) DW_ExtOpcode_##_N = _ID, - DW_ExtOpcode_XList -#undef X -} DW_ExtOpcodeEnum; - -#define DW_IDCaseKind_XList \ -X(CaseSensitive, 0x00) \ -X(UpCase, 0x01) \ -X(DownCase, 0x02) \ -X(CaseInsensitive, 0x03) - -typedef U64 DW_IDCaseKind; -typedef enum DW_IDCaseKindEnum -{ -#define X(_N,_ID) DW_IDCase_##_N = _ID, - DW_IDCaseKind_XList -#undef X -} DW_IDCaseKindEnum; - -#define DW_Vis_XList \ -X(Local, 0x01) \ -X(Exported, 0x02) \ -X(Qualified, 0x03) -typedef enum DW_Vis -{ -#define X(_N, _ID) DW_Vis_##_N = _ID, - DW_Vis_XList -#undef X -} DW_Vis; - -#define DW_TagKind_V3_XList \ -X(ArrayType, 0x01) \ -X(ClassType, 0x02) \ -X(EntryPoint, 0x03) \ -X(EnumerationType, 0x04) \ -X(FormalParameter, 0x05) \ -X(ImportedDeclaration, 0x08) \ -X(Label, 0x0a) \ -X(LexicalBlock, 0x0b) \ -X(Member, 0x0d) \ -X(PointerType, 0x0f) \ -X(ReferenceType, 0x10) \ -X(CompileUnit, 0x11) \ -X(StringType, 0x12) \ -X(StructureType, 0x13) \ -X(SubroutineType, 0x15) \ -X(Typedef, 0x16) \ -X(UnionType, 0x17) \ -X(UnspecifiedParameters, 0x18) \ -X(Variant, 0x19) \ -X(CommonBlock, 0x1a) \ -X(CommonInclusion, 0x1b) \ -X(Inheritance, 0x1c) \ -X(InlinedSubroutine, 0x1d) \ -X(Module, 0x1e) \ -X(PtrToMemberType, 0x1f) \ -X(SetType, 0x20) \ -X(SubrangeType, 0x21) \ -X(WithStmt, 0x22) \ -X(AccessDeclaration, 0x23) \ -X(BaseType, 0x24) \ -X(CatchBlock, 0x25) \ -X(ConstType, 0x26) \ -X(Constant, 0x27) \ -X(Enumerator, 0x28) \ -X(FileType, 0x29) \ -X(Friend, 0x2a) \ -X(NameList, 0x2b) \ -X(NameListItem, 0x2c) \ -X(PackedType, 0x2d) \ -X(SubProgram, 0x2e) \ -X(TemplateTypeParameter, 0x2f) \ -X(TemplateValueParameter, 0x30) \ -X(ThrownType, 0x31) \ -X(TryBlock, 0x32) \ -X(VariantPart, 0x33) \ -X(Variable, 0x34) \ -X(VolatileType, 0x35) \ -X(DwarfProcedure, 0x36) \ -X(RestrictType, 0x37) \ -X(InterfaceType, 0x38) \ -X(Namespace, 0x39) \ -X(ImportedModule, 0x3a) \ -X(UnspecifiedType, 0x3b) \ -X(PartialUnit, 0x3c) \ -X(ImportedUnit, 0x3d) \ -X(Condition, 0x3f) \ -X(SharedType, 0x40) - -#define DW_TagKind_V5_XList \ -X(TypeUnit, 0x41) \ -X(RValueReferenceType, 0x42) \ -X(TemplateAlias, 0x43) \ -X(CoarrayType, 0x44) \ -X(GenericSubrange, 0x45) \ -X(DynamicType, 0x46) \ -X(AtomicType, 0x47) \ -X(CallSite, 0x48) \ -X(CallSiteParameter, 0x49) \ -X(SkeletonUnit, 0x4A) \ -X(ImmutableType, 0x4B) - -#define DW_TagKind_GNU_XList \ -X(GNU_CallSite, 0x4109) \ -X(GNU_CallSiteParameter, 0x410a) - -typedef U64 DW_TagKind; -typedef enum DW_TagKindEnum -{ - DW_TagKind_Null, -#define X(_N,_ID) DW_TagKind_##_N = _ID, - DW_TagKind_V3_XList - DW_TagKind_V5_XList - DW_TagKind_GNU_XList -#undef X - DW_TagKind_UserLo = 0x4080, - DW_TagKind_UserHi = 0xffff -} DW_TagKindEnum; - -//- Attrib Class Encodings - -#define DW_AttribClass_V3_XList \ -X(Null, 0) \ -X(Undefined, 1) \ -X(Address, 2) \ -X(Block, 3) \ -X(Const, 4) \ -X(ExprLoc, 5) \ -X(Flag, 6) \ -X(LinePtr, 7) \ -X(LocListPtr, 8) \ -X(MacPtr, 9) \ -X(RngListPtr, 10) \ -X(Reference, 11) \ -X(String, 12) - -#define DW_AttribClass_V4_XList \ -X(LocList, 13) \ -X(RngList, 14) - -#define DW_AttribClass_V5_XList \ -X(StrOffsetsPtr, 15) \ -X(AddrPtr, 16) - -typedef U32 DW_AttribClass; -typedef enum DW_AttribClassEnum -{ -#define X(_N,_ID) DW_AttribClass_##_N = (1 << _ID), - DW_AttribClass_V3_XList - DW_AttribClass_V4_XList - DW_AttribClass_V5_XList -#undef X - DW_AttribClass_AllPtrs = DW_AttribClass_AddrPtr|DW_AttribClass_LinePtr|DW_AttribClass_LocList|DW_AttribClass_LocListPtr| DW_AttribClass_MacPtr| DW_AttribClass_RngList| DW_AttribClass_RngListPtr| DW_AttribClass_StrOffsetsPtr, -} DW_AttribClassEnum; - -//- Form Encodings - -#define DW_Form_V2_XList \ -X(Addr, 0x1) \ -X(Block2, 0x3) \ -X(Block4, 0x4) \ -X(Data2, 0x5) \ -X(Data4, 0x6) \ -X(Data8, 0x7) \ -X(String, 0x8) \ -X(Block, 0x9) \ -X(Block1, 0xa) \ -X(Data1, 0xb) \ -X(Flag, 0xc) \ -X(SData, 0xd) \ -X(Strp, 0xe) \ -X(UData, 0xf) \ -X(RefAddr, 0x10) \ -X(Ref1, 0x11) \ -X(Ref2, 0x12) \ -X(Ref4, 0x13) \ -X(Ref8, 0x14) \ -X(RefUData, 0x15) \ -X(Indirect, 0x16) - -#define DW_Form_AttribClass_V2_XList \ -X(Addr, DW_AttribClass_Address) \ -X(Block2, DW_AttribClass_Block) \ -X(Block4, DW_AttribClass_Block) \ -X(Data2, DW_AttribClass_Const) \ -X(Data4, DW_AttribClass_Const) \ -X(Data8, DW_AttribClass_Const) \ -X(String, DW_AttribClass_String) \ -X(Block, DW_AttribClass_Block) \ -X(Block1, DW_AttribClass_Block) \ -X(Data1, DW_AttribClass_Const) \ -X(Flag, DW_AttribClass_Flag) \ -X(SData, DW_AttribClass_Const) \ -X(Strp, DW_AttribClass_String) \ -X(UData, DW_AttribClass_Const) \ -X(RefAddr, DW_AttribClass_Reference) \ -X(Ref1, DW_AttribClass_Reference) \ -X(Ref2, DW_AttribClass_Reference) \ -X(Ref4, DW_AttribClass_Reference) \ -X(Ref8, DW_AttribClass_Reference) \ -X(RefUData, DW_AttribClass_Reference) \ -X(Indirect, DW_AttribClass_Null) - -#define DW_Form_V4_XList \ -X(SecOffset, 0x17) \ -X(ExprLoc, 0x18) \ -X(FlagPresent, 0x19) \ -X(RefSig8, 0x20) - -#define DW_Form_AttribClass_V4_XList \ -X(Addr, DW_AttribClass_Address) \ -X(Block2, DW_AttribClass_Block) \ -X(Block4, DW_AttribClass_Block) \ -X(Data2, DW_AttribClass_Const) \ -X(Data4, DW_AttribClass_Const) \ -X(Data8, DW_AttribClass_Const) \ -X(String, DW_AttribClass_String) \ -X(Block, DW_AttribClass_Block) \ -X(Block1, DW_AttribClass_Block) \ -X(Data1, DW_AttribClass_Const) \ -X(Flag, DW_AttribClass_Flag) \ -X(SData, DW_AttribClass_Const) \ -X(Strp, DW_AttribClass_String) \ -X(UData, DW_AttribClass_Const) \ -X(RefAddr, DW_AttribClass_Reference) \ -X(Ref1, DW_AttribClass_Reference) \ -X(Ref2, DW_AttribClass_Reference) \ -X(Ref4, DW_AttribClass_Reference) \ -X(Ref8, DW_AttribClass_Reference) \ -X(RefUData, DW_AttribClass_Reference) \ -X(Indirect, DW_AttribClass_Null) \ -X(SecOffset, DW_AttribClass_LinePtr|DW_AttribClass_LocListPtr|DW_AttribClass_MacPtr|DW_AttribClass_RngListPtr) \ -X(ExprLoc, DW_AttribClass_ExprLoc) \ -X(FlagPresent, DW_AttribClass_Flag) \ -X(RefSig8, DW_AttribClass_Reference) - -#define DW_Form_V5_XList \ -X(Strx, 0x1a) \ -X(Addrx, 0x1b) \ -X(RefSup4, 0x1c) \ -X(StrpSup, 0x1d) \ -X(Data16, 0x1e) \ -X(LineStrp, 0x1f) \ -X(ImplicitConst, 0x21) \ -X(LocListx, 0x22) \ -X(RngListx, 0x23) \ -X(RefSup8, 0x24) \ -X(Strx1, 0x25) \ -X(Strx2, 0x26) \ -X(Strx3, 0x27) \ -X(Strx4, 0x28) \ -X(Addrx1, 0x29) \ -X(Addrx2, 0x2a) \ -X(Addrx3, 0x2b) \ -X(Addrx4, 0x2c) - -#define DW_Form_AttribClass_V5_XList \ -X(Addr, DW_AttribClass_Address) \ -X(Block2, DW_AttribClass_Block) \ -X(Block4, DW_AttribClass_Block) \ -X(Data2, DW_AttribClass_Const) \ -X(Data4, DW_AttribClass_Const) \ -X(Data8, DW_AttribClass_Const) \ -X(String, DW_AttribClass_String) \ -X(Block, DW_AttribClass_Block) \ -X(Block1, DW_AttribClass_Block) \ -X(Data1, DW_AttribClass_Const) \ -X(Flag, DW_AttribClass_Flag) \ -X(SData, DW_AttribClass_Const) \ -X(Strp, DW_AttribClass_String) \ -X(UData, DW_AttribClass_Const) \ -X(RefAddr, DW_AttribClass_Reference) \ -X(Ref1, DW_AttribClass_Reference) \ -X(Ref2, DW_AttribClass_Reference) \ -X(Ref4, DW_AttribClass_Reference) \ -X(Ref8, DW_AttribClass_Reference) \ -X(RefUData, DW_AttribClass_Reference) \ -X(Indirect, DW_AttribClass_Null) \ -X(SecOffset, DW_AttribClass_AllPtrs) \ -X(ExprLoc, DW_AttribClass_ExprLoc) \ -X(FlagPresent, DW_AttribClass_Flag) \ -X(RefSig8, DW_AttribClass_Reference) \ -X(Strx, DW_AttribClass_String) \ -X(Addrx, DW_AttribClass_Address) \ -X(RefSup4, DW_AttribClass_Reference) \ -X(StrpSup, DW_AttribClass_String) \ -X(Data16, DW_AttribClass_Const) \ -X(LineStrp, DW_AttribClass_String) \ -X(ImplicitConst, DW_AttribClass_Const) \ -X(LocListx, DW_AttribClass_LocListPtr) \ -X(RngListx, DW_AttribClass_RngList) \ -X(RefSup8, DW_AttribClass_Reference) \ -X(Strx1, DW_AttribClass_String) \ -X(Strx2, DW_AttribClass_String) \ -X(Strx3, DW_AttribClass_String) \ -X(Strx4, DW_AttribClass_String) \ -X(Addrx1, DW_AttribClass_Address) \ -X(Addrx2, DW_AttribClass_Address) \ -X(Addrx3, DW_AttribClass_Address) \ -X(Addrx4, DW_AttribClass_Address) - -#define DW_Form_GNU_XList \ -X(GNU_AddrIndex, 0x1f01) \ -X(GNU_StrIndex, 0x1f02) \ -X(GNU_RefAlt, 0x1f20) \ -X(GNU_StrpAlt, 0x1f21) - -#define DW_Form_AttribClass_GNU_XList \ -X(GNU_AddrIndex, DW_AttribClass_Undefined) \ -X(GNU_StrIndex, DW_AttribClass_Undefined) \ -X(GNU_RefAlt, DW_AttribClass_Undefined) \ -X(GNU_StrpAlt, DW_AttribClass_String) - -typedef U64 DW_FormKind; -typedef enum DW_FormKindEnum -{ - DW_Form_Null, -#define X(_N, _ID) DW_Form_##_N = _ID, - DW_Form_V2_XList - DW_Form_V4_XList - DW_Form_V5_XList - DW_Form_GNU_XList -#undef X -} DW_FormKindEnum; - -typedef struct DW_Form -{ - DW_FormKind kind; - union { - String8 addr; - String8 block; - String8 data; - String8 string; - String8 exprloc; - B8 flag; - S64 sdata; - U64 udata; - U64 sec_offset; - U64 ref; - U64 strp_sup; - U64 xval; - U64 addrx; - U64 strx; - U64 rnglistx; - U64 ptr; - S64 implicit_const; - }; -} DW_Form; - -//- Attributes DWARF2 - -#define DW_AttribKind_V2_XList \ -X(Sibling, 0x1) \ -X(Location, 0x2) \ -X(Name, 0x3) \ -X(Ordering, 0x9) \ -X(ByteSize, 0xB) \ -X(BitOffset, 0xC) \ -X(BitSize, 0xD) \ -X(StmtList, 0x10) \ -X(LowPc, 0x11) \ -X(HighPc, 0x12) \ -X(Language, 0x13) \ -X(Discr, 0x15) \ -X(DiscrValue, 0x16) \ -X(Visibility, 0x17) \ -X(Import, 0x18) \ -X(StringLength, 0x19) \ -X(CommonReference, 0x1a) \ -X(CompDir, 0x1b) \ -X(ConstValue, 0x1c) \ -X(ContainingType, 0x1d) \ -X(DefaultValue, 0x1e) \ -X(Inline, 0x20) \ -X(IsOptional, 0x21) \ -X(LowerBound, 0x22) \ -X(Producer, 0x25) \ -X(Prototyped, 0x27) \ -X(ReturnAddr, 0x2a) \ -X(StartScope, 0x2c) \ -X(BitStride, 0x2e) \ -X(UpperBound, 0x2f) \ -X(AbstractOrigin, 0x31) \ -X(Accessibility, 0x32) \ -X(AddressClass, 0x33) \ -X(Artificial, 0x34) \ -X(BaseTypes, 0x35) \ -X(CallingConvention, 0x36) \ -X(Count, 0x37) \ -X(DataMemberLocation, 0x38) \ -X(DeclColumn, 0x39) \ -X(DeclFile, 0x3a) \ -X(DeclLine, 0x3b) \ -X(Declaration, 0x3c) \ -X(DiscrList, 0x3d) \ -X(Encoding, 0x3e) \ -X(External, 0x3f) \ -X(FrameBase, 0x40) \ -X(Friend, 0x41) \ -X(IdentifierCase, 0x42) \ -X(MacroInfo, 0x43) \ -X(NameListItem, 0x44) \ -X(Priority, 0x45) \ -X(Segment, 0x46) \ -X(Specification, 0x47) \ -X(StaticLink, 0x48) \ -X(Type, 0x49) \ -X(UseLocation, 0x4a) \ -X(VariableParameter, 0x4b) \ -X(Virtuality, 0x4c) \ -X(VTableElemLocation, 0x4d) - -#define DW_AttribKind_ClassFlags_V2_XList \ -X(Sibling, DW_AttribClass_Reference) \ -X(Location, DW_AttribClass_ExprLoc|DW_AttribClass_LocListPtr) \ -X(Name, DW_AttribClass_String) \ -X(Ordering, DW_AttribClass_Const) \ -X(ByteSize, DW_AttribClass_Const) \ -X(BitOffset, DW_AttribClass_Const) \ -X(BitSize, DW_AttribClass_Const) \ -X(StmtList, DW_AttribClass_Const) \ -X(LowPc, DW_AttribClass_Address) \ -X(HighPc, DW_AttribClass_Address) \ -X(Language, DW_AttribClass_Const) \ -X(Discr, DW_AttribClass_Reference) \ -X(DiscrValue, DW_AttribClass_Const) \ -X(Visibility, DW_AttribClass_Const) \ -X(Import, DW_AttribClass_Reference) \ -X(StringLength, DW_AttribClass_Block|DW_AttribClass_Const) \ -X(CommonReference, DW_AttribClass_Reference) \ -X(CompDir, DW_AttribClass_String) \ -X(ConstValue, DW_AttribClass_String|DW_AttribClass_Const|DW_AttribClass_Block) \ -X(ContainingType, DW_AttribClass_Reference) \ -X(DefaultValue, DW_AttribClass_Reference) \ -X(Inline, DW_AttribClass_Const) \ -X(IsOptional, DW_AttribClass_Flag) \ -X(LowerBound, DW_AttribClass_Const|DW_AttribClass_Reference) \ -X(Producer, DW_AttribClass_String) \ -X(Prototyped, DW_AttribClass_Flag) \ -X(ReturnAddr, DW_AttribClass_Block|DW_AttribClass_Const) \ -X(StartScope, DW_AttribClass_Const) \ -X(BitStride, DW_AttribClass_Const) /* dwarf-v1 DW_AttribKind_stride_size*/ \ -X(UpperBound, DW_AttribClass_Const|DW_AttribClass_Reference) \ -X(AbstractOrigin, DW_AttribClass_Reference) \ -X(Accessibility, DW_AttribClass_Const) \ -X(AddressClass, DW_AttribClass_Const) \ -X(Artificial, DW_AttribClass_Flag) \ -X(BaseTypes, DW_AttribClass_Reference) \ -X(CallingConvention, DW_AttribClass_Const) \ -X(Count, DW_AttribClass_Const|DW_AttribClass_Reference) \ -X(DataMemberLocation, DW_AttribClass_Block|DW_AttribClass_Reference) \ -X(DeclColumn, DW_AttribClass_Const) \ -X(DeclFile, DW_AttribClass_Const) \ -X(DeclLine, DW_AttribClass_Const) \ -X(Declaration, DW_AttribClass_Flag) \ -X(DiscrList, DW_AttribClass_Block) \ -X(Encoding, DW_AttribClass_Const) \ -X(External, DW_AttribClass_Flag) \ -X(FrameBase, DW_AttribClass_Block|DW_AttribClass_Const) \ -X(Friend, DW_AttribClass_Reference) \ -X(IdentifierCase, DW_AttribClass_Const) \ -X(MacroInfo, DW_AttribClass_Const) \ -X(NameListItem, DW_AttribClass_Block) \ -X(Priority, DW_AttribClass_Reference) \ -X(Segment, DW_AttribClass_Block|DW_AttribClass_Const) \ -X(Specification, DW_AttribClass_Reference) \ -X(StaticLink, DW_AttribClass_Block|DW_AttribClass_Const) \ -X(Type, DW_AttribClass_Reference) \ -X(UseLocation, DW_AttribClass_Block|DW_AttribClass_Const) \ -X(VariableParameter, DW_AttribClass_Flag) \ -X(Virtuality, DW_AttribClass_Const) \ -X(VTableElemLocation, DW_AttribClass_Block|DW_AttribClass_Reference) - -//- Attributes DWARF3 - -#define DW_AttribKind_V3_XList \ -X(Allocated, 0x4e) \ -X(Associated, 0x4f) \ -X(DataLocation, 0x50) \ -X(ByteStride, 0x51) \ -X(EntryPc, 0x52) \ -X(UseUtf8, 0x53) \ -X(Extension, 0x54) \ -X(Ranges, 0x55) \ -X(Trampoline, 0x56) \ -X(CallColumn, 0x57) \ -X(CallFile, 0x58) \ -X(CallLine, 0x59) \ -X(Description, 0x5a) \ -X(BinaryScale, 0x5b) \ -X(DecimalScale, 0x5c) \ -X(Small, 0x5d) \ -X(DecimalSign, 0x5e) \ -X(DigitCount, 0x5f) \ -X(PictureString, 0x60) \ -X(Mutable, 0x61) \ -X(ThreadsScaled, 0x62) \ -X(Explicit, 0x63) \ -X(ObjectPointer, 0x64) \ -X(Endianity, 0x65) \ -X(Elemental, 0x66) \ -X(Pure, 0x67) \ -X(Recursive, 0x68) - -#define DW_AttribKind_ClassFlags_V3_XList \ -X(Sibling, DW_AttribClass_Reference) \ -X(Location, DW_AttribClass_Block|DW_AttribClass_LocListPtr) \ -X(Name, DW_AttribClass_String) \ -X(Ordering, DW_AttribClass_Const) \ -X(ByteSize, DW_AttribClass_Block|DW_AttribClass_Const|DW_AttribClass_Reference) \ -X(BitOffset, DW_AttribClass_Block|DW_AttribClass_Const|DW_AttribClass_Reference) \ -X(BitSize, DW_AttribClass_Block|DW_AttribClass_Const|DW_AttribClass_Reference) \ -X(StmtList, DW_AttribClass_LinePtr) \ -X(LowPc, DW_AttribClass_Address) \ -X(HighPc, DW_AttribClass_Address) \ -X(Language, DW_AttribClass_Const) \ -X(Discr, DW_AttribClass_Reference) \ -X(DiscrValue, DW_AttribClass_Const) \ -X(Visibility, DW_AttribClass_Const) \ -X(Import, DW_AttribClass_Reference) \ -X(StringLength, DW_AttribClass_Block|DW_AttribClass_LocListPtr) \ -X(CommonReference, DW_AttribClass_Reference) \ -X(CompDir, DW_AttribClass_String) \ -X(ConstValue, DW_AttribClass_Block|DW_AttribClass_Const|DW_AttribClass_String) \ -X(ContainingType, DW_AttribClass_Reference) \ -X(DefaultValue, DW_AttribClass_Reference) \ -X(Inline, DW_AttribClass_Const) \ -X(IsOptional, DW_AttribClass_Flag) \ -X(LowerBound, DW_AttribClass_Block|DW_AttribClass_Const|DW_AttribClass_Reference) \ -X(Producer, DW_AttribClass_String) \ -X(Prototyped, DW_AttribClass_Flag) \ -X(ReturnAddr, DW_AttribClass_Block|DW_AttribClass_LocListPtr) \ -X(StartScope, DW_AttribClass_Const) \ -X(BitStride, DW_AttribClass_Const) \ -X(UpperBound, DW_AttribClass_Block|DW_AttribClass_Const|DW_AttribClass_Reference) \ -X(AbstractOrigin, DW_AttribClass_Reference) \ -X(Accessibility, DW_AttribClass_Const) \ -X(AddressClass, DW_AttribClass_Const) \ -X(Artificial, DW_AttribClass_Flag) \ -X(BaseTypes, DW_AttribClass_Reference) \ -X(CallingConvention, DW_AttribClass_Const) \ -X(Count, DW_AttribClass_Block|DW_AttribClass_Const|DW_AttribClass_Reference) \ -X(DataMemberLocation, DW_AttribClass_Block|DW_AttribClass_Const|DW_AttribClass_LocListPtr) \ -X(DeclColumn, DW_AttribClass_Const) \ -X(DeclFile, DW_AttribClass_Const) \ -X(DeclLine, DW_AttribClass_Const) \ -X(Declaration, DW_AttribClass_Flag) \ -X(DiscrList, DW_AttribClass_Block) \ -X(Encoding, DW_AttribClass_Const) \ -X(External, DW_AttribClass_Flag) \ -X(FrameBase, DW_AttribClass_Block|DW_AttribClass_LocListPtr) \ -X(Friend, DW_AttribClass_Reference) \ -X(IdentifierCase, DW_AttribClass_Const) \ -X(MacroInfo, DW_AttribClass_MacPtr) \ -X(NameListItem, DW_AttribClass_Block) \ -X(Priority, DW_AttribClass_Reference) \ -X(Segment, DW_AttribClass_Block|DW_AttribClass_LocListPtr) \ -X(Specification, DW_AttribClass_Reference) \ -X(StaticLink, DW_AttribClass_Block|DW_AttribClass_LocListPtr) \ -X(Type, DW_AttribClass_Reference) \ -X(UseLocation, DW_AttribClass_Block|DW_AttribClass_LocListPtr) \ -X(VariableParameter, DW_AttribClass_Flag) \ -X(Virtuality, DW_AttribClass_Const) \ -X(VTableElemLocation, DW_AttribClass_Block|DW_AttribClass_LocListPtr) \ -X(Allocated, DW_AttribClass_Block|DW_AttribClass_Const|DW_AttribClass_Reference) \ -X(Associated, DW_AttribClass_Block|DW_AttribClass_Const|DW_AttribClass_Reference) \ -X(DataLocation, DW_AttribClass_Block) \ -X(ByteStride, DW_AttribClass_Block|DW_AttribClass_Const|DW_AttribClass_Reference) \ -X(EntryPc, DW_AttribClass_Address) \ -X(UseUtf8, DW_AttribClass_Flag) \ -X(Extension, DW_AttribClass_Reference) \ -X(Ranges, DW_AttribClass_RngListPtr) \ -X(Trampoline, DW_AttribClass_Address|DW_AttribClass_Flag|DW_AttribClass_Reference|DW_AttribClass_String) \ -X(CallColumn, DW_AttribClass_Const) \ -X(CallFile, DW_AttribClass_Const) \ -X(CallLine, DW_AttribClass_Const) \ -X(Description, DW_AttribClass_String) \ -X(BinaryScale, DW_AttribClass_Const) \ -X(DecimalScale, DW_AttribClass_Const) \ -X(Small, DW_AttribClass_Reference) \ -X(DecimalSign, DW_AttribClass_Const) \ -X(DigitCount, DW_AttribClass_Const) \ -X(PictureString, DW_AttribClass_String) \ -X(Mutable, DW_AttribClass_Flag) \ -X(ThreadsScaled, DW_AttribClass_Flag) \ -X(Explicit, DW_AttribClass_Flag) \ -X(ObjectPointer, DW_AttribClass_Reference) \ -X(Endianity, DW_AttribClass_Const) \ -X(Elemental, DW_AttribClass_Flag) \ -X(Pure, DW_AttribClass_Flag) \ -X(Recursive, DW_AttribClass_Flag) - -//- Attributes DWARF4 - -#define DW_AttribKind_V4_XList \ -X(Signature, 0x69) \ -X(MainSubProgram, 0x6a) \ -X(DataBitOffset, 0x6b) \ -X(ConstExpr, 0x6c) \ -X(EnumClass, 0x6d) \ -X(LinkageName, 0x6e) - -#define DW_AttribKind_ClassFlags_V4_XList \ -X(Sibling, DW_AttribClass_Reference) \ -X(Location, DW_AttribClass_ExprLoc|DW_AttribClass_LocListPtr) \ -X(Name, DW_AttribClass_String) \ -X(Ordering, DW_AttribClass_Const) \ -X(ByteSize, DW_AttribClass_Const|DW_AttribClass_ExprLoc|DW_AttribClass_Reference) \ -X(BitOffset, DW_AttribClass_Const|DW_AttribClass_ExprLoc|DW_AttribClass_Reference) \ -X(BitSize, DW_AttribClass_Const|DW_AttribClass_ExprLoc|DW_AttribClass_Reference) \ -X(StmtList, DW_AttribClass_LinePtr) \ -X(LowPc, DW_AttribClass_Address) \ -X(HighPc, DW_AttribClass_Address|DW_AttribClass_Const) \ -X(Language, DW_AttribClass_Const) \ -X(Discr, DW_AttribClass_Reference) \ -X(DiscrValue, DW_AttribClass_Const) \ -X(Visibility, DW_AttribClass_Const) \ -X(Import, DW_AttribClass_Reference) \ -X(StringLength, DW_AttribClass_ExprLoc|DW_AttribClass_LocListPtr) \ -X(CommonReference, DW_AttribClass_Reference) \ -X(CompDir, DW_AttribClass_String) \ -X(ConstValue, DW_AttribClass_Block|DW_AttribClass_Const|DW_AttribClass_String) \ -X(ContainingType, DW_AttribClass_Reference) \ -X(DefaultValue, DW_AttribClass_Reference) \ -X(Inline, DW_AttribClass_Const) \ -X(IsOptional, DW_AttribClass_Flag) \ -X(LowerBound, DW_AttribClass_Const|DW_AttribClass_ExprLoc|DW_AttribClass_Reference) \ -X(Producer, DW_AttribClass_String) \ -X(Prototyped, DW_AttribClass_Flag) \ -X(ReturnAddr, DW_AttribClass_ExprLoc|DW_AttribClass_LocListPtr) \ -X(StartScope, DW_AttribClass_Const|DW_AttribClass_RngListPtr) \ -X(BitStride, DW_AttribClass_Const|DW_AttribClass_ExprLoc|DW_AttribClass_Reference) \ -X(UpperBound, DW_AttribClass_Const|DW_AttribClass_ExprLoc|DW_AttribClass_Reference) \ -X(AbstractOrigin, DW_AttribClass_Reference) \ -X(Accessibility, DW_AttribClass_Const) \ -X(AddressClass, DW_AttribClass_Const) \ -X(Artificial, DW_AttribClass_Flag) \ -X(BaseTypes, DW_AttribClass_Reference) \ -X(CallingConvention, DW_AttribClass_Const) \ -X(Count, DW_AttribClass_Const|DW_AttribClass_ExprLoc|DW_AttribClass_Reference) \ -X(DataMemberLocation, DW_AttribClass_Const|DW_AttribClass_ExprLoc|DW_AttribClass_LocListPtr) \ -X(DeclColumn, DW_AttribClass_Const) \ -X(DeclFile, DW_AttribClass_Const) \ -X(DeclLine, DW_AttribClass_Const) \ -X(Declaration, DW_AttribClass_Flag) \ -X(DiscrList, DW_AttribClass_Block) \ -X(Encoding, DW_AttribClass_Const) \ -X(External, DW_AttribClass_Flag) \ -X(FrameBase, DW_AttribClass_ExprLoc|DW_AttribClass_LocListPtr) \ -X(Friend, DW_AttribClass_Reference) \ -X(IdentifierCase, DW_AttribClass_Const) \ -X(MacroInfo, DW_AttribClass_MacPtr) \ -X(NameListItem, DW_AttribClass_Reference) \ -X(Priority, DW_AttribClass_Reference) \ -X(Segment, DW_AttribClass_ExprLoc|DW_AttribClass_LocListPtr) \ -X(Specification, DW_AttribClass_Reference) \ -X(StaticLink, DW_AttribClass_ExprLoc|DW_AttribClass_LocListPtr) \ -X(Type, DW_AttribClass_Reference) \ -X(UseLocation, DW_AttribClass_ExprLoc|DW_AttribClass_LocListPtr) \ -X(VariableParameter, DW_AttribClass_Flag) \ -X(Virtuality, DW_AttribClass_Const) \ -X(VTableElemLocation, DW_AttribClass_ExprLoc|DW_AttribClass_LocListPtr) \ -X(Allocated, DW_AttribClass_Const|DW_AttribClass_ExprLoc|DW_AttribClass_Reference) \ -X(Associated, DW_AttribClass_Const|DW_AttribClass_ExprLoc|DW_AttribClass_Reference) \ -X(DataLocation, DW_AttribClass_ExprLoc) \ -X(ByteStride, DW_AttribClass_Const|DW_AttribClass_ExprLoc|DW_AttribClass_Reference) \ -X(EntryPc, DW_AttribClass_Address) \ -X(UseUtf8, DW_AttribClass_Flag) \ -X(Extension, DW_AttribClass_Reference) \ -X(Ranges, DW_AttribClass_RngListPtr) \ -X(Trampoline, DW_AttribClass_Address|DW_AttribClass_Flag|DW_AttribClass_Reference|DW_AttribClass_String) \ -X(CallColumn, DW_AttribClass_Const) \ -X(CallFile, DW_AttribClass_Const) \ -X(CallLine, DW_AttribClass_Const) \ -X(Description, DW_AttribClass_String) \ -X(BinaryScale, DW_AttribClass_Const) \ -X(DecimalScale, DW_AttribClass_Const) \ -X(Small, DW_AttribClass_Reference) \ -X(DecimalSign, DW_AttribClass_Const) \ -X(DigitCount, DW_AttribClass_Const) \ -X(PictureString, DW_AttribClass_String) \ -X(Mutable, DW_AttribClass_Flag) \ -X(ThreadsScaled, DW_AttribClass_Flag) \ -X(Explicit, DW_AttribClass_Flag) \ -X(ObjectPointer, DW_AttribClass_Reference) \ -X(Endianity, DW_AttribClass_Const) \ -X(Elemental, DW_AttribClass_Flag) \ -X(Pure, DW_AttribClass_Flag) \ -X(Recursive, DW_AttribClass_Flag) \ -X(Signature, DW_AttribClass_Reference) \ -X(MainSubProgram, DW_AttribClass_Flag) \ -X(DataBitOffset, DW_AttribClass_Const) \ -X(ConstExpr, DW_AttribClass_Flag) \ -X(EnumClass, DW_AttribClass_Flag) \ -X(LinkageName, DW_AttribClass_String) - -//- Attributes DWARF5 - -#define DW_AttribKind_V5_XList \ -X(StringLengthBitSize, 0x6f) \ -X(StringLengthByteSize, 0x70) \ -X(Rank, 0x71) \ -X(StrOffsetsBase, 0x72) \ -X(AddrBase, 0x73) \ -X(RngListsBase, 0x74) \ -X(DwoName, 0x76) \ -X(Reference, 0x77) \ -X(RValueReference, 0x78) \ -X(Macros, 0x79) \ -X(CallAllCalls, 0x7a) \ -X(CallAllSourceCalls, 0x7b) \ -X(CallAllTailCalls, 0x7c) \ -X(CallReturnPc, 0x7d) \ -X(CallValue, 0x7e) \ -X(CallOrigin, 0x7f) \ -X(CallParameter, 0x80) \ -X(CallPc, 0x81) \ -X(CallTailCall, 0x82) \ -X(CallTarget, 0x83) \ -X(CallTargetClobbered, 0x84) \ -X(CallDataLocation, 0x85) \ -X(CallDataValue, 0x86) \ -X(NoReturn, 0x87) \ -X(Alignment, 0x88) \ -X(ExportSymbols, 0x89) \ -X(Deleted, 0x8a) \ -X(Defaulted, 0x8b) \ -X(LocListsBase, 0x8c) - -#define DW_AttribKind_ClassFlags_V5_XList \ -X(Sibling, DW_AttribClass_Reference) \ -X(Location, DW_AttribClass_ExprLoc|DW_AttribClass_LocListPtr) \ -X(Name, DW_AttribClass_String) \ -X(Ordering, DW_AttribClass_Const) \ -X(ByteSize, DW_AttribClass_Const|DW_AttribClass_ExprLoc|DW_AttribClass_Reference) \ -X(BitOffset, DW_AttribClass_Const|DW_AttribClass_ExprLoc|DW_AttribClass_Reference) \ -X(BitSize, DW_AttribClass_Const|DW_AttribClass_ExprLoc|DW_AttribClass_Reference) \ -X(StmtList, DW_AttribClass_LinePtr) \ -X(LowPc, DW_AttribClass_Address) \ -X(HighPc, DW_AttribClass_Address|DW_AttribClass_Const) \ -X(Language, DW_AttribClass_Const) \ -X(Discr, DW_AttribClass_Reference) \ -X(DiscrValue, DW_AttribClass_Const) \ -X(Visibility, DW_AttribClass_Const) \ -X(Import, DW_AttribClass_Reference) \ -X(StringLength, DW_AttribClass_ExprLoc|DW_AttribClass_LocListPtr) \ -X(CommonReference, DW_AttribClass_Reference) \ -X(CompDir, DW_AttribClass_String) \ -X(ConstValue, DW_AttribClass_Block|DW_AttribClass_Const|DW_AttribClass_String) \ -X(ContainingType, DW_AttribClass_Reference) \ -X(DefaultValue, DW_AttribClass_Reference) \ -X(Inline, DW_AttribClass_Const) \ -X(IsOptional, DW_AttribClass_Flag) \ -X(LowerBound, DW_AttribClass_Const|DW_AttribClass_ExprLoc|DW_AttribClass_Reference) \ -X(Producer, DW_AttribClass_String) \ -X(Prototyped, DW_AttribClass_Flag) \ -X(ReturnAddr, DW_AttribClass_ExprLoc|DW_AttribClass_LocListPtr) \ -X(StartScope, DW_AttribClass_Const|DW_AttribClass_RngListPtr) \ -X(BitStride, DW_AttribClass_Const|DW_AttribClass_ExprLoc|DW_AttribClass_Reference) \ -X(UpperBound, DW_AttribClass_Const|DW_AttribClass_ExprLoc|DW_AttribClass_Reference) \ -X(AbstractOrigin, DW_AttribClass_Reference) \ -X(Accessibility, DW_AttribClass_Const) \ -X(AddressClass, DW_AttribClass_Const) \ -X(Artificial, DW_AttribClass_Flag) \ -X(BaseTypes, DW_AttribClass_Reference) \ -X(CallingConvention, DW_AttribClass_Const) \ -X(Count, DW_AttribClass_Const|DW_AttribClass_ExprLoc|DW_AttribClass_Reference) \ -X(DataMemberLocation, DW_AttribClass_Const|DW_AttribClass_ExprLoc|DW_AttribClass_LocList) \ -X(DeclColumn, DW_AttribClass_Const) \ -X(DeclFile, DW_AttribClass_Const) \ -X(DeclLine, DW_AttribClass_Const) \ -X(Declaration, DW_AttribClass_Flag) \ -X(DiscrList, DW_AttribClass_Block) \ -X(Encoding, DW_AttribClass_Const) \ -X(External, DW_AttribClass_Flag) \ -X(FrameBase, DW_AttribClass_ExprLoc|DW_AttribClass_LocList) \ -X(Friend, DW_AttribClass_Reference) \ -X(IdentifierCase, DW_AttribClass_Const) \ -X(MacroInfo, DW_AttribClass_MacPtr) \ -X(NameListItem, DW_AttribClass_Reference) \ -X(Priority, DW_AttribClass_Reference) \ -X(Segment, DW_AttribClass_ExprLoc|DW_AttribClass_LocList) \ -X(Specification, DW_AttribClass_Reference) \ -X(StaticLink, DW_AttribClass_ExprLoc|DW_AttribClass_LocList) \ -X(Type, DW_AttribClass_Reference) \ -X(UseLocation, DW_AttribClass_ExprLoc|DW_AttribClass_LocList) \ -X(VariableParameter, DW_AttribClass_Flag) \ -X(Virtuality, DW_AttribClass_Const) \ -X(VTableElemLocation, DW_AttribClass_ExprLoc|DW_AttribClass_LocList) \ -X(Allocated, DW_AttribClass_Const|DW_AttribClass_ExprLoc|DW_AttribClass_Reference) \ -X(Associated, DW_AttribClass_Const|DW_AttribClass_ExprLoc|DW_AttribClass_Reference) \ -X(DataLocation, DW_AttribClass_ExprLoc) \ -X(ByteStride, DW_AttribClass_Const|DW_AttribClass_ExprLoc|DW_AttribClass_Reference) \ -X(EntryPc, DW_AttribClass_Address|DW_AttribClass_Const) \ -X(UseUtf8, DW_AttribClass_Flag) \ -X(Extension, DW_AttribClass_Reference) \ -X(Ranges, DW_AttribClass_RngList) \ -X(Trampoline, DW_AttribClass_Address|DW_AttribClass_Flag|DW_AttribClass_Reference|DW_AttribClass_String) \ -X(CallColumn, DW_AttribClass_Const) \ -X(CallFile, DW_AttribClass_Const) \ -X(CallLine, DW_AttribClass_Const) \ -X(Description, DW_AttribClass_String) \ -X(BinaryScale, DW_AttribClass_Const) \ -X(DecimalScale, DW_AttribClass_Const) \ -X(Small, DW_AttribClass_Reference) \ -X(DecimalSign, DW_AttribClass_Const) \ -X(DigitCount, DW_AttribClass_Const) \ -X(PictureString, DW_AttribClass_String) \ -X(Mutable, DW_AttribClass_Flag) \ -X(ThreadsScaled, DW_AttribClass_Flag) \ -X(Explicit, DW_AttribClass_Flag) \ -X(ObjectPointer, DW_AttribClass_Reference) \ -X(Endianity, DW_AttribClass_Const) \ -X(Elemental, DW_AttribClass_Flag) \ -X(Pure, DW_AttribClass_Flag) \ -X(Recursive, DW_AttribClass_Flag) \ -X(Signature, DW_AttribClass_Reference) \ -X(MainSubProgram, DW_AttribClass_Flag) \ -X(DataBitOffset, DW_AttribClass_Const) \ -X(ConstExpr, DW_AttribClass_Flag) \ -X(EnumClass, DW_AttribClass_Flag) \ -X(LinkageName, DW_AttribClass_String) \ -X(StringLengthBitSize, DW_AttribClass_Const) \ -X(StringLengthByteSize, DW_AttribClass_Const) \ -X(Rank, DW_AttribClass_Const|DW_AttribClass_ExprLoc) \ -X(StrOffsetsBase, DW_AttribClass_StrOffsetsPtr) \ -X(AddrBase, DW_AttribClass_AddrPtr) \ -X(RngListsBase, DW_AttribClass_RngListPtr) \ -X(DwoName, DW_AttribClass_String) \ -X(Reference, DW_AttribClass_Flag) \ -X(RValueReference, DW_AttribClass_Flag) \ -X(Macros, DW_AttribClass_MacPtr) \ -X(CallAllCalls, DW_AttribClass_Flag) \ -X(CallAllSourceCalls, DW_AttribClass_Flag) \ -X(CallAllTailCalls, DW_AttribClass_Flag) \ -X(CallReturnPc, DW_AttribClass_Address) \ -X(CallValue, DW_AttribClass_ExprLoc) \ -X(CallOrigin, DW_AttribClass_ExprLoc) \ -X(CallParameter, DW_AttribClass_Reference) \ -X(CallPc, DW_AttribClass_Address) \ -X(CallTailCall, DW_AttribClass_Flag) \ -X(CallTarget, DW_AttribClass_ExprLoc) \ -X(CallTargetClobbered, DW_AttribClass_ExprLoc) \ -X(CallDataLocation, DW_AttribClass_ExprLoc) \ -X(CallDataValue, DW_AttribClass_ExprLoc) \ -X(NoReturn, DW_AttribClass_Flag) \ -X(Alignment, DW_AttribClass_Const) \ -X(ExportSymbols, DW_AttribClass_Flag) \ -X(Deleted, DW_AttribClass_Flag) \ -X(Defaulted, DW_AttribClass_Const) \ -X(LocListsBase, DW_AttribClass_LocListPtr) - -//- Attributes GNU - -#define DW_AttribKind_GNU_XList \ -X(GNU_Vector, 0x2107) \ -X(GNU_GuardedBy, 0x2108) \ -X(GNU_PtGuardedBy, 0x2109) \ -X(GNU_Guarded, 0x210a) \ -X(GNU_PtGuarded, 0x210b) \ -X(GNU_LocksExcluded, 0x210c) \ -X(GNU_ExclusiveLocksRequired, 0x210d) \ -X(GNU_SharedLocksRequired, 0x210e) \ -X(GNU_OdrSignature, 0x210f) \ -X(GNU_TemplateName, 0x2110) \ -X(GNU_CallSiteValue, 0x2111) \ -X(GNU_CallSiteDataValue, 0x2112) \ -X(GNU_CallSiteTarget, 0x2113) \ -X(GNU_CallSiteTargetClobbered, 0x2114) \ -X(GNU_TailCall, 0x2115) \ -X(GNU_AllTailCallsSites, 0x2116) \ -X(GNU_AllCallSites, 0x2117) \ -X(GNU_AllSourceCallSites, 0x2118) \ -X(GNU_Macros, 0x2119) \ -X(GNU_Deleted, 0x211a) \ -X(GNU_DwoName, 0x2130) \ -X(GNU_DwoId, 0x2131) \ -X(GNU_RangesBase, 0x2132) \ -X(GNU_AddrBase, 0x2133) \ -X(GNU_PubNames, 0x2134) \ -X(GNU_PubTypes, 0x2135) \ -X(GNU_Discriminator, 0x2136) \ -X(GNU_LocViews, 0x2137) \ -X(GNU_EntryView, 0x2138) \ -X(GNU_DescriptiveType, 0x2302) \ -X(GNU_Numerator, 0x2303) \ -X(GNU_Denominator, 0x2304) \ -X(GNU_Bias, 0x2305) - -#define DW_AttribKind_ClassFlags_GNU_XList \ -X(GNU_Vector, DW_AttribClass_Flag) \ -X(GNU_GuardedBy, DW_AttribClass_Undefined) \ -X(GNU_PtGuardedBy, DW_AttribClass_Undefined) \ -X(GNU_Guarded, DW_AttribClass_Undefined) \ -X(GNU_PtGuarded, DW_AttribClass_Undefined) \ -X(GNU_LocksExcluded, DW_AttribClass_Undefined) \ -X(GNU_ExclusiveLocksRequired, DW_AttribClass_Undefined) \ -X(GNU_SharedLocksRequired, DW_AttribClass_Undefined) \ -X(GNU_OdrSignature, DW_AttribClass_Undefined) \ -X(GNU_TemplateName, DW_AttribClass_Undefined) \ -X(GNU_CallSiteValue, DW_AttribClass_ExprLoc) \ -X(GNU_CallSiteDataValue, DW_AttribClass_ExprLoc) \ -X(GNU_CallSiteTarget, DW_AttribClass_ExprLoc) \ -X(GNU_CallSiteTargetClobbered, DW_AttribClass_ExprLoc) \ -X(GNU_TailCall, DW_AttribClass_Flag) \ -X(GNU_AllTailCallsSites, DW_AttribClass_Flag) \ -X(GNU_AllCallSites, DW_AttribClass_Flag) \ -X(GNU_AllSourceCallSites, DW_AttribClass_Flag) \ -X(GNU_Macros, DW_AttribClass_Flag) \ -X(GNU_Deleted, DW_AttribClass_Undefined) \ -X(GNU_DwoName, DW_AttribClass_String) \ -X(GNU_DwoId, DW_AttribClass_Const) \ -X(GNU_RangesBase, DW_AttribClass_Undefined) \ -X(GNU_AddrBase, DW_AttribClass_AddrPtr) \ -X(GNU_PubNames, DW_AttribClass_Flag) \ -X(GNU_PubTypes, DW_AttribClass_Undefined) \ -X(GNU_Discriminator, DW_AttribClass_Const) \ -X(GNU_LocViews, DW_AttribClass_LocListPtr) \ -X(GNU_EntryView, DW_AttribClass_Undefined) \ -X(GNU_DescriptiveType, DW_AttribClass_Undefined) \ -X(GNU_Numerator, DW_AttribClass_Undefined) \ -X(GNU_Denominator, DW_AttribClass_Undefined) \ -X(GNU_Bias, DW_AttribClass_Undefined) - -//- Attributes LLVM - -#define DW_AttribKind_LLVM_XList \ -X(LLVM_IncludePath, 0x3e00) \ -X(LLVM_ConfigMacros, 0x3e01) \ -X(LLVM_SysRoot, 0x3e02) \ -X(LLVM_TagOffset, 0x3e03) \ -X(LLVM_ApiNotes, 0x3e07) - -#define DW_AttribKind_ClassFlags_LLVM_XList \ -X(LLVM_IncludePath, DW_AttribClass_String) \ -X(LLVM_ConfigMacros, DW_AttribClass_String) \ -X(LLVM_SysRoot, DW_AttribClass_String) \ -X(LLVM_TagOffset, DW_AttribClass_Undefined) \ -X(LLVM_ApiNotes, DW_AttribClass_String) - -//- Attributes Apple - -#define DW_AttribKind_APPLE_XList \ -X(APPLE_Optimized, 0x3fe1) \ -X(APPLE_Flags, 0x3fe2) \ -X(APPLE_Isa, 0x3fe3) \ -X(APPLE_Block, 0x3fe4) \ -X(APPLE_MajorRuntimeVers, 0x3fe5) \ -X(APPLE_RuntimeClass, 0x3fe6) \ -X(APPLE_OmitFramePtr, 0x3fe7) \ -X(APPLE_PropertyName, 0x3fe8) \ -X(APPLE_PropertyGetter, 0x3fe9) \ -X(APPLE_PropertySetter, 0x3fea) \ -X(APPLE_PropertyAttribute, 0x3feb) \ -X(APPLE_ObjcCompleteType, 0x3fec) \ -X(APPLE_Property, 0x3fed) \ -X(APPLE_ObjDirect, 0x3fee) \ -X(APPLE_Sdk, 0x3fef) - -#define DW_AttribKind_ClassFlags_APPLE_XList \ -X(APPLE_Optimized, DW_AttribClass_Flag) \ -X(APPLE_Flags, DW_AttribClass_Flag) \ -X(APPLE_Isa, DW_AttribClass_Flag) \ -X(APPLE_Block, DW_AttribClass_Undefined) \ -X(APPLE_MajorRuntimeVers, DW_AttribClass_Undefined) \ -X(APPLE_RuntimeClass, DW_AttribClass_Undefined) \ -X(APPLE_OmitFramePtr, DW_AttribClass_Flag) \ -X(APPLE_PropertyName, DW_AttribClass_Undefined) \ -X(APPLE_PropertyGetter, DW_AttribClass_Undefined) \ -X(APPLE_PropertySetter, DW_AttribClass_Undefined) \ -X(APPLE_PropertyAttribute, DW_AttribClass_Undefined) \ -X(APPLE_ObjcCompleteType, DW_AttribClass_Undefined) \ -X(APPLE_Property, DW_AttribClass_Undefined) \ -X(APPLE_ObjDirect, DW_AttribClass_Undefined) \ -X(APPLE_Sdk, DW_AttribClass_String) - -//- Attributes MIPS - -#define DW_AttribKind_MIPS_XList \ -X(MIPS_Fde, 0x2001) \ -X(MIPS_LoopBegin, 0x2002) \ -X(MIPS_TailLoopBegin, 0x2003) \ -X(MIPS_EpilogBegin, 0x2004) \ -X(MIPS_LoopUnrollFactor, 0x2005) \ -X(MIPS_SoftwarePipelineDepth, 0x2006) \ -X(MIPS_LinkageName, 0x2007) \ -X(MIPS_Stride, 0x2008) \ -X(MIPS_AbstractName, 0x2009) \ -X(MIPS_CloneOrigin, 0x200a) \ -X(MIPS_HasInlines, 0x200b) \ -X(MIPS_StrideByte, 0x200c) \ -X(MIPS_StrideElem, 0x200d) \ -X(MIPS_PtrDopeType, 0x200e) \ -X(MIPS_AllocatableDopeType, 0x200f) \ -X(MIPS_AssumedShapeDopeType, 0x2010) \ -X(MIPS_AssumedSize, 0x2011) - -#define DW_AttribKind_ClassFlags_MIPS_XList \ -X(MIPS_Fde, DW_AttribClass_Block) \ -X(MIPS_LoopBegin, DW_AttribClass_Block) \ -X(MIPS_TailLoopBegin, DW_AttribClass_Block) \ -X(MIPS_EpilogBegin, DW_AttribClass_Block) \ -X(MIPS_LoopUnrollFactor, DW_AttribClass_Block) \ -X(MIPS_SoftwarePipelineDepth, DW_AttribClass_Block) \ -X(MIPS_LinkageName, DW_AttribClass_String) \ -X(MIPS_Stride, DW_AttribClass_Block) \ -X(MIPS_AbstractName, DW_AttribClass_String) \ -X(MIPS_CloneOrigin, DW_AttribClass_String) \ -X(MIPS_HasInlines, DW_AttribClass_Reference) \ -X(MIPS_StrideByte, DW_AttribClass_Reference) \ -X(MIPS_StrideElem, DW_AttribClass_Reference) \ -X(MIPS_PtrDopeType, DW_AttribClass_Reference) \ -X(MIPS_AllocatableDopeType, DW_AttribClass_Reference) \ -X(MIPS_AssumedShapeDopeType, DW_AttribClass_Reference) \ -X(MIPS_AssumedSize, DW_AttribClass_Reference) - -typedef U32 DW_AttribKind; -typedef enum DW_AttribKindEnum -{ - DW_AttribKind_Null, -#define X(_N,_ID,...) DW_AttribKind_##_N = _ID, - DW_AttribKind_V2_XList - DW_AttribKind_V3_XList - DW_AttribKind_V4_XList - DW_AttribKind_V5_XList - DW_AttribKind_GNU_XList - DW_AttribKind_LLVM_XList - DW_AttribKind_APPLE_XList - DW_AttribKind_MIPS_XList -#undef X - DW_AttribKind_UserLo = 0x2000, - DW_AttribKind_UserHi = 0x3fff -} DW_AttribKindEnum; - -#define DW_ATE_XList \ -X(Null, 0x00) \ -X(Address, 0x01) \ -X(Boolean, 0x02) \ -X(ComplexFloat, 0x03) \ -X(Float, 0x04) \ -X(Signed, 0x05) \ -X(SignedChar, 0x06) \ -X(Unsigned, 0x07) \ -X(UnsignedChar, 0x08) \ -X(ImaginaryFloat, 0x09) \ -X(PackedDecimal, 0x0A) \ -X(NumericString, 0x0B) \ -X(Edited, 0x0C) \ -X(SignedFixed, 0x0D) \ -X(UnsignedFixed, 0x0E) \ -X(DecimalFloat, 0x0F) \ -X(Utf, 0x10) \ -X(Ucs, 0x11) \ -X(Ascii, 0x12) - -typedef U64 DW_ATE; -typedef enum DW_ATEEnum -{ -#define X(_N,_ID) DW_ATE_##_N = _ID, - DW_ATE_XList -#undef X -} DW_ATEnum; - -#define DW_CallingConventionKind_XList \ -X(Normal, 0x0) \ -X(Program, 0x1) \ -X(NoCall, 0x3) \ -X(PassByValue, 0x4) \ -X(PassByReference, 0x5) - -typedef U64 DW_CallingConventionKind; -typedef enum DW_CallingConventionKindEnum -{ -#define X(_N,_ID) DW_CallingConventionKind_##_N = _ID, - DW_CallingConventionKind_XList -#undef X -} DW_CallingConventionKindEnum; - -#define DW_AccessKind_XList \ -X(Public, 0x00) \ -X(Private, 0x01) \ -X(Protected, 0x02) - -typedef U64 DW_AccessKind; -typedef enum DW_AccessKindEnum -{ -#define X(_N,_ID) DW_AccessKind_##_N = _ID, - DW_AccessKind_XList -#undef X -} DW_AccessKindEnum; - -#define DW_VirtualityKind_XList \ -X(None, 0x00) \ -X(Virtual, 0x01) \ -X(PureVirtual, 0x02) - -typedef U64 DW_VirtualityKind; -typedef enum DW_VirtualityEnum -{ -#define X(_N,_ID) DW_VirtualityKind_##_N = _ID, - DW_VirtualityKind_XList -#undef X -} DW_VirtualityEnum; - -#define DW_RngListEntryKind \ -X(EndOfList, 0x00) \ -X(BaseAddressx, 0x01) \ -X(StartxEndx, 0x02) \ -X(StartxLength, 0x03) \ -X(OffsetPair, 0x04) \ -X(BaseAddress, 0x05) \ -X(StartEnd, 0x06) \ -X(StartLength, 0x07) - -typedef U8 DW_RLE; -typedef enum DW_RLE_Enum -{ -#define X(_N,_ID) DW_RLE_##_N = _ID, - DW_RngListEntryKind -#undef X -} DW_RLE_Enum; - -#define DW_LocListEntry_XList \ -X(EndOfList, 0x00) \ -X(BaseAddressx, 0x01) \ -X(StartxEndx, 0x02) \ -X(StartxLength, 0x03) \ -X(OffsetPair, 0x04) \ -X(DefaultLocation, 0x05) \ -X(BaseAddress, 0x06) \ -X(StartEnd, 0x07) \ -X(StartLength, 0x08) - -#define DW_LocListEntry_GNU_XList(X) \ -X(GNU_ViewPair, 0x9) - -typedef U8 DW_LLE; -typedef enum DW_LLE_Enum -{ -#define X(_N,_ID) DW_LLE_##_N = _ID, - DW_LocListEntry_XList -#undef X -} DW_LLEEnum; - -#define DW_AddrClass_XList \ -X(None, 0) \ -X(Near16, 1) \ -X(Far16, 2) \ -X(Huge16, 3) \ -X(Near32, 4) \ -X(Far32, 5) - -typedef U64 DW_AddrClass; -typedef enum DW_AddrClassEnum -{ -#define X(_N, _ID) DW_AddrClassKind_##_N = _ID, - DW_AddrClass_XList -#undef X -} DW_AddrClassEnum; - -#define DW_CompUnitKind_XList \ -X(Reserved, 0) \ -X(Compile, 1) \ -X(Type, 2) \ -X(Partial, 3) \ -X(Skeleton, 4) \ -X(SplitCompile, 5) \ -X(SplitType, 6) - -typedef U8 DW_CompUnitKind; -typedef enum DW_CompUnitKindEnum -{ -#define X(_N, _ID) DW_CompUnitKind_##_N = _ID, - DW_CompUnitKind_XList -#undef X - DW_CompUnitKind_UserLo = 0x80, - DW_CompUnitKind_UserHi = 0xff -} DW_CompUnitKindEnum; - -#define DW_LNCT_XList \ -X(Path, 0x1) \ -X(DirectoryIndex, 0x2) \ -X(TimeStamp, 0x3) \ -X(Size, 0x4) \ -X(MD5, 0x5) \ -X(LLVM_Source, 0x2001) - -typedef U64 DW_LNCT; -typedef enum DW_LNCTEnum -{ -#define X(_N, _ID) DW_LNCT_##_N = _ID, - DW_LNCT_XList -#undef X - DW_LNCT_UserLo = 0x2000, - DW_LNCT_UserHi = 0x3fff -} DW_LNCTEnum; - //////////////////////////////// -// CFA - -typedef enum -{ - DW_CFA_OperandType_Null, - DW_CFA_OperandType_Value, - DW_CFA_OperandType_Register, - DW_CFA_OperandType_Expression, -} DW_CFA_OperandType; - -// (opcode name, opcode id, operand count, operand types) -#define DW_CFA_Kind_XList \ -X(Nop, 0x0) \ -X(SetLoc, 0x1, DW_CFA_OperandType_Value) \ -X(AdvanceLoc1, 0x2, DW_CFA_OperandType_Value) \ -X(AdvanceLoc2, 0x3, DW_CFA_OperandType_Value) \ -X(AdvanceLoc4, 0x4, DW_CFA_OperandType_Value) \ -X(OffsetExt, 0x5, DW_CFA_OperandType_Register, DW_CFA_OperandType_Value) \ -X(RestoreExt, 0x6) \ -X(Undefined, 0x7, DW_CFA_OperandType_Register) \ -X(SameValue, 0x8, DW_CFA_OperandType_Register) \ -X(Register, 0x9, DW_CFA_OperandType_Register) \ -X(RememberState, 0xa) \ -X(RestoreState, 0xb) \ -X(DefCfa, 0xc, DW_CFA_OperandType_Register, DW_CFA_OperandType_Value) \ -X(DefCfaRegister, 0xd, DW_CFA_OperandType_Register) \ -X(DefCfaOffset, 0xe, DW_CFA_OperandType_Value) \ -X(DefCfaExpr, 0xf, DW_CFA_OperandType_Expression) \ -X(Expr, 0x10, DW_CFA_OperandType_Expression) \ -X(OffsetExtSf, 0x11, DW_CFA_OperandType_Register, DW_CFA_OperandType_Value) \ -X(DefCfaSf, 0x12, DW_CFA_OperandType_Register, DW_CFA_OperandType_Value) \ -X(DefCfaOffsetSf, 0x13, DW_CFA_OperandType_Value) \ -X(ValOffset, 0x14, DW_CFA_OperandType_Register, DW_CFA_OperandType_Value) \ -X(ValOffsetSf, 0x15, DW_CFA_OperandType_Register, DW_CFA_OperandType_Value) \ -X(ValExpr, 0x16, DW_CFA_OperandType_Register, DW_CFA_OperandType_Expression) \ -X(AdvanceLoc, 0x40, DW_CFA_OperandType_Value) \ -X(Offset, 0x80, DW_CFA_OperandType_Register, DW_CFA_OperandType_Value) \ -X(Restore, 0xc0, DW_CFA_OperandType_Register) - -#define DW_CFA_OperandMax 2 -#define DW_CFA_Mask_OpcodeHi 0xc0 -#define DW_CFA_Mask_Operand 0x3f - -typedef U8 DW_CFA_Opcode; -typedef enum DW_CFA_Enum -{ -#define X(_N, _ID, ...) DW_CFA_##_N = _ID, - DW_CFA_Kind_XList -#undef X -} DW_CFA_Enum; - -//////////////////////////////// -// Expression Opcodes - -typedef enum DW_ExprOperandType -{ - DW_ExprOperandType_Null, - DW_ExprOperandType_U8, - DW_ExprOperandType_U16, - DW_ExprOperandType_U32, - DW_ExprOperandType_U64, - DW_ExprOperandType_S8, - DW_ExprOperandType_S16, - DW_ExprOperandType_S32, - DW_ExprOperandType_S64, - DW_ExprOperandType_ULEB128, - DW_ExprOperandType_SLEB128, - DW_ExprOperandType_Addr, - DW_ExprOperandType_Block, - DW_ExprOperandType_DwarfUInt, -} -DW_ExprOperandType; - -// (opcode name, opcode id, operand count, pop count, push count, operand0 type, operand1 type) -#define DW_Expr_V3_XList \ -X(Null, 0x00, 0, 0, 0, Null, Null) \ -X(Addr, 0x03, 1, 0, 1, Addr, Null) \ -X(Deref, 0x06, 0, 1, 1, Null, Null) \ -X(Const1U, 0x08, 1, 0, 1, U8, Null) \ -X(Const1S, 0x09, 1, 0, 1, S8, Null) \ -X(Const2U, 0x0a, 1, 0, 1, U16, Null) \ -X(Const2S, 0x0b, 1, 0, 1, S16, Null) \ -X(Const4U, 0x0c, 1, 0, 1, U32, Null) \ -X(Const4S, 0x0d, 1, 0, 1, S32, Null) \ -X(Const8U, 0x0e, 1, 0, 1, U64, Null) \ -X(Const8S, 0x0f, 1, 0, 1, S64, Null) \ -X(ConstU, 0x10, 1, 0, 1, ULEB128, Null) \ -X(ConstS, 0x11, 1, 0, 1, SLEB128, Null) \ -X(Dup, 0x12, 0, 0, 1, Null, Null) \ -X(Drop, 0x13, 0, 1, 0, Null, Null) \ -X(Over, 0x14, 0, 0, 1, Null, Null) \ -X(Pick, 0x15, 1, 0, 1, U8, Null) \ -X(Swap, 0x16, 0, 0, 0, Null, Null) \ -X(Rot, 0x17, 0, 0, 0, Null, Null) \ -X(XDeref, 0x18, 0, 2, 1, Null, Null) \ -X(Abs, 0x19, 0, 1, 1, Null, Null) \ -X(And, 0x1a, 0, 2, 1, Null, Null) \ -X(Div, 0x1b, 0, 2, 1, Null, Null) \ -X(Minus, 0x1c, 0, 2, 1, Null, Null) \ -X(Mod, 0x1d, 0, 2, 1, Null, Null) \ -X(Mul, 0x1e, 0, 2, 1, Null, Null) \ -X(Neg, 0x1f, 0, 1, 1, Null, Null) \ -X(Not, 0x20, 0, 1, 1, Null, Null) \ -X(Or, 0x21, 0, 2, 1, Null, Null) \ -X(Plus, 0x22, 0, 2, 1, Null, Null) \ -X(PlusUConst, 0x23, 1, 1, 1, ULEB128, Null) \ -X(Shl, 0x24, 0, 2, 1, Null, Null) \ -X(Shr, 0x25, 0, 2, 1, Null, Null) \ -X(Shra, 0x26, 0, 2, 1, Null, Null) \ -X(Xor, 0x27, 0, 2, 1, Null, Null) \ -X(Bra, 0x28, 1, 1, 0, S16, Null) \ -X(Eq, 0x29, 0, 2, 1, Null, Null) \ -X(Ge, 0x2a, 0, 2, 1, Null, Null) \ -X(Gt, 0x2b, 0, 2, 1, Null, Null) \ -X(Le, 0x2c, 0, 2, 1, Null, Null) \ -X(Lt, 0x2d, 0, 2, 1, Null, Null) \ -X(Ne, 0x2e, 0, 2, 1, Null, Null) \ -X(Skip, 0x2f, 1, 0, 0, S16, Null) \ -X(Lit0, 0x30, 0, 0, 0, Null, Null) \ -X(Lit1, 0x31, 0, 0, 0, Null, Null) \ -X(Lit2, 0x32, 0, 0, 0, Null, Null) \ -X(Lit3, 0x33, 0, 0, 0, Null, Null) \ -X(Lit4, 0x34, 0, 0, 0, Null, Null) \ -X(Lit5, 0x35, 0, 0, 0, Null, Null) \ -X(Lit6, 0x36, 0, 0, 0, Null, Null) \ -X(Lit7, 0x37, 0, 0, 0, Null, Null) \ -X(Lit8, 0x38, 0, 0, 0, Null, Null) \ -X(Lit9, 0x39, 0, 0, 0, Null, Null) \ -X(Lit10, 0x3a, 0, 0, 0, Null, Null) \ -X(Lit11, 0x3b, 0, 0, 0, Null, Null) \ -X(Lit12, 0x3c, 0, 0, 0, Null, Null) \ -X(Lit13, 0x3d, 0, 0, 0, Null, Null) \ -X(Lit14, 0x3e, 0, 0, 0, Null, Null) \ -X(Lit15, 0x3f, 0, 0, 0, Null, Null) \ -X(Lit16, 0x40, 0, 0, 0, Null, Null) \ -X(Lit17, 0x41, 0, 0, 0, Null, Null) \ -X(Lit18, 0x42, 0, 0, 0, Null, Null) \ -X(Lit19, 0x43, 0, 0, 0, Null, Null) \ -X(Lit20, 0x44, 0, 0, 0, Null, Null) \ -X(Lit21, 0x45, 0, 0, 0, Null, Null) \ -X(Lit22, 0x46, 0, 0, 0, Null, Null) \ -X(Lit23, 0x47, 0, 0, 0, Null, Null) \ -X(Lit24, 0x48, 0, 0, 0, Null, Null) \ -X(Lit25, 0x49, 0, 0, 0, Null, Null) \ -X(Lit26, 0x4a, 0, 0, 0, Null, Null) \ -X(Lit27, 0x4b, 0, 0, 0, Null, Null) \ -X(Lit28, 0x4c, 0, 0, 0, Null, Null) \ -X(Lit29, 0x4d, 0, 0, 0, Null, Null) \ -X(Lit30, 0x4e, 0, 0, 0, Null, Null) \ -X(Lit31, 0x4f, 0, 0, 0, Null, Null) \ -X(Reg0, 0x50, 0, 0, 1, Null, Null) \ -X(Reg1, 0x51, 0, 0, 1, Null, Null) \ -X(Reg2, 0x52, 0, 0, 1, Null, Null) \ -X(Reg3, 0x53, 0, 0, 1, Null, Null) \ -X(Reg4, 0x54, 0, 0, 1, Null, Null) \ -X(Reg5, 0x55, 0, 0, 1, Null, Null) \ -X(Reg6, 0x56, 0, 0, 1, Null, Null) \ -X(Reg7, 0x57, 0, 0, 1, Null, Null) \ -X(Reg8, 0x58, 0, 0, 1, Null, Null) \ -X(Reg9, 0x59, 0, 0, 1, Null, Null) \ -X(Reg10, 0x5a, 0, 0, 1, Null, Null) \ -X(Reg11, 0x5b, 0, 0, 1, Null, Null) \ -X(Reg12, 0x5c, 0, 0, 1, Null, Null) \ -X(Reg13, 0x5d, 0, 0, 1, Null, Null) \ -X(Reg14, 0x5e, 0, 0, 1, Null, Null) \ -X(Reg15, 0x5f, 0, 0, 1, Null, Null) \ -X(Reg16, 0x60, 0, 0, 1, Null, Null) \ -X(Reg17, 0x61, 0, 0, 1, Null, Null) \ -X(Reg18, 0x62, 0, 0, 1, Null, Null) \ -X(Reg19, 0x63, 0, 0, 1, Null, Null) \ -X(Reg20, 0x64, 0, 0, 1, Null, Null) \ -X(Reg21, 0x65, 0, 0, 1, Null, Null) \ -X(Reg22, 0x66, 0, 0, 1, Null, Null) \ -X(Reg23, 0x67, 0, 0, 1, Null, Null) \ -X(Reg24, 0x68, 0, 0, 1, Null, Null) \ -X(Reg25, 0x69, 0, 0, 1, Null, Null) \ -X(Reg26, 0x6a, 0, 0, 1, Null, Null) \ -X(Reg27, 0x6b, 0, 0, 1, Null, Null) \ -X(Reg28, 0x6c, 0, 0, 1, Null, Null) \ -X(Reg29, 0x6d, 0, 0, 1, Null, Null) \ -X(Reg30, 0x6e, 0, 0, 1, Null, Null) \ -X(Reg31, 0x6f, 0, 0, 1, Null, Null) \ -X(BReg0, 0x70, 1, 0, 1, SLEB128, Null) \ -X(BReg1, 0x71, 1, 0, 1, SLEB128, Null) \ -X(BReg2, 0x72, 1, 0, 1, SLEB128, Null) \ -X(BReg3, 0x73, 1, 0, 1, SLEB128, Null) \ -X(BReg4, 0x74, 1, 0, 1, SLEB128, Null) \ -X(BReg5, 0x75, 1, 0, 1, SLEB128, Null) \ -X(BReg6, 0x76, 1, 0, 1, SLEB128, Null) \ -X(BReg7, 0x77, 1, 0, 1, SLEB128, Null) \ -X(BReg8, 0x78, 1, 0, 1, SLEB128, Null) \ -X(BReg9, 0x79, 1, 0, 1, SLEB128, Null) \ -X(BReg10, 0x7a, 1, 0, 1, SLEB128, Null) \ -X(BReg11, 0x7b, 1, 0, 1, SLEB128, Null) \ -X(BReg12, 0x7c, 1, 0, 1, SLEB128, Null) \ -X(BReg13, 0x7d, 1, 0, 1, SLEB128, Null) \ -X(BReg14, 0x7e, 1, 0, 1, SLEB128, Null) \ -X(BReg15, 0x7f, 1, 0, 1, SLEB128, Null) \ -X(BReg16, 0x80, 1, 0, 1, SLEB128, Null) \ -X(BReg17, 0x81, 1, 0, 1, SLEB128, Null) \ -X(BReg18, 0x82, 1, 0, 1, SLEB128, Null) \ -X(BReg19, 0x83, 1, 0, 1, SLEB128, Null) \ -X(BReg20, 0x84, 1, 0, 1, SLEB128, Null) \ -X(BReg21, 0x85, 1, 0, 1, SLEB128, Null) \ -X(BReg22, 0x86, 1, 0, 1, SLEB128, Null) \ -X(BReg23, 0x87, 1, 0, 1, SLEB128, Null) \ -X(BReg24, 0x88, 1, 0, 1, SLEB128, Null) \ -X(BReg25, 0x89, 1, 0, 1, SLEB128, Null) \ -X(BReg26, 0x8a, 1, 0, 1, SLEB128, Null) \ -X(BReg27, 0x8b, 1, 0, 1, SLEB128, Null) \ -X(BReg28, 0x8c, 1, 0, 1, SLEB128, Null) \ -X(BReg29, 0x8d, 1, 0, 1, SLEB128, Null) \ -X(BReg30, 0x8e, 1, 0, 1, SLEB128, Null) \ -X(BReg31, 0x8f, 1, 0, 1, SLEB128, Null) \ -X(RegX, 0x90, 1, 0, 1, ULEB128, Null) \ -X(FBReg, 0x91, 1, 0, 1, SLEB128, Null) \ -X(BRegX, 0x92, 2, 0, 1, ULEB128, SLEB128) \ -X(Piece, 0x93, 1, 0, 0, ULEB128, Null) \ -X(DerefSize, 0x94, 1, 1, 1, U8, Null) \ -X(XDerefSize, 0x95, 1, 2, 1, U8, Null) \ -X(Nop, 0x96, 0, 0, 0, Null, Null) \ -X(PushObjectAddress, 0x97, 0, 0, 1, Null, Null) \ -X(Call2, 0x98, 1, 0, 0, U16, Null) \ -X(Call4, 0x99, 1, 0, 0, U32, Null) \ -X(CallRef, 0x9a, 1, 0, 0, DwarfUInt,Null) \ -X(FormTlsAddress, 0x9b, 0, 0, 1, Null, Null) \ -X(CallFrameCfa, 0x9c, 0, 0, 1, Null, Null) \ -X(BitPiece, 0x9d, 2, 0, 0, ULEB128, ULEB128) - -#define DW_Expr_V4_XList \ -X(ImplicitValue, 0x9e, 2, 0, 1, Block, Null) \ -X(StackValue, 0x9f, 0, 0, 0, Null, Null) - -#define DW_Expr_V5_XList \ -X(ImplicitPointer, 0xa0, 2, 0, 1, DwarfUInt, SLEB128) \ -X(Addrx, 0xa1, 1, 0, 1, ULEB128, Null) \ -X(Constx, 0xa2, 1, 0, 1, ULEB128, Null) \ -X(EntryValue, 0xa3, 1, 0, 0, Block, Null) \ -X(ConstType, 0xa4, 2, 0, 1, ULEB128, Block) \ -X(RegvalType, 0xa5, 2, 0, 1, ULEB128, ULEB128) \ -X(DerefType, 0xa6, 2, 1, 1, U8, ULEB128) \ -X(XDerefType, 0xa7, 2, 2, 1, U8, ULEB128) \ -X(Convert, 0xa8, 1, 1, 1, ULEB128, Null) \ -X(Reinterpret, 0xa9, 1, 1, 1, ULEB128, Null) - -#define DW_Expr_GNU_XList \ -X(GNU_PushTlsAddress, 0xe0, 0, 0, 1, Null, Null) \ -X(GNU_UnInit, 0xf0, 0, 0, 0, Null, Null) \ -X(GNU_ImplicitPointer, 0xf2, 2, 0, 1, DwarfUInt, SLEB128) \ -X(GNU_EntryValue, 0xf3, 1, 0, 0, Block, Null) \ -X(GNU_ConstType, 0xf4, 2, 0, 1, ULEB128, Block) \ -X(GNU_RegvalType, 0xf5, 2, 0, 1, ULEB128, ULEB128) \ -X(GNU_DerefType, 0xf6, 2, 1, 1, U8, ULEB128) \ -X(GNU_Convert, 0xf7, 1, 1, 1, ULEB128, Null) \ -X(GNU_ParameterRef, 0xfa, 1, 0, 0, U32, Null) \ -X(GNU_AddrIndex, 0xfb, 0, 0, 1, Null, Null) \ -X(GNU_ConstIndex, 0xfc, 1, 0, 1, ULEB128, Null) - -typedef U8 DW_ExprOp; -typedef enum DW_ExprOpEnum -{ -#define X(_N, _ID, _OPER_COUNT, _POP_COUNT, _PUSH_COUNT, ...) DW_ExprOp_##_N = _ID, - DW_Expr_V3_XList - DW_Expr_V4_XList - DW_Expr_V5_XList - DW_Expr_GNU_XList -#undef X -} DW_ExprOpEnum; - -//- Regs - -#define DW_Regs_X64_XList \ -X(Rax, 0, rax, 0, 8) \ -X(Rdx, 1, rdx, 0, 8) \ -X(Rcx, 2, rcx, 0, 8) \ -X(Rbx, 3, rbx, 0, 8) \ -X(Rsi, 4, rsi, 0, 8) \ -X(Rdi, 5, rdi, 0, 8) \ -X(Rbp, 6, rbp, 0, 8) \ -X(Rsp, 7, rsp, 0, 8) \ -X(R8, 8, r8, 0, 8) \ -X(R9, 9, r9, 0, 8) \ -X(R10, 10, r10, 0, 8) \ -X(R11, 11, r11, 0, 8) \ -X(R12, 12, r12, 0, 8) \ -X(R13, 13, r13, 0, 8) \ -X(R14, 14, r14, 0, 8) \ -X(R15, 15, r15, 0, 8) \ -X(Rip, 16, rip, 0, 8) \ -X(Xmm0, 17, zmm0, 0, 16) \ -X(Xmm1, 18, zmm1, 0, 16) \ -X(Xmm2, 19, zmm2, 0, 16) \ -X(Xmm3, 20, zmm3, 0, 16) \ -X(Xmm4, 21, zmm4, 0, 16) \ -X(Xmm5, 22, zmm5, 0, 16) \ -X(Xmm6, 23, zmm6, 0, 16) \ -X(Xmm7, 24, zmm7, 0, 16) \ -X(Xmm8, 25, zmm8, 0, 16) \ -X(Xmm9, 26, zmm9, 0, 16) \ -X(Xmm10, 27, zmm10, 0, 16) \ -X(Xmm11, 28, zmm11, 0, 16) \ -X(Xmm12, 29, zmm12, 0, 16) \ -X(Xmm13, 30, zmm13, 0, 16) \ -X(Xmm14, 31, zmm14, 0, 16) \ -X(Xmm15, 32, zmm15, 0, 16) \ -X(Xmm16, 67, zmm16, 0, 16) \ -X(Xmm17, 68, zmm17, 0, 16) \ -X(Xmm18, 69, zmm18, 0, 16) \ -X(Xmm19, 70, zmm19, 0, 16) \ -X(Xmm20, 71, zmm20, 0, 16) \ -X(Xmm21, 72, zmm21, 0, 16) \ -X(Xmm22, 73, zmm22, 0, 16) \ -X(Xmm23, 74, zmm23, 0, 16) \ -X(Xmm24, 75, zmm24, 0, 16) \ -X(Xmm25, 76, zmm25, 0, 16) \ -X(Xmm26, 77, zmm26, 0, 16) \ -X(Xmm27, 78, zmm27, 0, 16) \ -X(Xmm28, 79, zmm28, 0, 16) \ -X(Xmm29, 80, zmm29, 0, 16) \ -X(Xmm30, 81, zmm30, 0, 16) \ -X(Xmm31, 82, zmm31, 0, 16) \ -X(St0, 33, st0, 0, 10) \ -X(St1, 34, st1, 0, 10) \ -X(St2, 35, st2, 0, 10) \ -X(St3, 36, st3, 0, 10) \ -X(St4, 37, st4, 0, 10) \ -X(St5, 38, st5, 0, 10) \ -X(St6, 39, st6, 0, 10) \ -X(St7, 40, st7, 0, 10) \ -X(Mm0, 41, st0, 0, 8) \ -X(Mm1, 42, st1, 0, 8) \ -X(Mm2, 43, st2, 0, 8) \ -X(Mm3, 44, st3, 0, 8) \ -X(Mm4, 45, st4, 0, 8) \ -X(Mm5, 46, st5, 0, 8) \ -X(Mm6, 47, st6, 0, 8) \ -X(Mm7, 48, st7, 0, 8) \ -X(Rflags, 49, rflags, 0, 4) \ -X(Es, 50, es, 0, 2) \ -X(Cs, 51, cs, 0, 2) \ -X(Ss, 52, ss, 0, 2) \ -X(Ds, 53, ds, 0, 2) \ -X(Fs, 54, fs, 0, 2) \ -X(Gs, 55, gs, 0, 2) \ -X(FsBase, 58, fsbase, 0, 8) \ -X(GsBase, 59, gsbase, 0, 8) -// privileged registers: -// X(Tr, 62, nil, 0, 0) \ -// X(Ldtr, 63, nil, 0, 0) - -typedef U32 DW_Reg; - -typedef DW_Reg DW_RegX64; -typedef enum DW_RegX64Enum -{ -#define X(_N,_ID,...) DW_RegX64_##_N = _ID, - DW_Regs_X64_XList -#undef X - DW_RegX64_Last -} DW_RegX64Enum; - -//////////////////////////////// -//~ rjf: Generated Code +//~ rjf: Includes #include "dwarf/generated/dwarf.meta.h" +#if defined(X64_H) +# include "dwarf/x64/dwarf_x64.h" +#endif //////////////////////////////// -// Speced Encodings +//~ rjf: Helper Types -internal DW_AttribClass dw_attrib_class_from_attrib_v2(DW_AttribKind k); -internal DW_AttribClass dw_attrib_class_from_attrib_v3(DW_AttribKind k); -internal DW_AttribClass dw_attrib_class_from_attrib_v4(DW_AttribKind k); -internal DW_AttribClass dw_attrib_class_from_attrib_v5(DW_AttribKind k); +typedef struct DW_ExprOpInfo DW_ExprOpInfo; +struct DW_ExprOpInfo +{ + U8 push_count; + U8 pop_count; + U8 operand_count; + B8 valid_for_cfa_exprs; + DW_ExprOperandKind operand_kinds[2]; + String8 name; + DW_Version version; +}; +global read_only DW_ExprOpInfo dw_expr_op_info_nil = {0}; //////////////////////////////// -// Extensions +//~ rjf: X-List Helpers -internal DW_AttribClass dw_attrib_class_from_attrib_gnu (DW_AttribKind k); -internal DW_AttribClass dw_attrib_class_from_attrib_llvm (DW_AttribKind k); -internal DW_AttribClass dw_attrib_class_from_attrib_apple(DW_AttribKind k); -internal DW_AttribClass dw_attrib_class_from_attrib_mips (DW_AttribKind k); -internal DW_AttribClass dw_attrib_class_from_attrib(DW_Version ver, DW_Ext ext, DW_AttribKind v); +//- rjf: format address sizes +internal U64 dw_addr_size_from_format(DW_Format fmt); + +//- rjf: attribute classes +internal DW_AttribClass dw_attrib_class_from_kind(DW_Version version, DW_ExtFlags exts, DW_AttribKind k); +internal DW_AttribClass dw_attrib_class_from_form_kind(DW_Version version, DW_ExtFlags exts, DW_FormKind k); + +//- rjf: expr op metadata +internal DW_ExprOpInfo *dw_info_from_expr_op(DW_Version version, DW_ExtFlags exts, DW_ExprOp op); + +//- rjf: enum -> string +internal String8 dw_string_from_format(DW_Format fmt); +internal String8 dw_string_from_tag_kind(DW_Version version, DW_ExtFlags exts, DW_TagKind k); +internal String8 dw_string_from_attrib_kind(DW_Version version, DW_ExtFlags exts, DW_AttribKind k); +internal String8 dw_string_from_form_kind(DW_Version version, DW_ExtFlags exts, DW_FormKind k); +internal String8 dw_string_from_language(DW_Language k); +internal String8 dw_string_from_comp_unit_kind(DW_CompUnitKind k); +internal String8 dw_string_from_inl_kind(DW_InlKind k); +internal String8 dw_string_from_access_kind(DW_AccessKind k); +internal String8 dw_string_from_calling_convention_kind(DW_CallingConventionKind k); +internal String8 dw_string_from_ate(DW_ATE k); +internal String8 dw_string_from_visibility_kind(DW_VisibilityKind k); +internal String8 dw_string_from_std_opcode(DW_StdOpcode opcode); +internal String8 dw_string_from_ext_opcode(DW_ExtOpcode opcode); +internal String8 dw_string_from_lle(DW_LLE lle); +internal String8 dw_string_from_rle(DW_RLE rle); +internal String8 dw_regular_name_from_section_kind(DW_SectionKind k); +internal String8 dw_macho_name_from_section_kind(DW_SectionKind k); +internal String8 dw_dwo_name_from_section_kind(DW_SectionKind k); +#define dw_string_from_expr_op(version, exts, op) (dw_info_from_expr_op((version), (exts), (op))->name) + +//- rjf: architecture info +internal U64 dw_reg_count_from_arch(Arch arch); //////////////////////////////// -// Form Class Encodings - -internal DW_AttribClass dw_attrib_class_from_form_kind(DW_Version ver, DW_FormKind k); -internal B32 dw_are_attrib_class_and_form_kind_compatible(DW_Version ver, DW_AttribClass attrib_class, DW_FormKind form_kind); -internal B32 dw_is_form_kind_ref(DW_Version ver, DW_Ext ext, DW_FormKind form_kind); +//~ TODO(rjf): OLD vvvvvvvvvvvvvvvvvvv +#if 0 //////////////////////////////// // xlist helpers // regs -internal U64 dw_reg_size_from_code_x64(DW_Reg reg_code); -internal U64 dw_reg_pos_from_code_x64(DW_Reg reg_code); internal U64 dw_reg_size_from_code(Arch arch, DW_Reg reg_code); internal U64 dw_reg_pos_from_code(Arch arch, DW_Reg reg_code); internal U64 dw_reg_count_from_arch(Arch arch); internal U64 dw_reg_max_size_from_arch(Arch arch); -internal U64 dw_sp_from_arch(Arch arch); internal U64 dw_size_from_format(DW_Format format); -// attrib form -> value class -internal DW_AttribClass dw_pick_attrib_value_class(DW_Version ver, DW_Ext ext, B32 relaxed, DW_AttribKind attrib, DW_FormKind form_kind); - -// lang -internal U64 dw_pick_default_lower_bound(DW_Language lang); - -// expr -internal U64 dw_operand_count_from_expr_op(DW_ExprOp op); -internal U64 dw_pop_count_from_expr_op(DW_ExprOp op); -internal U64 dw_push_count_from_expr_op(DW_ExprOp op); -internal DW_ExprOperandType * dw_operand_types_from_expr_opcode(DW_ExprOp op); - // CFA internal U64 dw_operand_count_from_cfa_opcode(DW_CFA_Opcode opcode); internal B32 dw_is_cfa_expr_opcode_invalid(DW_ExprOp opcode); @@ -1802,11 +88,6 @@ internal DW_CFA_OperandType * dw_operand_types_from_cfa_op(DW_CFA_Opcode opcode) //////////////////////////////// //~ rjf: String <=> Enum -internal String8 dw_string_from_format(DW_Format format); -internal String8 dw_string_from_expr_op(Arena *arena, DW_Version ver, DW_Ext ext, DW_ExprOp op); -internal String8 dw_string_from_tag_kind(Arena *arena, DW_TagKind kind); -internal String8 dw_string_from_attrib_kind(Arena *arena, DW_Version ver, DW_Ext ext, DW_AttribKind kind); -internal String8 dw_string_from_form_kind(Arena *arena, DW_Version ver, DW_FormKind kind); internal String8 dw_string_from_language(Arena *arena, DW_Language kind); internal String8 dw_string_from_comp_unit_kind(Arena *arena, DW_CompUnitKind kind); internal String8 dw_string_from_inl(Arena *arena, DW_InlKind kind); @@ -1824,31 +105,6 @@ internal String8 dw_string_from_cfa_opcode(DW_CFA_Opcode opcode); internal String8 dw_name_string_from_section_kind(DW_SectionKind k); internal String8 dw_mach_name_string_from_section_kind(DW_SectionKind k); internal String8 dw_dwo_name_string_from_section_kind (DW_SectionKind k); - -//////////////////////////////// -// Serializers - -internal U64 dw_write_to_buffer_uleb128(U8 buffer[10], U64 v); -internal U64 dw_write_to_buffer_sleb128(U8 buffer[10], U64 v); - -internal String8 dw_write_uleb128(Arena *arena, U64 v); -internal String8 dw_write_sleb128(Arena *arena, S64 v); -internal U64 dw_size_from_uleb128(U64 v); -internal U64 dw_size_from_sleb128(S64 v); - -internal void * dw_serial_push_length(Arena *arena, String8List *srl, DW_Format format, U64 length); -internal void * dw_serial_push_uint(Arena *arena, String8List *srl, DW_Format format, U64 v); -internal void * dw_serial_push_uleb128(Arena *arena, String8List *srl, U64 v); -internal void * dw_serial_push_sleb128(Arena *arena, String8List *srl, S64 v); - -//////////////////////////////// -// Form - -internal B32 dw_form_match(DW_Form a, DW_Form b); - -//////////////////////////////// -// Line - -internal U64 dw_length_from_std_opcode(DW_StdOpcode opcode); +#endif #endif // DWARF_H diff --git a/src/dwarf/dwarf.mdesk b/src/dwarf/dwarf.mdesk index 73a27850..dff8e513 100644 --- a/src/dwarf/dwarf.mdesk +++ b/src/dwarf/dwarf.mdesk @@ -1,6 +1,1327 @@ // Copyright (c) Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) +//////////////////////////////// +//~ rjf: Version + +@table(name) +DW_VersionTable: +{ + {Null} + {1} + {2} + {3} + {4} + {5} +} + +@enum(U16) DW_Version: +{ + @expand(DW_VersionTable a) `$(a.name)`; + `Last = DW_Version_5`; +} + +@table(name code) +DW_ExtTable: +{ + {GNU} + {LLVM} + {Apple} + {MIPS} +} + +@enum DW_Ext: +{ + `Null`, + @expand(DW_ExtTable a) `$(a.name)` +} + +@enum(U32) DW_ExtFlags: +{ + @expand(DW_ExtTable a) `$(a.name) = (1<foff, header->vsize ? header->foff + header->vsize : header->foff + header->fsize); - String8 name = coff_name_from_section_header(string_table, header); - - DW_SectionKind s = dw_section_kind_from_string(name); - B32 is_dwo = 0; - if (s == DW_Section_Null) { - s = dw_section_dwo_kind_from_string(name); - is_dwo = 1; - } - - if (s != DW_Section_Null) { - if (sect_status[s]) { - Assert(!"too many debug sections with identical name, picking first"); - } else { - sect_status[s] = 1; - DW_Section *d = &input.sec[s]; - d->name = push_str8_copy(arena, name); - d->data = str8_substr(raw_image, raw_data_range); - d->is_dwo = is_dwo; - } - } - } - - return input; -} diff --git a/src/dwarf/dwarf_dump.c b/src/dwarf/dwarf_dump.c index 69b83b23..9b6d6481 100644 --- a/src/dwarf/dwarf_dump.c +++ b/src/dwarf/dwarf_dump.c @@ -1185,7 +1185,7 @@ DW_PRINTER(dw_print_frame) for (U64 cursor = 0, desc_size; cursor < debug_frame.size; cursor += desc_size) { DW_DescriptorEntry desc = {0}; desc_size = dw_parse_descriptor_entry_header(debug_frame, cursor, &desc); - if (desc.type == DW_DescriptorEntryType_CIE) { + if (desc.type == DW_DescriptorEntryKind_CIE) { String8 raw_cie = str8_substr(debug_frame, desc.entry_range); U64 restore_pos = arena_pos(scratch.arena); DW_CIE *cie = push_array(scratch.arena, DW_CIE, 1); @@ -1202,8 +1202,8 @@ DW_PRINTER(dw_print_frame) desc_size = dw_parse_descriptor_entry_header(debug_frame, cursor, &desc); String8 raw_desc = str8_substr(debug_frame, desc.entry_range); switch (desc.type) { - case DW_DescriptorEntryType_Null: {} break; - case DW_DescriptorEntryType_CIE: { + case DW_DescriptorEntryKind_Null: {} break; + case DW_DescriptorEntryKind_CIE: { DW_CIE cie = {0}; if (dw_parse_cie(raw_desc, desc.format, arch, &cie)) { String8List init_insts_str_list = dw_string_list_from_cfi_program(scratch.arena, 0, arch, DW_Version_5, DW_Ext_All, cie.format, 0, &cie, dw_decode_ptr_debug_frame, &cie, cie.insts); @@ -1229,7 +1229,7 @@ DW_PRINTER(dw_print_frame) dw_printf("ERROR: unable to parse CIE @ %I64x\n", desc.entry_range.min); } } break; - case DW_DescriptorEntryType_FDE: { + case DW_DescriptorEntryKind_FDE: { DW_DescriptorEntry cie_desc = {0}; U64 cie_desc_size = dw_parse_descriptor_entry_header(debug_frame, desc.cie_pointer, &cie_desc); String8 cie_data = str8_substr(debug_frame, cie_desc.entry_range); diff --git a/src/dwarf/dwarf_dump_2.c b/src/dwarf/dwarf_dump_2.c new file mode 100644 index 00000000..04f7669c --- /dev/null +++ b/src/dwarf/dwarf_dump_2.c @@ -0,0 +1,718 @@ +// Copyright (c) Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +//////////////////////////////// +//~ rjf: Helpers + +internal String8List +dw_dump_list_from_expr(Arena *arena, String8 data, Arch arch, DW2_ParseCtx *ctx) +{ + String8List result = {0}; + Temp scratch = scratch_begin(&arena, 1); + ARCH_Info *arch_info = arch_info_from_arch(arch); + for(U64 off = 0; off < data.size;) + { + U64 start_off = 0; + + // rjf: read next op + U8 op = 0; + off += str8_deserial_read_struct(data, off, &op); + + // rjf: unpack op / operands + String8 operand_string = {0}; + U64 size_param = 0; + B32 is_signed = 0; + switch(op) + { + case DW_ExprOp_Lit0: case DW_ExprOp_Lit1: case DW_ExprOp_Lit2: + case DW_ExprOp_Lit3: case DW_ExprOp_Lit4: case DW_ExprOp_Lit5: + case DW_ExprOp_Lit6: case DW_ExprOp_Lit7: case DW_ExprOp_Lit8: + case DW_ExprOp_Lit9: case DW_ExprOp_Lit10: case DW_ExprOp_Lit11: + case DW_ExprOp_Lit12: case DW_ExprOp_Lit13: case DW_ExprOp_Lit14: + case DW_ExprOp_Lit15: case DW_ExprOp_Lit16: case DW_ExprOp_Lit17: + case DW_ExprOp_Lit18: case DW_ExprOp_Lit19: case DW_ExprOp_Lit20: + case DW_ExprOp_Lit21: case DW_ExprOp_Lit22: case DW_ExprOp_Lit23: + case DW_ExprOp_Lit24: case DW_ExprOp_Lit25: case DW_ExprOp_Lit26: + case DW_ExprOp_Lit27: case DW_ExprOp_Lit28: case DW_ExprOp_Lit29: + case DW_ExprOp_Lit30: case DW_ExprOp_Lit31: + { + U64 x = op - DW_ExprOp_Lit0; + operand_string = str8f(scratch.arena, "%I64u", x); + }break; + + case DW_ExprOp_Const1U:size_param = 1; goto const_n; + case DW_ExprOp_Const2U:size_param = 2; goto const_n; + case DW_ExprOp_Const4U:size_param = 4; goto const_n; + case DW_ExprOp_Const8U:size_param = 8; goto const_n; + case DW_ExprOp_Const1S:size_param = 1; is_signed = 1; goto const_n; + case DW_ExprOp_Const2S:size_param = 2; is_signed = 1; goto const_n; + case DW_ExprOp_Const4S:size_param = 4; is_signed = 1; goto const_n; + case DW_ExprOp_Const8S:size_param = 8; is_signed = 1; goto const_n; + const_n: + { + if(is_signed) + { + S64 x = 0; + off += str8_deserial_read(data, off, &x, size_param, 1); + x = extend_sign64(x, size_param); + operand_string = str8f(scratch.arena, "%I64d", x); + } + else + { + U64 x = 0; + off += str8_deserial_read(data, off, &x, size_param, 1); + operand_string = str8f(scratch.arena, "%I64u", x); + } + }break; + + case DW_ExprOp_Addr: + { + U64 addr = 0; + off += str8_deserial_read(data, off, &addr, ctx->addr_size, 1); + operand_string = str8f(scratch.arena, "0x%I64x", addr); + }break; + + case DW_ExprOp_ConstU: + { + U64 x = 0; + off += str8_deserial_read_uleb128(data, off, &x); + operand_string = str8f(scratch.arena, "%I64u", x); + }break; + + case DW_ExprOp_ConstS: + { + S64 x = 0; + off += str8_deserial_read_sleb128(data, off, &x); + operand_string = str8f(scratch.arena, "%I64d", x); + }break; + + case DW_ExprOp_Reg0: case DW_ExprOp_Reg1: case DW_ExprOp_Reg2: + case DW_ExprOp_Reg3: case DW_ExprOp_Reg4: case DW_ExprOp_Reg5: + case DW_ExprOp_Reg6: case DW_ExprOp_Reg7: case DW_ExprOp_Reg8: + case DW_ExprOp_Reg9: case DW_ExprOp_Reg10: case DW_ExprOp_Reg11: + case DW_ExprOp_Reg12: case DW_ExprOp_Reg13: case DW_ExprOp_Reg14: + case DW_ExprOp_Reg15: case DW_ExprOp_Reg16: case DW_ExprOp_Reg17: + case DW_ExprOp_Reg18: case DW_ExprOp_Reg19: case DW_ExprOp_Reg20: + case DW_ExprOp_Reg21: case DW_ExprOp_Reg22: case DW_ExprOp_Reg23: + case DW_ExprOp_Reg24: case DW_ExprOp_Reg25: case DW_ExprOp_Reg26: + case DW_ExprOp_Reg27: case DW_ExprOp_Reg28: case DW_ExprOp_Reg29: + case DW_ExprOp_Reg30: case DW_ExprOp_Reg31: + { + U64 reg_idx = op - DW_ExprOp_Reg0; + ARCH_RegCode regcode = arch_reg_code_from_dw(arch, (DW_RegCode)reg_idx); + operand_string = arch_info->reg_code_name_table[regcode]; + }break; + + case DW_ExprOp_RegX: + { + U64 reg_idx = 0; + off += str8_deserial_read_uleb128(data, off, ®_idx); + ARCH_RegCode regcode = arch_reg_code_from_dw(arch, (DW_RegCode)reg_idx); + operand_string = arch_info->reg_code_name_table[regcode]; + }break; + + case DW_ExprOp_ImplicitValue: + { + U64 value_size = 0; + off += str8_deserial_read_uleb128(data, off, &value_size); + Rng1U64 value_range = rng_1u64(off, off + value_size); + String8 value_data = str8_substr(data, value_range); + off += value_size; + String8List value_strings = numeric_str8_list_from_data(scratch.arena, 16, value_data, 1); + operand_string = str8_list_join(scratch.arena, &value_strings, &(StringJoin){.pre = str8_lit("{ "), .sep = str8_lit(", "), .post = str8_lit(" }")}); + }break; + + case DW_ExprOp_Piece: + { + U64 size = 0; + off += str8_deserial_read_uleb128(data, off, &size); + operand_string = str8f(scratch.arena, "%u", size); + }break; + + case DW_ExprOp_BitPiece: + { + U64 bit_size = 0, bit_off = 0; + off += str8_deserial_read_uleb128(data, off, &bit_size); + off += str8_deserial_read_uleb128(data, off, &bit_off); + operand_string = str8f(scratch.arena, "bit size %I64u, bit offset %I64u", bit_size, bit_off); + }break; + + case DW_ExprOp_Pick: + { + U8 stack_idx = 0; + off += str8_deserial_read_struct(data, off, &stack_idx); + operand_string = str8f(scratch.arena, "stack index %u", stack_idx); + }break; + + case DW_ExprOp_PlusUConst: + { + U64 addend = 0; + off += str8_deserial_read_uleb128(data, off, &addend); + operand_string = str8f(arena, "addend %I64u", addend); + }break; + + case DW_ExprOp_Skip: + { + S16 x = 0; + off += str8_deserial_read_struct(data, off, &x); + operand_string = str8f(scratch.arena, "%+d bytes", x); + }break; + + case DW_ExprOp_Bra: + { + S16 x = 0; + off += str8_deserial_read_struct(data, off, &x); + operand_string = str8f(scratch.arena, "%+d", x); + }break; + + case DW_ExprOp_BReg0: case DW_ExprOp_BReg1: case DW_ExprOp_BReg2: + case DW_ExprOp_BReg3: case DW_ExprOp_BReg4: case DW_ExprOp_BReg5: + case DW_ExprOp_BReg6: case DW_ExprOp_BReg7: case DW_ExprOp_BReg8: + case DW_ExprOp_BReg9: case DW_ExprOp_BReg10: case DW_ExprOp_BReg11: + case DW_ExprOp_BReg12: case DW_ExprOp_BReg13: case DW_ExprOp_BReg14: + case DW_ExprOp_BReg15: case DW_ExprOp_BReg16: case DW_ExprOp_BReg17: + case DW_ExprOp_BReg18: case DW_ExprOp_BReg19: case DW_ExprOp_BReg20: + case DW_ExprOp_BReg21: case DW_ExprOp_BReg22: case DW_ExprOp_BReg23: + case DW_ExprOp_BReg24: case DW_ExprOp_BReg25: case DW_ExprOp_BReg26: + case DW_ExprOp_BReg27: case DW_ExprOp_BReg28: case DW_ExprOp_BReg29: + case DW_ExprOp_BReg30: case DW_ExprOp_BReg31: + { + U64 reg_idx = op - DW_ExprOp_BReg0; + S64 reg_off = 0; + off += str8_deserial_read_sleb128(data, off, ®_off); + ARCH_RegCode regcode = arch_reg_code_from_dw(arch, (DW_RegCode)reg_idx); + operand_string = str8f(scratch.arena, "%S+%I64d", arch_info->reg_code_name_table[regcode], reg_off); + }break; + + case DW_ExprOp_FBReg: + { + S64 reg_off = 0; + off += str8_deserial_read_sleb128(data, off, ®_off); + operand_string = str8f(scratch.arena, "frame base offset %I64d", reg_off); + }break; + + case DW_ExprOp_BRegX: + { + U64 reg_idx = 0; + S64 reg_off = 0; + off += str8_deserial_read_uleb128(data, off, ®_idx); + off += str8_deserial_read_sleb128(data, off, ®_off); + ARCH_RegCode regcode = arch_reg_code_from_dw(arch, (DW_RegCode)reg_idx); + operand_string = str8f(scratch.arena, "%S+%I64d", arch_info->reg_code_name_table[regcode], reg_off); + }break; + + case DW_ExprOp_XDerefSize: + case DW_ExprOp_DerefSize: + { + U8 x = 0; + off += str8_deserial_read_struct(data, off, &x); + operand_string = str8f(scratch.arena, "%u", x); + }break; + + case DW_ExprOp_Call2: + { + U16 x = 0; + off += str8_deserial_read_struct(data, off, &x); + operand_string = str8f(scratch.arena, "%u", x); + }break; + case DW_ExprOp_Call4: + { + U32 x = 0; + off += str8_deserial_read_struct(data, off, &x); + operand_string = str8f(arena, "%u", x); + }break; + case DW_ExprOp_CallRef: + { + U64 x = 0; + off += dw2_read_fmt_u64(data, off, ctx->format, &x); + operand_string = str8f(scratch.arena, "%I64u", x); + }break; + case DW_ExprOp_ImplicitPointer: + case DW_GNU_ExprOp_ImplicitPointer: + { + U64 info_off = 0; + off += dw2_read_fmt_u64(data, off, ctx->format, &info_off); + S64 ptr = 0; + off += str8_deserial_read_sleb128(data, off, &ptr); + operand_string = str8f(scratch.arena, ".debug_info@0x%I64x, ptr %I64x", info_off, ptr); + } break; + case DW_ExprOp_Convert: + case DW_GNU_ExprOp_Convert: + { + U64 type_cu_off = 0; + off += str8_deserial_read_uleb128(data, off, &type_cu_off); + operand_string = str8f(scratch.arena, "TypeCuOff %#I64x", ctx->unit_base_info_off + type_cu_off); + }break; + case DW_GNU_ExprOp_ParameterRef: + { + // TODO: always 4 bytes? + U32 cu_off = 0; + off += str8_deserial_read_struct(data, off, &cu_off); + operand_string = str8f(scratch.arena, "CuOff %#x", ctx->unit_base_info_off + cu_off); + }break; + case DW_ExprOp_DerefType: + case DW_GNU_ExprOp_DerefType: + { + U8 deref_size = 0; + U64 type_cu_off = 0; + off += str8_deserial_read_struct(data, off, &deref_size); + off += str8_deserial_read_uleb128(data, off, &type_cu_off); + operand_string = str8f(scratch.arena, "%#x, TypeCuOff %#I64x", deref_size, ctx->unit_base_info_off + type_cu_off); + }break; + case DW_ExprOp_ConstType: + case DW_GNU_ExprOp_ConstType: + { + U64 type_cu_off = 0; + U8 const_value_size = 0; + off += str8_deserial_read_uleb128(data, off, &type_cu_off); + off += str8_deserial_read_struct(data, off, &const_value_size); + Rng1U64 const_value_range = rng_1u64(off, off + const_value_size); + String8 const_value_data = str8_substr(data, const_value_range); + String8List const_value_strings = numeric_str8_list_from_data(scratch.arena, 16, const_value_data, 1); + String8 const_value_str = str8_list_join(scratch.arena, &const_value_strings, &(StringJoin){.sep = str8_lit(", ")}); + operand_string = str8f(scratch.arena, "TypeCuOff %#I64x, Const Value { %S }", ctx->unit_base_info_off + type_cu_off, const_value_str); + off += const_value_size; + }break; + case DW_ExprOp_RegvalType: + case DW_GNU_ExprOp_RegvalType: + { + U64 reg_idx = 0, type_cu_off = 0; + off += str8_deserial_read_uleb128(data, off, ®_idx); + off += str8_deserial_read_uleb128(data, off, &type_cu_off); + ARCH_RegCode regcode = arch_reg_code_from_dw(arch, (DW_RegCode)reg_idx); + operand_string = str8f(scratch.arena, "%S, TypeCuOff %#I64x", arch_info->reg_code_name_table[regcode], ctx->unit_base_info_off + type_cu_off); + }break; + case DW_ExprOp_EntryValue: + case DW_GNU_ExprOp_EntryValue: + { + U64 block_size = 0; + off += str8_deserial_read_uleb128(data, off, &block_size); + Rng1U64 block_range = rng_1u64(off, off + block_size); + String8 block_data = str8_substr(data, block_range); + // TODO(rjf): WTF, recursive? let's not... - if we want this, let's just do the stack inside of this function + // String8List block_expr = dw_string_list_from_expression(scratch.arena, block_data, cu_base, addr_size, arch, ver, ext, format); + // operand_string = str8_list_join(scratch.arena, &block_expr, &(StringJoin){.pre = str8_lit("{ "), .sep = str8_lit(","), .post = str8_lit(" }")}); + operand_string = s("{...}"); + off += block_size; + } break; + case DW_ExprOp_Addrx: + { + U64 addr = 0; + off += str8_deserial_read_uleb128(data, off, &addr); + operand_string = str8f(scratch.arena, "%#I64x", addr); + }break; + } + + // rjf: push opcode info + String8 opcode_string = dw_string_from_expr_op(ctx->version, ctx->exts, op); + if(operand_string.size == 0) + { + str8_list_pushf(arena, &result, "%S", opcode_string); + } + else + { + str8_list_pushf(arena, &result, "%S(%S)", opcode_string, operand_string); + } + } + scratch_end(scratch); + return result; +} + +//////////////////////////////// +//~ rjf: Main Dump Entry Point + +internal String8List +dw_dump_list_from_raw(Arena *arena, DW_Raw *raw, Arch arch, DW_SectionFlags subset_flags) +{ + ProfBeginFunction(); + Temp scratch = scratch_begin(&arena, 1); + String8 indent = str8_lit(" "); + + ////////////////////////////// + //- rjf: set up + // + typedef struct DumpSubsetOutputNode DumpSubsetOutputNode; + struct DumpSubsetOutputNode + { + DumpSubsetOutputNode *next; + DW_SectionKind section_kind; + String8List *lane_strings; + }; + DumpSubsetOutputNode *first_output_node = 0; + DumpSubsetOutputNode *last_output_node = 0; + String8List *strings = 0; +#define dump(str) str8_list_push(arena, strings, (str)) +#define dumpf(...) str8_list_pushf(arena, strings, __VA_ARGS__) +#define DumpSubsetKind(kind) \ +if(lane_idx() == 0)\ +{\ +DumpSubsetOutputNode *n = push_array(scratch.arena, DumpSubsetOutputNode, 1);\ +SLLQueuePush(first_output_node, last_output_node, n);\ +n->section_kind = (kind);\ +n->lane_strings = push_array(scratch.arena, String8List, lane_count());\ +}\ +lane_sync_u64(&first_output_node, 0);\ +lane_sync_u64(&last_output_node, 0);\ +strings = &last_output_node->lane_strings[lane_idx()];\ +lane_sync(); if(subset_flags & (1ull<<(kind))) ProfScope((char *)dw_regular_name_from_section_kind(kind).str) +#define DumpSubset(name) DumpSubsetKind(DW_SectionKind_##name) + + ////////////////////////////// + //- rjf: .debug_abbrev + // + DumpSubset(Abbrev) + { + if(lane_idx() == 0) + { + String8 data = raw->sec[DW_SectionKind_Abbrev].data; + U64 idx = 0; + dumpf("\nabbrevs:\n{\n"); + for(U64 off = 0; off < data.size; idx += 1) + { + Temp scratch2 = scratch_begin(&scratch.arena, 1); + U64 start_off = off; + + // rjf: read abbrev info + DW2_AbbrevHeader abbrev_header = {0}; + DW2_AbbrevAttribList abbrev_attribs = {0}; + off += dw2_read_abbrev(scratch2.arena, data, off, &abbrev_header, &abbrev_attribs); + + // rjf: log + dumpf(" abbrev: // abbrev[%I64u], .debug_abbrev@0x%I64x\n", idx, start_off); + dumpf(" {\n"); + dumpf(" id: %I64u\n", abbrev_header.id); + dumpf(" tag_kind: %S (0x%I64x)\n", dw_string_from_tag_kind(DW_Version_Last, DW_ExtFlag_All, abbrev_header.tag_kind), abbrev_header.tag_kind); + dumpf(" has_children: %s\n", abbrev_header.has_children ? "yes" : "no"); + if(abbrev_attribs.count != 0) + { + dumpf(" attribs:\n {\n"); + for EachNode(n, DW2_AbbrevAttribNode, abbrev_attribs.first) + { + DW2_AbbrevAttrib *attrib = &n->v; + dumpf(" {%-24S %-16S%S}\n", + dw_string_from_attrib_kind(DW_Version_Last, DW_ExtFlag_All, attrib->attrib_kind), + dw_string_from_form_kind(DW_Version_Last, DW_ExtFlag_All, attrib->form_kind), + attrib->form_kind == DW_FormKind_ImplicitConst ? str8f(scratch2.arena, " (0x%I64x, %I64u)", attrib->implicit_const, attrib->implicit_const) : s("")); + } + dumpf(" }\n"); + } + dumpf(" }\n"); + + if(off == start_off) + { + break; + } + } + dumpf("}\n"); + } + } + + ////////////////////////////// + //- rjf: .debug_info + // + DumpSubset(Info) + { + //- rjf: gather unit ranges from .debug_info + Rng1U64Array *unit_info_ranges = 0; + if(lane_idx() == 0) + { + unit_info_ranges = push_array(scratch.arena, Rng1U64Array, 1); + *unit_info_ranges = dw2_unit_ranges_from_data(scratch.arena, raw->sec[DW_SectionKind_Info].data); + } + lane_sync_u64(&unit_info_ranges, 0); + U64 unit_count = unit_info_ranges->count; + + //- rjf: parse all .debug_info unit headers + DW2_UnitHeader *unit_headers = 0; + Rng1U64 *unit_info_tag_ranges = 0; + { + // rjf: set up + if(lane_idx() == 0) + { + unit_headers = push_array(scratch.arena, DW2_UnitHeader, unit_count); + unit_info_tag_ranges = push_array(scratch.arena, Rng1U64, unit_count); + } + lane_sync_u64(&unit_headers, 0); + lane_sync_u64(&unit_info_tag_ranges, 0); + + // rjf: parse all unit headers + String8 data = raw->sec[DW_SectionKind_Info].data; + Rng1U64 range = lane_range(unit_count); + for EachInRange(idx, range) + { + Rng1U64 unit_info_range = unit_info_ranges->v[idx]; + U64 bytes_read = dw2_read_unit_header(str8_substr(data, unit_info_range), 0, &unit_headers[idx]); + unit_info_tag_ranges[idx] = r1u64(unit_info_range.min + bytes_read, unit_info_range.max); + } + } + lane_sync(); + Rng1U64Array unit_info_tag_ranges_array = {unit_info_tag_ranges, unit_count}; + + //- rjf: build all abbreviation maps, build (unit -> abbrev map) + DW2_UnitAbbrevMapMap unit_abbrev_map_map = dw2_unit_abbrev_map_map_from_data(scratch.arena, raw->sec[DW_SectionKind_Abbrev].data, unit_headers, unit_count); + + //- rjf: parse all unit offsets tables + DW2_OffsetTableSet *offset_tables = 0; + if(lane_idx() == 0) + { + offset_tables = push_array(scratch.arena, DW2_OffsetTableSet, 1); + offset_tables[0] = dw2_offset_table_set_from_raw(scratch.arena, raw); + } + lane_sync_u64(&offset_tables, 0); + + //- rjf: build per-unit parsing contexts + DW2_ParseCtx *unit_parse_ctxs = 0; + { + if(lane_idx() == 0) + { + unit_parse_ctxs = push_array(scratch.arena, DW2_ParseCtx, unit_count); + } + lane_sync_u64(&unit_parse_ctxs, 0); + Rng1U64 range = lane_range(unit_count); + for EachInRange(unit_idx, range) + { + DW2_UnitHeader *hdr = &unit_headers[unit_idx]; + DW2_ParseCtx *ctx = &unit_parse_ctxs[unit_idx]; + + // rjf: equip initial bootstrapping info + dw2_parse_ctx_equip_unit_header(ctx, hdr, unit_info_ranges->v[unit_idx].min); + dw2_parse_ctx_equip_unit_abbrev_map(ctx, &unit_abbrev_map_map, unit_idx); + + // rjf: do bootstrapping parse of root tag + DW2_Tag root_tag_bootstrap = {0}; + dw2_read_tag(scratch.arena, raw, ctx, raw->sec[DW_SectionKind_Info].data, unit_info_tag_ranges[unit_idx].min, &root_tag_bootstrap); + + // rjf: equip info extracted from bootstrapping parse + dw2_parse_ctx_equip_unit_root_tag(ctx, &root_tag_bootstrap, offset_tables); + + // rjf: do non-bootstrapping parse + DW2_Tag root_tag = {0}; + dw2_read_tag(scratch.arena, raw, ctx, raw->sec[DW_SectionKind_Info].data, unit_info_tag_ranges[unit_idx].min, &root_tag); + + // rjf: equip info extracted from non-bootstrapping parse + dw2_parse_ctx_equip_unit_root_tag(ctx, &root_tag, offset_tables); + } + } + lane_sync(); + + //- rjf: dump all unit tag trees + String8List *unit_dumps = 0; + if(lane_idx() == 0) + { + unit_dumps = push_array(scratch.arena, String8List, unit_count); + } + lane_sync_u64(&unit_dumps, 0); + U64 unit_take_idx_ = 0; + U64 *unit_take_idx_ptr = &unit_take_idx_; + lane_sync_u64(&unit_take_idx_ptr, 0); + String8List *strings_restore = strings; + for(;;) + { + //- rjf: take unit + U64 unit_idx = ins_atomic_u64_inc_eval(unit_take_idx_ptr)-1; + if(unit_idx >= unit_count) + { + break; + } + + //- rjf: unpack unit + strings = &unit_dumps[unit_idx]; + DW2_ParseCtx *ctx = &unit_parse_ctxs[unit_idx]; + + //- rjf: dump + dumpf(" unit: // unit[%I64u], .debug_info@0x%I64x\n", unit_idx, ctx->unit_base_info_off); + dumpf(" {\n"); + dumpf(" version: %u\n", ctx->version); + dumpf(" format: %S\n", dw_string_from_format(ctx->format)); + dumpf(" abbrev_off: 0x%I64x\n", unit_headers[unit_idx].abbrev_off); + dumpf(" addr_size: %I64u\n", ctx->addr_size); + dumpf(" kind: %S\n", dw_string_from_comp_unit_kind(unit_headers[unit_idx].kind)); + dumpf(" dwo_id: 0x%I64x\n", unit_headers[unit_idx].dwo_id); + dumpf(" tags:\n {\n"); + { + U64 depth = 0; + for(U64 off = unit_info_tag_ranges[unit_idx].min; off < unit_info_tag_ranges[unit_idx].max;) + { + U64 start_off = off; + Temp tag_scratch = scratch_begin(&scratch.arena, 1); + + // rjf: parse next tag + DW2_Tag tag = {0}; + off += dw2_read_tag(tag_scratch.arena, raw, ctx, raw->sec[DW_SectionKind_Info].data, off, &tag); + + // rjf: non-zero tag -> dump + if(tag.kind != DW_TagKind_Null) + { + dumpf(" %.*s%S: // .debug_info@0x%I64x\n", depth, indent.str, dw_string_from_tag_kind(ctx->version, ctx->exts, tag.kind), start_off); + dumpf(" %.*s{\n", depth, indent.str); + for EachNode(n, DW2_AttribNode, tag.attribs.first) + { + DW2_Attrib *attrib = &n->v; + String8 kind_string = dw_string_from_attrib_kind(ctx->version, ctx->exts, attrib->attrib_kind); + String8 form_kind_string = dw_string_from_form_kind(ctx->version, ctx->exts, attrib->val.kind); + String8 value_string = {0}; + DW_AttribClass value_class = dw_attrib_class_from_form_kind(ctx->version, ctx->exts, attrib->val.kind); + switch(value_class) + { + default: + { + value_string = str8f(tag_scratch.arena, "0x%I64x", attrib->val.u128.u64[0]); + }break; + case DW_AttribClass_Address: + { + value_string = str8f(tag_scratch.arena, "0x%I64x", attrib->val.addr); + }break; + case DW_AttribClass_String: + { + value_string = str8f(tag_scratch.arena, "\"%S\"", attrib->val.string); + }break; + case DW_AttribClass_Flag: + { + value_string = attrib->val.u128.u64[0] ? s("true") : s("false"); + }break; + case DW_AttribClass_Block: + { + String8 block = attrib->val.string; + String8List block_strs = numeric_str8_list_from_data(tag_scratch.arena, 16, block, 1); + value_string = str8_list_join(tag_scratch.arena, &block_strs, &(StringJoin){.pre = s("{ "), .sep = s(", "), .post = s(" }")}); + }break; + case DW_AttribClass_ExprLoc: + { + String8 expr = attrib->val.string; + String8List expr_strs = dw_dump_list_from_expr(tag_scratch.arena, expr, arch, ctx); + value_string = str8_list_join(tag_scratch.arena, &expr_strs, &(StringJoin){.pre = s("{ "), .sep = s(", "), .post = s(" }")}); + }break; + } + dumpf(" %.*s %S: %S // %S\n", depth, indent.str, kind_string, value_string, form_kind_string); + } + } + + // rjf: push/pop + if(tag.has_children) + { + depth += 1; + } + else if(tag.kind == DW_TagKind_Null) + { + depth -= 1; + } + + // rjf: if tag does not have children -> end info + if(!tag.has_children) + { + dumpf(" %.*s}\n", depth, indent.str); + } + + scratch_end(tag_scratch); + if(off == start_off) + { + break; + } + } + } + dumpf(" }\n"); + dumpf(" }\n"); + } + strings = strings_restore; + + //- rjf: join unit dumps in order + if(lane_idx() == 0) + { + dumpf("\nunits:\n{\n"); + for EachIndex(unit_idx, unit_count) + { + str8_list_concat_in_place(strings, &unit_dumps[unit_idx]); + } + dumpf("}\n"); + } + lane_sync(); + } + + ////////////////////////////// + //- rjf: .debug_line + // + DumpSubset(Line) + { + // TODO(rjf) + } + + ////////////////////////////// + //- rjf: .debug_str + // + DumpSubset(Str) + { + if(lane_idx() == 0) + { + dumpf("\nstrings:\n{\n"); + String8 data = raw->sec[DW_SectionKind_Str].data; + U64 idx = 0; + for(U64 off = 0; off < data.size; idx += 1) + { + U64 start_off = off; + String8 string = {0}; + off += str8_deserial_read_cstr(data, off, &string); + dumpf(" {\"%S\"} // string[%I64u], .debug_str@0x%I64x\n", string, idx, start_off); + if(off == start_off) + { + break; + } + } + dumpf("}\n"); + } + } + + ////////////////////////////// + //- rjf: .debug_frame + // +#if 0 + DumpSubset(Frame) + { + // TODO(rjf) + } +#endif + + ////////////////////////////// + //- rjf: .debug_loc + // +#if 0 + DumpSubset(Loc) + { + // TODO(rjf) + } +#endif + + ////////////////////////////// + //- rjf: join results + // + String8List *result_strings_ptr = push_array(scratch.arena, String8List, 1); + lane_sync(); + if(lane_idx() == 0) + { + for EachNode(n, DumpSubsetOutputNode, first_output_node) + { + String8List subset_strings = {0}; + for EachIndex(idx, lane_count()) + { + str8_list_concat_in_place(&subset_strings, &n->lane_strings[idx]); + } + if(subset_strings.total_size != 0) + { + str8_list_pushf(arena, result_strings_ptr, "////////////////////////////////\n//~ %S\n", dw_regular_name_from_section_kind(n->section_kind)); + str8_list_concat_in_place(result_strings_ptr, &subset_strings); + str8_list_pushf(arena, result_strings_ptr, "\n"); + } + } + } + lane_sync(); + lane_sync_u64(&result_strings_ptr, 0); + String8List result_strings = *result_strings_ptr; + lane_sync(); + +#undef DumpSubsetKind +#undef DumpSubset +#undef dump +#undef dumpf + scratch_end(scratch); + ProfEnd(); + return result_strings; +} diff --git a/src/dwarf/dwarf_dump_2.h b/src/dwarf/dwarf_dump_2.h new file mode 100644 index 00000000..620c7562 --- /dev/null +++ b/src/dwarf/dwarf_dump_2.h @@ -0,0 +1,17 @@ +// Copyright (c) Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef DWARF_DUMP_2_H +#define DWARF_DUMP_2_H + +//////////////////////////////// +//~ rjf: Helpers + +internal String8List dw_dump_list_from_expr(Arena *arena, String8 data, Arch arch, DW2_ParseCtx *ctx); + +//////////////////////////////// +//~ rjf: Main Dump Entry Point + +internal String8List dw_dump_list_from_raw(Arena *arena, DW_Raw *raw, Arch arch, DW_SectionFlags subset_flags); + +#endif //DWARF_DUMP_2_H diff --git a/src/dwarf/dwarf_expr.c b/src/dwarf/dwarf_expr.c index f5f7f6e0..8f8e23ac 100644 --- a/src/dwarf/dwarf_expr.c +++ b/src/dwarf/dwarf_expr.c @@ -1578,111 +1578,3 @@ dw_eval_expr(Arena *arena, Arch arch, DW_Format format, U64 frame_base, U64 cfa, scratch_end(scratch); return result; } - -internal String8 -dw_encode_expr(Arena *arena, Arch arch, DW_Format format, DW_ExprEnc *encs, U64 encs_count) -{ - Temp scratch = scratch_begin(&arena, 1); - - String8List *srl = push_array(scratch.arena, String8List, 1); - str8_serial_begin(scratch.arena, srl); - - HashTable *label_map = hash_table_init(scratch.arena, 128); - - struct Fixup { U64 offset; String8 label; struct Fixup *next; }; - struct Fixup *first_fixup = 0, *last_fixup = 0; - - for EachIndex(i, encs_count) { - DW_ExprEnc *e = &encs[i]; - - switch (e->type) { - case DW_ExprEncType_Null: {} break; - case DW_ExprEncType_Op: { - str8_serial_push_struct(scratch.arena, srl, &e->op); - } break; - case DW_ExprEncType_U8: { - str8_serial_push_struct(scratch.arena, srl, &e->u8); - } break; - case DW_ExprEncType_U16: { - str8_serial_push_struct(scratch.arena, srl, &e->u16); - } break; - case DW_ExprEncType_U32: { - str8_serial_push_struct(scratch.arena, srl, &e->u32); - } break; - case DW_ExprEncType_U64: { - str8_serial_push_struct(scratch.arena, srl, &e->u64); - } break; - case DW_ExprEncType_S8: { - str8_serial_push_struct(scratch.arena, srl, &e->s8); - } break; - case DW_ExprEncType_S16: { - str8_serial_push_struct(scratch.arena, srl, &e->s16); - } break; - case DW_ExprEncType_S32: { - str8_serial_push_struct(scratch.arena, srl, &e->s32); - } break; - case DW_ExprEncType_S64: { - str8_serial_push_struct(scratch.arena, srl, &e->s64); - } break; - case DW_ExprEncType_ULEB128: { - str8_serial_push_string(scratch.arena, srl, dw_write_uleb128(scratch.arena, e->u64)); - } break; - case DW_ExprEncType_SLEB128: { - str8_serial_push_string(scratch.arena, srl, dw_write_sleb128(scratch.arena, e->s64)); - } break; - case DW_ExprEncType_Addr: { - U64 addr_size = byte_size_from_arch(arch); - Assert(addr_size <= sizeof(e->addr)); - str8_serial_push_string(scratch.arena, srl, str8((U8 *)&e->addr, addr_size)); - } break; - case DW_ExprEncType_Block: { - Assert(e->block.size <= max_U8); - str8_serial_push_u8(scratch.arena, srl, (U8)e->block.size); - str8_serial_push_string(scratch.arena, srl, e->block); - } break; - case DW_ExprEncType_DwarfUInt: { - switch (format) { - case DW_Format_Null: {} break; - case DW_Format_32Bit: { str8_serial_push_u32(scratch.arena, srl, e->u32); } break; - case DW_Format_64Bit: { str8_serial_push_u64(scratch.arena, srl, e->u64); } break; - default: { InvalidPath; } break; - } - } break; - case DW_ExprEncType_Label: { - struct Fixup *fixup = push_array(scratch.arena, struct Fixup, 1); - fixup->offset = srl->total_size; - fixup->label = e->label; - SLLQueuePush(first_fixup, last_fixup, fixup); - - S16 delta_placeholder = 0; - str8_serial_push_struct(scratch.arena, srl, &delta_placeholder); - } break; - case DW_ExprEncType_DeclLabel: { - hash_table_push_string_u64(scratch.arena, label_map, e->label, srl->total_size); - } break; - default: { InvalidPath; } break; - } - } - - // finalize expression - String8 expr = str8_serial_end(arena, srl); - - // apply fixups - for EachNode(fixup, struct Fixup, first_fixup) { - U64 label_offset; - if (!hash_table_search_string_u64(label_map, fixup->label, &label_offset)) { - Assert(0 && "undefined label"); - continue; - } - - S64 delta = (S64)label_offset - (S64)fixup->offset; - AssertAlways(min_S16 <= delta && delta <= max_S16); - - S16 *delta_ptr = (S16 *)(expr.str + fixup->offset); - *delta_ptr = delta; - } - - scratch_end(scratch); - return expr; -} - diff --git a/src/dwarf/dwarf_expr.h b/src/dwarf/dwarf_expr.h index d9c3bdae..432b5b22 100644 --- a/src/dwarf/dwarf_expr.h +++ b/src/dwarf/dwarf_expr.h @@ -229,8 +229,5 @@ internal String8 dw_string_from_expr_value(Arena *arena, U64 addr_size, DW_ExprV // evaluator internal DW_ExprEvalResult dw_eval_expr(Arena *arena, Arch arch, DW_Format format, U64 frame_base, U64 cfa, U64 tls_base, U64 op_limit, DW_Expr expr, void *reg_block, MachineOp_MemRead *mem_read, void *mem_read_ud, DW_ExprValue *value_out); -// encoder -internal String8 dw_encode_expr(Arena *arena, Arch arch, DW_Format format, DW_ExprEnc *encs, U64 encs_count); - #endif //DWARF_EXPR_H diff --git a/src/dwarf/dwarf_expr_2.c b/src/dwarf/dwarf_expr_2.c new file mode 100644 index 00000000..3e3d6397 --- /dev/null +++ b/src/dwarf/dwarf_expr_2.c @@ -0,0 +1,393 @@ +// Copyright (c) Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +//////////////////////////////// +//~ rjf: Expression Evaluation Functions + +internal DW_Eval +dw_eval(Arena *arena, DW_Format fmt, Arch arch, MemoryMap *memory_map, void *regs, U64 frame_base, U64 cfa, U64 tls, DW_EvalState *state, String8 expr, U64 op_idx_cap) +{ + DW_Eval result = {0}; + ARCH_Info *arch_info = arch_info_from_arch(arch); + B32 need_a_break = 0; + B32 done = 0; + for(;state->off < expr.size && !need_a_break && !done;) + { + U64 start_off = state->off; + U64 off = state->off; + + //- rjf: too many ops? -> error + if(state->op_idx >= op_idx_cap) + { + result.status = DW_EvalStatus_ExecOpLimitReached; + break; + } + + //- rjf: read next opcode + DW_ExprOp opcode = 0; + off += str8_deserial_read_struct(expr, off, &opcode); + + //- rjf: unpack opcode + U8 push_count = 0; + U8 pop_count = 0; + U8 operand_count = 0; + DW_ExprOperandKind operand_kinds[2] = {DW_ExprOperandKind_Null}; + { + DW_ExprOpInfo *op_info = dw_info_from_expr_op(DW_Version_5, DW_ExtFlag_All, opcode); + push_count = op_info->push_count; + pop_count = op_info->pop_count; + operand_count = op_info->operand_count; + MemoryCopy(operand_kinds, op_info->operand_kinds, sizeof(operand_kinds)); + } + + //- rjf: read operands + DW_EvalVal operands[2] = {0}; + for(U8 operand_idx = 0; operand_idx < operand_count; operand_idx += 1) + { + U64 fixed_bytes_to_read = 0; + switch(operand_kinds[operand_idx]) + { + default:{}break; + case DW_ExprOperandKind_U8: {fixed_bytes_to_read = 1;}goto fixed_read; + case DW_ExprOperandKind_U16: {fixed_bytes_to_read = 2;}goto fixed_read; + case DW_ExprOperandKind_U32: {fixed_bytes_to_read = 4;}goto fixed_read; + case DW_ExprOperandKind_U64: {fixed_bytes_to_read = 8;}goto fixed_read; + case DW_ExprOperandKind_S8: {fixed_bytes_to_read = 1;}goto fixed_read; + case DW_ExprOperandKind_S16: {fixed_bytes_to_read = 2;}goto fixed_read; + case DW_ExprOperandKind_S32: {fixed_bytes_to_read = 4;}goto fixed_read; + case DW_ExprOperandKind_S64: {fixed_bytes_to_read = 8;}goto fixed_read; + case DW_ExprOperandKind_Addr: {fixed_bytes_to_read = byte_size_from_arch(arch);}goto fixed_read; + case DW_ExprOperandKind_DwarfUInt:{fixed_bytes_to_read = dw_addr_size_from_format(fmt);}goto fixed_read; + fixed_read:; + { + off += str8_deserial_read(expr, off, &operands[operand_idx].u512.u64[0], fixed_bytes_to_read, fixed_bytes_to_read); + }break; + case DW_ExprOperandKind_ULEB128: + { + off += str8_deserial_read_uleb128(expr, off, &operands[operand_idx].u512.u64[0]); + }break; + case DW_ExprOperandKind_SLEB128: + { + off += str8_deserial_read_sleb128(expr, off, &operands[operand_idx].s64); + }break; + case DW_ExprOperandKind_Block: + { + U8 block_size = 0; + off += str8_deserial_read_struct(expr, off, &block_size); + operands[operand_idx].data = str8_prefix(str8_skip(expr, off), block_size); + }break; + } + } + + //- rjf: do pops + DW_EvalVal popped_vals[3] = {0}; + { + pop_count = Min(pop_count, ArrayCount(popped_vals)); + for(U8 pop_idx = 0; pop_idx < pop_count; pop_idx += 1) + { + if(state->top_val != 0) + { + DW_EvalValNode *popped = state->top_val; + SLLStackPop(state->top_val); + popped_vals[pop_idx] = popped->v; + SLLStackPush(state->free_val, popped); + } + } + } + + //- rjf: apply opcode + DW_EvalVal push_vals[3] = {0}; + U64 pick_target_idx = 0; + U64 deref_read_size = 0; + DW_RegCode reg_code_dw = 0; + switch(opcode) + { + //- rjf: unsuported ops + case DW_ExprOp_Call2: // DWARF function calls + case DW_ExprOp_Call4: // DWARF function calls + case DW_ExprOp_CallRef: // DWARF function calls + case DW_ExprOp_Addrx: // address in .debug_addr; not currently present for us, need to find how to preserve if we care + case DW_ExprOp_Constx: // address in .debug_addr; not currently present for us, need to find how to preserve if we care + case DW_ExprOp_EntryValue: // encodes a sub-expression as a data block - need to recursively evaluate, lol + case DW_ExprOp_ConstType: // pushes a constant - but refers into DWARF debug info to specify the type. we don't have that, need to figure out how to preserve mapping from DW type ID -> RDI type, if we care + case DW_ExprOp_DerefType: // same as above - refers to DWARF debug info + case DW_ExprOp_RegvalType: // same as above - refers to DWARF debug info + case DW_ExprOp_Piece: // pieces - used to stitch multiple sub-values together + case DW_ExprOp_BitPiece: // pieces - used to stitch multiple sub-values together + case DW_ExprOp_XDeref: // two entries popped from stack - second one is 'address space identifier'. not possible in this layer right now + case DW_ExprOp_XDerefSize: // does multiple address spaces + case DW_ExprOp_XDerefType: // refers to DWARF debug info, *and* does multiple address spaces + case DW_ExprOp_PushObjectAddress: // pushes "the address of the object currently being evaluated" ??? + case DW_ExprOp_ImplicitPointer: // refers to DWARF debug info + default: + { + done = 1; + result.status = DW_EvalStatus_UnsupportedOp; + }break; + + //- rjf: address ops + case DW_ExprOp_Deref:{deref_read_size = byte_size_from_arch(arch);}goto deref; + case DW_ExprOp_DerefSize:{deref_read_size = operands[0].u512.u64[0];}goto deref; + deref:; + { + U64 vaddr = popped_vals[0].u512.u64[0]; + U64 read_size = Min(deref_read_size, sizeof(push_vals[0].u512)); + Rng1U64 read_vaddr_range = r1u64(vaddr, vaddr+read_size); + if(!memory_map_read(memory_map, read_vaddr_range, &push_vals[0].u512)) + { + need_a_break = 1; + result.status = DW_EvalStatus_FailedMemoryRead; + result.missed_read_vaddr_range = read_vaddr_range; + } + }break; + + //- rjf: generic constants + case DW_ExprOp_Const1U: case DW_ExprOp_Const1S: + case DW_ExprOp_Const2U: case DW_ExprOp_Const2S: + case DW_ExprOp_Const4U: case DW_ExprOp_Const4S: + case DW_ExprOp_Const8U: case DW_ExprOp_Const8S: + case DW_ExprOp_ConstU: case DW_ExprOp_ConstS: + case DW_ExprOp_Addr: + { + push_vals[0] = operands[0]; + }break; + + //- rjf: stack ops + case DW_ExprOp_Dup:{push_vals[0] = popped_vals[0];}break; + case DW_ExprOp_Drop:{/* NOTE(rjf): nothing to do here - just pops */}break; + case DW_ExprOp_Swap:{push_vals[0] = popped_vals[1]; push_vals[1] = popped_vals[0];}break; + case DW_ExprOp_Rot: {push_vals[0] = popped_vals[0]; push_vals[1] = popped_vals[1]; push_vals[2] = popped_vals[2];}break; + case DW_ExprOp_Over:{pick_target_idx = 1;}break; + case DW_ExprOp_Pick:{pick_target_idx = operands[0].u512.u64[0];}goto pick_stack_val; + pick_stack_val:; + { + U64 idx = 0; + for(DW_EvalValNode *n = state->top_val; n != 0; n = n->next, idx += 1) + { + if(idx == pick_target_idx) + { + push_vals[0] = n->v; + break; + } + } + }break; + + //- rjf: arithmetic ops +#define UniOpU(k, op) case k:{push_vals[0].u512.u64[0] = op(popped_vals[0].u512.u64[0]);}break +#define UniOpS(k, op) case k:{push_vals[0].s64 = op(popped_vals[0].s64);}break +#define BinOpS(k, op) case k:{push_vals[0].s64 = popped_vals[1].s64 op popped_vals[0].s64;}break +#define BinOpU(k, op) case k:{push_vals[0].u512.u64[0] = popped_vals[1].u512.u64[0] op popped_vals[0].u512.u64[0];}break +#define BinOpDiv(k, op) case k:if(popped_vals[0].u512.u64[0] != 0){push_vals[0].u512.u64[0] = popped_vals[1].u512.u64[0] op popped_vals[0].u512.u64[0];}break + UniOpS(DW_ExprOp_Abs, abs_s64); + UniOpS(DW_ExprOp_Neg, -); + UniOpU(DW_ExprOp_Not, !); + BinOpU(DW_ExprOp_Minus, -); + BinOpU(DW_ExprOp_Mul, *); + BinOpU(DW_ExprOp_Plus, +); + BinOpU(DW_ExprOp_Shl, <<); + BinOpU(DW_ExprOp_Shr, >>); + BinOpU(DW_ExprOp_And, &); + BinOpU(DW_ExprOp_Or, |); + BinOpU(DW_ExprOp_Xor, ^); + BinOpS(DW_ExprOp_Shra, >>); + BinOpDiv(DW_ExprOp_Div, /); + BinOpDiv(DW_ExprOp_Mod, %); + BinOpU(DW_ExprOp_Eq, ==); + BinOpU(DW_ExprOp_Ne, !=); + BinOpS(DW_ExprOp_Ge, >=); + BinOpS(DW_ExprOp_Gt, >); + BinOpS(DW_ExprOp_Le, <=); + BinOpS(DW_ExprOp_Lt, <); +#undef UniOpU +#undef UniOpS +#undef BinOpS +#undef BinOpU +#undef BinOpDiv + case DW_ExprOp_PlusUConst: + { + push_vals[0].u512.u64[0] = popped_vals[0].u512.u64[0] + operands[0].u512.u64[0]; + }break; + + //- rjf: jumps/branches + case DW_ExprOp_Bra: + { + if(popped_vals[0].u512.u64[0] == 0) + { + break; + } + } // fallthrough + case DW_ExprOp_Skip: + { + S64 jump_delta = operands[0].s64; + if((jump_delta < 0 && -jump_delta > off) || (off + jump_delta >= expr.size)) + { + done = 1; + result.status = DW_EvalStatus_Error; + } + else + { + off += jump_delta; + } + }break; + + //- rjf: literals + case DW_ExprOp_Lit0: case DW_ExprOp_Lit1: case DW_ExprOp_Lit2: case DW_ExprOp_Lit3: + case DW_ExprOp_Lit4: case DW_ExprOp_Lit5: case DW_ExprOp_Lit6: case DW_ExprOp_Lit7: + case DW_ExprOp_Lit8: case DW_ExprOp_Lit9: case DW_ExprOp_Lit10: case DW_ExprOp_Lit11: + case DW_ExprOp_Lit12: case DW_ExprOp_Lit13: case DW_ExprOp_Lit14: case DW_ExprOp_Lit15: + case DW_ExprOp_Lit16: case DW_ExprOp_Lit17: case DW_ExprOp_Lit18: case DW_ExprOp_Lit19: + case DW_ExprOp_Lit20: case DW_ExprOp_Lit21: case DW_ExprOp_Lit22: case DW_ExprOp_Lit23: + case DW_ExprOp_Lit24: case DW_ExprOp_Lit25: case DW_ExprOp_Lit26: case DW_ExprOp_Lit27: + case DW_ExprOp_Lit28: case DW_ExprOp_Lit29: case DW_ExprOp_Lit30: case DW_ExprOp_Lit31: + { + U8 val = (opcode - DW_ExprOp_Lit0); + push_vals[0].u512.u8[0] = val; + }break; + + //- rjf: register reads (opcode-encoded reg) + case DW_ExprOp_Reg0: case DW_ExprOp_Reg1: case DW_ExprOp_Reg2: case DW_ExprOp_Reg3: + case DW_ExprOp_Reg4: case DW_ExprOp_Reg5: case DW_ExprOp_Reg6: case DW_ExprOp_Reg7: + case DW_ExprOp_Reg8: case DW_ExprOp_Reg9: case DW_ExprOp_Reg10: case DW_ExprOp_Reg11: + case DW_ExprOp_Reg12: case DW_ExprOp_Reg13: case DW_ExprOp_Reg14: case DW_ExprOp_Reg15: + case DW_ExprOp_Reg16: case DW_ExprOp_Reg17: case DW_ExprOp_Reg18: case DW_ExprOp_Reg19: + case DW_ExprOp_Reg20: case DW_ExprOp_Reg21: case DW_ExprOp_Reg22: case DW_ExprOp_Reg23: + case DW_ExprOp_Reg24: case DW_ExprOp_Reg25: case DW_ExprOp_Reg26: case DW_ExprOp_Reg27: + case DW_ExprOp_Reg28: case DW_ExprOp_Reg29: case DW_ExprOp_Reg30: case DW_ExprOp_Reg31: + {reg_code_dw = (opcode - DW_ExprOp_Reg0);}goto reg_read; + case DW_ExprOp_RegX: + {reg_code_dw = operands[0].u512.u64[0];}goto reg_read; + reg_read:; + { + ARCH_RegCode reg_code = arch_reg_code_from_dw(arch, reg_code_dw); + Rng1U16 reg_rng = arch_info->reg_code_rng_table[reg_code]; + U64 read_size = dim_1u16(reg_rng); + read_size = Min(read_size, sizeof(push_vals[0].u512)); + reg_rng.max = reg_rng.min + read_size; + arch_reg_block_read_range(arch_info, regs, reg_rng, &push_vals[0].u512); + }break; + + //- rjf: register reads (opcode-encoded reg + offset) + case DW_ExprOp_BReg0: case DW_ExprOp_BReg1: case DW_ExprOp_BReg2: case DW_ExprOp_BReg3: + case DW_ExprOp_BReg4: case DW_ExprOp_BReg5: case DW_ExprOp_BReg6: case DW_ExprOp_BReg7: + case DW_ExprOp_BReg8: case DW_ExprOp_BReg9: case DW_ExprOp_BReg10: case DW_ExprOp_BReg11: + case DW_ExprOp_BReg12: case DW_ExprOp_BReg13: case DW_ExprOp_BReg14: case DW_ExprOp_BReg15: + case DW_ExprOp_BReg16: case DW_ExprOp_BReg17: case DW_ExprOp_BReg18: case DW_ExprOp_BReg19: + case DW_ExprOp_BReg20: case DW_ExprOp_BReg21: case DW_ExprOp_BReg22: case DW_ExprOp_BReg23: + case DW_ExprOp_BReg24: case DW_ExprOp_BReg25: case DW_ExprOp_BReg26: case DW_ExprOp_BReg27: + case DW_ExprOp_BReg28: case DW_ExprOp_BReg29: case DW_ExprOp_BReg30: case DW_ExprOp_BReg31: + {reg_code_dw = (opcode - DW_ExprOp_BReg0);}goto based_reg_read; + case DW_ExprOp_BRegX: + {reg_code_dw = operands[0].u512.u64[0];}goto based_reg_read; + based_reg_read:; + { + ARCH_RegCode reg_code = arch_reg_code_from_dw(arch, reg_code_dw); + Rng1U16 reg_rng = arch_info->reg_code_rng_table[reg_code]; + U64 read_size = dim_1u16(reg_rng); + read_size = Min(read_size, sizeof(push_vals[0].u512)); + reg_rng.max = reg_rng.min + read_size; + arch_reg_block_read_range(arch_info, regs, reg_rng, &push_vals[0].u512); + push_vals[0].u512.u64[0] += operands[0].s64; + }break; + + //- rjf: frame base register read + case DW_ExprOp_FBReg: + { + push_vals[0].u512.u64[0] = (U64)(frame_base + operands[0].s64); + }break; + + //- rjf: TLS + case DW_ExprOp_FormTlsAddress: + { + U64 tls_off = popped_vals[0].u512.u64[0]; + U64 tls_addr = tls + tls_off; + push_vals[0].u512.u64[0] = tls_addr; + }break; + + //- rjf: call frame CFA + case DW_ExprOp_CallFrameCfa: + { + push_vals[0].u512.u64[0] = cfa; + }break; + + //- rjf: implicit location descriptions (value is known, location is not) + case DW_ExprOp_ImplicitValue: + { + push_vals[0] = operands[0]; + }break; + case DW_ExprOp_StackValue: + { + // NOTE(rjf): nothing to do here - program is over, value (but not location) has + // been evaluated and exists as the top entry on the stack. + }break; + + //- rjf: conversions + case DW_ExprOp_Convert: + case DW_ExprOp_Reinterpret: + { + if(operands[0].u512.u64[0] == 0) + { + push_vals[0] = popped_vals[0]; + } + else + { + // NOTE(rjf): with a nonzero operand, it is expecting us to read into the + // DWARF debug info and push as that base type. we do not have access to that + // in the general case here, and we need to figure out how to preserve that info + // if we care. + done = 1; + result.status = DW_EvalStatus_UnsupportedOp; + } + }break; + } + + //- rjf: do pushes + if(!done && !need_a_break) + { + push_count = Min(push_count, ArrayCount(push_vals)); + for(U8 push_idx = 0; push_idx < push_count; push_idx += 1) + { + DW_EvalValNode *n = state->free_val; + if(n != 0) + { + SLLStackPop(state->free_val); + } + else + { + n = push_array(arena, DW_EvalValNode, 1); + } + n->v = push_vals[push_idx]; + SLLStackPush(state->top_val, n); + } + } + + //- rjf: do not need a break? advance state. + // + // (if we *do* need a break, this means we need to ask the caller for + // more info, like memory reads, and so we want to resume from the + // offset just before this opcode) + // + if(!need_a_break) + { + state->op_idx += 1; + state->off = off; + } + + //- rjf: at the end of the expression? -> set success, fill result + if(off == expr.size) + { + result.status = DW_EvalStatus_Good; + if(state->top_val != 0) + { + result.val = state->top_val->v; + } + } + + //- rjf: did not advance? -> error + if(off == start_off) + { + result.status = DW_EvalStatus_Error; + break; + } + } + return result; +} diff --git a/src/dwarf/dwarf_expr_2.h b/src/dwarf/dwarf_expr_2.h new file mode 100644 index 00000000..e68a9617 --- /dev/null +++ b/src/dwarf/dwarf_expr_2.h @@ -0,0 +1,66 @@ +// Copyright (c) Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef DWARF_EXPR_2_H +#define DWARF_EXPR_2_H + +//////////////////////////////// +//~ rjf: Evaluation Values + +typedef struct DW_EvalVal DW_EvalVal; +struct DW_EvalVal +{ + union + { + U512 u512; + S64 s64; + }; + String8 data; +}; + +typedef struct DW_EvalValNode DW_EvalValNode; +struct DW_EvalValNode +{ + DW_EvalValNode *next; + DW_EvalVal v; +}; + +//////////////////////////////// +//~ rjf: Evaluation Results + +typedef enum DW_EvalStatus +{ + DW_EvalStatus_Error, + DW_EvalStatus_FailedMemoryRead, + DW_EvalStatus_ExecOpLimitReached, + DW_EvalStatus_UnsupportedOp, + DW_EvalStatus_Good, +} +DW_EvalStatus; + +typedef struct DW_Eval DW_Eval; +struct DW_Eval +{ + DW_EvalVal val; + DW_EvalStatus status; + Rng1U64 missed_read_vaddr_range; +}; + +//////////////////////////////// +//~ rjf: Evaluation State Machine + +typedef struct DW_EvalState DW_EvalState; +struct DW_EvalState +{ + U64 off; + U64 op_idx; + DW_EvalValNode *top_val; + DW_EvalValNode *free_val; +}; + +//////////////////////////////// +//~ rjf: Expression Evaluation Functions + +internal DW_Eval dw_eval(Arena *arena, DW_Format fmt, Arch arch, MemoryMap *memory_map, void *regs, U64 frame_base, U64 cfa, U64 tls, DW_EvalState *state, String8 expr, U64 op_idx_cap); + +#endif // DWARF_EXPR_2_H diff --git a/src/dwarf/dwarf_help.c b/src/dwarf/dwarf_help.c deleted file mode 100644 index 84ccf52c..00000000 --- a/src/dwarf/dwarf_help.c +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright (c) Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -internal DW_CallFrameInfo -dw_call_frame_info_from_data(Arena *arena, Arch arch, U64 rebase, String8 debug_frame) -{ - Temp scratch = scratch_begin(&arena, 1); - - // count CIE and FDE entries - U64 cie_count = 0, fde_count = 0; - for (U64 cursor = 0, desc_size; cursor < debug_frame.size; cursor += desc_size) { - DW_DescriptorEntry desc = {0}; - desc_size = dw_parse_descriptor_entry_header(debug_frame, cursor, &desc); - if (desc_size == 0) { break; } - if (desc.type == DW_DescriptorEntryType_CIE) { - cie_count += 1; - } else if (desc.type == DW_DescriptorEntryType_FDE) { - fde_count += 1; - } - } - - // parse CIEs and build (offset -> CIE) hash table - HashTable *cie_ht = hash_table_init(scratch.arena, (U64)(cie_count * 1.3)); - DW_CIE *cie = push_array(arena, DW_CIE, cie_count); - U64 parse_cie_count = 0; - for (U64 cursor = 0, desc_size; cursor < debug_frame.size; cursor += desc_size) { - DW_DescriptorEntry desc = {0}; - desc_size = dw_parse_descriptor_entry_header(debug_frame, cursor, &desc); - if (desc_size == 0) { break; } - if (desc.type == DW_DescriptorEntryType_CIE) { - if (dw_parse_cie(str8_skip(debug_frame, cursor), desc.format, arch, &cie[parse_cie_count])) { - hash_table_push_u64_raw(scratch.arena, cie_ht, cursor, &cie[parse_cie_count]); - parse_cie_count += 1; - } - } - } - - // parse FDEs - DW_FDE *fde = push_array(arena, DW_FDE, fde_count); - U64 parse_fde_count = 0; - for (U64 cursor = 0, desc_size; cursor < debug_frame.size; cursor += desc_size) { - DW_DescriptorEntry desc = {0}; - desc_size = dw_parse_descriptor_entry_header(debug_frame, cursor, &desc); - if (desc_size == 0) { break; } - if (desc.type == DW_DescriptorEntryType_FDE) { - DW_CIE *cie = hash_table_search_u64_raw(cie_ht, desc.cie_pointer); - if (dw_parse_fde(str8_skip(debug_frame, cursor), desc.format, cie, &fde[parse_fde_count])) { - fde[parse_fde_count].pc_range.min += rebase; - fde[parse_fde_count].pc_range.max += rebase; - parse_fde_count += 1; - } - } - } - - // fill out result - DW_CallFrameInfo cfi = {0}; - cfi.cie_count = parse_cie_count; - cfi.fde_count = parse_fde_count; - cfi.cie = cie; - cfi.fde = fde; - - scratch_end(scratch); - return cfi; -} - - diff --git a/src/dwarf/dwarf_help.h b/src/dwarf/dwarf_help.h deleted file mode 100644 index 2882c6b6..00000000 --- a/src/dwarf/dwarf_help.h +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef DWARF_HELP_H -#define DWARF_HELP_H - -typedef struct DW_CallFrameInfo -{ - U64 cie_count; - U64 fde_count; - DW_CIE *cie; - DW_FDE *fde; -} DW_CallFrameInfo; - -internal DW_CallFrameInfo dw_call_frame_info_from_data(Arena *arena, Arch arch, U64 rebase, String8 debug_frame); - -#endif // DWARF_HELP_H - diff --git a/src/dwarf/dwarf_inc.c b/src/dwarf/dwarf_inc.c index e6bfc490..42a9d935 100644 --- a/src/dwarf/dwarf_inc.c +++ b/src/dwarf/dwarf_inc.c @@ -2,17 +2,21 @@ // Licensed under the MIT license (https://opensource.org/license/mit/) #include "dwarf/dwarf.c" -#include "dwarf/dwarf_parse.c" -#include "dwarf/dwarf_expr.c" -#include "dwarf/dwarf_coff.c" -#include "dwarf/dwarf_elf.c" -#include "dwarf/dwarf_unwind.c" -#include "dwarf/dwarf_dump.c" -#include "dwarf/dwarf_help.c" -#include "dwarf/dwarf_writer.c" +// #include "dwarf/dwarf_parse.c" +// #include "dwarf/dwarf_expr.c" +#include "dwarf/dwarf_expr_2.c" +// #include "dwarf/dwarf_unwind.c" +// #include "dwarf/dwarf_dump.c" #include "dwarf/dwarf_parse_2.c" -#include "dwarf/eh_frame.c" -#include "dwarf/eh_dump.c" -#if defined(X64_H) -# include "dwarf/x64/dwarf_x64.c" +#include "dwarf/dwarf_unwind_2.c" +#include "dwarf/dwarf_dump_2.c" +#if defined(COFF_H) +# include "dwarf/dwarf_parse_coff.c" +#endif +#if defined(ELF_H) +# include "dwarf/dwarf_parse_elf.c" +#endif + +#if 0 // TODO(rjf): this uses concepts from the old parser +# include "dwarf/dwarf_writer.c" #endif diff --git a/src/dwarf/dwarf_inc.h b/src/dwarf/dwarf_inc.h index fa551f62..acb0be66 100644 --- a/src/dwarf/dwarf_inc.h +++ b/src/dwarf/dwarf_inc.h @@ -5,19 +5,23 @@ #define DWARF_INC_H #include "dwarf/dwarf.h" -#include "dwarf/dwarf_parse.h" -#include "dwarf/dwarf_expr.h" -#include "dwarf/dwarf_coff.h" -#include "dwarf/dwarf_elf.h" -#include "dwarf/dwarf_unwind.h" -#include "dwarf/dwarf_dump.h" -#include "dwarf/dwarf_help.h" -#include "dwarf/dwarf_writer.h" +// #include "dwarf/dwarf_parse.h" +// #include "dwarf/dwarf_expr.h" +#include "dwarf/dwarf_expr_2.h" +// #include "dwarf/dwarf_unwind.h" +// #include "dwarf/dwarf_dump.h" #include "dwarf/dwarf_parse_2.h" -#include "dwarf/eh_frame.h" -#include "dwarf/eh_dump.h" -#if defined(X64_H) -# include "dwarf/x64/dwarf_x64.h" +#include "dwarf/dwarf_unwind_2.h" +#include "dwarf/dwarf_dump_2.h" +#if defined(COFF_H) +# include "dwarf/dwarf_parse_coff.h" +#endif +#if defined(ELF_H) +# include "dwarf/dwarf_parse_elf.h" +#endif + +#if 0 // TODO(rjf): this uses concepts from the old parser +# include "dwarf/dwarf_writer.h" #endif #endif // DWARF_INC_H diff --git a/src/dwarf/dwarf_notes.txt b/src/dwarf/dwarf_notes.txt deleted file mode 100644 index 2c02214f..00000000 --- a/src/dwarf/dwarf_notes.txt +++ /dev/null @@ -1,42 +0,0 @@ ---- DWARF NOTES --------------------------------------------------------------- - -DWARF V4 Spec: http://www.dwarfstd.org/doc/DWARF4.pdf -DWARF V5 Spec: http://www.dwarfstd.org/doc/DWARF5.pdf - -------------------------------------------------------------------------------- - -$ (2021/04/30) On .debug_pubtypes, .debug_pubnames, and .debug_names: - -.debug_pubtypes and .debug_pubnames are tables that map from a string (the name -of a type or function respectively) to an offset into .debug_info, which is the -offset of the Debug Information Entry (DIE, in DWARF terminology) of the info -associated with the string. THESE TWO SECTIONS ARE OPTIONAL. They don't show up -in every DWARF-holding file, and so they cannot be relied upon as acceleration -structures. But we're going to support parsing them, to make things a bit nicer -in cases where they are present. DWARF doesn't have much in the way of acceler- -ation structures built in, so our rationale is that we should take anything we -can get to make the format a bit more in some subset of the possible cases. - -.debug_names is a DWARF V5 section that is intended to replace .debug_pubtypes -and .debug_pubnames. However, even in cases when DWARF V5 is produced at the -time of writing this, we have not found .debug_names sections being produced. -We did not exhaustively test all compilers and configurations, but it seems -that it is not well-supported at all by major compilers, and there's a very low -probability that a user will have that section, so our current thinking is -that there's no point in supporting it right now. - -------------------------------------------------------------------------------- - -$ (2021/04/30) On producing DWARF V5 with Clang: - -https://lists.llvm.org/pipermail/llvm-dev/2018-August/125068.html - -By default it looks like (at the time of writing this) that Clang, by default, -will produce DWARF V4. To produce DWARF V5, however, you can use the -gdwarf-5 -option. Even when that option is used, it seems that some features of V5 are -not used (for example, .debug_names). - -The above link also says that this will produce .debug_names, but it doesn't as -of Clang 10. - -------------------------------------------------------------------------------- diff --git a/src/dwarf/dwarf_parse.c b/src/dwarf/dwarf_parse.c index 4b8943b8..a8898477 100644 --- a/src/dwarf/dwarf_parse.c +++ b/src/dwarf/dwarf_parse.c @@ -53,18 +53,14 @@ str8_deserial_read_uleb128(String8 string, U64 off, U64 *value_out) { U8 byte = 0; U64 bytes_read = str8_deserial_read_struct(string, cursor, &byte); - if(bytes_read != sizeof(byte)) { break; } - U8 val = byte & 0x7fu; value |= ((U64)val) << shift; - cursor += bytes_read; shift += 7u; - if((byte & 0x80u) == 0) { break; @@ -92,13 +88,10 @@ str8_deserial_read_sleb128(String8 string, U64 off, S64 *value_out) { break; } - U8 val = byte & 0x7fu; value |= ((U64)val) << shift; - cursor += bytes_read; shift += 7u; - if((byte & 0x80u) == 0) { if(shift < sizeof(value) * 8 && (byte & 0x40u) != 0) @@ -2912,7 +2905,7 @@ dw_parse_descriptor_entry_header(String8 data, U64 off, DW_DescriptorEntry *desc U64 id_type = format == DW_Format_32Bit ? max_U32 : max_U64; desc_out->format = format; - desc_out->type = (id == id_type) ? DW_DescriptorEntryType_CIE : DW_DescriptorEntryType_FDE; + desc_out->type = (id == id_type) ? DW_DescriptorEntryKind_CIE : DW_DescriptorEntryKind_FDE; desc_out->entry_range = entry_range; desc_out->cie_pointer = id; desc_out->cie_pointer_off = length_size; @@ -2952,7 +2945,6 @@ dw_parse_cie(String8 data, DW_Format format, Arch arch, DW_CIE *cie_out) TryRead(str8_deserial_read_sleb128(data, cursor, &data_align_factor), cursor, exit); U64 ret_addr_reg = 0; - U64 ret_addr_reg_size = 0; if (version == DW_Version_1) { TryRead(str8_deserial_read(data, cursor, &ret_addr_reg, sizeof(U8), sizeof(U8)), cursor, exit); } else { TryRead(str8_deserial_read_uleb128(data, cursor, &ret_addr_reg), cursor, exit); } @@ -3012,14 +3004,14 @@ dw_parse_cfi(String8 data, U64 fde_offset, Arch arch, DW_CIE *cie_out, DW_FDE *f B32 is_parsed = 0; DW_DescriptorEntry fde_desc = {0}; dw_parse_descriptor_entry_header(data, fde_offset, &fde_desc); - if (fde_desc.type == DW_DescriptorEntryType_FDE) { + if (fde_desc.type == DW_DescriptorEntryKind_FDE) { U64 cie_pointer_off = fde_desc.format == DW_Format_32Bit ? 4 : 12; U64 cie_pointer = 0; U64 cie_pointer_size = str8_deserial_read_dwarf_uint(data, fde_offset + cie_pointer_off, fde_desc.format, &cie_pointer); if (cie_pointer_size) { DW_DescriptorEntry cie_desc = {0}; dw_parse_descriptor_entry_header(data, cie_pointer, &cie_desc); - if (cie_desc.type == DW_DescriptorEntryType_CIE) { + if (cie_desc.type == DW_DescriptorEntryKind_CIE) { if (dw_parse_cie(str8_substr(data, cie_desc.entry_range), cie_desc.format, arch, cie_out)) { if (dw_parse_fde(str8_substr(data, fde_desc.entry_range), fde_desc.format, cie_out, fde_out)) { is_parsed = 1; diff --git a/src/dwarf/dwarf_parse.h b/src/dwarf/dwarf_parse.h index edd7c1f0..2bddbe72 100644 --- a/src/dwarf/dwarf_parse.h +++ b/src/dwarf/dwarf_parse.h @@ -335,22 +335,6 @@ typedef struct DW_Expr //////////////////////////////// // .debug_frame -typedef enum -{ - DW_DescriptorEntryType_Null, - DW_DescriptorEntryType_CIE, - DW_DescriptorEntryType_FDE -} DW_DescriptorEntryType; - -typedef struct DW_DescriptorEntry -{ - DW_DescriptorEntryType type; - DW_Format format; - Rng1U64 entry_range; - U64 cie_pointer_off; - U64 cie_pointer; -} DW_DescriptorEntry; - typedef struct DW_CIE { String8 insts; @@ -418,8 +402,6 @@ internal U64 dw_hash_from_string(String8 string); internal U64 str8_deserial_read_dwarf_packed_size(String8 string, U64 off, U64 *size_out); internal U64 str8_deserial_read_dwarf_uint (String8 string, U64 off, DW_Format format, U64 *uint_out); -internal U64 str8_deserial_read_uleb128 (String8 string, U64 off, U64 *value_out); -internal U64 str8_deserial_read_sleb128 (String8 string, U64 off, S64 *value_out); internal U64 str8_deserial_read_uleb128_array(Arena *arena, String8 string, U64 off, U64 count, U64 **arr_out); internal U64 str8_deserial_read_sleb128_array(Arena *arena, String8 string, U64 off, U64 count, S64 **arr_out); diff --git a/src/dwarf/dwarf_parse_2.c b/src/dwarf/dwarf_parse_2.c index 39ee4e1f..2bba4b22 100644 --- a/src/dwarf/dwarf_parse_2.c +++ b/src/dwarf/dwarf_parse_2.c @@ -56,6 +56,17 @@ dw2_read_fmt_u64(String8 data, U64 off, DW_Format format, U64 *out) return bytes_read; } +internal DW_SectionKind +dw_section_kind_from_string(String8 string) +{ + DW_SectionKind s = DW_SectionKind_Null; + if(0){} +#define X(name, regular_string, macho_string, dwo_string) else if(str8_match(string, s(regular_string), 0) || str8_match(string, s(macho_string), 0) || str8_match(string, s(dwo_string), 0)) { s = DW_SectionKind_##name; } + DW_SectionKind_XList +#undef X + return s; +} + //////////////////////////////// //~ rjf: Unit Range Set Parsing (Top-Level .debug_info, .debug_aranges) @@ -190,6 +201,72 @@ dw2_read_unit_header(String8 data, U64 off, DW2_UnitHeader *out) //////////////////////////////// //~ rjf: Abbreviation Map Parsing +internal U64 +dw2_read_abbrev(Arena *arena, String8 data, U64 off, DW2_AbbrevHeader *header_out, DW2_AbbrevAttribList *attribs_out) +{ + U64 start_off = off; + + // rjf: read tag abbrev + U64 id = 0; + U64 tag_kind = 0; + U8 has_children = 0; + { + off += str8_deserial_read_uleb128(data, off, &id); + if(id != 0) + { + off += str8_deserial_read_uleb128(data, off, &tag_kind); + off += str8_deserial_read_struct(data, off, &has_children); + } + } + + // rjf: read all tag attribute abbreviations + DW2_AbbrevAttribList attribs = {0}; + if(id != 0) + { + for(;;) + { + U64 attrib_start_off = off; + U64 attrib_kind = 0; + U64 attrib_form_kind = 0; + U64 implicit_const = 0; + off += str8_deserial_read_uleb128(data, off, &attrib_kind); + off += str8_deserial_read_uleb128(data, off, &attrib_form_kind); + if(attrib_form_kind == DW_FormKind_ImplicitConst) + { + off += str8_deserial_read_uleb128(data, off, &implicit_const); + } + if(attrib_kind != 0 && attribs_out != 0) + { + DW2_AbbrevAttribNode *n = push_array(arena, DW2_AbbrevAttribNode, 1); + SLLQueuePush(attribs.first, attribs.last, n); + attribs.count += 1; + n->v.attrib_kind = attrib_kind; + n->v.form_kind = attrib_form_kind; + n->v.implicit_const = implicit_const; + } + if(off == attrib_start_off || attrib_kind == 0) + { + break; + } + } + } + + // rjf: fill + if(header_out != 0) + { + header_out->id = id; + header_out->tag_kind = tag_kind; + header_out->has_children = has_children; + } + if(attribs_out != 0) + { + MemoryCopyStruct(attribs_out, &attribs); + } + + U64 bytes_read = (off-start_off); + return bytes_read; +} + internal DW2_AbbrevMap dw2_abbrev_map_from_data(Arena *arena, String8 data, U64 off) { @@ -206,60 +283,29 @@ dw2_abbrev_map_from_data(Arena *arena, String8 data, U64 off) { U64 start_off = read_off; - // rjf: read tag abbrev - U64 id = 0; - U64 tag_kind = 0; - U8 has_children = 0; - { - read_off += str8_deserial_read_uleb128(data, read_off, &id); - if(id != 0) - { - read_off += str8_deserial_read_uleb128(data, read_off, &tag_kind); - read_off += str8_deserial_read_struct(data, read_off, &has_children); - } - } + // rjf: read next abbrev + DW2_AbbrevHeader header = {0}; + read_off += dw2_read_abbrev(arena, data, read_off, &header, 0); // rjf: count tag - if(id != 0) + if(header.id != 0) { tag_count += 1; } - // rjf: read all tag attribute abbreviations - if(id != 0) - { - for(;;) - { - U64 attrib_start_off = read_off; - U64 attrib_kind = 0; - U64 attrib_form_kind = 0; - U64 implicit_const = 0; - read_off += str8_deserial_read_uleb128(data, read_off, &attrib_kind); - read_off += str8_deserial_read_uleb128(data, read_off, &attrib_form_kind); - if(attrib_form_kind == DW_Form_ImplicitConst) - { - read_off += str8_deserial_read_uleb128(data, read_off, &implicit_const); - } - if(read_off == attrib_start_off || attrib_kind == 0) - { - break; - } - } - } - // rjf: if building (second loop) -> insert into map if(build) { - U64 hash = u64_hash_from_str8(str8_struct(&id)); + U64 hash = u64_hash_from_str8(str8_struct(&header.id)); U64 slot_idx = hash%map.slots_count; DW2_AbbrevMapNode *n = push_array(arena, DW2_AbbrevMapNode, 1); SLLStackPush(map.slots[slot_idx], n); - n->id = id; + n->id = header.id; n->off = start_off; } // rjf: no tag ID, or no movement? -> exit - if(read_off == start_off || id == 0) + if(read_off == start_off || header.id == 0) { break; } @@ -276,11 +322,132 @@ dw2_abbrev_map_from_data(Arena *arena, String8 data, U64 off) return map; } +internal DW2_UnitAbbrevMapMap +dw2_unit_abbrev_map_map_from_data(Arena *arena, String8 data, DW2_UnitHeader *unit_headers, U64 unit_headers_count) +{ + DW2_UnitAbbrevMapMap result = {0}; + Temp scratch = scratch_begin(&arena, 1); + + //- rjf: gather deduplicated .debug_abbrev offsets, representing beginnings + // of abbreviation tables. we want to do this because many units may refer + // to the same abbreviation table. + // + typedef struct AbbrevOffNode AbbrevOffNode; + struct AbbrevOffNode + { + AbbrevOffNode *order_next; + AbbrevOffNode *hash_next; + U64 off; + U64 idx; + }; + U64 abbrev_off_slots_count = unit_headers_count; + AbbrevOffNode **abbrev_off_slots = 0; + U64 abbrev_map_count = 0; + U64 *abbrev_map_off_from_idx_table = 0; + DW2_AbbrevMap *abbrev_map_from_idx_table = 0; + DW2_AbbrevMap **abbrev_map_from_unit_idx_table = 0; + ProfScope("gather deduplicated .debug_abbrev offsets") if(lane_idx() == 0) + { + AbbrevOffNode *abbrev_off_first = 0; + AbbrevOffNode *abbrev_off_last = 0; + abbrev_off_slots = push_array(scratch.arena, AbbrevOffNode *, abbrev_off_slots_count); + for EachIndex(unit_idx, unit_headers_count) + { + U64 abbrev_off = unit_headers[unit_idx].abbrev_off; + U64 hash = u64_hash_from_str8(str8_struct(&abbrev_off)); + U64 slot_idx = hash%abbrev_off_slots_count; + AbbrevOffNode *node = 0; + for(AbbrevOffNode *n = abbrev_off_slots[slot_idx]; n != 0; n = n->hash_next) + { + if(n->off == abbrev_off) + { + node = n; + break; + } + } + if(node == 0) + { + node = push_array(arena, AbbrevOffNode, 1); + SLLStackPush_N(abbrev_off_slots[slot_idx], node, hash_next); + SLLQueuePush_N(abbrev_off_first, abbrev_off_last, node, order_next); + node->off = abbrev_off; + node->idx = abbrev_map_count; + abbrev_map_count += 1; + } + } + abbrev_map_off_from_idx_table = push_array(scratch.arena, U64, abbrev_map_count); + abbrev_map_from_idx_table = push_array(arena, DW2_AbbrevMap, abbrev_map_count); + abbrev_map_from_unit_idx_table = push_array(arena, DW2_AbbrevMap *, unit_headers_count); + { + U64 abbrev_map_idx = 0; + for(AbbrevOffNode *n = abbrev_off_first; n != 0; n = n->order_next) + { + abbrev_map_off_from_idx_table[abbrev_map_idx] = n->off; + abbrev_map_idx += 1; + } + } + } + lane_sync_u64(&abbrev_off_slots, 0); + lane_sync_u64(&abbrev_map_count, 0); + lane_sync_u64(&abbrev_map_off_from_idx_table, 0); + lane_sync_u64(&abbrev_map_from_idx_table, 0); + lane_sync_u64(&abbrev_map_from_unit_idx_table, 0); + + //- rjf: build all unique abbreviation maps + ProfScope("build all unique abbreviation maps") + { + U64 abbrev_map_take_idx = 0; + U64 *abbrev_map_take_idx_ptr = &abbrev_map_take_idx; + lane_sync_u64(&abbrev_map_take_idx_ptr, 0); + for(;;) + { + U64 abbrev_map_idx = ins_atomic_u64_inc_eval(abbrev_map_take_idx_ptr) - 1; + if(abbrev_map_idx >= abbrev_map_count) + { + break; + } + abbrev_map_from_idx_table[abbrev_map_idx] = dw2_abbrev_map_from_data(arena, data, abbrev_map_off_from_idx_table[abbrev_map_idx]); + } + lane_sync(); + } + + //- rjf: build unit -> abbrev map table + ProfScope("build unit -> abbrev map table") + { + Rng1U64 range = lane_range(unit_headers_count); + for EachInRange(unit_idx, range) + { + U64 abbrev_off = unit_headers[unit_idx].abbrev_off; + U64 hash = u64_hash_from_str8(str8_struct(&abbrev_off)); + U64 slot_idx = hash%abbrev_off_slots_count; + U64 abbrev_map_idx = 0; + for(AbbrevOffNode *n = abbrev_off_slots[slot_idx]; n != 0; n = n->hash_next) + { + if(n->off == abbrev_off) + { + abbrev_map_idx = n->idx; + break; + } + } + abbrev_map_from_unit_idx_table[unit_idx] = &abbrev_map_from_idx_table[abbrev_map_idx]; + } + lane_sync(); + } + + //- rjf: fill result + result.map_count = abbrev_map_count; + result.map_from_idx_table = abbrev_map_from_idx_table; + result.map_from_unit_idx_table = abbrev_map_from_unit_idx_table; + + scratch_end(scratch); + return result; +} + //////////////////////////////// //~ rjf: Form Value Parsing internal U64 -dw2_read_form_val(DW2_ParseCtx *ctx, String8 data, U64 off, DW_FormKind form_kind, U64 implicit_const, DW2_FormVal *out) +dw2_read_form_val(DW_Raw *raw, DW2_ParseCtx *ctx, String8 data, U64 off, DW_FormKind form_kind, U64 implicit_const, DW2_FormVal *out) { U64 start_off = off; DW2_FormVal val = {form_kind}; @@ -291,60 +458,60 @@ dw2_read_form_val(DW2_ParseCtx *ctx, String8 data, U64 off, DW_FormKind form_kin { //- rjf: no-ops default: - case DW_Form_Indirect: - case DW_Form_Null: + case DW_FormKind_Indirect: + case DW_FormKind_Null: {}break; //- rjf: 1-byte uint reads - case DW_Form_Ref1: - case DW_Form_Data1: - case DW_Form_Flag: - case DW_Form_Strx1: - case DW_Form_Addrx1: + case DW_FormKind_Ref1: + case DW_FormKind_Data1: + case DW_FormKind_Flag: + case DW_FormKind_Strx1: + case DW_FormKind_Addrx1: bytes_to_read = 1; goto read_fixed_uint; //- rjf: 2-byte uint reads - case DW_Form_Ref2: - case DW_Form_Data2: - case DW_Form_Strx2: - case DW_Form_Addrx2: + case DW_FormKind_Ref2: + case DW_FormKind_Data2: + case DW_FormKind_Strx2: + case DW_FormKind_Addrx2: bytes_to_read = 2; goto read_fixed_uint; //- rjf: 3-byte uint reads - case DW_Form_Strx3: - case DW_Form_Addrx3: + case DW_FormKind_Strx3: + case DW_FormKind_Addrx3: bytes_to_read = 3; goto read_fixed_uint; //- rjf: 4-byte uint reads - case DW_Form_Data4: - case DW_Form_Ref4: - case DW_Form_RefSup4: - case DW_Form_Strx4: - case DW_Form_Addrx4: + case DW_FormKind_Data4: + case DW_FormKind_Ref4: + case DW_FormKind_RefSup4: + case DW_FormKind_Strx4: + case DW_FormKind_Addrx4: bytes_to_read = 4; goto read_fixed_uint; //- rjf: 8-byte uint reads - case DW_Form_Data8: - case DW_Form_Ref8: - case DW_Form_RefSig8: - case DW_Form_RefSup8: + case DW_FormKind_Data8: + case DW_FormKind_Ref8: + case DW_FormKind_RefSig8: + case DW_FormKind_RefSup8: bytes_to_read = 8; goto read_fixed_uint; //- rjf: 16-byte uint reads - case DW_Form_Data16: + case DW_FormKind_Data16: bytes_to_read = 16; goto read_fixed_uint; //- rjf: address-sized reads - case DW_Form_Addr: + case DW_FormKind_Addr: bytes_to_read = ctx->addr_size; goto read_fixed_uint; //- rjf: format-defined-size reads - case DW_Form_RefAddr: - case DW_Form_SecOffset: - case DW_Form_LineStrp: - case DW_Form_Strp: - case DW_Form_StrpSup: - bytes_to_read = dw_size_from_format(ctx->format); goto read_fixed_uint; + case DW_FormKind_RefAddr: + case DW_FormKind_SecOffset: + case DW_FormKind_LineStrp: + case DW_FormKind_Strp: + case DW_FormKind_StrpSup: + bytes_to_read = dw_addr_size_from_format(ctx->format); goto read_fixed_uint; //- rjf: fixed-size uint reads read_fixed_uint: @@ -353,26 +520,26 @@ dw2_read_form_val(DW2_ParseCtx *ctx, String8 data, U64 off, DW_FormKind form_kin }break; //- rjf: uleb128 reads - case DW_Form_UData: - case DW_Form_RefUData: - case DW_Form_Strx: - case DW_Form_Addrx: - case DW_Form_LocListx: - case DW_Form_RngListx: + case DW_FormKind_UData: + case DW_FormKind_RefUData: + case DW_FormKind_Strx: + case DW_FormKind_Addrx: + case DW_FormKind_LocListx: + case DW_FormKind_RngListx: { off += str8_deserial_read_uleb128(data, off, &val.u128.u64[0]); }break; //- rjf: sleb128 reads - case DW_Form_SData: + case DW_FormKind_SData: { off += str8_deserial_read_sleb128(data, off, (S64 *)(&val.u128.u64[0])); }break; //- rjf: fixed-size uint reads / skips - case DW_Form_Block1: bytes_to_read = 1; goto read_fixed_uint_and_skip; - case DW_Form_Block2: bytes_to_read = 2; goto read_fixed_uint_and_skip; - case DW_Form_Block4: bytes_to_read = 4; goto read_fixed_uint_and_skip; + case DW_FormKind_Block1: bytes_to_read = 1; goto read_fixed_uint_and_skip; + case DW_FormKind_Block2: bytes_to_read = 2; goto read_fixed_uint_and_skip; + case DW_FormKind_Block4: bytes_to_read = 4; goto read_fixed_uint_and_skip; read_fixed_uint_and_skip: { U64 size = 0; @@ -384,7 +551,7 @@ dw2_read_form_val(DW2_ParseCtx *ctx, String8 data, U64 off, DW_FormKind form_kin }break; //- rjf: uleb128 read & skip - case DW_Form_Block: + case DW_FormKind_Block: { U64 size = 0; U64 og_read_off = off; @@ -394,7 +561,7 @@ dw2_read_form_val(DW2_ParseCtx *ctx, String8 data, U64 off, DW_FormKind form_kin }break; //- rjf: strings - case DW_Form_String: + case DW_FormKind_String: { String8 string = {0}; U64 og_read_off = off; @@ -403,24 +570,25 @@ dw2_read_form_val(DW2_ParseCtx *ctx, String8 data, U64 off, DW_FormKind form_kin }break; //- rjf: implicit constants (no reading in .debug_info; value comes from .debug_abbrev) - case DW_Form_ImplicitConst: + case DW_FormKind_ImplicitConst: { val.u128.u64[0] = implicit_const; }break; //- rjf: exprloc - case DW_Form_ExprLoc: + case DW_FormKind_ExprLoc: { U64 size = 0; U64 og_read_off = off; U64 bytes_read = str8_deserial_read_uleb128(data, off, &size); val.u128.u64[0] = og_read_off + bytes_read; val.u128.u64[1] = size; + val.string = str8_substr(data, r1u64(og_read_off, og_read_off+size)); off += size + bytes_read; }break; //- rjf: flag present - case DW_Form_FlagPresent: + case DW_FormKind_FlagPresent: { val.u128.u64[0] = 1; }break; @@ -431,32 +599,32 @@ dw2_read_form_val(DW2_ParseCtx *ctx, String8 data, U64 off, DW_FormKind form_kin switch(form_kind) { default:{}break; - case DW_Form_Strx1: - case DW_Form_Strx2: - case DW_Form_Strx3: - case DW_Form_Strx4: - case DW_Form_Strx: + case DW_FormKind_Strx1: + case DW_FormKind_Strx2: + case DW_FormKind_Strx3: + case DW_FormKind_Strx4: + case DW_FormKind_Strx: if(ctx->str_offsets_table != 0) { U64 entry_idx = val.u128.u64[0]; U64 string_data_off = 0; if(dw2_try_offset_from_table_idx(ctx->str_offsets_table, entry_idx, &string_data_off)) { - String8 string_section_data = ctx->raw->sec[DW_Section_Str].data; + String8 string_section_data = raw->sec[DW_SectionKind_Str].data; val.string = str8_cstring_capped(string_section_data.str + string_data_off, string_section_data.str + string_section_data.size); } }break; - case DW_Form_LineStrp: + case DW_FormKind_LineStrp: { - String8 string_section_data = ctx->raw->sec[DW_Section_LineStr].data; + String8 string_section_data = raw->sec[DW_SectionKind_LineStr].data; val.string = str8_cstring_capped(string_section_data.str + val.u128.u64[0], string_section_data.str + string_section_data.size); }break; - case DW_Form_Strp: - case DW_Form_StrpSup: + case DW_FormKind_Strp: + case DW_FormKind_StrpSup: { - String8 string_section_data = ctx->raw->sec[DW_Section_Str].data; + String8 string_section_data = raw->sec[DW_SectionKind_Str].data; val.string = str8_cstring_capped(string_section_data.str + val.u128.u64[0], string_section_data.str + string_section_data.size); }break; @@ -468,11 +636,11 @@ dw2_read_form_val(DW2_ParseCtx *ctx, String8 data, U64 off, DW_FormKind form_kin switch(form_kind) { default:{val.addr = val.u128.u64[0];}break; - case DW_Form_Addrx: - case DW_Form_Addrx1: - case DW_Form_Addrx2: - case DW_Form_Addrx3: - case DW_Form_Addrx4: + case DW_FormKind_Addrx: + case DW_FormKind_Addrx1: + case DW_FormKind_Addrx2: + case DW_FormKind_Addrx3: + case DW_FormKind_Addrx4: if(ctx->addr_table != 0) { U64 addr_idx = val.u128.u64[0]; @@ -486,11 +654,114 @@ dw2_read_form_val(DW2_ParseCtx *ctx, String8 data, U64 off, DW_FormKind form_kin return bytes_read; } +//////////////////////////////// +//~ rjf: Parse Context Construction + +internal void +dw2_parse_ctx_equip_unit_header(DW2_ParseCtx *ctx_out, DW2_UnitHeader *hdr, U64 unit_base_info_off) +{ + ctx_out->version = hdr->version; + ctx_out->format = hdr->format; + ctx_out->addr_size = hdr->addr_size; + ctx_out->unit_base_info_off = unit_base_info_off; +} + +internal void +dw2_parse_ctx_equip_unit_abbrev_map(DW2_ParseCtx *ctx_out, DW2_UnitAbbrevMapMap *map, U64 unit_idx) +{ + ctx_out->abbrev_map = map->map_from_unit_idx_table[unit_idx]; +} + +internal void +dw2_parse_ctx_equip_unit_root_tag(DW2_ParseCtx *ctx_out, DW2_Tag *tag, DW2_OffsetTableSet *offset_tables) +{ + // rjf: unpack attributes + DW2_Attrib *low_pc_attrib = &dw2_attrib_nil; + DW2_Attrib *lang_attrib = &dw2_attrib_nil; + DW2_Attrib *rnglists_base_off_attrib = &dw2_attrib_nil; + DW2_Attrib *str_offsets_base_off_attrib = &dw2_attrib_nil; + DW2_Attrib *addr_base_off_attrib = &dw2_attrib_nil; + DW2_Attrib *loclist_base_off_attrib = &dw2_attrib_nil; + DW2_Attrib *comp_dir_attrib = &dw2_attrib_nil; + for EachNode(n, DW2_AttribNode, tag->attribs.first) + { + switch(n->v.attrib_kind) + { + default:{}break; +#define Case(name, name_upper) case DW_AttribKind_##name_upper:{name##_attrib = &n->v;}break + Case(low_pc, LowPc); + Case(lang, Language); + Case(rnglists_base_off, RngListsBase); + Case(str_offsets_base_off, StrOffsetsBase); + Case(addr_base_off, AddrBase); + Case(loclist_base_off, LocListsBase); + Case(comp_dir, CompDir); +#undef Case + } + } + + // rjf: fill basics + ctx_out->unit_base_addr = low_pc_attrib->val.addr; + ctx_out->language = lang_attrib->val.u128.u64[0]; + ctx_out->unit_dir = comp_dir_attrib->val.string; + + // rjf: find offset tables + Rng1U64Array rnglists_tables_ranges_array = {offset_tables->rnglists_tables_ranges, offset_tables->rnglists_tables_count}; + Rng1U64Array str_offsets_tables_ranges_array = {offset_tables->str_offsets_tables_ranges, offset_tables->str_offsets_tables_count}; + Rng1U64Array addr_tables_ranges_array = {offset_tables->addr_tables_ranges, offset_tables->addr_tables_count}; + Rng1U64Array loclist_tables_ranges_array = {offset_tables->loclists_tables_ranges, offset_tables->loclists_tables_count}; + { + // rjf: find rnglists table + { + U64 rnglists_base_off = rnglists_base_off_attrib->val.u128.u64[0]; + U64 rnglists_table_num = rng1u64_array_num_from_value__binary_search(&rnglists_tables_ranges_array, rnglists_base_off); + if(0 < rnglists_table_num && rnglists_table_num <= rnglists_tables_ranges_array.count) + { + DW2_OffsetTable *table = &offset_tables->rnglists_tables[rnglists_table_num-1]; + ctx_out->rnglists_table = table; + } + } + + // rjf: find str offsets table + { + U64 str_offsets_base_off = str_offsets_base_off_attrib->val.u128.u64[0]; + U64 str_offsets_table_num = rng1u64_array_num_from_value__binary_search(&str_offsets_tables_ranges_array, str_offsets_base_off); + if(0 < str_offsets_table_num && str_offsets_table_num <= str_offsets_tables_ranges_array.count) + { + DW2_OffsetTable *table = &offset_tables->str_offsets_tables[str_offsets_table_num-1]; + ctx_out->str_offsets_table = table; + } + } + + // rjf: find addr table + { + U64 addr_base_off = addr_base_off_attrib->val.u128.u64[0]; + U64 addr_table_num = rng1u64_array_num_from_value__binary_search(&addr_tables_ranges_array, addr_base_off); + if(0 < addr_table_num && addr_table_num <= addr_tables_ranges_array.count) + { + DW2_OffsetTable *table = &offset_tables->addr_tables[addr_table_num-1]; + ctx_out->addr_table = table; + } + } + + // rjf: find loclists table + { + U64 loclist_base_off = loclist_base_off_attrib->val.u128.u64[0]; + U64 loclist_table_num = rng1u64_array_num_from_value__binary_search(&loclist_tables_ranges_array, loclist_base_off); + if(0 < loclist_table_num && loclist_table_num <= loclist_tables_ranges_array.count) + { + DW2_OffsetTable *table = &offset_tables->loclists_tables[loclist_table_num-1]; + ctx_out->loclists_table = table; + } + } + } +} + //////////////////////////////// //~ rjf: Tag Parsing internal U64 -dw2_read_tag(Arena *arena, DW2_ParseCtx *ctx, String8 data, U64 off, DW2_Tag *tag_out) +dw2_read_tag(Arena *arena, DW_Raw *raw, DW2_ParseCtx *ctx, String8 data, U64 off, DW2_Tag *tag_out) { U64 start_off = off; @@ -519,7 +790,7 @@ dw2_read_tag(Arena *arena, DW2_ParseCtx *ctx, String8 data, U64 off, DW2_Tag *ta U8 has_children = 0; U64 attrib_abbrev_read_off = 0; { - String8 abbrev_data = ctx->raw->sec[DW_Section_Abbrev].data; + String8 abbrev_data = raw->sec[DW_SectionKind_Abbrev].data; U64 abbrev_read_off = abbrev_off; U64 id = 0; abbrev_read_off += str8_deserial_read_uleb128(abbrev_data, abbrev_read_off, &id); @@ -535,7 +806,7 @@ dw2_read_tag(Arena *arena, DW2_ParseCtx *ctx, String8 data, U64 off, DW2_Tag *ta DW2_AttribList attribs = {0}; if(abbrev_id != 0) { - String8 abbrev_data = ctx->raw->sec[DW_Section_Abbrev].data; + String8 abbrev_data = raw->sec[DW_SectionKind_Abbrev].data; U64 abbrev_read_off = attrib_abbrev_read_off; for(;;) { @@ -547,13 +818,13 @@ dw2_read_tag(Arena *arena, DW2_ParseCtx *ctx, String8 data, U64 off, DW2_Tag *ta U64 implicit_const = 0; abbrev_read_off += str8_deserial_read_uleb128(abbrev_data, abbrev_read_off, &attrib_kind); abbrev_read_off += str8_deserial_read_uleb128(abbrev_data, abbrev_read_off, &attrib_form_kind); - if(attrib_form_kind == DW_Form_ImplicitConst) + if(attrib_form_kind == DW_FormKind_ImplicitConst) { abbrev_read_off += str8_deserial_read_uleb128(abbrev_data, abbrev_read_off, &implicit_const); } // rjf: indirect form -> form kind is encoded in .debug_info (because why not) - if(attrib_form_kind == DW_Form_Indirect) + if(attrib_form_kind == DW_FormKind_Indirect) { off += str8_deserial_read_uleb128(data, off, &attrib_form_kind); } @@ -562,7 +833,7 @@ dw2_read_tag(Arena *arena, DW2_ParseCtx *ctx, String8 data, U64 off, DW2_Tag *ta DW2_FormVal val = {0}; if(attrib_kind != 0) { - off += dw2_read_form_val(ctx, data, off, attrib_form_kind, implicit_const, &val); + off += dw2_read_form_val(raw, ctx, data, off, attrib_form_kind, implicit_const, &val); } // rjf: push @@ -615,14 +886,14 @@ dw2_reference_info_off_from_form_val(DW2_ParseCtx *ctx, DW2_FormVal *v) switch(v->kind) { default:{}break; - case DW_Form_Ref1: - case DW_Form_Ref2: - case DW_Form_Ref4: - case DW_Form_Ref8: + case DW_FormKind_Ref1: + case DW_FormKind_Ref2: + case DW_FormKind_Ref4: + case DW_FormKind_Ref8: { result = ctx->unit_base_info_off + v->u128.u64[0]; }break; - // TODO(rjf): DW_Form_RefAddr, DW_Form_RefUData, DW_Form_RefSig8, DW_Form_RefSup8, etc. + // TODO(rjf): DW_FormKind_RefAddr, DW_FormKind_RefUData, DW_FormKind_RefSig8, DW_FormKind_RefSup8, etc. } return result; } @@ -631,7 +902,7 @@ dw2_reference_info_off_from_form_val(DW2_ParseCtx *ctx, DW2_FormVal *v) //~ rjf: Line Table Parsing internal U64 -dw2_read_line_table_header(Arena *arena, DW2_ParseCtx *ctx, String8 data, U64 off, DW2_LineTableHeader *out) +dw2_read_line_table_header(Arena *arena, DW_Raw *raw, DW2_ParseCtx *ctx, String8 data, U64 off, DW2_LineTableHeader *out) { Temp scratch = scratch_begin(&arena, 1); U64 start_off = off; @@ -847,7 +1118,7 @@ dw2_read_line_table_header(Arena *arena, DW2_ParseCtx *ctx, String8 data, U64 of DW_LNCT lnct = (DW_LNCT)entry_formats[entry_format_idx*2 + 0]; DW_FormKind form_kind = (DW_FormKind)entry_formats[entry_format_idx*2 + 1]; DW2_FormVal form_val = {0}; - off += dw2_read_form_val(ctx, data, off, form_kind, 0, &form_val); + off += dw2_read_form_val(raw, ctx, data, off, form_kind, 0, &form_val); switch(lnct) { default: @@ -952,7 +1223,7 @@ dw2_read_offset_table(String8 data, U64 off, DW2_OffsetTable *out) off += str8_deserial_read_struct(data, off, &segment_selector_size); // rjf: determine entry size - U64 entry_size = dw_size_from_format(format); + U64 entry_size = dw_addr_size_from_format(format); if(addr_size != 0) { entry_size = addr_size + segment_selector_size; @@ -975,6 +1246,31 @@ dw2_read_offset_table(String8 data, U64 off, DW2_OffsetTable *out) return bytes_read; } +internal DW2_OffsetTableList +dw2_offset_table_list_from_data(Arena *arena, String8 data) +{ + DW2_OffsetTableList list = {0}; + for(U64 off = 0; off < data.size;) + { + U64 start_off = off; + DW2_OffsetTable table = {0}; + off += dw2_read_offset_table(data, off, &table); + if(table.entries != 0) + { + DW2_OffsetTableNode *n = push_array(arena, DW2_OffsetTableNode, 1); + SLLQueuePush(list.first, list.last, n); + n->v = table; + n->range = r1u64(start_off, off); + list.count += 1; + } + if(off == start_off) + { + break; + } + } + return list; +} + internal B32 dw2_try_offset_from_table_idx(DW2_OffsetTable *tbl, U64 idx, U64 *out) { @@ -1003,6 +1299,48 @@ dw2_try_offset_from_table_idx(DW2_OffsetTable *tbl, U64 idx, U64 *out) return result; } +internal DW2_OffsetTableSet +dw2_offset_table_set_from_raw(Arena *arena, DW_Raw *raw) +{ + DW2_OffsetTableSet result = {0}; + { + struct + { + String8 data; + U64 *tables_count_out; + DW2_OffsetTable **tables_out; + Rng1U64 **tables_ranges_out; + } + tasks[] = + { + {raw->sec[DW_SectionKind_StrOffsets].data, &result.str_offsets_tables_count, &result.str_offsets_tables, &result.str_offsets_tables_ranges}, + {raw->sec[DW_SectionKind_RngLists].data, &result.rnglists_tables_count, &result.rnglists_tables, &result.rnglists_tables_ranges}, + {raw->sec[DW_SectionKind_Addr].data, &result.addr_tables_count, &result.addr_tables, &result.addr_tables_ranges}, + {raw->sec[DW_SectionKind_LocLists].data, &result.loclists_tables_count, &result.loclists_tables, &result.loclists_tables_ranges}, + }; + for EachElement(task_idx, tasks) + { + Temp scratch = scratch_begin(&arena, 1); + String8 data = tasks[task_idx].data; + DW2_OffsetTableList tables = dw2_offset_table_list_from_data(scratch.arena, data); + tasks[task_idx].tables_count_out[0] = tables.count; + tasks[task_idx].tables_out[0] = push_array(arena, DW2_OffsetTable, tables.count); + tasks[task_idx].tables_ranges_out[0] = push_array(arena, Rng1U64, tables.count); + { + U64 idx = 0; + for EachNode(n, DW2_OffsetTableNode, tables.first) + { + tasks[task_idx].tables_out[0][idx] = n->v; + tasks[task_idx].tables_ranges_out[0][idx] = n->range; + idx += 1; + } + } + scratch_end(scratch); + } + } + return result; +} + //////////////////////////////// //~ rjf: Range List Parsing (.debug_rnglists) @@ -1020,7 +1358,7 @@ dw2_rnglist_from_form_val(Arena *arena, DW2_ParseCtx *ctx, DW_Raw *raw, DW2_Form case DW_Version_3: case DW_Version_4: { - String8 data = raw->sec[DW_Section_Ranges].data; + String8 data = raw->sec[DW_SectionKind_Ranges].data; U64 ranges_off = ranges_off = form_val.u128.u64[0];; U64 base_addr = ctx->unit_base_addr; U64 sentinel = (ctx->addr_size == 4 ? max_U32 : max_U64); @@ -1063,7 +1401,7 @@ dw2_rnglist_from_form_val(Arena *arena, DW2_ParseCtx *ctx, DW_Raw *raw, DW2_Form //- rjf: @dwarf5 case DW_Version_5: { - String8 data = raw->sec[DW_Section_RngLists].data; + String8 data = raw->sec[DW_SectionKind_RngLists].data; // rjf: determine section offset - sometimes this is encoded directly, // other times it is relative, based on the form kind - more variability @@ -1072,11 +1410,11 @@ dw2_rnglist_from_form_val(Arena *arena, DW2_ParseCtx *ctx, DW_Raw *raw, DW2_Form switch(form_val.kind) { default:{}break; - case DW_Form_SecOffset: + case DW_FormKind_SecOffset: { rnglist_off = form_val.u128.u64[0]; }break; - case DW_Form_RngListx: + case DW_FormKind_RngListx: if(ctx->rnglists_table != 0) { U64 rnglist_off_idx = form_val.u128.u64[0]; @@ -1219,7 +1557,7 @@ dw2_loclist_from_form_val(Arena *arena, DW2_ParseCtx *ctx, DW_Raw *raw, DW2_Form case DW_Version_3: case DW_Version_4: { - String8 data = raw->sec[DW_Section_Loc].data; + String8 data = raw->sec[DW_SectionKind_Loc].data; U64 locs_off = locs_off = form_val.u128.u64[0]; U64 base_addr = ctx->unit_base_addr; U64 sentinel = (ctx->addr_size == 4 ? max_U32 : max_U64); @@ -1269,7 +1607,7 @@ dw2_loclist_from_form_val(Arena *arena, DW2_ParseCtx *ctx, DW_Raw *raw, DW2_Form //- rjf: @dwarf5 case DW_Version_5: { - String8 data = raw->sec[DW_Section_LocLists].data; + String8 data = raw->sec[DW_SectionKind_LocLists].data; // rjf: determine section offset - sometimes this is encoded directly, // other times it is relative, based on the form kind - more variability @@ -1278,11 +1616,11 @@ dw2_loclist_from_form_val(Arena *arena, DW2_ParseCtx *ctx, DW_Raw *raw, DW2_Form switch(form_val.kind) { default:{}break; - case DW_Form_SecOffset: + case DW_FormKind_SecOffset: { loclist_off = form_val.u128.u64[0]; }break; - case DW_Form_LocListx: + case DW_FormKind_LocListx: if(ctx->rnglists_table != 0) { U64 loclist_off_idx = form_val.u128.u64[0]; @@ -1425,3 +1763,154 @@ dw2_loclist_from_form_val(Arena *arena, DW2_ParseCtx *ctx, DW_Raw *raw, DW2_Form } return result; } + +//////////////////////////////// +//~ rjf: Unwind Info Parsing (.debug_frame) + +internal U64 +dw2_read_cfi_header(String8 data, U64 off, DW_CFIHeader *cfi_header_out) +{ + U64 start_off = off; + + // rjf: read length / format + U64 length = 0; + DW_Format fmt = DW_Format_Null; + U64 length_size = dw2_read_initial_length(data, off, &length, &fmt); + + // rjf: find entry info, read ID + Rng1U64 entry_range = r1u64(off, off + length_size + length); + String8 entry_data = str8_substr(data, entry_range); + U64 id = 0; + U64 id_size = dw2_read_fmt_u64(entry_data, length_size, fmt, &id); + + // rjf: fill + cfi_header_out->kind = ((fmt == DW_Format_32Bit && id == max_U32) || (fmt == DW_Format_64Bit && id == max_U64)) ? DW_CFIKind_CIE : DW_CFIKind_FDE; + cfi_header_out->fmt = fmt; + cfi_header_out->entry_range = entry_range; + cfi_header_out->cie_pointer = id; + cfi_header_out->cie_pointer_off = off + length_size; + + U64 bytes_read = (off - start_off); + return bytes_read; +} + +internal U64 +dw2_read_cie(String8 data, U64 off, DW_Format fmt, Arch arch, DW_CIE *cie_out) +{ + U64 start_off = off; + + // rjf: read id + U64 id = 0; + off += dw2_read_fmt_u64(data, off, fmt, &id); + + // rjf: read version + U8 version = 0; + off += str8_deserial_read_struct(data, off, &version); + + // rjf: read aug string + String8 aug_string = {0}; + U64 aug_string_off = off; + off += str8_deserial_read_cstr(data, off, &aug_string); + U64 aug_string_off_opl = off; + + // rjf: read address/segment-selector size + U8 address_size = 0; + U8 segment_selector_size = 0; + if(version >= DW_Version_4) + { + off += str8_deserial_read_struct(data, off, &address_size); + off += str8_deserial_read_struct(data, off, &segment_selector_size); + } + else + { + address_size = byte_size_from_arch(arch); + } + + // rjf: read alignments + U64 code_align_factor = 0; + U64 data_align_factor = 0; + off += str8_deserial_read_uleb128(data, off, &code_align_factor); + off += str8_deserial_read_uleb128(data, off, &data_align_factor); + + // rjf: read return address register encoding + U64 ret_addr_reg = 0; + switch(version) + { + case DW_Version_1: + { + off += str8_deserial_read(data, off, &ret_addr_reg, 1, 1); + }break; + default: + { + off += str8_deserial_read_uleb128(data, off, &ret_addr_reg); + }break; + } + + // rjf: fill output + { + cie_out->aug_string_range = r1u64(aug_string_off, aug_string_off_opl); + cie_out->code_align_factor = code_align_factor; + cie_out->data_align_factor = data_align_factor; + cie_out->ret_addr_reg = ret_addr_reg; + cie_out->format = fmt; + cie_out->version = version; + cie_out->address_size = address_size; + cie_out->segment_selector_size = segment_selector_size; + } + + U64 bytes_read = (off - start_off); + return bytes_read; +} + +internal U64 +dw2_read_fde(String8 data, U64 off, DW_Format fmt, Arch arch, DW_CIE *cie, DW_FDE *fde_out) +{ + U64 start_off = off; + + // rjf: skip format-dependent prefix + off += (fmt == DW_Format_32Bit) ? 4 : 12; + + // rjf: read cie ptr + U64 cie_ptr = 0; + off += dw2_read_fmt_u64(data, off, fmt, &cie_ptr); + + // rjf: read address of first instruction + U64 pc_begin = 0; + off += str8_deserial_read(data, off, &pc_begin, cie->address_size, cie->address_size); + + // rjf: read instruction range size + U64 pc_range_size = 0; + off += str8_deserial_read(data, off, &pc_range_size, cie->address_size, cie->address_size); + + // rjf: skip aug data + off += dim_1u64(cie->aug_string_range); + + // rjf: fill output + fde_out->cie_pointer = cie_ptr; + fde_out->pc_range = r1u64(pc_begin, pc_begin + pc_range_size); + + U64 bytes_read = (off - start_off); + return bytes_read; +} + +internal B32 +dw2_try_cfi_from_data(String8 data, U64 off, Arch arch, DW_CIE *cie_out, DW_FDE *fde_out) +{ + B32 result = 0; + { + DW_CFIHeader fde_header = {0}; + off += dw2_read_cfi_header(data, off, &fde_header); + if(fde_header.kind == DW_CFIKind_FDE) + { + DW_CFIHeader cie_header = {0}; + dw2_read_cfi_header(data, fde_header.cie_pointer, &cie_header); + if(cie_header.kind == DW_CFIKind_CIE) + { + result = 1; + dw2_read_cie(data, cie_header.entry_range.min, cie_header.fmt, arch, cie_out); + dw2_read_fde(data, fde_header.entry_range.min, fde_header.fmt, arch, cie_out, fde_out); + } + } + } + return result; +} diff --git a/src/dwarf/dwarf_parse_2.h b/src/dwarf/dwarf_parse_2.h index 3687c3f4..a4d5606c 100644 --- a/src/dwarf/dwarf_parse_2.h +++ b/src/dwarf/dwarf_parse_2.h @@ -4,6 +4,23 @@ #ifndef DWARF_PARSE_2_H #define DWARF_PARSE_2_H +//////////////////////////////// +//~ rjf: Raw Section Data + +typedef struct DW_Section DW_Section; +struct DW_Section +{ + String8 name; + String8 data; +}; + +typedef struct DW_Raw DW_Raw; +struct DW_Raw +{ + DW_Section sec[DW_SectionKind_COUNT]; + DW_Section sup[DW_SectionKind_COUNT]; +}; + //////////////////////////////// //~ rjf: Unit Headers @@ -21,6 +38,37 @@ struct DW2_UnitHeader //////////////////////////////// //~ rjf: Abbreviation Map (ID -> .debug_abbrev Offset) +typedef struct DW2_AbbrevAttrib DW2_AbbrevAttrib; +struct DW2_AbbrevAttrib +{ + DW_AttribKind attrib_kind; + DW_FormKind form_kind; + U64 implicit_const; +}; + +typedef struct DW2_AbbrevAttribNode DW2_AbbrevAttribNode; +struct DW2_AbbrevAttribNode +{ + DW2_AbbrevAttribNode *next; + DW2_AbbrevAttrib v; +}; + +typedef struct DW2_AbbrevAttribList DW2_AbbrevAttribList; +struct DW2_AbbrevAttribList +{ + DW2_AbbrevAttribNode *first; + DW2_AbbrevAttribNode *last; + U64 count; +}; + +typedef struct DW2_AbbrevHeader DW2_AbbrevHeader; +struct DW2_AbbrevHeader +{ + U64 id; + DW_TagKind tag_kind; + B8 has_children; +}; + typedef struct DW2_AbbrevMapNode DW2_AbbrevMapNode; struct DW2_AbbrevMapNode { @@ -36,6 +84,14 @@ struct DW2_AbbrevMap U64 slots_count; }; +typedef struct DW2_UnitAbbrevMapMap DW2_UnitAbbrevMapMap; +struct DW2_UnitAbbrevMapMap +{ + U64 map_count; + DW2_AbbrevMap *map_from_idx_table; + DW2_AbbrevMap **map_from_unit_idx_table; +}; + //////////////////////////////// //~ rjf: Offset Tables (.debug_str_offsets, .debug_rnglists) @@ -51,18 +107,55 @@ struct DW2_OffsetTable void *entries; }; +typedef struct DW2_OffsetTableNode DW2_OffsetTableNode; +struct DW2_OffsetTableNode +{ + DW2_OffsetTableNode *next; + DW2_OffsetTable v; + Rng1U64 range; +}; + +typedef struct DW2_OffsetTableList DW2_OffsetTableList; +struct DW2_OffsetTableList +{ + DW2_OffsetTableNode *first; + DW2_OffsetTableNode *last; + U64 count; +}; + +typedef struct DW2_OffsetTableSet DW2_OffsetTableSet; +struct DW2_OffsetTableSet +{ + // rjf: .debug_str_offsets + U64 str_offsets_tables_count; + DW2_OffsetTable *str_offsets_tables; + Rng1U64 *str_offsets_tables_ranges; + + // rjf: .debug_rnglists + U64 rnglists_tables_count; + DW2_OffsetTable *rnglists_tables; + Rng1U64 *rnglists_tables_ranges; + + // rjf: .debug_addr + U64 addr_tables_count; + DW2_OffsetTable *addr_tables; + Rng1U64 *addr_tables_ranges; + + // rjf: .debug_loclists + U64 loclists_tables_count; + DW2_OffsetTable *loclists_tables; + Rng1U64 *loclists_tables_ranges; +}; + //////////////////////////////// //~ rjf: Parsing Context Bundle typedef struct DW2_ParseCtx DW2_ParseCtx; struct DW2_ParseCtx { - DW_Raw *raw; - // NOTE(rjf): all subsequent fields optional - top-level code fills out whatever - // it can from context. DW_Version version; DW_Format format; - DW_Ext ext; + DW_ExtFlags exts; U64 addr_size; U64 unit_base_addr; U64 unit_base_info_off; @@ -73,6 +166,7 @@ struct DW2_ParseCtx DW2_OffsetTable *loclists_table; String8 unit_dir; String8 unit_file; + DW_Language language; }; //////////////////////////////// @@ -229,6 +323,57 @@ struct DW2_LocList U64 count; }; +//////////////////////////////// +//~ rjf: Unwind Info + +typedef enum DW_CFIKind +{ + DW_CFIKind_Null, + DW_CFIKind_CIE, // Common Information Entry + DW_CFIKind_FDE, // Frame Description Entry + DW_CFIKind_COUNT +} +DW_CFIKind; + +typedef struct DW_CFIHeader DW_CFIHeader; +struct DW_CFIHeader +{ + DW_CFIKind kind; + DW_Format fmt; + Rng1U64 entry_range; + U64 cie_pointer_off; + U64 cie_pointer; +}; + +typedef struct DW_CIE DW_CIE; +struct DW_CIE +{ + Rng1U64 aug_string_range; + Rng1U64 aug_data_range; + U64 code_align_factor; + S64 data_align_factor; + U64 ret_addr_reg; + U64 ext[4]; + DW_Format format; + U8 version; + U8 address_size; + U8 segment_selector_size; +}; + +typedef struct DW_FDE DW_FDE; +struct DW_FDE +{ + U64 cie_pointer; + Rng1U64 pc_range; +}; + +typedef struct DW_CFI DW_CFI; +struct DW_CFI +{ + DW_CIE cie; + DW_FDE fde; +}; + //////////////////////////////// //~ rjf: Globals @@ -239,6 +384,7 @@ global read_only DW2_Attrib dw2_attrib_nil = {0}; internal U64 dw2_read_initial_length(String8 data, U64 off, U64 *out, DW_Format *fmt_out); internal U64 dw2_read_fmt_u64(String8 data, U64 off, DW_Format format, U64 *out); +internal DW_SectionKind dw_section_kind_from_string(String8 string); //////////////////////////////// //~ rjf: Unit Range Set Parsing (Top-Level .debug_info, .debug_aranges) @@ -253,30 +399,41 @@ internal U64 dw2_read_unit_header(String8 data, U64 off, DW2_UnitHeader *out); //////////////////////////////// //~ rjf: Abbreviation Map Parsing +internal U64 dw2_read_abbrev(Arena *arena, String8 data, U64 off, DW2_AbbrevHeader *header_out, DW2_AbbrevAttribList *attribs_out); internal DW2_AbbrevMap dw2_abbrev_map_from_data(Arena *arena, String8 data, U64 off); +internal DW2_UnitAbbrevMapMap dw2_unit_abbrev_map_map_from_data(Arena *arena, String8 data, DW2_UnitHeader *unit_headers, U64 unit_headers_count); //////////////////////////////// //~ rjf: Form Value Parsing -internal U64 dw2_read_form_val(DW2_ParseCtx *ctx, String8 data, U64 off, DW_FormKind form_kind, U64 implicit_const, DW2_FormVal *out); +internal U64 dw2_read_form_val(DW_Raw *raw, DW2_ParseCtx *ctx, String8 data, U64 off, DW_FormKind form_kind, U64 implicit_const, DW2_FormVal *out); + +//////////////////////////////// +//~ rjf: Parse Context Construction + +internal void dw2_parse_ctx_equip_unit_header(DW2_ParseCtx *ctx_out, DW2_UnitHeader *hdr, U64 unit_base_info_off); +internal void dw2_parse_ctx_equip_unit_abbrev_map(DW2_ParseCtx *ctx_out, DW2_UnitAbbrevMapMap *map, U64 unit_idx); +internal void dw2_parse_ctx_equip_unit_root_tag(DW2_ParseCtx *ctx_out, DW2_Tag *tag, DW2_OffsetTableSet *offset_tables); //////////////////////////////// //~ rjf: Tag Parsing -internal U64 dw2_read_tag(Arena *arena, DW2_ParseCtx *ctx, String8 data, U64 off, DW2_Tag *tag_out); +internal U64 dw2_read_tag(Arena *arena, DW_Raw *raw, DW2_ParseCtx *ctx, String8 data, U64 off, DW2_Tag *tag_out); internal DW2_Attrib *dw2_attrib_from_kind(DW2_Tag *tag, DW_AttribKind kind); internal U64 dw2_reference_info_off_from_form_val(DW2_ParseCtx *ctx, DW2_FormVal *v); //////////////////////////////// //~ rjf: Line Table Parsing -internal U64 dw2_read_line_table_header(Arena *arena, DW2_ParseCtx *ctx, String8 data, U64 off, DW2_LineTableHeader *out); +internal U64 dw2_read_line_table_header(Arena *arena, DW_Raw *raw, DW2_ParseCtx *ctx, String8 data, U64 off, DW2_LineTableHeader *out); //////////////////////////////// //~ rjf: Offset Table Parsing (.debug_str_offsets, .debug_rnglists) internal U64 dw2_read_offset_table(String8 data, U64 off, DW2_OffsetTable *out); +internal DW2_OffsetTableList dw2_offset_table_list_from_data(Arena *arena, String8 data); internal B32 dw2_try_offset_from_table_idx(DW2_OffsetTable *tbl, U64 idx, U64 *out); +internal DW2_OffsetTableSet dw2_offset_table_set_from_raw(Arena *arena, DW_Raw *raw); //////////////////////////////// //~ rjf: Range List Parsing (.debug_rnglists) @@ -288,4 +445,12 @@ internal Rng1U64List dw2_rnglist_from_form_val(Arena *arena, DW2_ParseCtx *ctx, internal DW2_LocList dw2_loclist_from_form_val(Arena *arena, DW2_ParseCtx *ctx, DW_Raw *raw, DW2_FormVal form_val); +//////////////////////////////// +//~ rjf: Unwind Info Parsing (.debug_frame) + +internal U64 dw2_read_cfi_header(String8 data, U64 off, DW_CFIHeader *cfi_header_out); +internal U64 dw2_read_cie(String8 data, U64 off, DW_Format fmt, Arch arch, DW_CIE *cie_out); +internal U64 dw2_read_fde(String8 data, U64 off, DW_Format fmt, Arch arch, DW_CIE *cie, DW_FDE *fde_out); +internal B32 dw2_try_cfi_from_data(String8 data, U64 off, Arch arch, DW_CIE *cie_out, DW_FDE *fde_out); + #endif // DWARF_PARSE_2_H diff --git a/src/dwarf/dwarf_parse_coff.c b/src/dwarf/dwarf_parse_coff.c new file mode 100644 index 00000000..617f06ec --- /dev/null +++ b/src/dwarf/dwarf_parse_coff.c @@ -0,0 +1,45 @@ +// Copyright (c) Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +internal B32 +dw_is_dwarf_present_coff_section_table(String8 string_table, U64 section_count, COFF_SectionHeader *section_table) +{ + B32 is_dwarf_present = 0; + for EachIndex(idx, section_count) + { + COFF_SectionHeader *header = §ion_table[idx]; + String8 name = coff_name_from_section_header(string_table, header); + DW_SectionKind s = dw_section_kind_from_string(name); + is_dwarf_present = s != DW_SectionKind_Null; + if(is_dwarf_present) + { + break; + } + } + return is_dwarf_present; +} + +internal DW_Raw +dw_input_from_coff_section_table(Arena *arena, String8 raw_image, String8 string_table, U64 section_count, COFF_SectionHeader *section_table) +{ + DW_Raw input = {0}; + B32 sect_status[ArrayCount(input.sec)] = {0}; + for EachIndex(idx, section_count) + { + COFF_SectionHeader *header = §ion_table[idx]; + Rng1U64 raw_data_range = rng_1u64(header->foff, header->vsize ? header->foff + header->vsize : header->foff + header->fsize); + String8 name = coff_name_from_section_header(string_table, header); + DW_SectionKind s = dw_section_kind_from_string(name); + if(s != DW_SectionKind_Null) + { + if(!sect_status[s]) + { + sect_status[s] = 1; + DW_Section *d = &input.sec[s]; + d->name = push_str8_copy(arena, name); + d->data = str8_substr(raw_image, raw_data_range); + } + } + } + return input; +} diff --git a/src/dwarf/dwarf_coff.h b/src/dwarf/dwarf_parse_coff.h similarity index 82% rename from src/dwarf/dwarf_coff.h rename to src/dwarf/dwarf_parse_coff.h index 797324ef..3df0b5be 100644 --- a/src/dwarf/dwarf_coff.h +++ b/src/dwarf/dwarf_parse_coff.h @@ -1,11 +1,11 @@ // Copyright (c) Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) -#ifndef DWARF_COFF_H -#define DWARF_COFF_H +#ifndef DWARF_PARSE_COFF_H +#define DWARF_PARSE_COFF_H internal B32 dw_is_dwarf_present_coff_section_table(String8 string_table, U64 section_count, COFF_SectionHeader *section_table); internal DW_Raw dw_input_from_coff_section_table(Arena *arena, String8 raw_image, String8 string_table, U64 section_count, COFF_SectionHeader *section_table); -#endif // DWARF_COFF_H +#endif // DWARF_PARSE_COFF_H diff --git a/src/dwarf/dwarf_elf.c b/src/dwarf/dwarf_parse_elf.c similarity index 87% rename from src/dwarf/dwarf_elf.c rename to src/dwarf/dwarf_parse_elf.c index 63b09dd4..a837c483 100644 --- a/src/dwarf/dwarf_elf.c +++ b/src/dwarf/dwarf_parse_elf.c @@ -11,11 +11,7 @@ dw_is_dwarf_present_from_elf_bin(String8 data, ELF_Bin *bin) if(shdr->sh_type != ELF_ShType_ProgBits) { continue; } String8 name = elf_name_from_shdr64(data, bin, shdr); DW_SectionKind s = dw_section_kind_from_string(name); - if(s == DW_Section_Null) - { - s = dw_section_dwo_kind_from_string(name); - } - is_dwarf_present = (s != DW_Section_Null); + is_dwarf_present = (s != DW_SectionKind_Null); if(is_dwarf_present) { break; @@ -28,7 +24,7 @@ dw_is_dwarf_present_from_elf_bin(String8 data, ELF_Bin *bin) #include "third_party/sinfl/sinfl.h" internal DW_Raw -dw_input_from_elf_bin(Arena *arena, String8 data, ELF_Bin *bin) +dw_raw_from_elf_bin(Arena *arena, String8 data, ELF_Bin *bin) { DW_Raw result = {0}; B32 is_section_present[ArrayCount(result.sec)] = {0}; @@ -41,14 +37,8 @@ dw_input_from_elf_bin(Arena *arena, String8 data, ELF_Bin *bin) String8 section_name = elf_name_from_shdr64(data, bin, shdr); DW_SectionKind section_kind = dw_section_kind_from_string(section_name); String8 section_data__maybe_compressed = str8_substr(data, r1u64(shdr->sh_offset, shdr->sh_offset + shdr->sh_size)); - B32 is_dwo = 0; - if(section_kind == DW_Section_Null) - { - section_kind = dw_section_dwo_kind_from_string(section_name); - is_dwo = (section_kind != DW_Section_Null); - } - if(section_kind == DW_Section_Null) { continue; } // skip unknown sections + if(section_kind == DW_SectionKind_Null) { continue; } // skip unknown sections if(is_section_present[section_kind]) { continue; } // skip duplicate sections //- rjf: decompress section data if needed @@ -108,7 +98,6 @@ dw_input_from_elf_bin(Arena *arena, String8 data, ELF_Bin *bin) DW_Section *d = &result.sec[section_kind]; d->name = push_str8_copy(arena, section_name); d->data = section_data__uncompressed; - d->is_dwo = is_dwo; } return result; } diff --git a/src/dwarf/dwarf_elf.h b/src/dwarf/dwarf_parse_elf.h similarity index 53% rename from src/dwarf/dwarf_elf.h rename to src/dwarf/dwarf_parse_elf.h index 5d5965e3..06ddc8ce 100644 --- a/src/dwarf/dwarf_elf.h +++ b/src/dwarf/dwarf_parse_elf.h @@ -1,10 +1,10 @@ // Copyright (c) Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) -#ifndef DWARF_ELF_H -#define DWARF_ELF_H +#ifndef DWARF_PARSE_ELF_H +#define DWARF_PARSE_ELF_H internal B32 dw_is_dwarf_present_from_elf_bin(String8 raw_image, ELF_Bin *bin); -internal DW_Raw dw_input_from_elf_bin(Arena *arena, String8 raw_image, ELF_Bin *bin); +internal DW_Raw dw_raw_from_elf_bin(Arena *arena, String8 raw_image, ELF_Bin *bin); -#endif // DWARF_ELF_H +#endif // DWARF_PARSE_ELF_H diff --git a/src/dwarf/dwarf_unwind_2.c b/src/dwarf/dwarf_unwind_2.c new file mode 100644 index 00000000..ae447aab --- /dev/null +++ b/src/dwarf/dwarf_unwind_2.c @@ -0,0 +1,2 @@ +// Copyright (c) Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) diff --git a/src/dwarf/dwarf_unwind_2.h b/src/dwarf/dwarf_unwind_2.h new file mode 100644 index 00000000..ca1bea9a --- /dev/null +++ b/src/dwarf/dwarf_unwind_2.h @@ -0,0 +1,44 @@ +// Copyright (c) Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef DWARF_UNWIND_2_H +#define DWARF_UNWIND_2_H + +typedef enum DW_UnwindRuleCode +{ + DW_UnwindRuleCode_SameVal, + DW_UnwindRuleCode_Undefined, + DW_UnwindRuleCode_Off, + DW_UnwindRuleCode_ValOff, + DW_UnwindRuleCode_Reg, + DW_UnwindRuleCode_Expr, + DW_UnwindRuleCode_ValExpr, + DW_UnwindRuleCode_Architectural, +} +DW_UnwindRuleCode; + +typedef struct DW_UnwindRule DW_UnwindRule; +struct DW_UnwindRule +{ + DW_UnwindRuleCode code; + DW_RegCode reg_code; + S64 s64; + String8 expr; +}; + +typedef struct DW_CFIRow DW_CFIRow; +struct DW_CFIRow +{ + U64 base_pc_off; + DW_UnwindRule cfa_rule; + DW_UnwindRule *reg_rules; +}; + +typedef struct DW_CFIRowNode DW_CFIRowNode; +struct DW_CFIRowNode +{ + DW_CFIRowNode *next; + DW_CFIRow v; +}; + +#endif // DWARF_UNWIND_2_H diff --git a/src/dwarf/dwarf_writer.c b/src/dwarf/dwarf_writer.c index 1cb92f71..fa824277 100644 --- a/src/dwarf/dwarf_writer.c +++ b/src/dwarf/dwarf_writer.c @@ -97,6 +97,119 @@ dw_writer_tag_chunk_list_push(Arena *arena, DW_WriterTagChunkList *list, U64 cap return result; } +internal U64 +dw_write_to_buffer_uleb128(U8 buffer[10], U64 v) +{ + U64 buffer_size = 0; + U64 value = v; + do { + U8 byte = value & 0x7f; + value >>= 7; + if (value != 0) { + byte |= 0x80; + } + Assert(buffer_size < 10); + buffer[buffer_size++] = byte; + } while (value > 0); + return buffer_size; +} + +internal U64 +dw_write_to_buffer_sleb128(U8 buffer[10], U64 v) +{ + U64 buffer_size = 0; + for (S64 value = v, more = 1; more != 0; ) { + U8 byte = value & 0x7f; + value >>= 7; + U8 sign_bit = byte & 0x40; + if ((value == 0 && sign_bit == 0) || (value == -1 && sign_bit != 0)) { + more = 0; + } else { + byte |= 0x80; + } + Assert(buffer_size < 10); + buffer[buffer_size++] = byte; + } + return buffer_size; +} + +internal String8 +dw_write_uleb128(Arena *arena, U64 v) +{ + U8 buffer[10]; + U64 buffer_size = dw_write_to_buffer_uleb128(buffer, v); + return str8_copy(arena, str8(buffer, buffer_size)); +} + +internal String8 +dw_write_sleb128(Arena *arena, S64 v) +{ + U8 buffer[10]; + U64 buffer_size = dw_write_to_buffer_sleb128(buffer, v); + return str8_copy(arena, str8(buffer, buffer_size)); +} + +internal U64 +dw_size_from_uleb128(U64 v) +{ + U8 buffer[10]; + return dw_write_to_buffer_uleb128(buffer, v); +} + +internal U64 +dw_size_from_sleb128(S64 v) +{ + U8 buffer[10]; + return dw_write_to_buffer_sleb128(buffer, v); +} + +internal void * +dw_serial_push_length(Arena *arena, String8List *srl, DW_Format format, U64 length) +{ + void *result; + if (format == DW_Format_64Bit) { + U8 buffer[sizeof(U32) + sizeof(U64)]; + MemorySet(&buffer[0], 0xff, sizeof(U32)); + MemoryCopy(buffer + sizeof(U32), &length, sizeof(length)); + result = str8_serial_push_string(arena, srl, str8_array_fixed(buffer)); + } else { + result = str8_serial_push_string(arena, srl, str8((U8 *)&length, sizeof(U32))); + } + return result; +} + +internal void * +dw_serial_push_uint(Arena *arena, String8List *srl, DW_Format format, U64 v) +{ + String8 uint; + if (format == DW_Format_64Bit) { + U64 *ptr = push_array(arena, U64, 1); + *ptr = v; + uint = str8((U8 *)ptr, sizeof(*ptr)); + } else { + U32 *ptr = push_array(arena, U32, 1); + *ptr = safe_cast_u32(v); + uint = str8((U8 *)ptr, sizeof(*ptr)); + } + return str8_serial_push_string(arena, srl, uint); +} + +internal void * +dw_serial_push_uleb128(Arena *arena, String8List *srl, U64 v) +{ + U8 buffer[10]; + U64 buffer_size = dw_write_to_buffer_uleb128(buffer, v); + return str8_serial_push_string(arena, srl, str8(buffer, buffer_size)); +} + +internal void * +dw_serial_push_sleb128(Arena *arena, String8List *srl, S64 v) +{ + U8 buffer[10]; + U64 buffer_size = dw_write_to_buffer_sleb128(buffer, v); + return str8_serial_push_string(arena, srl, str8(buffer, buffer_size)); +} + internal DW_Writer * dw_writer_begin(DW_Format format, DW_Version version, DW_CompUnitKind cu_kind, Arch arch) { @@ -1195,7 +1308,18 @@ dw_writer_emit(DW_Writer *writer) str8_serial_push_struct(writer->arena, srl, &writer->line.opcode_base); // (12) standard_opcode_lengths U8 *std_op_lens = push_array(writer->arena, U8, writer->line.opcode_base - 1); - for (U64 i = 1; i < writer->line.opcode_base; i += 1) { std_op_lens[i-1] = dw_length_from_std_opcode(i); } + for (U64 i = 1; i < writer->line.opcode_base; i += 1) + { + U64 len = 0; + switch(i) + { + default:{}break; +#define X(name, code, len_) case DW_StdOpcode_##name:{len = (len_);}break; + DW_StdOpcode_XList +#undef X + } + std_op_lens[i-1] = len; + } str8_serial_push_string(writer->arena, srl, str8(std_op_lens, writer->line.opcode_base - 1)); // directory table str8_list_concat_in_place(srl, &dir_table_srl); @@ -1277,3 +1401,109 @@ dw_writer_emit_to_obj(DW_Writer *writer, OBJ *obj) } #endif +internal String8 +dw_encode_expr(Arena *arena, Arch arch, DW_Format format, DW_ExprEnc *encs, U64 encs_count) +{ + Temp scratch = scratch_begin(&arena, 1); + + String8List *srl = push_array(scratch.arena, String8List, 1); + str8_serial_begin(scratch.arena, srl); + + HashTable *label_map = hash_table_init(scratch.arena, 128); + + struct Fixup { U64 offset; String8 label; struct Fixup *next; }; + struct Fixup *first_fixup = 0, *last_fixup = 0; + + for EachIndex(i, encs_count) { + DW_ExprEnc *e = &encs[i]; + + switch (e->type) { + case DW_ExprEncType_Null: {} break; + case DW_ExprEncType_Op: { + str8_serial_push_struct(scratch.arena, srl, &e->op); + } break; + case DW_ExprEncType_U8: { + str8_serial_push_struct(scratch.arena, srl, &e->u8); + } break; + case DW_ExprEncType_U16: { + str8_serial_push_struct(scratch.arena, srl, &e->u16); + } break; + case DW_ExprEncType_U32: { + str8_serial_push_struct(scratch.arena, srl, &e->u32); + } break; + case DW_ExprEncType_U64: { + str8_serial_push_struct(scratch.arena, srl, &e->u64); + } break; + case DW_ExprEncType_S8: { + str8_serial_push_struct(scratch.arena, srl, &e->s8); + } break; + case DW_ExprEncType_S16: { + str8_serial_push_struct(scratch.arena, srl, &e->s16); + } break; + case DW_ExprEncType_S32: { + str8_serial_push_struct(scratch.arena, srl, &e->s32); + } break; + case DW_ExprEncType_S64: { + str8_serial_push_struct(scratch.arena, srl, &e->s64); + } break; + case DW_ExprEncType_ULEB128: { + str8_serial_push_string(scratch.arena, srl, dw_write_uleb128(scratch.arena, e->u64)); + } break; + case DW_ExprEncType_SLEB128: { + str8_serial_push_string(scratch.arena, srl, dw_write_sleb128(scratch.arena, e->s64)); + } break; + case DW_ExprEncType_Addr: { + U64 addr_size = byte_size_from_arch(arch); + Assert(addr_size <= sizeof(e->addr)); + str8_serial_push_string(scratch.arena, srl, str8((U8 *)&e->addr, addr_size)); + } break; + case DW_ExprEncType_Block: { + Assert(e->block.size <= max_U8); + str8_serial_push_u8(scratch.arena, srl, (U8)e->block.size); + str8_serial_push_string(scratch.arena, srl, e->block); + } break; + case DW_ExprEncType_DwarfUInt: { + switch (format) { + case DW_Format_Null: {} break; + case DW_Format_32Bit: { str8_serial_push_u32(scratch.arena, srl, e->u32); } break; + case DW_Format_64Bit: { str8_serial_push_u64(scratch.arena, srl, e->u64); } break; + default: { InvalidPath; } break; + } + } break; + case DW_ExprEncType_Label: { + struct Fixup *fixup = push_array(scratch.arena, struct Fixup, 1); + fixup->offset = srl->total_size; + fixup->label = e->label; + SLLQueuePush(first_fixup, last_fixup, fixup); + + S16 delta_placeholder = 0; + str8_serial_push_struct(scratch.arena, srl, &delta_placeholder); + } break; + case DW_ExprEncType_DeclLabel: { + hash_table_push_string_u64(scratch.arena, label_map, e->label, srl->total_size); + } break; + default: { InvalidPath; } break; + } + } + + // finalize expression + String8 expr = str8_serial_end(arena, srl); + + // apply fixups + for EachNode(fixup, struct Fixup, first_fixup) { + U64 label_offset; + if (!hash_table_search_string_u64(label_map, fixup->label, &label_offset)) { + Assert(0 && "undefined label"); + continue; + } + + S64 delta = (S64)label_offset - (S64)fixup->offset; + AssertAlways(min_S16 <= delta && delta <= max_S16); + + S16 *delta_ptr = (S16 *)(expr.str + fixup->offset); + *delta_ptr = delta; + } + + scratch_end(scratch); + return expr; +} diff --git a/src/dwarf/dwarf_writer.h b/src/dwarf/dwarf_writer.h index 2e1a8923..aa09c59d 100644 --- a/src/dwarf/dwarf_writer.h +++ b/src/dwarf/dwarf_writer.h @@ -245,6 +245,23 @@ typedef struct DW_Writer DW_WriterTagChunkList tag_chunk_list; } DW_Writer; + +//////////////////////////////// +// Serializers + +internal U64 dw_write_to_buffer_uleb128(U8 buffer[10], U64 v); +internal U64 dw_write_to_buffer_sleb128(U8 buffer[10], U64 v); + +internal String8 dw_write_uleb128(Arena *arena, U64 v); +internal String8 dw_write_sleb128(Arena *arena, S64 v); +internal U64 dw_size_from_uleb128(U64 v); +internal U64 dw_size_from_sleb128(S64 v); + +internal void * dw_serial_push_length(Arena *arena, String8List *srl, DW_Format format, U64 length); +internal void * dw_serial_push_uint(Arena *arena, String8List *srl, DW_Format format, U64 v); +internal void * dw_serial_push_uleb128(Arena *arena, String8List *srl, U64 v); +internal void * dw_serial_push_sleb128(Arena *arena, String8List *srl, S64 v); + //////////////////////////////// // Writer @@ -334,5 +351,8 @@ internal DW_IntEnc dw_int_enc_from_uint(U64 v); internal U64 dw_size_from_sint(S64 v); internal U64 dw_size_from_uint(U64 v); +// encoder +internal String8 dw_encode_expr(Arena *arena, Arch arch, DW_Format format, DW_ExprEnc *encs, U64 encs_count); + #endif // DWARF_WRITER_H diff --git a/src/dwarf/eh_dump.c b/src/dwarf/eh_dump.c deleted file mode 100644 index 591a35cb..00000000 --- a/src/dwarf/eh_dump.c +++ /dev/null @@ -1,141 +0,0 @@ -// Copyright (c) Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -internal String8List -eh_dump_list_from_data(Arena *arena, Arch arch, U64 eh_frame_hdr_vaddr, U64 eh_frame_vaddr, String8 eh_frame_hdr, String8 eh_frame, EH_DumpSubsetFlags subset_flags) -{ - Temp scratch = scratch_begin(&arena, 1); - String8List strings = {0}; -#define dumpf(...) str8_list_pushf(arena, &strings, __VA_ARGS__) - - EH_PtrCtx ptr_ctx = { .data_vaddr = eh_frame_hdr_vaddr }; - EH_FrameHdr hdr = eh_parse_frame_hdr(eh_frame_hdr, byte_size_from_arch(arch), &ptr_ctx); - DW_Ext ext = DW_Ext_All; - - if (subset_flags & EH_DumpSubsetFlag_EhFrameHdr) { - dumpf("eh_frame_hdr:\n"); - dumpf("{\n"); - dumpf(" version: %u\n", hdr.version); - dumpf(" eh_frame_ptr_enc: %S\n", eh_string_from_ptr_enc(scratch.arena, hdr.eh_frame_ptr_enc)); - dumpf(" table_enc: %S\n", eh_string_from_ptr_enc(scratch.arena, hdr.table_enc)); - dumpf(" fde_count: %llu\n", hdr.fde_count); - if (hdr.eh_frame_ptr_enc != EH_PtrEnc_Omit) { - dumpf(" eh_frame_ptr: 0x%I64x\n", hdr.eh_frame_ptr); - } - - dumpf(" Entries:\n"); - dumpf(" {\n"); - for (U64 cursor = 0; cursor < hdr.table.size; ) { - U64 entry_off = cursor; - - U64 pc = 0; - U64 pc_size = eh_parse_ptr(hdr.table, cursor, cursor, &ptr_ctx, hdr.table_enc, &pc); - if (pc_size == 0) { break; } - cursor += pc_size; - - U64 fde_addr = 0; - U64 fde_addr_size = eh_parse_ptr(hdr.table, cursor, cursor, &ptr_ctx, hdr.table_enc, &fde_addr); - if (fde_addr_size == 0) { break; } - cursor += fde_addr_size; - - U64 fde_offset = fde_addr - eh_frame_vaddr; - dumpf(" { off=0x%04I64x, pc=0x%I64x, fde=0x%I64x }\n", entry_off, pc, fde_offset); - } - dumpf(" }\n"); - - dumpf("}\n"); - - } - - if (subset_flags & EH_DumpSubsetFlag_EhFrame) { - dumpf(".eh_frame:\n"); - dumpf("{\n"); - for (U64 cursor = 0; cursor < eh_frame.size; ) { - DW_DescriptorEntry desc = {0}; - U64 desc_size = eh_parse_descriptor_entry_header(eh_frame, cursor, &desc); - if (desc_size == 0) { break; } - - switch (desc.type) { - case DW_DescriptorEntryType_Null: break; - case DW_DescriptorEntryType_CIE: { - String8 cie_data = str8_substr(eh_frame, desc.entry_range); - DW_CIE cie = {0}; - if (eh_parse_cie(cie_data, desc.format, arch, eh_frame_vaddr + cursor, &ptr_ctx, &cie)) { - String8List init_insts_str_list = dw_string_list_from_cfi_program(scratch.arena, 0, arch, DW_Version_5, ext, cie.format, 0, &cie, eh_decode_ptr, &ptr_ctx, cie.insts); - dumpf(" CIE: // entry range: %r\n", desc.entry_range); - dumpf(" {\n"); - dumpf(" Format: %S\n", dw_string_from_format(desc.format)); - dumpf(" Version: %u\n", cie.version); - dumpf(" Aug string: \"%S\"\n", cie.aug_string); - dumpf(" Code align: %I64u\n", cie.code_align_factor); - dumpf(" Data align: %I64d\n", cie.data_align_factor); - dumpf(" Return addr reg: %u\n", cie.ret_addr_reg); - if (cie.version > DW_Version_3) { - dumpf(" Address size: %u\n", cie.address_size); - dumpf(" Segment selector size: %u\n", cie.segment_selector_size); - } - dumpf(" Initial Insturction:\n"); - dumpf(" {\n"); - for EachNode(n, String8Node, init_insts_str_list.first) { dumpf(" %S\n", n->string); } - dumpf(" }\n"); - dumpf(" }\n"); - } else { - dumpf("ERROR: unable to parse CIE @ %I64x\n", desc.entry_range.min); - } - } break; - case DW_DescriptorEntryType_FDE: { - U64 cie_offset = desc.cie_pointer_off - desc.cie_pointer; - - DW_CIE cie = {0}; - { - DW_DescriptorEntry cie_desc = {0}; - eh_parse_descriptor_entry_header(eh_frame, cie_offset, &cie_desc); - if (cie_desc.type == DW_DescriptorEntryType_CIE) { - String8 cie_data = str8_substr(eh_frame, cie_desc.entry_range); - eh_parse_cie(cie_data, cie_desc.format, arch, eh_frame_vaddr + cie_offset, &ptr_ctx, &cie); - } - } - - String8 fde_raw = str8_substr(eh_frame, desc.entry_range); - DW_FDE fde = {0}; - if (eh_parse_fde(fde_raw, desc.format, eh_frame_vaddr + cursor, &cie, &ptr_ctx, &fde)) { - String8List insts_str_list = dw_string_list_from_cfi_program(scratch.arena, 0, arch, hdr.version, ext, fde.format, 0, &cie, eh_decode_ptr, &ptr_ctx, fde.insts); - - dumpf(" FDE: // entry range: %r\n", desc.entry_range); - dumpf(" {\n"); - { - dumpf(" Format: %S\n", dw_string_from_format(fde.format)); - dumpf(" CIE: 0x%I64x\n", cie_offset); - dumpf(" PC range: %r\n", fde.pc_range); - dumpf(" Instructions:\n"); - dumpf(" {\n"); - for EachNode(n, String8Node, insts_str_list.first) { dumpf(" %S\n", n->string); } - dumpf(" }\n"); - - dumpf(" Unwind:\n"); - dumpf(" {\n"); - DW_CFI_Unwind *cfi_unwind = dw_cfi_unwind_init(scratch.arena, arch, &cie, &fde, eh_decode_ptr, &ptr_ctx); - do { - String8 cfa_str = dw_string_from_cfa(scratch.arena, arch, cie.address_size, hdr.version, ext, fde.format, cfi_unwind->row->cfa); - String8 cfi_row_str = dw_string_from_cfi_row(scratch.arena, arch, cie.address_size, hdr.version, ext, fde.format, cfi_unwind->row); - dumpf(" { PC: 0x%I64x, CFA: %-7S, Rules: { %S }\n", cfi_unwind->pc, cfa_str, cfi_row_str); - } while (dw_cfi_next_row(scratch.arena, cfi_unwind)); - dumpf(" }\n"); - } - } else { - dumpf("ERROR: unable to parse FDE @ %I64x\n", desc.entry_range.min); - } - dumpf(" }\n"); - } break; - } - - cursor += desc_size; - } - dumpf("}\n"); - } - -#undef dumpf - scratch_end(scratch); - return strings; -} - diff --git a/src/dwarf/eh_frame.c b/src/dwarf/eh_frame.c deleted file mode 100644 index cf455905..00000000 --- a/src/dwarf/eh_frame.c +++ /dev/null @@ -1,470 +0,0 @@ -// Copyright (c) Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -internal U64 -eh_parse_ptr(String8 frame_base, U64 off, U64 pc, EH_PtrCtx *ptr_ctx, EH_PtrEnc encoding, U64 *ptr_out) -{ - U64 ptr_off = off; - - if (encoding == EH_PtrEnc_Omit) { - return 0; - } - - // align read offset as needed - if (encoding == EH_PtrEnc_Aligned) { - ptr_off = AlignPow2(ptr_off, ptr_ctx->ptr_align); - encoding = EH_PtrEnc_Ptr; - } - - // decode pointer value - U64 decode_size = 0; - U64 raw_ptr_size = 0; - U64 raw_ptr = 0; - switch (encoding & EH_PtrEnc_TypeMask) { - default: { InvalidPath; } break; - - case EH_PtrEnc_Ptr : { raw_ptr_size = 8; } goto ufixed; - case EH_PtrEnc_UData2: { raw_ptr_size = 2; } goto ufixed; - case EH_PtrEnc_UData4: { raw_ptr_size = 4; } goto ufixed; - case EH_PtrEnc_UData8: { raw_ptr_size = 8; } goto ufixed; - ufixed: { - decode_size += str8_deserial_read(frame_base, ptr_off, &raw_ptr, raw_ptr_size, raw_ptr_size); - } break; - - // TODO: Signed is actually just a flag that indicates this int is negavite. - // There shouldn't be a read for Signed. - // For instance, (EH_PtrEnc_UData2 | EH_PtrEnc_Signed) == EH_PtrEnc_SData etc. - case EH_PtrEnc_Signed: { raw_ptr_size = 8; } goto sfixed; - - case EH_PtrEnc_SData2: { raw_ptr_size = 2; } goto sfixed; - case EH_PtrEnc_SData4: { raw_ptr_size = 4; } goto sfixed; - case EH_PtrEnc_SData8: { raw_ptr_size = 8; } goto sfixed; - sfixed: { - decode_size += str8_deserial_read(frame_base, ptr_off, &raw_ptr, raw_ptr_size, raw_ptr_size); - raw_ptr = extend_sign64(raw_ptr, raw_ptr_size); - } break; - - case EH_PtrEnc_ULEB128: { decode_size += str8_deserial_read_uleb128(frame_base, ptr_off, &raw_ptr); } break; - case EH_PtrEnc_SLEB128: { decode_size += str8_deserial_read_sleb128(frame_base, ptr_off, (S64*)&raw_ptr); } break; - } - - // apply relative bases - if (decode_size > 0) { - U64 ptr = raw_ptr; - switch (encoding & EH_PtrEnc_ModifierMask) { - case 0: break; - case EH_PtrEnc_PcRel: { ptr = pc + raw_ptr; } break; - case EH_PtrEnc_TextRel: { ptr = ptr_ctx->text_vaddr + raw_ptr; } break; - case EH_PtrEnc_DataRel: { ptr = ptr_ctx->data_vaddr + raw_ptr; } break; - case EH_PtrEnc_FuncRel: { - Assert(!"TODO: need a sample to verify implementation"); - ptr = ptr_ctx->func_vaddr + raw_ptr; - } break; - default: { InvalidPath; } break; - } - - if (ptr_out) { - *ptr_out = ptr; - } - } - - return decode_size; -} - -internal EH_FrameHdr -eh_parse_frame_hdr(String8 data, U64 address_size, EH_PtrCtx *ptr_ctx) -{ - EH_FrameHdr header = {0}; - U64 cursor = 0; - - U64 version_size = str8_deserial_read_struct(data, cursor, &header.version); - if (version_size == 0) { goto exit; } - cursor += version_size; - - if (header.version == 1) { - U64 eh_frame_ptr_enc_size = str8_deserial_read_struct(data, cursor, &header.eh_frame_ptr_enc); - if (eh_frame_ptr_enc_size == 0) { goto exit; } - cursor += eh_frame_ptr_enc_size; - - U64 fde_count_enc_size = str8_deserial_read_struct(data, cursor, &header.fde_count_enc); - if (fde_count_enc_size == 0) { goto exit; } - cursor += fde_count_enc_size; - - U64 table_enc_size = str8_deserial_read_struct(data, cursor, &header.table_enc); - if (table_enc_size == 0) { goto exit; } - cursor += table_enc_size; - - cursor += eh_parse_ptr(data, cursor, cursor, ptr_ctx, header.eh_frame_ptr_enc, &header.eh_frame_ptr); - cursor += eh_parse_ptr(data, cursor, cursor, ptr_ctx, header.fde_count_enc, &header.fde_count); - - switch (header.table_enc & EH_PtrEnc_TypeMask) { - case EH_PtrEnc_Ptr: { header.field_byte_size = address_size; } break; - case EH_PtrEnc_ULEB128: { InvalidPath; } break; // TODO: when loading module convert these to UData8 - case EH_PtrEnc_UData2: { header.field_byte_size = 2; } break; - case EH_PtrEnc_UData4: { header.field_byte_size = 4; } break; - case EH_PtrEnc_UData8: { header.field_byte_size = 8; } break; - case EH_PtrEnc_Signed: { header.field_byte_size = address_size; } break; - case EH_PtrEnc_SLEB128: { InvalidPath; } break; // TODO: when loading module convert these to UData8 - case EH_PtrEnc_SData2: { header.field_byte_size = 2; } break; - case EH_PtrEnc_SData4: { header.field_byte_size = 4; } break; - case EH_PtrEnc_SData8: { header.field_byte_size = 8; } break; - default: { InvalidPath; } break; - } - header.entry_byte_size = header.field_byte_size * 2; - - header.table = str8_skip(data, cursor); - AssertAlways(header.table.size == header.entry_byte_size * header.fde_count); - } else { - Assert(0 && "unknown version"); - } - -exit:; - return header; -} - -internal U64 -eh_parse_aug_data(String8 aug_string, String8 aug_data, U64 pc, EH_PtrCtx *ptr_ctx, EH_Augmentation *aug_out) -{ - // TODO: - // Handle "eh" param, it indicates presence of EH Data field. - // On 32bit arch it is a 4-byte and on 64-bit 8-byte value. - // Reference: https://refspecs.linuxfoundation.org/LSB_3.0.0/LSB-PDA/LSB-PDA/ehframechpt.html - // Reference doc doesn't clarify structure for EH Data though - - U64 cursor = 0; - - U64 aug_data_size = 0; - EH_AugFlags aug_flags = 0; - EH_PtrEnc lsda_encoding = EH_PtrEnc_Omit; - EH_PtrEnc addr_encoding = EH_PtrEnc_UData8; - EH_PtrEnc handler_encoding = EH_PtrEnc_Omit; - U64 handler_ip = 0; - if (str8_match(str8_prefix(aug_string, 1), str8_lit("z"), 0)) { - cursor = str8_deserial_read_uleb128(aug_data, cursor, &aug_data_size); - - for (U8 *ptr = aug_string.str+1; ptr < (aug_string.str+aug_string.size); ptr += 1) { - switch (*ptr) { - case 'L': { - cursor += str8_deserial_read_struct(aug_data, cursor, &lsda_encoding); - aug_flags |= EH_AugFlag_HasLSDA; - } break; - case 'P': { - cursor += str8_deserial_read_struct(aug_data, cursor, &handler_encoding); - cursor += eh_parse_ptr(aug_data, cursor, pc + cursor, ptr_ctx, handler_encoding, &handler_ip); - aug_flags |= EH_AugFlag_HasHandler; - } break; - case 'R': { - cursor += str8_deserial_read_struct(aug_data, cursor, &addr_encoding); - aug_flags |= EH_AugFlag_HasAddrEnc; - } break; - case 'S': { - aug_flags |= EH_AugFlag_SignalFrame; - } break; - default: { Assert(!"failed to parse augmentation string"); goto exit; } break; - } - } - } - - if (aug_out) { - aug_out->handler_ip = handler_ip; - aug_out->handler_encoding = handler_encoding; - aug_out->lsda_encoding = handler_encoding; - aug_out->addr_encoding = addr_encoding; - aug_out->flags = aug_flags; - aug_out->size = aug_data_size; - } - -exit:; - U64 parse_size = cursor; - return parse_size; -} - -internal U64 -eh_parse_descriptor_entry_header(String8 data, U64 off, DW_DescriptorEntry *desc_out) -{ - U32 first_four_bytes = 0; - str8_deserial_read_struct(data, off, &first_four_bytes); - DW_Format format = first_four_bytes == max_U32 ? DW_Format_64Bit : DW_Format_32Bit; - - U64 length = 0; - U64 length_size = str8_deserial_read_dwarf_packed_size(data, off, &length); - if (length_size == 0) { goto exit; } - - Rng1U64 entry_range = rng_1u64(off, off + length_size + length); - String8 entry_data = str8_substr(data, entry_range); - U64 id = 0; - U64 id_size = str8_deserial_read_dwarf_uint(entry_data, length_size, format, &id); - if (id_size == 0) { goto exit; } - - desc_out->format = format; - desc_out->type = (id == 0) ? DW_DescriptorEntryType_CIE : DW_DescriptorEntryType_FDE; - desc_out->entry_range = entry_range; - desc_out->cie_pointer = id; - desc_out->cie_pointer_off = off + length_size; - -exit:; - return length + length_size; -} - -internal B32 -eh_parse_cie(String8 data, DW_Format format, Arch arch, U64 pc, EH_PtrCtx *ptr_ctx, DW_CIE *cie_out) -{ - B32 is_parsed = 0; - U64 cursor = format == DW_Format_32Bit ? 4 : 12; - - U64 cie_id = 0; - U64 cie_id_size = str8_deserial_read_dwarf_uint(data, cursor, format, &cie_id); - if (cie_id_size == 0) { goto exit; } - cursor += cie_id_size; - - U8 version = 0; - U64 version_size = str8_deserial_read_struct(data, cursor, &version); - if (version_size == 0) { goto exit; } - cursor += version_size; - - String8 aug_string = {0}; - String8 aug_data = {0}; - EH_Augmentation aug = {0}; - U64 code_align_factor = 0; - S64 data_align_factor = 0; - U64 ret_addr_reg = max_U64; - if (version == 1) { - U64 aug_string_size = str8_deserial_read_cstr(data, cursor, &aug_string); - if (aug_string_size == 0) { goto exit; } - cursor += aug_string_size; - - U64 eh_data = 0; - if (str8_match(aug_string, str8_lit("eh"), 0)) { - U64 arch_byte_size = byte_size_from_arch(arch); - cursor += str8_deserial_read(data, cursor, &eh_data, arch_byte_size, arch_byte_size); - } - - U64 code_align_factor_size = str8_deserial_read_uleb128(data, cursor, &code_align_factor); - if (code_align_factor_size == 0) { goto exit; } - cursor += code_align_factor_size; - - U64 data_align_factor_size = str8_deserial_read_sleb128(data, cursor, &data_align_factor); - if (data_align_factor_size == 0) { goto exit; } - cursor += data_align_factor_size; - - ret_addr_reg = 0; - U64 ret_addr_reg_size = str8_deserial_read(data, cursor, &ret_addr_reg, sizeof(U8), sizeof(U8)); - if (ret_addr_reg_size == 0) { goto exit; } - cursor += ret_addr_reg_size; - - U64 aug_data_size = eh_parse_aug_data(aug_string, str8_skip(data, cursor), pc + cursor, ptr_ctx, &aug); - aug_data = str8_substr(data, r1u64(cursor, cursor + aug.size)); - cursor += aug_data_size; - } else { - Assert(0 && "unexpected version"); - } - - cie_out->insts = str8_skip(data, cursor); - cie_out->aug_string = aug_string; - cie_out->aug_data = aug_data; - cie_out->code_align_factor = code_align_factor; - cie_out->data_align_factor = data_align_factor; - cie_out->ret_addr_reg = ret_addr_reg; - cie_out->format = format; - cie_out->version = version; - cie_out->address_size = byte_size_from_arch(arch); - cie_out->segment_selector_size = 0; - - cie_out->ext[EH_CIE_Ext_LSDAEnc] = EH_PtrEnc_Omit; - cie_out->ext[EH_CIE_Ext_AddrEnc] = EH_PtrEnc_Omit; - cie_out->ext[EH_CIE_Ext_HandlerEnc] = EH_PtrEnc_Omit; - - if (aug.flags & EH_AugFlag_HasLSDA) { - cie_out->ext[EH_CIE_Ext_LSDAEnc] = aug.lsda_encoding; - } - if (aug.flags & EH_AugFlag_HasAddrEnc) { - cie_out->ext[EH_CIE_Ext_AddrEnc] = aug.addr_encoding; - } - if (aug.flags & EH_AugFlag_HasHandler) { - cie_out->ext[EH_CIE_Ext_HandlerEnc] = aug.handler_encoding; - cie_out->ext[EH_CIE_Ext_HandlerIp ] = aug.handler_ip; - } - - is_parsed = 1; -exit:; - return is_parsed; -} - -internal B32 -eh_parse_fde(String8 data, DW_Format format, U64 pc, DW_CIE *cie, EH_PtrCtx *ptr_ctx, DW_FDE *fde_out) -{ - B32 is_parsed = 0; - U64 cursor = format == DW_Format_32Bit ? 8 : 20; - - U64 pc_begin = 0; - U64 pc_delta = 0; - EH_PtrEnc addr_enc = cie->ext[EH_CIE_Ext_AddrEnc]; - if (addr_enc != EH_PtrEnc_Omit) { - U64 pc_begin_size = eh_parse_ptr(data, cursor, pc + cursor, ptr_ctx, addr_enc, &pc_begin); - if (pc_begin_size == 0) { goto exit; } - cursor += pc_begin_size; - - U64 pc_delta_size = eh_parse_ptr(data, cursor, pc + cursor, ptr_ctx, addr_enc & EH_PtrEnc_TypeMask, &pc_delta); - if (pc_delta_size == 0) { goto exit; } - cursor += pc_delta_size; - } - - if (cursor + cie->aug_data.size > data.size) { goto exit; } - cursor += cie->aug_data.size; - - fde_out->format = format; - fde_out->pc_range = rng_1u64(pc_begin, pc_begin + pc_delta); - fde_out->insts = str8_skip(data, cursor); - - is_parsed = 1; -exit:; - return is_parsed; -} - -internal U64 -eh_find_nearest_fde(EH_FrameHdr header, EH_PtrCtx *ptr_ctx, U64 pc) -{ - U64 fde_addr = max_U64; - U64 fde_idx = max_U64; - - if (header.version == 1) { - if (header.fde_count > 0) { - U64 first = 0; - U64 first_size = eh_parse_ptr(header.table, 0, ptr_ctx->pc_vaddr, ptr_ctx, header.table_enc, &first); - AssertAlways(first_size); - if (first == pc) { - fde_idx = 0; - goto exit; - } - if (first > pc) { - goto exit; - } - - U64 last_off = header.table.size - header.entry_byte_size; - U64 last = 0; - U64 last_size = eh_parse_ptr(header.table, last_off, ptr_ctx->pc_vaddr + last_off, ptr_ctx, header.table_enc, &last); - AssertAlways(last_size); - if (last <= pc) { - fde_idx = header.fde_count - 1; - goto exit; - } - - U64 l = 0; - U64 r = header.fde_count - 1; - while (l <= r) { - U64 m = l + (r - l) / 2; - U64 m_pc_off = m * header.entry_byte_size; - U64 m_pc = 0; - U64 m_pc_size = eh_parse_ptr(header.table, m_pc_off, ptr_ctx->pc_vaddr + m_pc_off, ptr_ctx, header.table_enc, &m_pc); - Assert(m_pc_size); - if (m_pc > pc) { - r = m - 1; - } else if (m_pc < pc) { - l = m + 1; - } else { - fde_idx = m; - goto exit; - } - } - - fde_idx = l > 0 ? l-1 : 0; - } - } - -exit:; - if (fde_idx < header.fde_count) { - U64 fde_addr_off = (fde_idx * header.entry_byte_size) + header.field_byte_size; - U64 fde_addr_size = eh_parse_ptr(header.table, fde_addr_off, ptr_ctx->pc_vaddr + fde_addr_off, ptr_ctx, header.table_enc, &fde_addr); - Assert(fde_addr_size); - } - return fde_addr; -} - -internal int -eh_frame_hdr_entry_sort(void *raw_a, void *raw_b) -{ - return ((EH_FrameHdrEntry *)raw_a)->addr < ((EH_FrameHdrEntry *)raw_b)->addr; -} - -internal String8 -eh_frame_hdr_from_call_frame_info(Arena *arena, U64 fde_count, U64 *fde_offsets, DW_FDE *fde) -{ - Temp scratch = scratch_begin(&arena, 1); - - // make .eh_frame_hdr - String8List srl = {0}; - str8_serial_begin(scratch.arena, &srl); - str8_serial_push_u8(scratch.arena, &srl, 1); // version - str8_serial_push_u8(scratch.arena, &srl, EH_PtrEnc_Omit); // omit eh_frame_ptr field - str8_serial_push_u8(scratch.arena, &srl, EH_PtrEnc_UData8); // fde_count encoding - str8_serial_push_u8(scratch.arena, &srl, EH_PtrEnc_UData8); // table encoding - str8_serial_push_u64(scratch.arena, &srl, fde_count); // fde_count - String8 header = str8_serial_end(scratch.arena, &srl); - - // alloc buffer for output - U64 buffer_size = header.size + sizeof(EH_FrameHdrEntry) * fde_count; - U8 *buffer = push_array(arena, U8, buffer_size); - - // copy header - MemoryCopyStr8(buffer, header); - - // write the table - EH_FrameHdrEntry *table = (EH_FrameHdrEntry *)(buffer + header.size); - for EachIndex(fde_idx, fde_count) { - table[fde_idx].addr = fde[fde_idx].pc_range.min; - table[fde_idx].fde_offset = fde_offsets[fde_idx]; - } - radsort(table, fde_count, eh_frame_hdr_entry_sort); - - String8 eh_frame_hdr = str8(buffer, buffer_size); - scratch_end(scratch); - return eh_frame_hdr; -} - -internal -DW_DECODE_PTR(eh_decode_ptr) -{ - EH_DecodePtrCtx *ctx = ud; - return eh_parse_ptr(data, 0, ctx->ptr_ctx->pc_vaddr, ctx->ptr_ctx, ctx->addr_enc, ptr_out); -} - -internal String8 -eh_string_from_ptr_enc_type(EH_PtrEnc type) -{ - switch (type) { - case EH_PtrEnc_Ptr: return str8_lit("Ptr"); - case EH_PtrEnc_ULEB128: return str8_lit("ULEB128"); - case EH_PtrEnc_UData2: return str8_lit("UData2"); - case EH_PtrEnc_UData4: return str8_lit("UData4"); - case EH_PtrEnc_UData8: return str8_lit("UData8"); - case EH_PtrEnc_Signed: return str8_lit("Signed"); - case EH_PtrEnc_SLEB128: return str8_lit("SLEB128"); - case EH_PtrEnc_SData2: return str8_lit("SData2"); - case EH_PtrEnc_SData4: return str8_lit("SData4"); - case EH_PtrEnc_SData8: return str8_lit("SData8"); - } - return str8_zero(); -} - -internal String8 -eh_string_from_ptr_enc_modifier(EH_PtrEnc modifier) -{ - switch (modifier) { - case EH_PtrEnc_PcRel: return str8_lit("PcRel"); - case EH_PtrEnc_TextRel: return str8_lit("TextRel"); - case EH_PtrEnc_DataRel: return str8_lit("DataRel"); - case EH_PtrEnc_FuncRel: return str8_lit("FuncRel"); - case EH_PtrEnc_Aligned: return str8_lit("Aligned"); - } - return str8_zero(); -} - -internal String8 -eh_string_from_ptr_enc(Arena *arena, EH_PtrEnc enc) -{ - String8 type_str = eh_string_from_ptr_enc_type(enc & EH_PtrEnc_TypeMask); - String8 modifer_str = eh_string_from_ptr_enc_modifier(enc & EH_PtrEnc_ModifierMask); - String8 indir_str = enc & EH_PtrEnc_Indirect ? str8_lit("Indirect") : str8_zero(); - String8 result = str8f(arena, "Type: %S, Modifier %S (%S)", type_str, modifer_str, indir_str); - return result; -} diff --git a/src/dwarf/generated/dwarf.meta.c b/src/dwarf/generated/dwarf.meta.c index 0378fad9..ceb29e6f 100644 --- a/src/dwarf/generated/dwarf.meta.c +++ b/src/dwarf/generated/dwarf.meta.c @@ -3,3 +3,72 @@ //- GENERATED CODE +C_LINKAGE_BEGIN +String8 dw_regular_name_from_section_kind_table[18] = +{ +str8_lit_comp(""), +str8_lit_comp(".debug_abbrev"), +str8_lit_comp(".debug_aranges"), +str8_lit_comp(".debug_frame"), +str8_lit_comp(".debug_info"), +str8_lit_comp(".debug_line"), +str8_lit_comp(".debug_loc"), +str8_lit_comp(".debug_macinfo"), +str8_lit_comp(".debug_pubnames"), +str8_lit_comp(".debug_pubtypes"), +str8_lit_comp(".debug_ranges"), +str8_lit_comp(".debug_str"), +str8_lit_comp(".debug_addr"), +str8_lit_comp(".debug_loclists"), +str8_lit_comp(".debug_rnglists"), +str8_lit_comp(".debug_str_offsets"), +str8_lit_comp(".debug_line_str"), +str8_lit_comp(".debug_names"), +}; + +String8 dw_macho_name_from_section_kind_table[18] = +{ +str8_lit_comp(""), +str8_lit_comp("__debug_abbrev"), +str8_lit_comp("__debug_aranges"), +str8_lit_comp("__debug_frame"), +str8_lit_comp("__debug_info"), +str8_lit_comp("__debug_line"), +str8_lit_comp("__debug_loc"), +str8_lit_comp("__debug_macinfo"), +str8_lit_comp("__debug_pubnames"), +str8_lit_comp("__debug_pubtypes"), +str8_lit_comp("__debug_ranges"), +str8_lit_comp("__debug_str"), +str8_lit_comp("__debug_addr"), +str8_lit_comp("__debug_loclists"), +str8_lit_comp("__debug_rnglists"), +str8_lit_comp("__debug_str_offsets"), +str8_lit_comp("__debug_line_str"), +str8_lit_comp("__debug_names"), +}; + +String8 dw_dwo_name_from_section_kind_table[18] = +{ +str8_lit_comp(""), +str8_lit_comp(".debug_abbrev.dwo"), +str8_lit_comp(".debug_aranges.dwo"), +str8_lit_comp(".debug_frame.dwo"), +str8_lit_comp(".debug_info.dwo"), +str8_lit_comp(".debug_line.dwo"), +str8_lit_comp(".debug_loc.dwo"), +str8_lit_comp(".debug_macinfo.dwo"), +str8_lit_comp(".debug_pubnames.dwo"), +str8_lit_comp(".debug_pubtypes.dwo"), +str8_lit_comp(".debug_ranges.dwo"), +str8_lit_comp(".debug_str.dwo"), +str8_lit_comp(".debug_addr.dwo"), +str8_lit_comp(".debug_loclists.dwo"), +str8_lit_comp(".debug_rnglists.dwo"), +str8_lit_comp(".debug_str_offsets.dwo"), +str8_lit_comp(".debug_line_str.dwo"), +str8_lit_comp(".debug_names.dwo"), +}; + +C_LINKAGE_END + diff --git a/src/dwarf/generated/dwarf.meta.h b/src/dwarf/generated/dwarf.meta.h index 39719f80..5255dcca 100644 --- a/src/dwarf/generated/dwarf.meta.h +++ b/src/dwarf/generated/dwarf.meta.h @@ -6,6 +6,959 @@ #ifndef DWARF_META_H #define DWARF_META_H +typedef U16 DW_Version; +typedef enum DW_VersionEnum +{ +DW_Version_Null, +DW_Version_1, +DW_Version_2, +DW_Version_3, +DW_Version_4, +DW_Version_5, +DW_Version_Last = DW_Version_5, +} DW_VersionEnum; + +typedef enum DW_Ext +{ +DW_Ext_Null, +DW_Ext_GNU, +DW_Ext_LLVM, +DW_Ext_Apple, +DW_Ext_MIPS, +} DW_Ext; + +typedef U32 DW_ExtFlags; +typedef enum DW_ExtFlagsEnum +{ +DW_ExtFlag_GNU = (1<kind != reader_attrib->v.attrib_kind) { goto exit; } if (writer_attrib->form.reader.kind != reader_attrib->v.form.kind) { goto exit; } - if ( ! dw_form_match(writer_attrib->form.reader, reader_attrib->v.form)) { goto exit; } + if ( ! dw_test__form_match(writer_attrib->form.reader, reader_attrib->v.form)) { goto exit; } writer_attrib = writer_attrib->next; reader_attrib = reader_attrib->next; } @@ -1011,7 +1092,6 @@ Test(plus_uconst) TestCheck(expr_value.addr == 0x123 + 4); } -#if 0 Test(reg_split_spill) { // setup register context diff --git a/src/eh_frame/eh_frame.c b/src/eh_frame/eh_frame.c new file mode 100644 index 00000000..06fdc93e --- /dev/null +++ b/src/eh_frame/eh_frame.c @@ -0,0 +1,650 @@ +// Copyright (c) Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#if 0 +internal U64 +eh_parse_ptr(String8 frame_base, U64 off, U64 pc, EH_PtrCtx *ptr_ctx, EH_PtrEnc encoding, U64 *ptr_out) +{ + U64 ptr_off = off; + + if (encoding == EH_PtrEnc_Omit) { + return 0; + } + + // align read offset as needed + if (encoding == EH_PtrEnc_Aligned) { + ptr_off = AlignPow2(ptr_off, ptr_ctx->ptr_align); + encoding = EH_PtrEnc_Ptr; + } + + // decode pointer value + U64 decode_size = 0; + U64 raw_ptr_size = 0; + U64 raw_ptr = 0; + switch (encoding & EH_PtrEnc_TypeMask) { + default: { InvalidPath; } break; + + case EH_PtrEnc_Ptr : { raw_ptr_size = 8; } goto ufixed; + case EH_PtrEnc_UData2: { raw_ptr_size = 2; } goto ufixed; + case EH_PtrEnc_UData4: { raw_ptr_size = 4; } goto ufixed; + case EH_PtrEnc_UData8: { raw_ptr_size = 8; } goto ufixed; + ufixed: { + decode_size += str8_deserial_read(frame_base, ptr_off, &raw_ptr, raw_ptr_size, raw_ptr_size); + } break; + + // TODO: Signed is actually just a flag that indicates this int is negavite. + // There shouldn't be a read for Signed. + // For instance, (EH_PtrEnc_UData2 | EH_PtrEnc_Signed) == EH_PtrEnc_SData etc. + case EH_PtrEnc_Signed: { raw_ptr_size = 8; } goto sfixed; + + case EH_PtrEnc_SData2: { raw_ptr_size = 2; } goto sfixed; + case EH_PtrEnc_SData4: { raw_ptr_size = 4; } goto sfixed; + case EH_PtrEnc_SData8: { raw_ptr_size = 8; } goto sfixed; + sfixed: { + decode_size += str8_deserial_read(frame_base, ptr_off, &raw_ptr, raw_ptr_size, raw_ptr_size); + raw_ptr = extend_sign64(raw_ptr, raw_ptr_size); + } break; + + case EH_PtrEnc_ULEB128: { decode_size += str8_deserial_read_uleb128(frame_base, ptr_off, &raw_ptr); } break; + case EH_PtrEnc_SLEB128: { decode_size += str8_deserial_read_sleb128(frame_base, ptr_off, (S64*)&raw_ptr); } break; + } + + // apply relative bases + if (decode_size > 0) { + U64 ptr = raw_ptr; + switch (encoding & EH_PtrEnc_ModifierMask) { + case 0: break; + case EH_PtrEnc_PcRel: { ptr = pc + raw_ptr; } break; + case EH_PtrEnc_TextRel: { ptr = ptr_ctx->text_vaddr + raw_ptr; } break; + case EH_PtrEnc_DataRel: { ptr = ptr_ctx->data_vaddr + raw_ptr; } break; + case EH_PtrEnc_FuncRel: { + Assert(!"TODO: need a sample to verify implementation"); + ptr = ptr_ctx->func_vaddr + raw_ptr; + } break; + default: { InvalidPath; } break; + } + + if (ptr_out) { + *ptr_out = ptr; + } + } + + return decode_size; +} +#endif + +internal EH_FrameHdr +eh_parse_frame_hdr(String8 data, U64 address_size, EH_PtrCtx *ptr_ctx) +{ + EH_FrameHdr header = {0}; + U64 cursor = 0; + + U64 version_size = str8_deserial_read_struct(data, cursor, &header.version); + if (version_size == 0) { goto exit; } + cursor += version_size; + + if (header.version == 1) { + U64 eh_frame_ptr_enc_size = str8_deserial_read_struct(data, cursor, &header.eh_frame_ptr_enc); + if (eh_frame_ptr_enc_size == 0) { goto exit; } + cursor += eh_frame_ptr_enc_size; + + U64 fde_count_enc_size = str8_deserial_read_struct(data, cursor, &header.fde_count_enc); + if (fde_count_enc_size == 0) { goto exit; } + cursor += fde_count_enc_size; + + U64 table_enc_size = str8_deserial_read_struct(data, cursor, &header.table_enc); + if (table_enc_size == 0) { goto exit; } + cursor += table_enc_size; + + cursor += eh_read_ptr(data, cursor, cursor, ptr_ctx, header.eh_frame_ptr_enc, &header.eh_frame_ptr); + cursor += eh_read_ptr(data, cursor, cursor, ptr_ctx, header.fde_count_enc, &header.fde_count); + + switch (header.table_enc & EH_PtrEnc_TypeMask) { + case EH_PtrEnc_Ptr: { header.field_byte_size = address_size; } break; + case EH_PtrEnc_ULEB128: { InvalidPath; } break; // TODO: when loading module convert these to UData8 + case EH_PtrEnc_UData2: { header.field_byte_size = 2; } break; + case EH_PtrEnc_UData4: { header.field_byte_size = 4; } break; + case EH_PtrEnc_UData8: { header.field_byte_size = 8; } break; + case EH_PtrEnc_Signed: { header.field_byte_size = address_size; } break; + case EH_PtrEnc_SLEB128: { InvalidPath; } break; // TODO: when loading module convert these to UData8 + case EH_PtrEnc_SData2: { header.field_byte_size = 2; } break; + case EH_PtrEnc_SData4: { header.field_byte_size = 4; } break; + case EH_PtrEnc_SData8: { header.field_byte_size = 8; } break; + default: { InvalidPath; } break; + } + header.entry_byte_size = header.field_byte_size * 2; + + header.table = str8_skip(data, cursor); + AssertAlways(header.table.size == header.entry_byte_size * header.fde_count); + } else { + Assert(0 && "unknown version"); + } + + exit:; + return header; +} + +#if 0 +internal U64 +eh_parse_aug_data(String8 aug_string, String8 aug_data, U64 pc, EH_PtrCtx *ptr_ctx, EH_Augmentation *aug_out) +{ + // TODO: + // Handle "eh" param, it indicates presence of EH Data field. + // On 32bit arch it is a 4-byte and on 64-bit 8-byte value. + // Reference: https://refspecs.linuxfoundation.org/LSB_3.0.0/LSB-PDA/LSB-PDA/ehframechpt.html + // Reference doc doesn't clarify structure for EH Data though + + U64 cursor = 0; + + U64 aug_data_size = 0; + EH_AugFlags aug_flags = 0; + EH_PtrEnc lsda_encoding = EH_PtrEnc_Omit; + EH_PtrEnc addr_encoding = EH_PtrEnc_UData8; + EH_PtrEnc handler_encoding = EH_PtrEnc_Omit; + U64 handler_ip = 0; + if (str8_match(str8_prefix(aug_string, 1), str8_lit("z"), 0)) { + cursor = str8_deserial_read_uleb128(aug_data, cursor, &aug_data_size); + + for (U8 *ptr = aug_string.str+1; ptr < (aug_string.str+aug_string.size); ptr += 1) { + switch (*ptr) { + case 'L': { + cursor += str8_deserial_read_struct(aug_data, cursor, &lsda_encoding); + aug_flags |= EH_AugFlag_HasLSDA; + } break; + case 'P': { + cursor += str8_deserial_read_struct(aug_data, cursor, &handler_encoding); + cursor += eh_parse_ptr(aug_data, cursor, pc + cursor, ptr_ctx, handler_encoding, &handler_ip); + aug_flags |= EH_AugFlag_HasHandler; + } break; + case 'R': { + cursor += str8_deserial_read_struct(aug_data, cursor, &addr_encoding); + aug_flags |= EH_AugFlag_HasAddrEnc; + } break; + case 'S': { + aug_flags |= EH_AugFlag_SignalFrame; + } break; + default: { Assert(!"failed to parse augmentation string"); goto exit; } break; + } + } + } + + if (aug_out) { + aug_out->handler_ip = handler_ip; + aug_out->handler_encoding = handler_encoding; + aug_out->lsda_encoding = handler_encoding; + aug_out->addr_encoding = addr_encoding; + aug_out->flags = aug_flags; + aug_out->size = aug_data_size; + } + + exit:; + U64 parse_size = cursor; + return parse_size; +} +#endif + +internal U64 +eh_read_ptr(String8 data, U64 off, U64 pc, EH_PtrCtx *ptr_ctx, EH_PtrEnc encoding, U64 *ptr_out) +{ + U64 start_off = off; + if(encoding != EH_PtrEnc_Omit) + { + // rjf: apply aligned encoding & align pointer offset + if(encoding == EH_PtrEnc_Aligned) + { + off = AlignPow2(off, ptr_ctx->ptr_align); + encoding = EH_PtrEnc_Ptr; + } + U64 start_ptr_read_off = off; + + // rjf: decode pointer value + U64 raw_ptr_size = 0; + U64 raw_ptr = 0; + switch(encoding & EH_PtrEnc_TypeMask) + { + default:{}break; + + // rjf: fixed-size pointer reads + case EH_PtrEnc_Ptr: {raw_ptr_size = 8;}goto ufixed; + case EH_PtrEnc_UData2:{raw_ptr_size = 2;}goto ufixed; + case EH_PtrEnc_UData4:{raw_ptr_size = 4;}goto ufixed; + case EH_PtrEnc_UData8:{raw_ptr_size = 8;}goto ufixed; + ufixed: + { + off += str8_deserial_read(data, off, &raw_ptr, raw_ptr_size, raw_ptr_size); + }break; + + // TODO: Signed is actually just a flag that indicates this int is negavite. + // There shouldn't be a read for Signed. + // For instance, (EH_PtrEnc_UData2 | EH_PtrEnc_Signed) == EH_PtrEnc_SData etc. + case EH_PtrEnc_Signed:{raw_ptr_size = 8;}goto sfixed; + + // rjf: fixed-size pointer reads w/ sign extension + case EH_PtrEnc_SData2:{raw_ptr_size = 2;}goto sfixed; + case EH_PtrEnc_SData4:{raw_ptr_size = 4;}goto sfixed; + case EH_PtrEnc_SData8:{raw_ptr_size = 8;}goto sfixed; + sfixed: + { + off += str8_deserial_read(data, off, &raw_ptr, raw_ptr_size, raw_ptr_size); + raw_ptr = extend_sign64(raw_ptr, raw_ptr_size); + }break; + + // rjf: uleb128/sleb128 + case EH_PtrEnc_ULEB128:{off += str8_deserial_read_uleb128(data, off, &raw_ptr); }break; + case EH_PtrEnc_SLEB128:{off += str8_deserial_read_sleb128(data, off, (S64*)&raw_ptr);}break; + } + + // rjf: apply relative bases + U64 ptr = raw_ptr; + if(off != start_ptr_read_off) + { + switch(encoding & EH_PtrEnc_ModifierMask) + { + default:{}break; + case 0:{}break; + case EH_PtrEnc_PcRel: { ptr = pc + raw_ptr; } break; + case EH_PtrEnc_TextRel:{ ptr = ptr_ctx->text_vaddr + raw_ptr; } break; + case EH_PtrEnc_DataRel:{ ptr = ptr_ctx->data_vaddr + raw_ptr; } break; + case EH_PtrEnc_FuncRel: + { + Assert(!"TODO: need a sample to verify implementation"); + ptr = ptr_ctx->func_vaddr + raw_ptr; + }break; + } + } + + // rjf: fill output + if(ptr_out) + { + *ptr_out = ptr; + } + } + U64 bytes_read = (off - start_off); + return bytes_read; +} + +internal U64 +eh_read_aug_data(String8 data, U64 off, String8 string, U64 pc, EH_PtrCtx *ptr_ctx, EH_Augmentation *aug_out) +{ + // TODO: + // Handle "eh" param, it indicates presence of EH Data field. + // On 32bit arch it is a 4-byte and on 64-bit 8-byte value. + // Reference: https://refspecs.linuxfoundation.org/LSB_3.0.0/LSB-PDA/LSB-PDA/ehframechpt.html + // Reference doc doesn't clarify structure for EH Data though + U64 start_off = off; + U64 aug_data_size = 0; + EH_AugFlags aug_flags = 0; + EH_PtrEnc lsda_encoding = EH_PtrEnc_Omit; + EH_PtrEnc addr_encoding = EH_PtrEnc_UData8; + EH_PtrEnc handler_encoding = EH_PtrEnc_Omit; + U64 handler_ip = 0; + if(str8_match(str8_prefix(string, 1), str8_lit("z"), 0)) + { + off += str8_deserial_read_uleb128(data, off, &aug_data_size); + for(U8 *ptr = string.str+1; ptr < (string.str+string.size); ptr += 1) + { + switch(*ptr) + { + default:{goto exit;}break; + case 'L': + { + off += str8_deserial_read_struct(data, off, &lsda_encoding); + aug_flags |= EH_AugFlag_HasLSDA; + }break; + case 'P': + { + off += str8_deserial_read_struct(data, off, &handler_encoding); + off += eh_read_ptr(data, off, pc + (off - start_off), ptr_ctx, handler_encoding, &handler_ip); + aug_flags |= EH_AugFlag_HasHandler; + }break; + case 'R': + { + off += str8_deserial_read_struct(data, off, &addr_encoding); + aug_flags |= EH_AugFlag_HasAddrEnc; + }break; + case 'S': + { + aug_flags |= EH_AugFlag_SignalFrame; + }break; + } + } + } + if(aug_out != 0) + { + aug_out->handler_ip = handler_ip; + aug_out->handler_encoding = handler_encoding; + aug_out->lsda_encoding = handler_encoding; + aug_out->addr_encoding = addr_encoding; + aug_out->flags = aug_flags; + aug_out->size = aug_data_size; + } + exit:; + U64 bytes_read = (off - start_off); + return bytes_read; +} + +internal U64 +eh_read_cfi_header(String8 data, U64 off, DW_CFIHeader *cfi_header_out) +{ + U64 start_off = off; + + // rjf: read length / format + U64 length = 0; + DW_Format fmt = DW_Format_Null; + U64 length_size = dw2_read_initial_length(data, off, &length, &fmt); + + // rjf: find entry info, read ID + Rng1U64 entry_range = r1u64(off, off + length_size + length); + String8 entry_data = str8_substr(data, entry_range); + U64 id = 0; + U64 id_size = dw2_read_fmt_u64(entry_data, length_size, fmt, &id); + + // rjf: fill + cfi_header_out->kind = (id == 0) ? DW_CFIKind_CIE : DW_CFIKind_FDE; + cfi_header_out->fmt = fmt; + cfi_header_out->entry_range = entry_range; + cfi_header_out->cie_pointer = id; + cfi_header_out->cie_pointer_off = off + length_size; + + U64 bytes_read = (off - start_off); + return bytes_read; +} + +internal U64 +eh_read_cie(String8 data, U64 off, DW_Format fmt, Arch arch, U64 pc, EH_PtrCtx *ptr_ctx, DW_CIE *cie_out) +{ + U64 start_off = off; + + // rjf: skip format-dependent prefix + off += (fmt == DW_Format_32Bit) ? 4 : 12; + + // rjf: read id + U64 id = 0; + off += dw2_read_fmt_u64(data, off, fmt, &id); + + // rjf: read version + U8 version = 0; + off += str8_deserial_read_struct(data, off, &version); + + // rjf: read entry info + U64 aug_string_off_first = 0; + U64 aug_string_off_opl = 0; + U64 aug_data_off_first = 0; + U64 aug_data_off_opl = 0; + U64 code_align_factor = 0; + S64 data_align_factor = 0; + U64 ret_addr_reg = 0; + EH_Augmentation aug = {0}; + if(version == 1) + { + // rjf: read aug string + aug_string_off_first = off; + String8 aug_string = {0}; + off += str8_deserial_read_cstr(data, off, &aug_string); + aug_string_off_opl = off; + + // rjf: read eh data + U64 eh_data = 0; + if(str8_match(aug_string, s("eh"), 0)) + { + U64 arch_byte_size = byte_size_from_arch(arch); + off += str8_deserial_read(data, off, &eh_data, arch_byte_size, arch_byte_size); + } + + // rjf: read align factors + off += str8_deserial_read_uleb128(data, off, &code_align_factor); + off += str8_deserial_read_sleb128(data, off, &data_align_factor); + + // rjf: read return address register + off += str8_deserial_read(data, off, &ret_addr_reg, sizeof(U8), sizeof(U8)); + + // rjf: parse augmentation + aug_data_off_first = off; + U64 aug_data_size = eh_read_aug_data(data, off, aug_string, pc + (off - start_off), ptr_ctx, &aug); + aug_data_off_opl = off + aug.size; + off += aug_data_size; + } + + // rjf: fill output + cie_out->aug_string_range = r1u64(aug_string_off_first, aug_string_off_opl); + cie_out->aug_data_range = r1u64(aug_data_off_first, aug_data_off_opl); + cie_out->code_align_factor = code_align_factor; + cie_out->data_align_factor = data_align_factor; + cie_out->ret_addr_reg = ret_addr_reg; + cie_out->format = fmt; + cie_out->version = version; + cie_out->address_size = byte_size_from_arch(arch); + cie_out->segment_selector_size = 0; + cie_out->ext[EH_CIE_Ext_LSDAEnc] = EH_PtrEnc_Omit; + cie_out->ext[EH_CIE_Ext_AddrEnc] = EH_PtrEnc_Omit; + cie_out->ext[EH_CIE_Ext_HandlerEnc] = EH_PtrEnc_Omit; + if(aug.flags & EH_AugFlag_HasLSDA) + { + cie_out->ext[EH_CIE_Ext_LSDAEnc] = aug.lsda_encoding; + } + if(aug.flags & EH_AugFlag_HasAddrEnc) + { + cie_out->ext[EH_CIE_Ext_AddrEnc] = aug.addr_encoding; + } + if(aug.flags & EH_AugFlag_HasHandler) + { + cie_out->ext[EH_CIE_Ext_HandlerEnc] = aug.handler_encoding; + cie_out->ext[EH_CIE_Ext_HandlerIp ] = aug.handler_ip; + } + + U64 bytes_read = (off - start_off); + return bytes_read; +} + +internal U64 +eh_read_fde(String8 data, U64 off, DW_Format fmt, Arch arch, U64 pc, EH_PtrCtx *ptr_ctx, DW_CIE *cie, DW_FDE *fde_out) +{ + U64 start_off = off; + + // rjf: skip format-dependent prefix + off += (fmt == DW_Format_32Bit) ? 8 : 20; + + // rjf: read pc begin / delta + U64 pc_begin = 0; + U64 pc_delta = 0; + EH_PtrEnc addr_enc = cie->ext[EH_CIE_Ext_AddrEnc]; + if(addr_enc != EH_PtrEnc_Omit) + { + off += eh_read_ptr(data, off, pc + (off - start_off), ptr_ctx, addr_enc, &pc_begin); + off += eh_read_ptr(data, off, pc + (off - start_off), ptr_ctx, addr_enc & EH_PtrEnc_TypeMask, &pc_delta); + } + + // rjf: skip aug data + off += dim_1u64(cie->aug_data_range); + + // rjf: fill output + fde_out->pc_range = r1u64(pc_begin, pc_begin + pc_delta); + + U64 bytes_read = (off - start_off); + return bytes_read; +} + +#if 0 +internal B32 +eh_parse_fde(String8 data, DW_Format format, U64 pc, DW_CIE *cie, EH_PtrCtx *ptr_ctx, DW_FDE *fde_out) +{ + B32 is_parsed = 0; + U64 cursor = format == DW_Format_32Bit ? 8 : 20; + + U64 pc_begin = 0; + U64 pc_delta = 0; + EH_PtrEnc addr_enc = cie->ext[EH_CIE_Ext_AddrEnc]; + if (addr_enc != EH_PtrEnc_Omit) { + U64 pc_begin_size = eh_parse_ptr(data, cursor, pc + cursor, ptr_ctx, addr_enc, &pc_begin); + if (pc_begin_size == 0) { goto exit; } + cursor += pc_begin_size; + + U64 pc_delta_size = eh_parse_ptr(data, cursor, pc + cursor, ptr_ctx, addr_enc & EH_PtrEnc_TypeMask, &pc_delta); + if (pc_delta_size == 0) { goto exit; } + cursor += pc_delta_size; + } + + if (cursor + cie->aug_data.size > data.size) { goto exit; } + cursor += cie->aug_data.size; + + fde_out->format = format; + fde_out->pc_range = rng_1u64(pc_begin, pc_begin + pc_delta); + fde_out->insts = str8_skip(data, cursor); + + is_parsed = 1; + exit:; + return is_parsed; +} +#endif + +#if 0 +internal U64 +eh_find_nearest_fde(EH_FrameHdr header, EH_PtrCtx *ptr_ctx, U64 pc) +{ + U64 fde_addr = max_U64; + U64 fde_idx = max_U64; + + if (header.version == 1) { + if (header.fde_count > 0) { + U64 first = 0; + U64 first_size = eh_parse_ptr(header.table, 0, ptr_ctx->pc_vaddr, ptr_ctx, header.table_enc, &first); + AssertAlways(first_size); + if (first == pc) { + fde_idx = 0; + goto exit; + } + if (first > pc) { + goto exit; + } + + U64 last_off = header.table.size - header.entry_byte_size; + U64 last = 0; + U64 last_size = eh_parse_ptr(header.table, last_off, ptr_ctx->pc_vaddr + last_off, ptr_ctx, header.table_enc, &last); + AssertAlways(last_size); + if (last <= pc) { + fde_idx = header.fde_count - 1; + goto exit; + } + + U64 l = 0; + U64 r = header.fde_count - 1; + while (l <= r) { + U64 m = l + (r - l) / 2; + U64 m_pc_off = m * header.entry_byte_size; + U64 m_pc = 0; + U64 m_pc_size = eh_parse_ptr(header.table, m_pc_off, ptr_ctx->pc_vaddr + m_pc_off, ptr_ctx, header.table_enc, &m_pc); + Assert(m_pc_size); + if (m_pc > pc) { + r = m - 1; + } else if (m_pc < pc) { + l = m + 1; + } else { + fde_idx = m; + goto exit; + } + } + + fde_idx = l > 0 ? l-1 : 0; + } + } + + exit:; + if (fde_idx < header.fde_count) { + U64 fde_addr_off = (fde_idx * header.entry_byte_size) + header.field_byte_size; + U64 fde_addr_size = eh_parse_ptr(header.table, fde_addr_off, ptr_ctx->pc_vaddr + fde_addr_off, ptr_ctx, header.table_enc, &fde_addr); + Assert(fde_addr_size); + } + return fde_addr; +} +#endif + +internal int +eh_frame_hdr_entry_sort(void *raw_a, void *raw_b) +{ + return ((EH_FrameHdrEntry *)raw_a)->addr < ((EH_FrameHdrEntry *)raw_b)->addr; +} + +internal String8 +eh_frame_hdr_from_call_frame_info(Arena *arena, U64 fde_count, U64 *fde_offsets, DW_FDE *fde) +{ + Temp scratch = scratch_begin(&arena, 1); + + // make .eh_frame_hdr + String8List srl = {0}; + str8_serial_begin(scratch.arena, &srl); + str8_serial_push_u8(scratch.arena, &srl, 1); // version + str8_serial_push_u8(scratch.arena, &srl, EH_PtrEnc_Omit); // omit eh_frame_ptr field + str8_serial_push_u8(scratch.arena, &srl, EH_PtrEnc_UData8); // fde_count encoding + str8_serial_push_u8(scratch.arena, &srl, EH_PtrEnc_UData8); // table encoding + str8_serial_push_u64(scratch.arena, &srl, fde_count); // fde_count + String8 header = str8_serial_end(scratch.arena, &srl); + + // alloc buffer for output + U64 buffer_size = header.size + sizeof(EH_FrameHdrEntry) * fde_count; + U8 *buffer = push_array(arena, U8, buffer_size); + + // copy header + MemoryCopyStr8(buffer, header); + + // write the table + EH_FrameHdrEntry *table = (EH_FrameHdrEntry *)(buffer + header.size); + for EachIndex(fde_idx, fde_count) { + table[fde_idx].addr = fde[fde_idx].pc_range.min; + table[fde_idx].fde_offset = fde_offsets[fde_idx]; + } + radsort(table, fde_count, eh_frame_hdr_entry_sort); + + String8 eh_frame_hdr = str8(buffer, buffer_size); + scratch_end(scratch); + return eh_frame_hdr; +} + +#if 0 +internal +DW_DECODE_PTR(eh_decode_ptr) +{ + EH_DecodePtrCtx *ctx = ud; + return eh_parse_ptr(data, 0, ctx->ptr_ctx->pc_vaddr, ctx->ptr_ctx, ctx->addr_enc, ptr_out); +} +#endif + +internal String8 +eh_string_from_ptr_enc_type(EH_PtrEnc type) +{ + switch (type) { + case EH_PtrEnc_Ptr: return str8_lit("Ptr"); + case EH_PtrEnc_ULEB128: return str8_lit("ULEB128"); + case EH_PtrEnc_UData2: return str8_lit("UData2"); + case EH_PtrEnc_UData4: return str8_lit("UData4"); + case EH_PtrEnc_UData8: return str8_lit("UData8"); + case EH_PtrEnc_Signed: return str8_lit("Signed"); + case EH_PtrEnc_SLEB128: return str8_lit("SLEB128"); + case EH_PtrEnc_SData2: return str8_lit("SData2"); + case EH_PtrEnc_SData4: return str8_lit("SData4"); + case EH_PtrEnc_SData8: return str8_lit("SData8"); + } + return str8_zero(); +} + +internal String8 +eh_string_from_ptr_enc_modifier(EH_PtrEnc modifier) +{ + switch (modifier) { + case EH_PtrEnc_PcRel: return str8_lit("PcRel"); + case EH_PtrEnc_TextRel: return str8_lit("TextRel"); + case EH_PtrEnc_DataRel: return str8_lit("DataRel"); + case EH_PtrEnc_FuncRel: return str8_lit("FuncRel"); + case EH_PtrEnc_Aligned: return str8_lit("Aligned"); + } + return str8_zero(); +} + +internal String8 +eh_string_from_ptr_enc(Arena *arena, EH_PtrEnc enc) +{ + String8 type_str = eh_string_from_ptr_enc_type(enc & EH_PtrEnc_TypeMask); + String8 modifer_str = eh_string_from_ptr_enc_modifier(enc & EH_PtrEnc_ModifierMask); + String8 indir_str = enc & EH_PtrEnc_Indirect ? str8_lit("Indirect") : str8_zero(); + String8 result = str8f(arena, "Type: %S, Modifier %S (%S)", type_str, modifer_str, indir_str); + return result; +} diff --git a/src/dwarf/eh_frame.h b/src/eh_frame/eh_frame.h similarity index 85% rename from src/dwarf/eh_frame.h rename to src/eh_frame/eh_frame.h index 9ae4a257..e4517d8a 100644 --- a/src/dwarf/eh_frame.h +++ b/src/eh_frame/eh_frame.h @@ -20,7 +20,7 @@ enum EH_PtrEnc_SData2 = 0x0A, // Signed 16-bit value EH_PtrEnc_SData4 = 0x0B, // Signed 32-bit value EH_PtrEnc_SData8 = 0x0C, // Signed 64-bit value - + EH_PtrEnc_TypeMask = 0x0F, }; @@ -31,7 +31,7 @@ enum EH_PtrEnc_DataRel = 0x30, // Value is relative to the .got or .eh_frame_hdr section. EH_PtrEnc_FuncRel = 0x40, // Value is relative to the function. EH_PtrEnc_Aligned = 0x50, // Value is aligned to an address unit sized boundary. - + EH_PtrEnc_ModifierMask = 0x70, }; @@ -104,13 +104,23 @@ typedef struct EH_DecodePtrCtx EH_PtrEnc addr_enc; EH_PtrCtx *ptr_ctx; } EH_DecodePtrCtx; - + //////////////////////////////// +#if 0 internal U64 eh_parse_ptr(String8 frame_base, U64 off, U64 pc, EH_PtrCtx *ptr_ctx, EH_PtrEnc encoding, U64 *ptr_out); +#endif internal EH_FrameHdr eh_parse_frame_hdr(String8 data, U64 address_size, EH_PtrCtx *ptr_ctx); +#if 0 internal U64 eh_parse_aug_data(String8 aug_string, String8 aug_data, U64 pc, EH_PtrCtx *ptr_ctx, EH_Augmentation *aug_out); -internal B32 eh_parse_cie(String8 data, DW_Format format, Arch arch, U64 pc, EH_PtrCtx *ptr_ctx, DW_CIE *cie_out); +#endif + +internal U64 eh_read_ptr(String8 data, U64 off, U64 pc, EH_PtrCtx *ptr_ctx, EH_PtrEnc encoding, U64 *ptr_out); +internal U64 eh_read_aug_data(String8 data, U64 off, String8 string, U64 pc, EH_PtrCtx *ptr_ctx, EH_Augmentation *aug_out); +internal U64 eh_read_cfi_header(String8 data, U64 off, DW_CFIHeader *cfi_header_out); +internal U64 eh_read_cie(String8 data, U64 off, DW_Format fmt, Arch arch, U64 pc, EH_PtrCtx *ptr_ctx, DW_CIE *cie_out); +internal U64 eh_read_fde(String8 data, U64 off, DW_Format fmt, Arch arch, U64 pc, EH_PtrCtx *ptr_ctx, DW_CIE *cie, DW_FDE *fde_out); + internal B32 eh_parse_fde(String8 data, DW_Format format, U64 pc, DW_CIE *cie, EH_PtrCtx *ptr_ctx, DW_FDE *fde_out); internal U64 eh_find_nearest_fde(EH_FrameHdr header, EH_PtrCtx *ptr_ctx, U64 pc); internal String8 eh_frame_hdr_from_call_frame_info(Arena *arena, U64 fde_count, U64 *fde_offsets, struct DW_FDE *fde); diff --git a/src/eh_frame/eh_frame_dump.c b/src/eh_frame/eh_frame_dump.c new file mode 100644 index 00000000..fad2a3a4 --- /dev/null +++ b/src/eh_frame/eh_frame_dump.c @@ -0,0 +1,152 @@ +// Copyright (c) Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +internal String8List +eh_dump_list_from_data(Arena *arena, Arch arch, U64 eh_frame_hdr_vaddr, U64 eh_frame_vaddr, String8 eh_frame_hdr, String8 eh_frame, EH_DumpSubsetFlags subset_flags) +{ + Temp scratch = scratch_begin(&arena, 1); + String8List strings = {0}; +#define dumpf(...) str8_list_pushf(arena, &strings, __VA_ARGS__) + + EH_PtrCtx ptr_ctx = {.data_vaddr = eh_frame_hdr_vaddr}; + EH_FrameHdr hdr = eh_parse_frame_hdr(eh_frame_hdr, byte_size_from_arch(arch), &ptr_ctx); + DW_ExtFlags exts = DW_ExtFlag_All; + + //- rjf: eh frame header + if(subset_flags & EH_DumpSubsetFlag_EhFrameHdr) + { + dumpf(".eh_frame_hdr:\n"); + dumpf("{\n"); + dumpf(" version: %u\n", hdr.version); + dumpf(" eh_frame_ptr_enc: %S\n", eh_string_from_ptr_enc(scratch.arena, hdr.eh_frame_ptr_enc)); + dumpf(" table_enc: %S\n", eh_string_from_ptr_enc(scratch.arena, hdr.table_enc)); + dumpf(" fde_count: %llu\n", hdr.fde_count); + if(hdr.eh_frame_ptr_enc != EH_PtrEnc_Omit) + { + dumpf(" eh_frame_ptr: 0x%I64x\n", hdr.eh_frame_ptr); + } + dumpf(" Entries:\n"); + dumpf(" {\n"); + for(U64 cursor = 0; cursor < hdr.table.size; ) + { + U64 entry_off = cursor; + U64 pc = 0; + U64 pc_size = eh_read_ptr(hdr.table, cursor, cursor, &ptr_ctx, hdr.table_enc, &pc); + if (pc_size == 0) { break; } + cursor += pc_size; + U64 fde_addr = 0; + U64 fde_addr_size = eh_read_ptr(hdr.table, cursor, cursor, &ptr_ctx, hdr.table_enc, &fde_addr); + if (fde_addr_size == 0) { break; } + cursor += fde_addr_size; + U64 fde_offset = fde_addr - eh_frame_vaddr; + dumpf(" { off=0x%04I64x, pc=0x%I64x, fde=0x%I64x }\n", entry_off, pc, fde_offset); + } + dumpf(" }\n"); + dumpf("}\n"); + } + + //- rjf: eh frame data + if(subset_flags & EH_DumpSubsetFlag_EhFrame) + { +#if 0 + dumpf(".eh_frame:\n"); + dumpf("{\n"); + for(U64 cursor = 0; cursor < eh_frame.size;) + { + DW_CFIHeader header = {0}; + U64 header_size = eh_read_cfi_header(eh_frame, cursor, &header); + switch(header.kind) + { + case DW_CFIKind_Null:{}break; + case DW_CFIKind_CIE: + { + DW_CIE cie = {0}; + eh_read_cie(eh_frame, header.entry_range.min, header.fmt, arch, eh_frame_vaddr + cursor, &ptr_ctx, &cie); + if(cie.version != 0) + { + String8List init_insts_str_list = dw_string_list_from_cfi_program(scratch.arena, 0, arch, DW_Version_5, ext, cie.format, 0, &cie, eh_decode_ptr, &ptr_ctx, cie.insts); + String8 aug_string = str8_substr(eh_frame, cie.aug_string_range); + dumpf(" CIE: // entry range: [0x%I64x, 0x%I64x)\n", header.entry_range.min, header.entry_range.max); + dumpf(" {\n"); + dumpf(" Format: %S\n", dw_string_from_format(desc.format)); + dumpf(" Version: %u\n", cie.version); + dumpf(" Aug string: \"%S\"\n", aug_string); + dumpf(" Code align: %I64u\n", cie.code_align_factor); + dumpf(" Data align: %I64d\n", cie.data_align_factor); + dumpf(" Return addr reg: %u\n", cie.ret_addr_reg); + if(cie.version > DW_Version_3) + { + dumpf(" Address size: %u\n", cie.address_size); + dumpf(" Segment selector size: %u\n", cie.segment_selector_size); + } + dumpf(" Initial Insturction:\n"); + dumpf(" {\n"); + for EachNode(n, String8Node, init_insts_str_list.first) + { + dumpf(" %S\n", n->string); + } + dumpf(" }\n"); + dumpf(" }\n"); + } + else + { + dumpf("ERROR: unable to parse CIE @ %I64x\n", header.entry_range.min); + } + }break; + case DW_CFIKind_FDE: + { + U64 cie_offset = header.cie_pointer_off - header.cie_pointer; + + DW_CIE cie = {0}; + { + DW_DescriptorEntry cie_desc = {0}; + eh_parse_descriptor_entry_header(eh_frame, cie_offset, &cie_desc); + if (cie_desc.type == DW_DescriptorEntryKind_CIE) { + String8 cie_data = str8_substr(eh_frame, cie_desc.entry_range); + eh_parse_cie(cie_data, cie_desc.format, arch, eh_frame_vaddr + cie_offset, &ptr_ctx, &cie); + } + } + + String8 fde_raw = str8_substr(eh_frame, desc.entry_range); + DW_FDE fde = {0}; + if (eh_parse_fde(fde_raw, desc.format, eh_frame_vaddr + cursor, &cie, &ptr_ctx, &fde)) { + String8List insts_str_list = dw_string_list_from_cfi_program(scratch.arena, 0, arch, hdr.version, ext, fde.format, 0, &cie, eh_decode_ptr, &ptr_ctx, fde.insts); + + dumpf(" FDE: // entry range: %r\n", desc.entry_range); + dumpf(" {\n"); + { + dumpf(" Format: %S\n", dw_string_from_format(fde.format)); + dumpf(" CIE: 0x%I64x\n", cie_offset); + dumpf(" PC range: %r\n", fde.pc_range); + dumpf(" Instructions:\n"); + dumpf(" {\n"); + for EachNode(n, String8Node, insts_str_list.first) { dumpf(" %S\n", n->string); } + dumpf(" }\n"); + + dumpf(" Unwind:\n"); + dumpf(" {\n"); + DW_CFI_Unwind *cfi_unwind = dw_cfi_unwind_init(scratch.arena, arch, &cie, &fde, eh_decode_ptr, &ptr_ctx); + do { + String8 cfa_str = dw_string_from_cfa(scratch.arena, arch, cie.address_size, hdr.version, ext, fde.format, cfi_unwind->row->cfa); + String8 cfi_row_str = dw_string_from_cfi_row(scratch.arena, arch, cie.address_size, hdr.version, ext, fde.format, cfi_unwind->row); + dumpf(" { PC: 0x%I64x, CFA: %-7S, Rules: { %S }\n", cfi_unwind->pc, cfa_str, cfi_row_str); + } while (dw_cfi_next_row(scratch.arena, cfi_unwind)); + dumpf(" }\n"); + } + } else { + dumpf("ERROR: unable to parse FDE @ %I64x\n", desc.entry_range.min); + } + dumpf(" }\n"); + } break; + } + + cursor += desc_size; + } + dumpf("}\n"); +#endif + } + +#undef dumpf + scratch_end(scratch); + return strings; +} diff --git a/src/dwarf/eh_dump.h b/src/eh_frame/eh_frame_dump.h similarity index 92% rename from src/dwarf/eh_dump.h rename to src/eh_frame/eh_frame_dump.h index 8ba5a6cb..7ded82bd 100644 --- a/src/dwarf/eh_dump.h +++ b/src/eh_frame/eh_frame_dump.h @@ -8,8 +8,8 @@ //~ Dump Subset Types #define EH_DumpSubset_XList \ - X(EhFrameHdr, eh_frame_hdr, ".eh_frame_hdr") \ - X(EhFrame, eh_frame, ".eh_frame") +X(EhFrameHdr, eh_frame_hdr, ".eh_frame_hdr") \ +X(EhFrame, eh_frame, ".eh_frame") typedef enum EH_DumpSubset { #define X(name, name_lower, title) EH_DumpSubset_##name, diff --git a/src/eh_frame/unwind/eh_frame_unwind.c b/src/eh_frame/unwind/eh_frame_unwind.c new file mode 100644 index 00000000..2f300e71 --- /dev/null +++ b/src/eh_frame/unwind/eh_frame_unwind.c @@ -0,0 +1,696 @@ +// Copyright (c) Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +internal UWND_StepResult +eh_uwnd_step(Arch arch, MemoryMap *memory_map, UWND_ModuleInfo *module_info, U64 tls_vaddr, void *regs, U64 *cfa_out) +{ + Temp scratch = scratch_begin(0, 0); + B32 done = 0; + UWND_StepResult result = {0}; + EH_UWND_ModuleUnwindInfo *unwind_info = (EH_UWND_ModuleUnwindInfo *)module_info->unwind_info; + if(unwind_info != 0) + { + //- rjf: unpack context + EH_FrameHdr *header = &unwind_info->header; + EH_PtrCtx *ptr_ctx = &unwind_info->ptr_ctx; + ARCH_Info *arch_info = arch_info_from_arch(arch); + U64 pc = arch_ip_from_reg_block(arch_info, regs); + + //- rjf: set up new register values + void *new_regs = push_array(scratch.arena, U8, arch_info->reg_block_size); + MemoryCopy(new_regs, regs, arch_info->reg_block_size); + + //- rjf: find nearest FDE entry to the IP + U64 fde_vaddr = 0; + if(header->version == 1 && header->fde_count != 0) + { + // rjf: binary search to find FDE number + U64 fde_num = 0; + { + U64 min_idx = 0; + U64 max_idx = header->fde_count-1; + for(;min_idx <= max_idx;) + { + U64 mid_idx = min_idx + (max_idx - min_idx) / 2; + U64 mid_pc_off = mid_idx*header->entry_byte_size; + U64 mid_pc = 0; + eh_read_ptr(header->table, mid_pc_off, ptr_ctx->pc_vaddr + mid_pc_off, ptr_ctx, header->table_enc, &mid_pc); + if(mid_pc > pc) + { + max_idx = mid_idx - 1; + } + else if(mid_pc < pc) + { + min_idx = mid_idx + 1; + } + else + { + fde_num = mid_idx + 1; + break; + } + } + if(fde_num == 0) + { + fde_num = min_idx > 0 ? min_idx : 1; + } + } + + // rjf: FDE number -> FDE vaddr + if(0 < fde_num && fde_num <= header->fde_count) + { + U64 fde_vaddr_off = ((fde_num-1) * header->entry_byte_size) + header->field_byte_size; + eh_read_ptr(header->table, fde_vaddr_off, ptr_ctx->pc_vaddr + fde_vaddr_off, ptr_ctx, header->table_enc, &fde_vaddr); + } + } + + //- rjf: parse info from FDE entry + DW_CIE cie = {0}; + DW_FDE fde = {0}; + U64 cie_inst_data_off = 0; + U64 fde_inst_data_off = 0; + String8 cie_inst_data = {0}; + String8 fde_inst_data = {0}; + if(fde_vaddr != 0) + { + // rjf: determine FDE's full address range + Rng1U64 fde_vaddr_range = {0}; + DW_Format fde_fmt = DW_Format_Null; + { + // rjf: read initial length data + String8 initial_length_data = {0}; + { + U8 *initial_length_data_buffer = push_array(scratch.arena, U8, 12); + Rng1U64 initial_length_vaddr_range = r1u64(fde_vaddr, fde_vaddr+12); + U64 initial_length_data_size = memory_map_read(memory_map, initial_length_vaddr_range, initial_length_data_buffer); + initial_length_data = str8(initial_length_data_buffer, initial_length_data_size); + if(initial_length_data_size < 4) + { + done = 1; + result.status = UWND_StepStatus_FailedMemoryRead; + result.missed_read_vaddr_range = initial_length_vaddr_range; + } + } + + // rjf: compute initial length + U64 length = 0; + if(!done) + { + dw2_read_initial_length(initial_length_data, 0, &length, &fde_fmt); + } + + // rjf: determine vaddr range of all FDE data + // + // TODO(rjf): are we sure this is supposed to *not* advance past the 8 byte length field + // when the initial 4 bytes are max_U32? this would mean that in 32-bit format, the length + // *does not* include the length encoding itself, but in 64-bit format, the length *does* + // include the length, but not the initial 4 bytes max_U32 marker. WTF? + // + fde_vaddr_range = r1u64(fde_vaddr, fde_vaddr + 4 + length); + } + + // rjf: read FDE data + String8 fde_data = {0}; + if(!done) + { + U64 fde_data_expected_size = dim_1u64(fde_vaddr_range); + U8 *fde_data_buffer = push_array(scratch.arena, U8, fde_data_expected_size); + U64 fde_data_size = memory_map_read(memory_map, fde_vaddr_range, fde_data_buffer); + if(fde_data_size != fde_data_expected_size) + { + done = 1; + result.status = UWND_StepStatus_FailedMemoryRead; + result.missed_read_vaddr_range = fde_vaddr_range; + } + fde_data = str8(fde_data_buffer, fde_data_size); + } + + // rjf: FDE data -> CIE address + U64 cie_vaddr = 0; + if(!done) + { + U64 cie_delta_off = (fde_fmt == DW_Format_32Bit ? 4 : 12); + U64 cie_delta = 0; + dw2_read_fmt_u64(fde_data, cie_delta_off, fde_fmt, &cie_delta); + cie_vaddr = (fde_vaddr + cie_delta_off) - cie_delta; + } + + // rjf: determine CIE's full address range + Rng1U64 cie_vaddr_range = {0}; + DW_Format cie_fmt = DW_Format_Null; + { + // rjf: read initial length data + String8 initial_length_data = {0}; + { + U8 *initial_length_data_buffer = push_array(scratch.arena, U8, 12); + Rng1U64 initial_length_vaddr_range = r1u64(cie_vaddr, cie_vaddr+12); + U64 initial_length_data_size = memory_map_read(memory_map, initial_length_vaddr_range, initial_length_data_buffer); + initial_length_data = str8(initial_length_data_buffer, initial_length_data_size); + if(initial_length_data_size < 4) + { + done = 1; + result.status = UWND_StepStatus_FailedMemoryRead; + result.missed_read_vaddr_range = initial_length_vaddr_range; + } + } + + // rjf: compute initial length + U64 length = 0; + if(!done) + { + dw2_read_initial_length(initial_length_data, 0, &length, &cie_fmt); + } + + // rjf: form full address range of CIE data + // + // TODO(rjf): is this busted? see above note in FDE parse + // + cie_vaddr_range = r1u64(cie_vaddr, cie_vaddr + 4 + length); + } + + // rjf: read CIE data + String8 cie_data = {0}; + if(!done) + { + U64 cie_data_expected_size = dim_1u64(cie_vaddr_range); + U8 *cie_data_buffer = push_array(scratch.arena, U8, cie_data_expected_size); + U64 cie_data_size = memory_map_read(memory_map, cie_vaddr_range, cie_data_buffer); + if(cie_data_size != cie_data_expected_size) + { + done = 1; + result.status = UWND_StepStatus_FailedMemoryRead; + result.missed_read_vaddr_range = cie_vaddr_range; + } + cie_data = str8(cie_data_buffer, cie_data_size); + } + + // rjf: parse CIE data, & FDE data + cie_inst_data_off = eh_read_cie(cie_data, 0, cie_fmt, arch, cie_vaddr_range.min, ptr_ctx, &cie); + fde_inst_data_off = eh_read_fde(fde_data, 0, fde_fmt, arch, fde_vaddr_range.min, ptr_ctx, &cie, &fde); + cie_inst_data = str8_skip(cie_data, cie_inst_data_off); + fde_inst_data = str8_skip(fde_data, fde_inst_data_off); + } + + //- rjf: parse CIE/FDE info, produce CFI row for this PC + U64 reg_count = dw_reg_count_from_arch(arch); + DW_CFIRow cfi_row = {0}; + cfi_row.reg_rules = push_array(scratch.arena, DW_UnwindRule, reg_count); + if(!done && contains_1u64(fde.pc_range, pc)) + { + U64 code_align_factor = cie.code_align_factor; + S64 data_align_factor = cie.data_align_factor; + DW_CFIRow initial_row = {0}; + initial_row.reg_rules = push_array(scratch.arena, DW_UnwindRule, reg_count); + DW_CFIRowNode *top_row = 0; + DW_CFIRowNode *free_row = 0; + struct + { + String8 inst_data; + U64 inst_data_off; + } + program_tasks[] = + { + {cie_inst_data, cie_inst_data_off}, + {fde_inst_data, fde_inst_data_off}, + }; + B32 computed_row = 0; + for EachElement(program_task_idx, program_tasks) + { + // rjf: exit if we computed the target row + if(computed_row) + { + break; + } + + // rjf: run unwind instruction program + String8 inst_data = program_tasks[program_task_idx].inst_data; + U64 inst_data_off = program_tasks[program_task_idx].inst_data_off; + for(U64 off = 0; off < inst_data.size;) + { + U64 start_off = off; + + // rjf: read next opcode + DW_CFAOpCode raw_opcode = DW_CFAOpCode_Nop; + off += str8_deserial_read_struct(inst_data, off, &raw_opcode); + + // rjf: unpack opcode (raw opcode can contain implicit operands) + DW_CFAOpCode opcode = raw_opcode & ~DW_CFAOpCodeMask_OpcodeHi; + U64 implicit_operand = 0; + if((raw_opcode & DW_CFAOpCodeMask_OpcodeHi) != 0) + { + opcode = (raw_opcode & DW_CFAOpCodeMask_OpcodeHi); + implicit_operand = raw_opcode & DW_CFAOpCodeMask_Operand; + } + + // rjf: apply opcode + U64 new_base_pc_off = cfi_row.base_pc_off; + switch(opcode) + { + default:{}break; + case DW_CFAOpCode_Nop:{}break; + + //- rjf: location adjustments + case DW_CFAOpCode_SetLoc: + { + off += eh_read_ptr(inst_data, off, ptr_ctx->pc_vaddr + inst_data_off + off, ptr_ctx, header->table_enc, &new_base_pc_off); + }break; + case DW_CFAOpCode_AdvanceLoc: + { + new_base_pc_off += implicit_operand * code_align_factor; + }break; + case DW_CFAOpCode_AdvanceLoc1: + { + U8 delta = 0; + off += str8_deserial_read_struct(inst_data, off, &delta); + new_base_pc_off += delta; + }break; + case DW_CFAOpCode_AdvanceLoc2: + { + U16 delta = 0; + off += str8_deserial_read_struct(inst_data, off, &delta); + new_base_pc_off += delta; + }break; + case DW_CFAOpCode_AdvanceLoc4: + { + U32 delta = 0; + off += str8_deserial_read_struct(inst_data, off, &delta); + new_base_pc_off += delta; + }break; + + //- rjf: row operations + case DW_CFAOpCode_RememberState: + { + // rjf: alloc row node + DW_CFIRowNode *n = free_row; + if(n != 0) + { + SLLStackPop(free_row); + } + else + { + n = push_array(scratch.arena, DW_CFIRowNode, 1); + n->v.reg_rules = push_array(scratch.arena, DW_UnwindRule, reg_count); + } + + // rjf: copy current state + n->v.base_pc_off = cfi_row.base_pc_off; + n->v.cfa_rule = cfi_row.cfa_rule; + MemoryCopy(n->v.reg_rules, cfi_row.reg_rules, sizeof(cfi_row.reg_rules[0])*reg_count); + + // rjf: push + SLLStackPush(top_row, n); + }break; + case DW_CFAOpCode_RestoreState: + if(top_row != 0) + { + // rjf: pop row + DW_CFIRowNode *n = top_row; + SLLStackPop(top_row); + + // rjf: copy to current state + cfi_row.base_pc_off = n->v.base_pc_off; + cfi_row.cfa_rule = n->v.cfa_rule; + MemoryCopy(cfi_row.reg_rules, n->v.reg_rules, sizeof(cfi_row.reg_rules[0])*reg_count); + + // rjf: free row + SLLStackPush(free_row, n); + }break; + + //- rjf: register rules + case DW_CFAOpCode_OffsetExt: + { + U64 reg = 0; + U64 reg_off = 0; + off += str8_deserial_read_uleb128(inst_data, off, ®); + off += str8_deserial_read_uleb128(inst_data, off, ®_off); + reg_off *= data_align_factor; + if(reg < reg_count) + { + cfi_row.reg_rules[reg].code = DW_UnwindRuleCode_Off; + cfi_row.reg_rules[reg].s64 = (S64)reg_off; + } + }break; + case DW_CFAOpCode_OffsetExtSf: + { + U64 reg = 0; + S64 reg_off = 0; + off += str8_deserial_read_uleb128(inst_data, off, ®); + off += str8_deserial_read_sleb128(inst_data, off, ®_off); + reg_off *= data_align_factor; + if(reg < reg_count) + { + cfi_row.reg_rules[reg].code = DW_UnwindRuleCode_Off; + cfi_row.reg_rules[reg].s64 = reg_off; + } + }break; + case DW_CFAOpCode_Undefined: + { + U64 reg = 0; + off += str8_deserial_read_uleb128(inst_data, off, ®); + if(reg < reg_count) + { + cfi_row.reg_rules[reg].code = DW_UnwindRuleCode_Undefined; + } + }break; + case DW_CFAOpCode_SameValue: + { + U64 reg = 0; + off += str8_deserial_read_uleb128(inst_data, off, ®); + if(reg < reg_count) + { + cfi_row.reg_rules[reg].code = DW_UnwindRuleCode_SameVal; + } + }break; + case DW_CFAOpCode_Register: + { + U64 dst_reg = 0; + U64 src_reg = 0; + off += str8_deserial_read_uleb128(inst_data, off, &dst_reg); + off += str8_deserial_read_uleb128(inst_data, off, &src_reg); + if(dst_reg < reg_count) + { + cfi_row.reg_rules[dst_reg].code = DW_UnwindRuleCode_Reg; + cfi_row.reg_rules[dst_reg].reg_code = src_reg; + } + }break; + case DW_CFAOpCode_Expr: + { + U64 reg = 0; + U64 expr_size = 0; + off += str8_deserial_read_uleb128(inst_data, off, ®); + off += str8_deserial_read_uleb128(inst_data, off, &expr_size); + if(reg < reg_count) + { + cfi_row.reg_rules[reg].code = DW_UnwindRuleCode_Expr; + cfi_row.reg_rules[reg].expr = str8_substr(inst_data, r1u64(off, off+expr_size)); + } + }break; + case DW_CFAOpCode_ValOffset: + { + U64 reg = 0; + U64 val_off = 0; + off += str8_deserial_read_uleb128(inst_data, off, ®); + off += str8_deserial_read_uleb128(inst_data, off, &val_off); + val_off *= data_align_factor; + if(reg < reg_count) + { + cfi_row.reg_rules[reg].code = DW_UnwindRuleCode_ValOff; + cfi_row.reg_rules[reg].s64 = (S64)val_off; + } + }break; + case DW_CFAOpCode_ValOffsetSf: + { + U64 reg = 0; + S64 val_off = 0; + off += str8_deserial_read_uleb128(inst_data, off, ®); + off += str8_deserial_read_sleb128(inst_data, off, &val_off); + val_off *= data_align_factor; + if(reg < reg_count) + { + cfi_row.reg_rules[reg].code = DW_UnwindRuleCode_ValOff; + cfi_row.reg_rules[reg].s64 = val_off; + } + }break; + case DW_CFAOpCode_ValExpr: + { + U64 reg = 0; + U64 expr_size = 0; + off += str8_deserial_read_uleb128(inst_data, off, ®); + off += str8_deserial_read_uleb128(inst_data, off, &expr_size); + if(reg < reg_count) + { + cfi_row.reg_rules[reg].code = DW_UnwindRuleCode_ValExpr; + cfi_row.reg_rules[reg].expr = str8_substr(inst_data, r1u64(off, off+expr_size)); + } + }break; + case DW_CFAOpCode_Offset: + { + U64 offset = 0; + off += str8_deserial_read_uleb128(inst_data, off, &offset); + offset *= data_align_factor; + U64 reg = implicit_operand; + if(reg < reg_count) + { + cfi_row.reg_rules[reg].code = DW_UnwindRuleCode_Off; + cfi_row.reg_rules[reg].s64 = (S64)offset; + } + }break; + + //- rjf: CFA rules + case DW_CFAOpCode_DefCfa: + { + U64 reg = 0; + U64 reg_off = 0; + off += str8_deserial_read_uleb128(inst_data, off, ®); + off += str8_deserial_read_uleb128(inst_data, off, ®_off); + cfi_row.cfa_rule.code = DW_UnwindRuleCode_Reg; + cfi_row.cfa_rule.reg_code = reg; + cfi_row.cfa_rule.s64 = (S64)reg_off; + }break; + case DW_CFAOpCode_DefCfaSf: + { + U64 reg = 0; + S64 reg_off = 0; + off += str8_deserial_read_uleb128(inst_data, off, ®); + off += str8_deserial_read_sleb128(inst_data, off, ®_off); + cfi_row.cfa_rule.code = DW_UnwindRuleCode_Reg; + cfi_row.cfa_rule.reg_code = reg; + cfi_row.cfa_rule.s64 = (S64)(reg_off * data_align_factor); + }break; + case DW_CFAOpCode_DefCfaRegister: + { + U64 reg = 0; + off += str8_deserial_read_uleb128(inst_data, off, ®); + cfi_row.cfa_rule.code = DW_UnwindRuleCode_Reg; + cfi_row.cfa_rule.reg_code = reg; + }break; + case DW_CFAOpCode_DefCfaOffset: + { + U64 reg_off = 0; + off += str8_deserial_read_uleb128(inst_data, off, ®_off); + cfi_row.cfa_rule.s64 = (S64)reg_off; + }break; + case DW_CFAOpCode_DefCfaOffsetSf: + { + S64 reg_off = 0; + off += str8_deserial_read_sleb128(inst_data, off, ®_off); + cfi_row.cfa_rule.s64 = (S64)reg_off; + }break; + case DW_CFAOpCode_DefCfaExpr: + { + U64 expr_size = 0; + off += str8_deserial_read_uleb128(inst_data, off, &expr_size); + cfi_row.cfa_rule.code = DW_UnwindRuleCode_Expr; + cfi_row.cfa_rule.expr = str8_substr(inst_data, r1u64(off, off+expr_size)); + }break; + + //- rjf: register state resets + case DW_CFAOpCode_Restore: + { + U64 reg = implicit_operand; + if(reg < reg_count) + { + cfi_row.reg_rules[reg] = initial_row.reg_rules[reg]; + } + }break; + case DW_CFAOpCode_RestoreExt: + { + U64 reg = 0; + off += str8_deserial_read_uleb128(inst_data, off, ®); + if(reg < reg_count) + { + cfi_row.reg_rules[reg] = initial_row.reg_rules[reg]; + } + }break; + } + + // rjf: exit early if we found the CFI row for this pc + { + U64 pc_off = (pc - fde.pc_range.min); + if(cfi_row.base_pc_off <= pc_off && pc_off < new_base_pc_off) + { + computed_row = 1; + break; + } + } + + // rjf: commit changes to CFI row base pc + cfi_row.base_pc_off = new_base_pc_off; + + // rjf: abort if we made no parsing progress + if(off == start_off) + { + break; + } + } + + // rjf: on cie program -> save current state as 'initial state', to be + // potentially restored by fde + { + initial_row.base_pc_off = cfi_row.base_pc_off; + initial_row.cfa_rule = cfi_row.cfa_rule; + MemoryCopy(initial_row.reg_rules, cfi_row.reg_rules, sizeof(initial_row.reg_rules[0])*reg_count); + } + } + } + + //- rjf: evaluate frame base vaddr (CFA) via CFI row rule + U64 cfa = 0; + if(!done) switch(cfi_row.cfa_rule.code) + { + default:{}break; + case DW_UnwindRuleCode_Reg: + { + U64 cfa_reg_val = 0; + DW_RegCode dw_reg_code = cfi_row.cfa_rule.reg_code; + ARCH_RegCode reg_code = arch_reg_code_from_dw(arch, dw_reg_code); + arch_reg_block_read_range(arch_info, regs, arch_info->reg_code_rng_table[reg_code], &cfa_reg_val); + cfa = cfa_reg_val + cfi_row.cfa_rule.s64; + }break; + case DW_UnwindRuleCode_Expr: + { + String8 expr = cfi_row.cfa_rule.expr; + DW_EvalState eval_state = {0}; + DW_Eval eval = dw_eval(scratch.arena, cie.format, arch, memory_map, regs, 0, 0, tls_vaddr, &eval_state, expr, 1000); + if(eval.status == DW_EvalStatus_FailedMemoryRead) + { + done = 1; + result.status = UWND_StepStatus_FailedMemoryRead; + result.missed_read_vaddr_range = eval.missed_read_vaddr_range; + } + else + { + cfa = eval.val.u512.u64[0]; + } + }break; + } + + //- rjf: apply CFI row rules to registers + if(!done) for EachIndex(reg_idx, reg_count) + { + DW_UnwindRule *rule = &cfi_row.reg_rules[reg_idx]; + ARCH_RegCode reg_code = arch_reg_code_from_dw(arch, reg_idx); + Rng1U16 reg_rng = arch_info->reg_code_rng_table[reg_code]; + U64 reg_size = dim_1u16(reg_rng); + switch(rule->code) + { + default: + case DW_UnwindRuleCode_Undefined: + case DW_UnwindRuleCode_SameVal:{}break; + case DW_UnwindRuleCode_Off: + { + Temp temp = temp_begin(scratch.arena); + U64 addr = cfa + rule->s64; + Rng1U64 reg_value_vaddr_range = r1u64(addr, addr+reg_size); + U8 *reg_value_buffer = push_array(temp.arena, U8, reg_size); + if(memory_map_read(memory_map, reg_value_vaddr_range, reg_value_buffer) != reg_size) + { + done = 1; + result.status = UWND_StepStatus_FailedMemoryRead; + result.missed_read_vaddr_range = reg_value_vaddr_range; + } + if(!done) + { + arch_reg_block_write_range(arch_info, new_regs, reg_rng, reg_value_buffer); + } + temp_end(temp); + }break; + case DW_UnwindRuleCode_ValOff: + { + U64 reg_value = cfa + rule->s64; + Rng1U16 write_range = r1u16(reg_rng.min, Min(reg_rng.max, reg_rng.min + sizeof(reg_value))); + arch_reg_block_write_range(arch_info, new_regs, write_range, ®_value); + }break; + case DW_UnwindRuleCode_Reg: + { + Temp temp = temp_begin(scratch.arena); + DW_RegCode src_reg_code_dw = rule->reg_code; + ARCH_RegCode src_reg_code = arch_reg_code_from_dw(arch, src_reg_code_dw); + Rng1U16 src_reg_rng = arch_info->reg_code_rng_table[src_reg_code]; + U64 src_reg_size = dim_1u16(src_reg_rng); + U64 read_size = Min(src_reg_size, reg_size); + U8 *src_reg_val_buffer = push_array(temp.arena, U8, read_size); + arch_reg_block_read_range(arch_info, regs, src_reg_rng, src_reg_val_buffer); + arch_reg_block_write_range(arch_info, new_regs, reg_rng, src_reg_val_buffer); + temp_end(temp); + }break; + case DW_UnwindRuleCode_Expr: + { + Temp temp = temp_begin(scratch.arena); + String8 expr = rule->expr; + DW_EvalState eval_state = {0}; + DW_Eval eval = dw_eval(scratch.arena, cie.format, arch, memory_map, regs, 0, cfa, tls_vaddr, &eval_state, expr, 1000); + if(eval.status == DW_EvalStatus_FailedMemoryRead) + { + done = 1; + result.status = UWND_StepStatus_FailedMemoryRead; + result.missed_read_vaddr_range = eval.missed_read_vaddr_range; + } + if(!done) + { + U64 reg_val_vaddr = eval.val.u512.u64[0]; + Rng1U64 reg_value_vaddr_range = r1u64(reg_val_vaddr, reg_val_vaddr+reg_size); + U8 *reg_value_buffer = push_array(temp.arena, U8, reg_size); + if(memory_map_read(memory_map, reg_value_vaddr_range, reg_value_buffer) != reg_size) + { + done = 1; + result.status = UWND_StepStatus_FailedMemoryRead; + result.missed_read_vaddr_range = reg_value_vaddr_range; + } + else + { + arch_reg_block_write_range(arch_info, new_regs, reg_rng, reg_value_buffer); + } + } + temp_end(scratch); + }break; + case DW_UnwindRuleCode_ValExpr: + { + Temp temp = temp_begin(scratch.arena); + String8 expr = rule->expr; + DW_EvalState eval_state = {0}; + DW_Eval eval = dw_eval(scratch.arena, cie.format, arch, memory_map, regs, 0, cfa, tls_vaddr, &eval_state, expr, 1000); + if(eval.status == DW_EvalStatus_FailedMemoryRead) + { + done = 1; + result.status = UWND_StepStatus_FailedMemoryRead; + result.missed_read_vaddr_range = eval.missed_read_vaddr_range; + } + if(!done) + { + U512 reg_val = eval.val.u512; + arch_reg_block_write_range(arch_info, new_regs, reg_rng, ®_val); + } + temp_end(scratch); + }break; + case DW_UnwindRuleCode_Architectural: + { + // NOTE(rjf): the DWARF spec says that this code implies: + // + // "The rule is defined externally to this specification by the augmenter" + // + // so leaving this blank until we know exactly what that means. + }break; + } + } + + //- TODO(rjf): old code was replacing the stack pointer with the CFA. + // this is surely incorrect, no? isn't the entire point of the CFA + // to be *not* necessarily the stack pointer? and wouldn't the stack + // pointer be modified via the above rules? so what are we doing + // here? + // + if(!done) + { + arch_reg_block_write_sp(arch_info, new_regs, cfa); + } + + //- rjf: commit new register values, if we succeeded + if(!done) + { + result.status = UWND_StepStatus_Good; + MemoryCopy(regs, new_regs, arch_info->reg_block_size); + } + } + scratch_end(scratch); + return result; +} diff --git a/src/eh_frame/unwind/eh_frame_unwind.h b/src/eh_frame/unwind/eh_frame_unwind.h new file mode 100644 index 00000000..d83bb5a4 --- /dev/null +++ b/src/eh_frame/unwind/eh_frame_unwind.h @@ -0,0 +1,22 @@ +// Copyright (c) Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef EH_FRAME_UNWIND_H +#define EH_FRAME_UNWIND_H + +//////////////////////////////// +//~ rjf: Module Unwind Info Type + +typedef struct EH_UWND_ModuleUnwindInfo EH_UWND_ModuleUnwindInfo; +struct EH_UWND_ModuleUnwindInfo +{ + EH_FrameHdr header; + EH_PtrCtx ptr_ctx; +}; + +//////////////////////////////// +//~ rjf: Unwinding Abstraction Implementation + +internal UWND_StepResult eh_uwnd_step(Arch arch, MemoryMap *memory_map, UWND_ModuleInfo *module_info, U64 tls_vaddr, void *regs, U64 *cfa_out); + +#endif // EH_FRAME_UNWIND_H diff --git a/src/linux/base/linux_base.c b/src/linux/base/linux_base.c index 9545350d..d9675138 100644 --- a/src/linux/base/linux_base.c +++ b/src/linux/base/linux_base.c @@ -276,7 +276,7 @@ commit_memory_large(void *ptr, U64 size) } //////////////////////////////// -//~ rjf: @os_hooks Shared Memory +//~ rjf: @per_os_impl Shared Memory internal SharedMemory shared_memory_alloc(U64 size, String8 name) diff --git a/src/linux/demon/linux_demon.c b/src/linux/demon/linux_demon.c index e7571235..06117347 100644 --- a/src/linux/demon/linux_demon.c +++ b/src/linux/demon/linux_demon.c @@ -515,7 +515,6 @@ lnx_dmn_module_info_from_process_module(Arena *arena, pid_t pid, int memory_fd, info->tls_index = tls_index; info->tls_offset = tls_offset; info->eh_frame_header_vaddr_range = eh_frame_header_vaddr_range; - info->cfi_rebase = module_rebase; info->raddbg_info_voff_range = raddbg_info_voff_range; info->raddbg_is_attached_marker_voff = raddbg_is_attached_marker_voff; } @@ -1016,6 +1015,7 @@ lnx_dmn_thread_alloc(LNX_DMN_Process *process, LNX_DMN_ThreadState thread_state, thread->state = thread_state; thread->process = process; thread->reg_block = reg_block; + thread->dtv_base_vaddr = lnx_dmn_tls_root_vaddr_from_reg_block(process->fd, process->ctx->arch, reg_block); if(thread_state == LNX_DMN_ThreadState_Stopped) { thread->is_reg_block_dirty = !lnx_dmn_thread_read_reg_block(thread); @@ -1823,6 +1823,30 @@ lnx_dmn_set_single_step_flag(LNX_DMN_Thread *thread, B32 is_on) return is_flag_set; } +internal U64 +lnx_dmn_tls_root_vaddr_from_reg_block(int fd, Arch arch, void *reg_block) +{ + U64 result = 0; + switch(arch) + { + case Arch_x86: + case Arch_arm32: + case Arch_arm64: + case Arch_Null: + case Arch_COUNT:{}break; + case Arch_x64: + { + X64_RegBlock *reg_block_x64 = (X64_RegBlock *)reg_block; + U64 dtv_pointer = 0; + if(lnx_dmn_read_struct(fd, reg_block_x64->fsbase + 8, &dtv_pointer)) + { + result = dtv_pointer; + } + }break; + } + return result; +} + internal void lnx_dmn_thread_ptr_list_push_node(LNX_DMN_ThreadPtrList *list, LNX_DMN_ThreadPtrNode *n) { @@ -1896,6 +1920,8 @@ lnx_dmn_push_event_create_thread(Arena *arena, DMN_EventList *events, LNX_DMN_Th e->thread = lnx_dmn_handle_from_thread(thread); e->arch = thread->process->ctx->arch; e->code = thread->tid; + // TODO(rjf): e->stack_pointer = ???; + e->tls_root_vaddr = thread->dtv_base_vaddr; } internal void @@ -3239,46 +3265,6 @@ dmn_process_write(DMN_Handle process_handle, Rng1U64 range, void *src) //- rjf: threads -internal U64 -dmn_stack_base_vaddr_from_thread(DMN_Handle thread_handle) -{ - return 0; -} - -internal U64 -dmn_tls_root_vaddr_from_thread(DMN_Handle thread_handle) -{ - U64 tls_root_vaddr = max_U64; - DMN_AccessScope - { - LNX_DMN_Thread *thread = lnx_dmn_thread_from_handle(thread_handle); - if(thread) - { - switch(thread->process->ctx->arch) - { - case Arch_Null: {} break; - case Arch_x64: - { - X64_RegBlock *reg_block = thread->reg_block; - U64 dtv_pointer = 0; - if(lnx_dmn_read_struct(thread->process->fd, reg_block->fsbase + 8, &dtv_pointer)) - { - tls_root_vaddr = dtv_pointer; - } - } break; - case Arch_x86: - case Arch_arm32: - case Arch_arm64: - { - NotImplemented; - } break; - default: { InvalidPath; } break; - } - } - } - return tls_root_vaddr; -} - internal B32 dmn_thread_read_reg_block(DMN_Handle thread_handle, void *reg_block) { @@ -3316,6 +3302,40 @@ dmn_thread_write_reg_block(DMN_Handle thread_handle, void *reg_block) return result; } +internal B32 +dmn_thread_get_module_tls_vaddr(DMN_Handle thread_handle, DMN_Handle module_handle, U64 *vaddr_out) +{ + B32 result = 0; + DMN_AccessScope + { + LNX_DMN_Thread *thread = lnx_dmn_thread_from_handle(thread_handle); + if(thread != 0) + { + LNX_DMN_Process *process = thread->process; + LNX_DMN_Module *module = lnx_dmn_module_from_handle(module_handle); + if(process != 0 && module != 0) + { + Arch arch = thread->process->ctx->arch; + U64 addr_size = byte_size_from_arch(arch); + U64 dtv_base_vaddr = thread->dtv_base_vaddr; + U64 dtv_entry_size = 2 * addr_size; + U64 module_dtv_ptr_vaddr = dtv_base_vaddr + module->tls_index * dtv_entry_size; + U64 module_dtv_vaddr = 0; + if(lnx_dmn_read(process->fd, r1u64(module_dtv_ptr_vaddr, module_dtv_ptr_vaddr+addr_size), &module_dtv_vaddr) == addr_size) + { + U64 unallocated_dtv_vaddr_marker = (addr_size == 4 ? max_U32 : max_U64); + if(module_dtv_vaddr != unallocated_dtv_vaddr_marker) + { + vaddr_out[0] = module_dtv_vaddr + module->tls_offset; + result = 1; + } + } + } + } + } + return result; +} + //- rjf: system process listing internal void diff --git a/src/linux/demon/linux_demon.h b/src/linux/demon/linux_demon.h index 721a2570..64a40edc 100644 --- a/src/linux/demon/linux_demon.h +++ b/src/linux/demon/linux_demon.h @@ -176,6 +176,7 @@ typedef struct LNX_DMN_Thread B32 pass_through_signal; U64 pass_through_signo; U64 orig_rax; + U64 dtv_base_vaddr; struct LNX_DMN_Thread *next; struct LNX_DMN_Thread *prev; @@ -476,6 +477,7 @@ internal void lnx_dmn_thread_write_sp(LNX_DMN_Thread *thread, U64 sp); internal B32 lnx_dmn_thread_read_reg_block(LNX_DMN_Thread *thread); internal B32 lnx_dmn_thread_write_reg_block(LNX_DMN_Thread *thread); internal B32 lnx_dmn_set_single_step_flag(LNX_DMN_Thread *thread, B32 is_on); +internal U64 lnx_dmn_tls_root_vaddr_from_reg_block(int fd, Arch arch, void *reg_block); //////////////////////////////// //~ List Helpers diff --git a/src/linux/window_manager/linux_window_manager.c b/src/linux/window_manager/linux_window_manager.c index 3787a031..49ea308b 100644 --- a/src/linux/window_manager/linux_window_manager.c +++ b/src/linux/window_manager/linux_window_manager.c @@ -20,7 +20,7 @@ lnx_window_from_x11window(Window window) } //////////////////////////////// -//~ rjf: @os_hooks Main Initialization API (Implemented Per-OS) +//~ rjf: @per_os_impl Main Initialization API (Implemented Per-OS) internal void wm_init(void) @@ -68,14 +68,14 @@ wm_init(void) lnx_wm_state->cursors[map[idx].cursor] = XCreateFontCursor(lnx_wm_state->display, map[idx].id); } } - + // create wakeup event for polling lnx_wm_state->wakeup_fd = eventfd(0, EFD_CLOEXEC); Assert(lnx_wm_state->wakeup_fd > 0); } //////////////////////////////// -//~ rjf: @os_hooks Graphics System Info (Implemented Per-OS) +//~ rjf: @per_os_impl Graphics System Info (Implemented Per-OS) internal WM_SystemInfo * wm_get_system_info(void) @@ -84,7 +84,7 @@ wm_get_system_info(void) } //////////////////////////////// -//~ rjf: @os_hooks Clipboards (Implemented Per-OS) +//~ rjf: @per_os_impl Clipboards (Implemented Per-OS) internal void wm_set_clipboard_text(String8 string) @@ -100,7 +100,7 @@ wm_get_clipboard_text(Arena *arena) } //////////////////////////////// -//~ rjf: @os_hooks Windows (Implemented Per-OS) +//~ rjf: @per_os_impl Windows (Implemented Per-OS) internal WM_Window wm_window_open(Rng2F32 rect, WM_WindowFlags flags, String8 title) @@ -342,7 +342,7 @@ wm_dpi_from_window(WM_Window handle) } //////////////////////////////// -//~ rjf: @os_hooks External Windows (Implemented Per-OS) +//~ rjf: @per_os_impl External Windows (Implemented Per-OS) internal WM_ExtWindow wm_focused_external_window(void) @@ -359,7 +359,7 @@ wm_focus_external_window(WM_ExtWindow handle) } //////////////////////////////// -//~ rjf: @os_hooks Monitors (Implemented Per-OS) +//~ rjf: @per_os_impl Monitors (Implemented Per-OS) internal WM_MonitorArray wm_push_monitors_array(Arena *arena) @@ -407,7 +407,7 @@ wm_dpi_from_monitor(WM_Monitor monitor) } //////////////////////////////// -//~ rjf: @os_hooks Events (Implemented Per-OS) +//~ rjf: @per_os_impl Events (Implemented Per-OS) internal void wm_send_wakeup_event(void) @@ -448,210 +448,210 @@ wm_get_events(Arena *arena, B32 wait) switch(evt.type) { default:{}break; - - //- rjf: key presses/releases - case KeyPress: - case KeyRelease: - { - // rjf: determine flags - WM_Modifiers modifiers = 0; - if(evt.xkey.state & ShiftMask) { modifiers |= WM_Modifier_Shift; } - if(evt.xkey.state & ControlMask) { modifiers |= WM_Modifier_Ctrl; } - if(evt.xkey.state & Mod1Mask) { modifiers |= WM_Modifier_Alt; } - // rjf: map keycode -> keysym & codepoint - LNX_WM_Window *window = lnx_window_from_x11window(evt.xkey.window); - KeySym keysym = 0; - U8 text[256] = {0}; - U64 text_size = Xutf8LookupString(window->xic, &evt.xkey, (char *)text, sizeof(text), &keysym, 0); - - // rjf: map keysym -> WM_Key - B32 is_right_sided = 0; - WM_Key key = WM_Key_Null; - switch(keysym) + //- rjf: key presses/releases + case KeyPress: + case KeyRelease: { - default: + // rjf: determine flags + WM_Modifiers modifiers = 0; + if(evt.xkey.state & ShiftMask) { modifiers |= WM_Modifier_Shift; } + if(evt.xkey.state & ControlMask) { modifiers |= WM_Modifier_Ctrl; } + if(evt.xkey.state & Mod1Mask) { modifiers |= WM_Modifier_Alt; } + + // rjf: map keycode -> keysym & codepoint + LNX_WM_Window *window = lnx_window_from_x11window(evt.xkey.window); + KeySym keysym = 0; + U8 text[256] = {0}; + U64 text_size = Xutf8LookupString(window->xic, &evt.xkey, (char *)text, sizeof(text), &keysym, 0); + + // rjf: map keysym -> WM_Key + B32 is_right_sided = 0; + WM_Key key = WM_Key_Null; + switch(keysym) { - if(0){} - else if(XK_F1 <= keysym && keysym <= XK_F24) { key = (WM_Key)(WM_Key_F1 + (keysym - XK_F1)); } - else if('0' <= keysym && keysym <= '9') { key = WM_Key_0 + (keysym-'0'); } - }break; - case XK_Escape:{key = WM_Key_Esc;};break; - case XK_BackSpace:{key = WM_Key_Backspace;}break; - case XK_Delete:{key = WM_Key_Delete;}break; - case XK_Return:{key = WM_Key_Return;}break; - case XK_Pause:{key = WM_Key_Pause;}break; - case XK_Tab:{key = WM_Key_Tab;}break; - case XK_Left:{key = WM_Key_Left;}break; - case XK_Right:{key = WM_Key_Right;}break; - case XK_Up:{key = WM_Key_Up;}break; - case XK_Down:{key = WM_Key_Down;}break; - case XK_Home:{key = WM_Key_Home;}break; - case XK_End:{key = WM_Key_End;}break; - case XK_Page_Up:{key = WM_Key_PageUp;}break; - case XK_Page_Down:{key = WM_Key_PageDown;}break; - case XK_Alt_L:{ key = WM_Key_Alt; }break; - case XK_Alt_R:{ key = WM_Key_Alt; is_right_sided = 1;}break; - case XK_Shift_L:{ key = WM_Key_Shift; }break; - case XK_Shift_R:{ key = WM_Key_Shift; is_right_sided = 1;}break; - case XK_Control_L:{ key = WM_Key_Ctrl; }break; - case XK_Control_R:{ key = WM_Key_Ctrl; is_right_sided = 1;}break; - case '-':{key = WM_Key_Minus;}break; - case '=':{key = WM_Key_Equal;}break; - case '[':{key = WM_Key_LeftBracket;}break; - case ']':{key = WM_Key_RightBracket;}break; - case ';':{key = WM_Key_Semicolon;}break; - case '\'':{key = WM_Key_Quote;}break; - case '.':{key = WM_Key_Period;}break; - case ',':{key = WM_Key_Comma;}break; - case '/':{key = WM_Key_Slash;}break; - case '\\':{key = WM_Key_BackSlash;}break; - case '\t':{key = WM_Key_Tab;}break; - case 'a':case 'A':{key = WM_Key_A;}break; - case 'b':case 'B':{key = WM_Key_B;}break; - case 'c':case 'C':{key = WM_Key_C;}break; - case 'd':case 'D':{key = WM_Key_D;}break; - case 'e':case 'E':{key = WM_Key_E;}break; - case 'f':case 'F':{key = WM_Key_F;}break; - case 'g':case 'G':{key = WM_Key_G;}break; - case 'h':case 'H':{key = WM_Key_H;}break; - case 'i':case 'I':{key = WM_Key_I;}break; - case 'j':case 'J':{key = WM_Key_J;}break; - case 'k':case 'K':{key = WM_Key_K;}break; - case 'l':case 'L':{key = WM_Key_L;}break; - case 'm':case 'M':{key = WM_Key_M;}break; - case 'n':case 'N':{key = WM_Key_N;}break; - case 'o':case 'O':{key = WM_Key_O;}break; - case 'p':case 'P':{key = WM_Key_P;}break; - case 'q':case 'Q':{key = WM_Key_Q;}break; - case 'r':case 'R':{key = WM_Key_R;}break; - case 's':case 'S':{key = WM_Key_S;}break; - case 't':case 'T':{key = WM_Key_T;}break; - case 'u':case 'U':{key = WM_Key_U;}break; - case 'v':case 'V':{key = WM_Key_V;}break; - case 'w':case 'W':{key = WM_Key_W;}break; - case 'x':case 'X':{key = WM_Key_X;}break; - case 'y':case 'Y':{key = WM_Key_Y;}break; - case 'z':case 'Z':{key = WM_Key_Z;}break; - case ' ':{key = WM_Key_Space;}break; - } - - // rjf: push text event - if(evt.type == KeyPress && text_size != 0) - { - for(U64 off = 0; off < text_size;) - { - UnicodeDecode decode = utf8_decode(text+off, text_size-off); - if(decode.codepoint != 0 && (decode.codepoint >= 32 || decode.codepoint == '\t')) + default: { - WM_Event *e = wm_event_list_push_new(arena, &evts, WM_EventKind_Text); - e->window.u64[0] = (U64)window; - e->character = decode.codepoint; - } - if(decode.inc == 0) - { - break; - } - off += decode.inc; + if(0){} + else if(XK_F1 <= keysym && keysym <= XK_F24) { key = (WM_Key)(WM_Key_F1 + (keysym - XK_F1)); } + else if('0' <= keysym && keysym <= '9') { key = WM_Key_0 + (keysym-'0'); } + }break; + case XK_Escape:{key = WM_Key_Esc;};break; + case XK_BackSpace:{key = WM_Key_Backspace;}break; + case XK_Delete:{key = WM_Key_Delete;}break; + case XK_Return:{key = WM_Key_Return;}break; + case XK_Pause:{key = WM_Key_Pause;}break; + case XK_Tab:{key = WM_Key_Tab;}break; + case XK_Left:{key = WM_Key_Left;}break; + case XK_Right:{key = WM_Key_Right;}break; + case XK_Up:{key = WM_Key_Up;}break; + case XK_Down:{key = WM_Key_Down;}break; + case XK_Home:{key = WM_Key_Home;}break; + case XK_End:{key = WM_Key_End;}break; + case XK_Page_Up:{key = WM_Key_PageUp;}break; + case XK_Page_Down:{key = WM_Key_PageDown;}break; + case XK_Alt_L:{ key = WM_Key_Alt; }break; + case XK_Alt_R:{ key = WM_Key_Alt; is_right_sided = 1;}break; + case XK_Shift_L:{ key = WM_Key_Shift; }break; + case XK_Shift_R:{ key = WM_Key_Shift; is_right_sided = 1;}break; + case XK_Control_L:{ key = WM_Key_Ctrl; }break; + case XK_Control_R:{ key = WM_Key_Ctrl; is_right_sided = 1;}break; + case '-':{key = WM_Key_Minus;}break; + case '=':{key = WM_Key_Equal;}break; + case '[':{key = WM_Key_LeftBracket;}break; + case ']':{key = WM_Key_RightBracket;}break; + case ';':{key = WM_Key_Semicolon;}break; + case '\'':{key = WM_Key_Quote;}break; + case '.':{key = WM_Key_Period;}break; + case ',':{key = WM_Key_Comma;}break; + case '/':{key = WM_Key_Slash;}break; + case '\\':{key = WM_Key_BackSlash;}break; + case '\t':{key = WM_Key_Tab;}break; + case 'a':case 'A':{key = WM_Key_A;}break; + case 'b':case 'B':{key = WM_Key_B;}break; + case 'c':case 'C':{key = WM_Key_C;}break; + case 'd':case 'D':{key = WM_Key_D;}break; + case 'e':case 'E':{key = WM_Key_E;}break; + case 'f':case 'F':{key = WM_Key_F;}break; + case 'g':case 'G':{key = WM_Key_G;}break; + case 'h':case 'H':{key = WM_Key_H;}break; + case 'i':case 'I':{key = WM_Key_I;}break; + case 'j':case 'J':{key = WM_Key_J;}break; + case 'k':case 'K':{key = WM_Key_K;}break; + case 'l':case 'L':{key = WM_Key_L;}break; + case 'm':case 'M':{key = WM_Key_M;}break; + case 'n':case 'N':{key = WM_Key_N;}break; + case 'o':case 'O':{key = WM_Key_O;}break; + case 'p':case 'P':{key = WM_Key_P;}break; + case 'q':case 'Q':{key = WM_Key_Q;}break; + case 'r':case 'R':{key = WM_Key_R;}break; + case 's':case 'S':{key = WM_Key_S;}break; + case 't':case 'T':{key = WM_Key_T;}break; + case 'u':case 'U':{key = WM_Key_U;}break; + case 'v':case 'V':{key = WM_Key_V;}break; + case 'w':case 'W':{key = WM_Key_W;}break; + case 'x':case 'X':{key = WM_Key_X;}break; + case 'y':case 'Y':{key = WM_Key_Y;}break; + case 'z':case 'Z':{key = WM_Key_Z;}break; + case ' ':{key = WM_Key_Space;}break; } - } + + // rjf: push text event + if(evt.type == KeyPress && text_size != 0) + { + for(U64 off = 0; off < text_size;) + { + UnicodeDecode decode = utf8_decode(text+off, text_size-off); + if(decode.codepoint != 0 && (decode.codepoint >= 32 || decode.codepoint == '\t')) + { + WM_Event *e = wm_event_list_push_new(arena, &evts, WM_EventKind_Text); + e->window.u64[0] = (U64)window; + e->character = decode.codepoint; + } + if(decode.inc == 0) + { + break; + } + off += decode.inc; + } + } + + // rjf: push key event + { + WM_Event *e = wm_event_list_push_new(arena, &evts, evt.type == KeyPress ? WM_EventKind_Press : WM_EventKind_Release); + e->window.u64[0] = (U64)window; + e->modifiers = modifiers; + e->key = key; + e->right_sided = is_right_sided; + } + }break; - // rjf: push key event + //- rjf: mouse button presses/releases + case ButtonPress: + case ButtonRelease: { - WM_Event *e = wm_event_list_push_new(arena, &evts, evt.type == KeyPress ? WM_EventKind_Press : WM_EventKind_Release); - e->window.u64[0] = (U64)window; - e->modifiers = modifiers; - e->key = key; - e->right_sided = is_right_sided; - } - }break; - - //- rjf: mouse button presses/releases - case ButtonPress: - case ButtonRelease: - { - // rjf: determine flags - WM_Modifiers modifiers = 0; - if(evt.xbutton.state & ShiftMask) { modifiers |= WM_Modifier_Shift; } - if(evt.xbutton.state & ControlMask) { modifiers |= WM_Modifier_Ctrl; } - if(evt.xbutton.state & Mod1Mask) { modifiers |= WM_Modifier_Alt; } + // rjf: determine flags + WM_Modifiers modifiers = 0; + if(evt.xbutton.state & ShiftMask) { modifiers |= WM_Modifier_Shift; } + if(evt.xbutton.state & ControlMask) { modifiers |= WM_Modifier_Ctrl; } + if(evt.xbutton.state & Mod1Mask) { modifiers |= WM_Modifier_Alt; } + + // rjf: map button -> WM_Key + WM_Key key = WM_Key_Null; + switch(evt.xbutton.button) + { + default:{}break; + case Button1:{key = WM_Key_LeftMouseButton;}break; + case Button2:{key = WM_Key_MiddleMouseButton;}break; + case Button3:{key = WM_Key_RightMouseButton;}break; + } + + // rjf: push event + LNX_WM_Window *window = lnx_window_from_x11window(evt.xbutton.window); + if(key != WM_Key_Null) + { + WM_Event *e = wm_event_list_push_new(arena, &evts, evt.type == ButtonPress ? WM_EventKind_Press : WM_EventKind_Release); + e->window.u64[0] = (U64)window; + e->modifiers = modifiers; + e->key = key; + e->pos = v2f32((F32)evt.xbutton.x, (F32)evt.xbutton.y); + } + else if(evt.xbutton.button == Button4 || + evt.xbutton.button == Button5) + { + WM_Event *e = wm_event_list_push_new(arena, &evts, WM_EventKind_Scroll); + e->window.u64[0] = (U64)window; + e->modifiers = modifiers; + e->delta = v2f32(0, evt.xbutton.button == Button4 ? -1.f : +1.f); + e->pos = v2f32((F32)evt.xbutton.x, (F32)evt.xbutton.y); + } + }break; - // rjf: map button -> WM_Key - WM_Key key = WM_Key_Null; - switch(evt.xbutton.button) - { - default:{}break; - case Button1:{key = WM_Key_LeftMouseButton;}break; - case Button2:{key = WM_Key_MiddleMouseButton;}break; - case Button3:{key = WM_Key_RightMouseButton;}break; - } - - // rjf: push event - LNX_WM_Window *window = lnx_window_from_x11window(evt.xbutton.window); - if(key != WM_Key_Null) - { - WM_Event *e = wm_event_list_push_new(arena, &evts, evt.type == ButtonPress ? WM_EventKind_Press : WM_EventKind_Release); - e->window.u64[0] = (U64)window; - e->modifiers = modifiers; - e->key = key; - e->pos = v2f32((F32)evt.xbutton.x, (F32)evt.xbutton.y); - } - else if(evt.xbutton.button == Button4 || - evt.xbutton.button == Button5) - { - WM_Event *e = wm_event_list_push_new(arena, &evts, WM_EventKind_Scroll); - e->window.u64[0] = (U64)window; - e->modifiers = modifiers; - e->delta = v2f32(0, evt.xbutton.button == Button4 ? -1.f : +1.f); - e->pos = v2f32((F32)evt.xbutton.x, (F32)evt.xbutton.y); - } - }break; - - //- rjf: mouse motion - case MotionNotify: - { - LNX_WM_Window *window = lnx_window_from_x11window(evt.xclient.window); - WM_Event *e = wm_event_list_push_new(arena, &evts, WM_EventKind_MouseMove); - e->window.u64[0] = (U64)window; - e->pos.x = (F32)evt.xmotion.x; - e->pos.y = (F32)evt.xmotion.y; - set_mouse_cursor = 1; - }break; - - //- rjf: window focus/unfocus - case FocusIn: - { - }break; - case FocusOut: - { - LNX_WM_Window *window = lnx_window_from_x11window(evt.xfocus.window); - WM_Event *e = wm_event_list_push_new(arena, &evts, WM_EventKind_WindowLoseFocus); - e->window.u64[0] = (U64)window; - }break; - - //- rjf: client messages - case ClientMessage: - { - if((Atom)evt.xclient.data.l[0] == lnx_wm_state->wm_delete_window_atom) + //- rjf: mouse motion + case MotionNotify: { LNX_WM_Window *window = lnx_window_from_x11window(evt.xclient.window); - WM_Event *e = wm_event_list_push_new(arena, &evts, WM_EventKind_WindowClose); + WM_Event *e = wm_event_list_push_new(arena, &evts, WM_EventKind_MouseMove); e->window.u64[0] = (U64)window; - } - else if((Atom)evt.xclient.data.l[0] == lnx_wm_state->wm_sync_request_atom) + e->pos.x = (F32)evt.xmotion.x; + e->pos.y = (F32)evt.xmotion.y; + set_mouse_cursor = 1; + }break; + + //- rjf: window focus/unfocus + case FocusIn: { - LNX_WM_Window *window = lnx_window_from_x11window(evt.xclient.window); - if(window != 0) + }break; + case FocusOut: + { + LNX_WM_Window *window = lnx_window_from_x11window(evt.xfocus.window); + WM_Event *e = wm_event_list_push_new(arena, &evts, WM_EventKind_WindowLoseFocus); + e->window.u64[0] = (U64)window; + }break; + + //- rjf: client messages + case ClientMessage: + { + if((Atom)evt.xclient.data.l[0] == lnx_wm_state->wm_delete_window_atom) { - window->counter_value = 0; - window->counter_value |= evt.xclient.data.l[2]; - window->counter_value |= (evt.xclient.data.l[3] << 32); - XSyncValue value; - XSyncIntToValue(&value, window->counter_value); - XSyncSetCounter(lnx_wm_state->display, window->counter_xid, value); + LNX_WM_Window *window = lnx_window_from_x11window(evt.xclient.window); + WM_Event *e = wm_event_list_push_new(arena, &evts, WM_EventKind_WindowClose); + e->window.u64[0] = (U64)window; } - } - }break; + else if((Atom)evt.xclient.data.l[0] == lnx_wm_state->wm_sync_request_atom) + { + LNX_WM_Window *window = lnx_window_from_x11window(evt.xclient.window); + if(window != 0) + { + window->counter_value = 0; + window->counter_value |= evt.xclient.data.l[2]; + window->counter_value |= (evt.xclient.data.l[3] << 32); + XSyncValue value; + XSyncIntToValue(&value, window->counter_value); + XSyncSetCounter(lnx_wm_state->display, window->counter_xid, value); + } + } + }break; } if(set_mouse_cursor) @@ -716,7 +716,7 @@ wm_mouse_from_window(WM_Window handle) } //////////////////////////////// -//~ rjf: @os_hooks Cursors (Implemented Per-OS) +//~ rjf: @per_os_impl Cursors (Implemented Per-OS) internal void wm_set_cursor(WM_Cursor cursor) @@ -725,7 +725,7 @@ wm_set_cursor(WM_Cursor cursor) } //////////////////////////////// -//~ rjf: @os_hooks Native User-Facing Graphical Messages (Implemented Per-OS) +//~ rjf: @per_os_impl Native User-Facing Graphical Messages (Implemented Per-OS) internal void wm_graphical_message(B32 error, String8 title, String8 message) @@ -745,7 +745,7 @@ wm_graphical_pick_file(Arena *arena, String8 initial_path) } //////////////////////////////// -//~ rjf: @os_hooks Shell Operations +//~ rjf: @per_os_impl Shell Operations internal void wm_show_in_filesystem_ui(String8 path) diff --git a/src/metagen/metagen_main.c b/src/metagen/metagen_main.c index 52393377..198a3409 100644 --- a/src/metagen/metagen_main.c +++ b/src/metagen/metagen_main.c @@ -220,7 +220,24 @@ entry_point(CmdLine *cmdline) MD_Node *file = n->v.root; for MD_EachNode(node, file->first) { + // rjf: @enum tags -> generate strings from table generators MD_Node *tag = md_tag_from_string(node, str8_lit("enum"), 0); + String8List gen_strings = {0}; + if(!md_node_is_nil(tag)) + { + gen_strings = mg_string_list_from_table_gen(mg_arena, table_grid_map, table_col_map, str8_lit(""), node); + } + + // rjf: @table_enum tags -> generate strings from 'name' column from table + if(md_node_is_nil(tag)) + { + tag = md_tag_from_string(node, str8_lit("table_enum"), 0); + String8 gen_string = str8f(mg_arena, "{ expand(%S a) `$(a.name) = $(a.code)` }", node->string); + MD_Node *gen_node = md_tree_from_string(mg_arena, gen_string); + gen_strings = mg_string_list_from_table_gen(mg_arena, table_grid_map, table_col_map, str8_lit(""), node); + } + + // rjf: generate if(!md_node_is_nil(tag)) { String8 enum_name = node->string; @@ -232,7 +249,6 @@ entry_point(CmdLine *cmdline) String8 enum_base_type_name = tag->first->string; String8 layer_key = mg_layer_key_from_path(file->string); MG_Layer *layer = mg_layer_from_key(layer_key); - String8List gen_strings = mg_string_list_from_table_gen(mg_arena, table_grid_map, table_col_map, str8_lit(""), node); if(enum_base_type_name.size == 0) { str8_list_pushf(mg_arena, &layer->enums, "typedef enum %S\n{\n", enum_name); diff --git a/src/pe/x64/unwind/pe_x64_unwind.c b/src/pe/x64/unwind/pe_x64_unwind.c new file mode 100644 index 00000000..2db6604b --- /dev/null +++ b/src/pe/x64/unwind/pe_x64_unwind.c @@ -0,0 +1,936 @@ +// Copyright (c) Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +//////////////////////////////// +//~ rjf: Helpers + +internal PE_IntelPdata * +pe_x64_uwnd_intel_pdata_from_voff(PE_X64_UWND_ModuleUnwindInfo *unwind_info, U64 voff) +{ + PE_IntelPdata *first_pdata = 0; + { + PE_IntelPdata *pdatas = unwind_info->pdatas; + U64 pdatas_count = unwind_info->pdatas_count; + if(pdatas_count != 0 && voff >= pdatas[0].voff_first) + { + // NOTE(rjf): + // + // binary search: + // find max index s.t. pdata_array[index].voff_first <= voff + // we assume (i < j) -> (pdata_array[i].voff_first < pdata_array[j].voff_first) + // + U64 index = pdatas_count; + U64 min = 0; + U64 opl = pdatas_count; + for(;;) + { + U64 mid = (min + opl)/2; + PE_IntelPdata *pdata = pdatas + mid; + if(voff < pdata->voff_first) + { + opl = mid; + } + else if(pdata->voff_first < voff) + { + min = mid; + } + else + { + index = mid; + break; + } + if(min + 1 >= opl) + { + index = min; + break; + } + } + + // rjf: if we are in range fill result + { + PE_IntelPdata *pdata = pdatas + index; + if(pdata->voff_first <= voff && voff < pdata->voff_one_past_last) + { + first_pdata = pdata; + } + } + } + } + return first_pdata; +} + +internal X64_RegCode +pe_x64_uwnd_reg_code_from_pe_gpr_reg(PE_UnwindGprRegX64 gpr_reg) +{ + X64_RegCode result = X64_RegCode_nil; + switch(gpr_reg) + { + case PE_UnwindGprRegX64_RAX:{result = X64_RegCode_rax;}break; + case PE_UnwindGprRegX64_RCX:{result = X64_RegCode_rcx;}break; + case PE_UnwindGprRegX64_RDX:{result = X64_RegCode_rdx;}break; + case PE_UnwindGprRegX64_RBX:{result = X64_RegCode_rbx;}break; + case PE_UnwindGprRegX64_RSP:{result = X64_RegCode_rsp;}break; + case PE_UnwindGprRegX64_RBP:{result = X64_RegCode_rbp;}break; + case PE_UnwindGprRegX64_RSI:{result = X64_RegCode_rsi;}break; + case PE_UnwindGprRegX64_RDI:{result = X64_RegCode_rdi;}break; + case PE_UnwindGprRegX64_R8 :{result = X64_RegCode_r8;}break; + case PE_UnwindGprRegX64_R9 :{result = X64_RegCode_r9;}break; + case PE_UnwindGprRegX64_R10:{result = X64_RegCode_r10;}break; + case PE_UnwindGprRegX64_R11:{result = X64_RegCode_r11;}break; + case PE_UnwindGprRegX64_R12:{result = X64_RegCode_r12;}break; + case PE_UnwindGprRegX64_R13:{result = X64_RegCode_r13;}break; + case PE_UnwindGprRegX64_R14:{result = X64_RegCode_r14;}break; + case PE_UnwindGprRegX64_R15:{result = X64_RegCode_r15;}break; + } + return result; +} + +//////////////////////////////// +//~ rjf: Unwinding Abstraction Implementation + +internal UWND_StepResult +pe_x64_uwnd_step(Arch arch, MemoryMap *memory_map, UWND_ModuleInfo *module_info, U64 tls_vaddr, void *regs_opaque, U64 *cfa_out) +{ + UWND_StepResult result = {UWND_StepStatus_Error}; + PE_X64_UWND_ModuleUnwindInfo *unwind_info = (PE_X64_UWND_ModuleUnwindInfo *)module_info->unwind_info; + X64_RegBlock *regs = (X64_RegBlock *)regs_opaque; + if(unwind_info != 0) + { + B32 done = 0; + Temp scratch = scratch_begin(0, 0); + X64_RegBlock *new_regs = push_array(scratch.arena, X64_RegBlock, 1); + MemoryCopyStruct(new_regs, regs); + + //////////////////////////// + //- rjf: on PE/x64, the frame base address is always RSP + // + cfa_out[0] = new_regs->rsp; + + //////////////////////////// + //- rjf: unpack context from rip + // + U64 rip_voff = new_regs->rip - module_info->base_vaddr; + PE_IntelPdata *first_pdata = pe_x64_uwnd_intel_pdata_from_voff(unwind_info, rip_voff); + + //////////////////////////// + //- rjf: pdata -> detect if in epilog + // + B32 has_pdata_and_in_epilog = 0; + U64 instruction_data_size_cap = 256; + Rng1U64 instruction_data_vaddr_range = r1u64(new_regs->rip, new_regs->rip+instruction_data_size_cap); + String8 instruction_data = {0}; + if(!done && first_pdata) + { + // NOTE(allen): There are restrictions placed on how an epilog is allowed + // to be formed (https://docs.microsoft.com/en-us/cpp/build/prolog-and-epilog?view=msvc-160) + // Here we interpret machine code directly according to the rules + // given there to determine if the code we're looking at looks like an epilog. + + //- rjf: read instruction memory + U8 *instruction_data_buffer = push_array(scratch.arena, U8, instruction_data_size_cap); + U64 instruction_data_size = memory_map_read(memory_map, instruction_data_vaddr_range, instruction_data_buffer); + instruction_data = str8(instruction_data_buffer, instruction_data_size); + if(instruction_data.size == 0) + { + done = 1; + result.status = UWND_StepStatus_FailedMemoryRead; + result.missed_read_vaddr_range = instruction_data_vaddr_range; + } + + //- rjf: set up parsing state + B32 is_epilog = 0; + + //- rjf: check first instruction, determine if we should keep parsing & from where + U64 read_off = 0; + B32 keep_parsing = 0; + if(!done && instruction_data.size >= 4) + { + U8 inst[4] = {0}; + str8_deserial_read(instruction_data, read_off, &inst[0], sizeof(inst), sizeof(inst)); + keep_parsing = 1; + if((inst[0] & 0xF8) == 0x48) + { + switch(inst[1]) + { + // rjf: add $nnnn,%rsp + case 0x81: + { + if(inst[0] == 0x48 && inst[2] == 0xC4) + { + read_off += 7; + } + else + { + keep_parsing = 0; + } + }break; + + // rjf: add $n,%rsp + case 0x83: + { + if(inst[0] == 0x48 && inst[2] == 0xC4) + { + read_off += 4; + } + else + { + keep_parsing = 0; + } + }break; + + // rjf: lea n(reg),%rsp + case 0x8D: + { + if((inst[0] & 0x06) == 0 && + ((inst[2] >> 3) & 0x07) == 0x04 && + (inst[2] & 0x07) != 0x04) + { + U8 imm_size = (inst[2] >> 6); + + // rjf: 1-byte immediate + if(imm_size == 1) + { + read_off += 4; + } + + // rjf: 4-byte immediate + else if(imm_size == 2) + { + read_off += 7; + } + + // rjf: other case + else + { + keep_parsing = 0; + } + } + else + { + keep_parsing = 0; + } + }break; + } + } + } + + //- rjf: continue parsing instructions + for(;!done && keep_parsing;) + { + // rjf: read next instruction byte + B32 inst_byte_good = 1; + U8 inst_byte = 0; + if(str8_deserial_read_struct(instruction_data, read_off, &inst_byte) == 0) + { + inst_byte_good = 0; + keep_parsing = 0; + } + read_off += 1; + + // rjf: when (... I don't know ...) rely on the next byte + B32 check_inst_byte_good = inst_byte_good; + U8 check_inst_byte = inst_byte; + if(inst_byte_good && (inst_byte & 0xF0) == 0x40) + { + if(str8_deserial_read_struct(instruction_data, read_off, &check_inst_byte) == 0) + { + check_inst_byte_good = 0; + keep_parsing = 0; + } + read_off += 1; + } + + // rjf: check instruction byte + if(check_inst_byte_good) + { + switch(check_inst_byte) + { + default:{keep_parsing = 0;}break; + + // rjf: pop + case 0x58:case 0x59:case 0x5A:case 0x5B: + case 0x5C:case 0x5D:case 0x5E:case 0x5F: + { + }break; + + // rjf: ret + case 0xC2: + case 0xC3: + { + is_epilog = 1; + keep_parsing = 0; + }break; + + // rjf: jmp nnnn + case 0xE9: + { + // + // TODO(rjf): old code here was reading the jump's immediate value, applying the delta, + // and continued parsing if the jump destination address fell within the same pdata. + // but from what I can tell, the only legal usage of a jump within an epilog is to end + // the epilog, and jump to another function; if the jump stays in the function, it is + // an illegal epilog sequence. so, I've instead just changed this to treat any jmps + // which escape the pdata, and also end the pdata, as marking the epilog. + // + // I am not sure if this is correct w.r.t. chained pdatas, however, since presumably + // a jump could also land elsewhere in the same function - we may need to also look for + // stack pointer modifications etc. - those together with a jump in this context may + // let us tell the difference. + // + S32 imm = 0; + B32 imm_good = (str8_deserial_read_struct(instruction_data, read_off, &imm) == sizeof(imm)); + U64 next_vaddr = (instruction_data_vaddr_range.min + read_off + sizeof(imm) + imm); + U64 next_voff = (next_vaddr - module_info->base_vaddr); + if((next_voff < first_pdata->voff_first || first_pdata->voff_one_past_last <= next_voff) && + read_off + sizeof(imm) == first_pdata->voff_one_past_last) + { + is_epilog = 1; + } + keep_parsing = 0; + }break; + + // rjf: rep; ret (for amd64 prediction bug) + case 0xF3: + { + U8 next_inst_byte = 0; + B32 next_inst_byte_good = (str8_deserial_read_struct(instruction_data, read_off, &next_inst_byte) == sizeof(next_inst_byte)); + read_off += 1; + keep_parsing = 0; + if(next_inst_byte_good) + { + is_epilog = (next_inst_byte == 0xC3); + } + }break; + } + } + } + has_pdata_and_in_epilog = is_epilog; + } + + //////////////////////////// + //- rjf: pdata & in epilog -> epilog unwind + // + if(!done && first_pdata && has_pdata_and_in_epilog) + { + U64 read_off = 0; + for(B32 keep_parsing = 1; !done && keep_parsing;) + { + //- rjf: assume no more parsing after this instruction + keep_parsing = 0; + + //- rjf: read next instruction byte + U8 inst_byte = 0; + if(str8_deserial_read_struct(instruction_data, read_off, &inst_byte) == 0) + { + done = 1; + } + read_off += 1; + + //- rjf: extract rex from instruction byte + U8 rex = 0; + if((inst_byte & 0xF0) == 0x40) + { + rex = inst_byte & 0xF; // rex prefix + if(str8_deserial_read_struct(instruction_data, read_off, &inst_byte) == 0) + { + done = 1; + } + read_off += 1; + } + + //- rjf: parse remainder of instruction + switch(inst_byte) + { + // rjf: pop + case 0x58:case 0x59:case 0x5A:case 0x5B: + case 0x5C:case 0x5D:case 0x5E:case 0x5F: + { + // rjf: read value at rsp + U64 sp = new_regs->rsp; + U64 value = 0; + if(memory_map_read_struct(memory_map, sp, &value) != sizeof(value)) + { + done = 1; + result.status = UWND_StepStatus_FailedMemoryRead; + result.missed_read_vaddr_range = r1u64(sp, sp+sizeof(value)); + } + + // rjf: modify registers + if(!done) + { + PE_UnwindGprRegX64 gpr_reg = (inst_byte - 0x58) + (rex & 1)*8; + X64_RegCode reg_code = pe_x64_uwnd_reg_code_from_pe_gpr_reg(gpr_reg); + Rng1U16 reg_rng = x64_reg_code_rng_table[reg_code]; + U64 *reg_ptr = (U64 *)((U8 *)®s + reg_rng.min); + reg_ptr[0] = value; + new_regs->rsp = sp + 8; + } + + // rjf: not a final instruction, so keep parsing + keep_parsing = 1; + }break; + + // rjf: add $nnnn,%rsp + case 0x81: + { + // rjf: skip one byte (we already know what it is in this scenario) + read_off += 1; + + // rjf: read the 4-byte immediate + S32 imm = 0; + B32 imm_is_good = (str8_deserial_read_struct(instruction_data, read_off, &imm) == sizeof(imm)); + if(!imm_is_good) + { + done = 1; + } + read_off += sizeof(imm); + + // rjf: update stack pointer + new_regs->rsp = (U64)(new_regs->rsp + imm); + + // rjf: not a final instruction; keep parsing + keep_parsing = 1; + }break; + + // rjf: add $n,%rsp + case 0x83: + { + // rjf: skip one byte (we already know what it is in this scenario) + read_off += 1; + + // rjf: read the 1-byte immediate + S8 imm = 0; + B32 imm_is_good = (str8_deserial_read_struct(instruction_data, read_off, &imm) == sizeof(imm)); + if(!imm_is_good) + { + done = 1; + } + read_off += sizeof(imm); + + // rjf: update stack pointer + new_regs->rsp = (U64)(new_regs->rsp + imm); + + // rjf: not a final instruction; keep parsing + keep_parsing = 1; + }break; + + // rjf: lea imm8/imm32,$rsp + case 0x8D: + { + // rjf: read source register + U8 modrm = 0; + B32 modrm_is_good = (str8_deserial_read_struct(instruction_data, read_off, &modrm) == sizeof(modrm)); + if(!modrm_is_good) + { + done = 1; + } + read_off += 1; + + // rjf: unpack register value + PE_UnwindGprRegX64 gpr_reg = (modrm & 7) + (rex & 1)*8; + X64_RegCode reg_code = pe_x64_uwnd_reg_code_from_pe_gpr_reg(gpr_reg); + Rng1U16 reg_rng = x64_reg_code_rng_table[reg_code]; + U64 *reg_ptr = (U64 *)((U8 *)®s + reg_rng.min); + U64 reg_value = reg_ptr[0]; + + // rjf: read immediate + S32 imm = 0; + if(!done) + { + // rjf: read 1-byte immediate + if((modrm >> 6) == 1) + { + S8 imm8 = 0; + B32 imm8_is_good = (str8_deserial_read_struct(instruction_data, read_off, &imm8) == sizeof(imm8)); + if(!imm8_is_good) + { + done = 1; + } + read_off += 1; + imm = (S32)imm8; + } + + // rjf: read 4-byte immediate + else + { + B32 imm32_is_good = (str8_deserial_read_struct(instruction_data, read_off, &imm) == sizeof(imm)); + done = !imm32_is_good; + read_off += sizeof(imm); + } + } + + // rjf: update stack pointer + if(!done) + { + new_regs->rsp = (U64)(reg_value + imm); + } + + // rjf: not a final instruction; keep parsing + keep_parsing = 1; + }break; + + // rjf: ret $nn + case 0xC2: + { + // rjf: read new ip + U64 sp = new_regs->rsp; + U64 new_ip = 0; + if(memory_map_read_struct(memory_map, sp, &new_ip) != sizeof(new_ip)) + { + done = 1; + result.status = UWND_StepStatus_FailedMemoryRead; + result.missed_read_vaddr_range = r1u64(sp, sp+sizeof(new_ip)); + } + + // rjf: read 2-byte immediate & advance stack pointer + U16 imm = 0; + if(!done) + { + done = (str8_deserial_read_struct(instruction_data, read_off, &imm) < sizeof(imm)); + } + U64 new_sp = sp + 8 + imm; + + // rjf: commit registers + if(!done) + { + new_regs->rip = new_ip; + new_regs->rsp = new_sp; + } + }break; + + // rjf: ret / rep; ret + case 0xF3: + { + // Assert(!"Hit me!"); + }break; + case 0xC3: + { + // rjf: read new ip + U64 sp = new_regs->rsp; + U64 new_ip = 0; + if(memory_map_read_struct(memory_map, sp, &new_ip) != sizeof(new_ip)) + { + done = 1; + result.status = UWND_StepStatus_FailedMemoryRead; + result.missed_read_vaddr_range = r1u64(sp, sp+sizeof(new_ip)); + } + + // rjf: advance stack pointer + U64 new_sp = sp + 8; + + // rjf: commit registers + if(!done) + { + new_regs->rip = new_ip; + new_regs->rsp = new_sp; + } + }break; + + // rjf: jmp nnnn + case 0xE9: + { + // Assert(!"Hit Me"); + // TODO(allen): general idea: read the immediate, move the ip, leave the sp, done + // we don't have any cases to exercise this right now. no guess implementation! + }break; + + // rjf: Sjmp n + case 0xEB: + { + // Assert(!"Hit Me"); + // TODO(allen): general idea: read the immediate, move the ip, leave the sp, done + // we don't have any cases to exercise this right now. no guess implementation! + }break; + } + } + } + + ////////////////////////////// + //- rjf: pdata & not in epilog -> xdata unwind + // + B32 xdata_unwind_did_machframe = 0; + if(!done && first_pdata && !has_pdata_and_in_epilog) + { + //- rjf: get frame reg + B32 bad_frame_reg_info = 0; + X64_RegCode frame_reg_code = X64_RegCode_rsp; + U64 frame_off = 0; + { + // rjf: read unwind info header + U64 unwind_info_off = first_pdata->voff_unwind_info; + PE_UnwindInfo unwind_info = {0}; + if(memory_map_read_struct(memory_map, module_info->base_vaddr+unwind_info_off, &unwind_info) != sizeof(unwind_info)) + { + done = 1; + result.status = UWND_StepStatus_FailedMemoryRead; + result.missed_read_vaddr_range = r1u64(module_info->base_vaddr+unwind_info_off, module_info->base_vaddr+unwind_info_off+sizeof(unwind_info));; + } + + // rjf: unpack header + U32 frame_reg_id = PE_UNWIND_INFO_REG_FROM_FRAME(unwind_info.frame); + U64 frame_off_val = PE_UNWIND_INFO_OFF_FROM_FRAME(unwind_info.frame); + if(frame_reg_id != 0) + { + frame_reg_code = pe_x64_uwnd_reg_code_from_pe_gpr_reg(frame_reg_id); + bad_frame_reg_info = (frame_reg_code == X64_RegCode_nil); // NOTE(rjf): frame_reg should never be 0 at this point, in valid exe + } + frame_off = frame_off_val; + } + + //- rjf: iterate pdatas, apply opcodes + PE_IntelPdata *last_pdata = 0; + PE_IntelPdata *pdata = first_pdata; + if(!done && !bad_frame_reg_info) for(B32 keep_parsing = 1; !done && keep_parsing && pdata != last_pdata;) + { + //- rjf: read unwind info + U64 unwind_info_off = pdata->voff_unwind_info; + PE_UnwindInfo unwind_info = {0}; + if(memory_map_read_struct(memory_map, module_info->base_vaddr+unwind_info_off, &unwind_info) != sizeof(unwind_info)) + { + done = 1; + result.status = UWND_StepStatus_FailedMemoryRead; + result.missed_read_vaddr_range = r1u64(module_info->base_vaddr+unwind_info_off, module_info->base_vaddr+unwind_info_off+sizeof(unwind_info));; + } + + //- rjf: read unwind codes + PE_UnwindCode *unwind_codes = push_array(scratch.arena, PE_UnwindCode, unwind_info.codes_num); + Rng1U64 unwind_codes_vaddr_range = r1u64(module_info->base_vaddr+unwind_info_off+sizeof(unwind_info), + module_info->base_vaddr+unwind_info_off+sizeof(unwind_info)+sizeof(PE_UnwindCode)*unwind_info.codes_num); + if(memory_map_read(memory_map, unwind_codes_vaddr_range, unwind_codes) != dim_1u64(unwind_codes_vaddr_range)) + { + done = 1; + result.status = UWND_StepStatus_FailedMemoryRead; + result.missed_read_vaddr_range = unwind_codes_vaddr_range;; + } + + //- rjf: unpack frame base + U64 frame_base = new_regs->rsp; + if(frame_reg_code != X64_RegCode_nil) + { + Rng1U16 frame_reg_rng = x64_reg_code_rng_table[frame_reg_code]; + U64 raw_frame_base = *(U64 *)((U8 *)regs + frame_reg_rng.min); + U64 adjusted_frame_base = raw_frame_base - frame_off*16; + frame_base = adjusted_frame_base; + } + + //- rjf: apply opcodes + PE_UnwindCode *code_ptr = unwind_codes; + PE_UnwindCode *code_opl = unwind_codes + unwind_info.codes_num; + for(PE_UnwindCode *next_code_ptr = 0; code_ptr < code_opl; code_ptr = next_code_ptr) + { + // rjf: unpack opcode info + U32 op_code = PE_UNWIND_OPCODE_FROM_FLAGS(code_ptr->flags); + U32 op_info = PE_UNWIND_INFO_FROM_FLAGS(code_ptr->flags); + U32 slot_count = pe_slot_count_from_unwind_op_code(op_code); + if(op_code == PE_UnwindOpCode_ALLOC_LARGE && op_info == 1) + { + slot_count += 1; + } + + // rjf: detect bad slot counts + if(slot_count == 0 || code_ptr+slot_count > code_opl) + { + keep_parsing = 0; + done = 1; + break; + } + + // rjf: set next op code pointer + next_code_ptr = code_ptr + slot_count; + + // rjf: interpret this op code + U64 code_voff = pdata->voff_first + code_ptr->off_in_prolog; + if(code_voff <= rip_voff) + { + switch(op_code) + { + case PE_UnwindOpCode_PUSH_NONVOL: + { + // rjf: read value from stack pointer + U64 rsp = new_regs->rsp; + U64 value = 0; + if(memory_map_read_struct(memory_map, rsp, &value) != sizeof(value)) + { + done = 1; + result.status = UWND_StepStatus_FailedMemoryRead; + result.missed_read_vaddr_range = r1u64(rsp, rsp+sizeof(value)); + } + + // rjf: commit registers + if(!done) + { + X64_RegCode reg_code = pe_x64_uwnd_reg_code_from_pe_gpr_reg(op_info); + Rng1U16 reg_rng = x64_reg_code_rng_table[reg_code]; + *(U64 *)((U8 *)regs + reg_rng.min) = value; + new_regs->rsp = rsp + 8; + } + }break; + + case PE_UnwindOpCode_ALLOC_LARGE: + { + // rjf: read alloc size + U64 size = 0; + if(op_info == 0) + { + size = code_ptr[1].u16*8; + } + else if(op_info == 1) + { + size = code_ptr[1].u16 + ((U32)code_ptr[2].u16 << 16); + } + else + { + done = 1; + } + + // rjf: advance stack pointer + if(!done) + { + new_regs->rsp += size; + } + }break; + + case PE_UnwindOpCode_ALLOC_SMALL: + { + // rjf: advance stack pointer + new_regs->rsp += op_info*8 + 8; + }break; + + case PE_UnwindOpCode_SET_FPREG: + { + // rjf: put stack pointer back to the frame base + new_regs->rsp = frame_base; + }break; + + case PE_UnwindOpCode_SAVE_NONVOL: + { + // rjf: read value from frame base + U64 off = code_ptr[1].u16*8; + U64 addr = frame_base + off; + U64 value = 0; + if(memory_map_read_struct(memory_map, addr, &value) != sizeof(value)) + { + done = 1; + result.status = UWND_StepStatus_FailedMemoryRead; + result.missed_read_vaddr_range = r1u64(addr, addr+sizeof(value)); + } + + // rjf: commit to register + if(!done) + { + X64_RegCode reg_code = pe_x64_uwnd_reg_code_from_pe_gpr_reg(op_info); + Rng1U16 reg_rng = x64_reg_code_rng_table[reg_code]; + *(U64 *)((U8 *)regs + reg_rng.min) = value; + } + }break; + + case PE_UnwindOpCode_SAVE_NONVOL_FAR: + { + // rjf: read value from frame base + U64 off = code_ptr[1].u16 + ((U32)code_ptr[2].u16 << 16); + U64 addr = frame_base + off; + U64 value = 0; + if(memory_map_read_struct(memory_map, addr, &value) != sizeof(value)) + { + done = 1; + result.status = UWND_StepStatus_FailedMemoryRead; + result.missed_read_vaddr_range = r1u64(addr, addr+sizeof(value)); + } + + // rjf: commit to register + if(!done) + { + X64_RegCode reg_code = pe_x64_uwnd_reg_code_from_pe_gpr_reg(op_info); + Rng1U16 reg_rng = x64_reg_code_rng_table[reg_code]; + *(U64 *)((U8 *)regs + reg_rng.min) = value; + } + }break; + + case PE_UnwindOpCode_EPILOG: + { + keep_parsing = 1; + }break; + + case PE_UnwindOpCode_SPARE_CODE: + { + // TODO(rjf): ??? + keep_parsing = 0; + done = 1; + }break; + + case PE_UnwindOpCode_SAVE_XMM128: + { + // rjf: read new register values + U8 buf[16]; + U64 off = code_ptr[1].u16*16; + U64 addr = frame_base + off; + Rng1U64 read_vaddr_range = r1u64(addr, addr+sizeof(buf)); + if(memory_map_read(memory_map, read_vaddr_range, buf) != sizeof(buf)) + { + done = 1; + result.status = UWND_StepStatus_FailedMemoryRead; + result.missed_read_vaddr_range = read_vaddr_range; + } + + // rjf: commit to register + if(!done) + { + void *xmm_reg = (&new_regs->zmm0) + op_info; + MemoryCopy(xmm_reg, buf, sizeof(buf)); + } + }break; + + case PE_UnwindOpCode_SAVE_XMM128_FAR: + { + // rjf: read new register values + U8 buf[16]; + U64 off = code_ptr[1].u16 + ((U32)code_ptr[2].u16 << 16); + U64 addr = frame_base + off; + Rng1U64 read_vaddr_range = r1u64(addr, addr+sizeof(buf)); + if(memory_map_read(memory_map, read_vaddr_range, buf) != sizeof(buf)) + { + done = 1; + result.status = UWND_StepStatus_FailedMemoryRead; + result.missed_read_vaddr_range = read_vaddr_range; + } + + // rjf: commit to register + if(!done) + { + void *xmm_reg = (&new_regs->zmm0) + op_info; + MemoryCopy(xmm_reg, buf, sizeof(buf)); + } + }break; + + case PE_UnwindOpCode_PUSH_MACHFRAME: + { + // NOTE(rjf): this was found by stepping through kernel code after an exception was + // thrown, encountered in the exception_stepping_tests (after the throw) in mule_main + if(op_info > 1) + { + done = 1; + } + + // rjf: unpack stack pointer + U64 sp_og = new_regs->rsp; + U64 sp_adj = sp_og; + if(op_info == 1) + { + sp_adj += 8; + } + U64 sp_after_ip = sp_adj + 8; + U64 sp_after_ss = sp_after_ip + 8; + U64 sp_after_rflags = sp_after_ss + 8; + + // rjf: read new ip value + U64 ip_value = 0; + if(!done && memory_map_read_struct(memory_map, sp_adj, &ip_value) != sizeof(ip_value)) + { + done = 1; + result.status = UWND_StepStatus_FailedMemoryRead; + result.missed_read_vaddr_range = r1u64(sp_adj, sp_adj + sizeof(ip_value)); + } + + // rjf: read new ss value + U16 ss_value = 0; + if(!done && memory_map_read_struct(memory_map, sp_after_ip, &ss_value) != sizeof(ss_value)) + { + done = 1; + result.status = UWND_StepStatus_FailedMemoryRead; + result.missed_read_vaddr_range = r1u64(sp_after_ip, sp_after_ip + sizeof(ss_value)); + } + + // rjf: read new rflags value + U64 rflags_value = 0; + if(!done && memory_map_read_struct(memory_map, sp_after_ss, &rflags_value) != sizeof(rflags_value)) + { + done = 1; + result.status = UWND_StepStatus_FailedMemoryRead; + result.missed_read_vaddr_range = r1u64(sp_after_ss, sp_after_ss + sizeof(rflags_value)); + } + + // rjf: read new sp value + U64 sp_value = 0; + if(!done && memory_map_read_struct(memory_map, sp_after_rflags, &sp_value) != sizeof(sp_value)) + { + done = 1; + result.status = UWND_StepStatus_FailedMemoryRead; + result.missed_read_vaddr_range = r1u64(sp_after_rflags, sp_after_rflags + sizeof(sp_value)); + } + + // rjf: commit registers + if(!done) + { + new_regs->rip = ip_value; + new_regs->ss = ss_value; + new_regs->rflags = rflags_value; + new_regs->rsp = sp_value; + } + + // rjf: mark machine frame + xdata_unwind_did_machframe = !done; + }break; + } + } + } + + //- rjf: iterate to next pdata + if(keep_parsing) + { + U32 flags = PE_UNWIND_INFO_FLAGS_FROM_HDR(unwind_info.header); + if(!(flags & PE_UnwindInfoFlag_CHAINED)) + { + break; + } + U64 code_count_rounded = AlignPow2(unwind_info.codes_num, sizeof(PE_UnwindCode)); + U64 code_size = code_count_rounded*sizeof(PE_UnwindCode); + U64 chained_pdata_off = unwind_info_off + sizeof(PE_UnwindInfo) + code_size; + last_pdata = pdata; + pdata = push_array(scratch.arena, PE_IntelPdata, 1); + if(memory_map_read_struct(memory_map, module_info->base_vaddr+chained_pdata_off, pdata) != sizeof(*pdata)) + { + done = 1; + result.status = UWND_StepStatus_FailedMemoryRead; + result.missed_read_vaddr_range = r1u64(module_info->base_vaddr+chained_pdata_off, module_info->base_vaddr+chained_pdata_off+sizeof(*pdata)); + } + } + } + } + + ////////////////////////////// + //- rjf: no pdata, or didn't do machframe in xdata unwind -> unwind by reading stack pointer + // + if(!done && (!first_pdata || (!has_pdata_and_in_epilog && !xdata_unwind_did_machframe))) + { + // rjf: read new ip + U64 sp = new_regs->rsp; + U64 new_ip = 0; + if(memory_map_read_struct(memory_map, sp, &new_ip) != sizeof(new_ip)) + { + done = 1; + result.status = UWND_StepStatus_FailedMemoryRead; + result.missed_read_vaddr_range = r1u64(sp, sp+sizeof(new_ip)); + } + + // rjf: commit + if(!done) + { + U64 new_sp = sp+8; + new_regs->rip = new_ip; + new_regs->rsp = new_sp; + } + } + + ////////////////////////////// + //- rjf: if not done -> successful step + // + if(!done) + { + result.status = UWND_StepStatus_Good; + MemoryCopyStruct(regs, new_regs); + } + + scratch_end(scratch); + } + return result; +} diff --git a/src/pe/x64/unwind/pe_x64_unwind.h b/src/pe/x64/unwind/pe_x64_unwind.h new file mode 100644 index 00000000..152ba472 --- /dev/null +++ b/src/pe/x64/unwind/pe_x64_unwind.h @@ -0,0 +1,28 @@ +// Copyright (c) Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef PE_X64_UNWIND_H +#define PE_X64_UNWIND_H + +//////////////////////////////// +//~ rjf: Module Unwind Info Type + +typedef struct PE_X64_UWND_ModuleUnwindInfo PE_X64_UWND_ModuleUnwindInfo; +struct PE_X64_UWND_ModuleUnwindInfo +{ + PE_IntelPdata *pdatas; + U64 pdatas_count; +}; + +//////////////////////////////// +//~ rjf: Helpers + +internal PE_IntelPdata *pe_x64_uwnd_intel_pdata_from_voff(PE_X64_UWND_ModuleUnwindInfo *unwind_info, U64 voff); +internal X64_RegCode pe_x64_uwnd_reg_code_from_pe_gpr_reg(PE_UnwindGprRegX64 gpr_reg); + +//////////////////////////////// +//~ rjf: Unwinding Abstraction Implementation + +internal UWND_StepResult pe_x64_uwnd_step(Arch arch, MemoryMap *memory_map, UWND_ModuleInfo *module_info, U64 tls_vaddr, void *regs_opaque, U64 *cfa_out); + +#endif // PE_X64_UNWIND_H diff --git a/src/radbin/radbin.c b/src/radbin/radbin.c index edfc12a4..81eba9d5 100644 --- a/src/radbin/radbin.c +++ b/src/radbin/radbin.c @@ -824,7 +824,7 @@ rb_thread_entry_point(void *p) ELF_Bin bin = elf_bin_from_data(scratch.arena, dbg_data); convert_params.arch = arch_from_elf_machine(bin.hdr.e_machine); convert_params.base_vaddr = elf_base_addr_from_bin(&bin); - convert_params.raw = dw_input_from_elf_bin(scratch.arena, dbg_data, &bin); + convert_params.raw = dw_raw_from_elf_bin(scratch.arena, dbg_data, &bin); convert_params.path_style = PathStyle_UnixAbsolute; convert_params.binary_sections = e2r_rdi_binary_sections_from_elf_section_table(arena, dbg_data, &bin, &bin.shdrs); scratch_end(scratch); @@ -1442,7 +1442,7 @@ rb_thread_entry_point(void *p) else if(f->format == RB_FileFormat_ELF32 || f->format == RB_FileFormat_ELF64) { - dw = dw_input_from_elf_bin(arena, f->data, &elf); + dw = dw_raw_from_elf_bin(arena, f->data, &elf); } } } @@ -1526,8 +1526,11 @@ rb_thread_entry_point(void *p) { if(lane_idx() == 0) { - str8_list_pushf(arena, &output_blobs, "// %S (%S) (DWARF)\n\n", deterministic ? str8_skip_last_slash(f->path) : f->path, f->format ? rb_file_format_display_name_table[f->format] : str8_lit("Unsupported format")); - String8List dump = dw_dump_list_from_sections(arena, &dw, arch, dw_dump_subset_flags, verbose); + str8_list_pushf(arena, &output_blobs, "\n// %S (%S) (DWARF)\n\n", deterministic ? str8_skip_last_slash(f->path) : f->path, f->format ? rb_file_format_display_name_table[f->format] : str8_lit("Unsupported format")); + } + String8List dump = dw_dump_list_from_raw(arena, &dw, arch, dw_dump_subset_flags); + if(lane_idx() == 0) + { str8_list_concat_in_place(&output_blobs, &dump); } lane_sync(); diff --git a/src/radbin/radbin_main.c b/src/radbin/radbin_main.c index fd1b2ad1..2ada5f51 100644 --- a/src/radbin/radbin_main.c +++ b/src/radbin/radbin_main.c @@ -25,7 +25,13 @@ #include "elf/elf_dump.h" #include "codeview/codeview.h" #include "codeview/codeview_parse.h" -#include "dwarf/dwarf_inc.h" +#include "dwarf/dwarf.h" +#include "dwarf/dwarf_parse_2.h" +#include "dwarf/dwarf_parse_coff.h" +#include "dwarf/dwarf_parse_elf.h" +#include "dwarf/dwarf_dump_2.h" +#include "eh_frame/eh_frame.h" +#include "eh_frame/eh_frame_dump.h" #include "msf/msf.h" #include "msf/msf_parse.h" #include "pdb/pdb.h" @@ -53,7 +59,13 @@ #include "elf/elf_dump.c" #include "codeview/codeview.c" #include "codeview/codeview_parse.c" -#include "dwarf/dwarf_inc.c" +#include "dwarf/dwarf.c" +#include "dwarf/dwarf_parse_2.c" +#include "dwarf/dwarf_parse_coff.c" +#include "dwarf/dwarf_parse_elf.c" +#include "dwarf/dwarf_dump_2.c" +#include "eh_frame/eh_frame.c" +#include "eh_frame/eh_frame_dump.c" #include "msf/msf.c" #include "msf/msf_parse.c" #include "pdb/pdb.c" diff --git a/src/raddbg/raddbg_core.c b/src/raddbg/raddbg_core.c index 7490c613..5888ea27 100644 --- a/src/raddbg/raddbg_core.c +++ b/src/raddbg/raddbg_core.c @@ -11380,7 +11380,7 @@ rd_frame(void) U64 rip_vaddr = d_query_cached_rip_from_thread_unwind(thread, unwind_count); D_Entity *module = d_module_from_process_vaddr(process, rip_vaddr); U64 rip_voff = d_voff_from_vaddr(module, rip_vaddr); - U64 tls_root_vaddr = d_cached_tls_root_vaddr_from_thread(thread->handle); + U64 tls_root_vaddr = thread->tls_root_vaddr; ProfEnd(); //////////////////////////// @@ -12212,8 +12212,10 @@ rd_frame(void) { for EachIndex(idx, modules.count) { + Access *access = access_open(); D_Entity *module = modules.v[idx]; - String8 raddbg_data = d_raddbg_data_from_module(scratch.arena, module->handle); + D_ModuleInfo *module_info = d_info_from_module(access, module->handle); + String8 raddbg_data = module_info->raddbg_data; U8 split_char = 0; String8List raddbg_data_text_parts = str8_split(scratch.arena, raddbg_data, &split_char, 1, 0); U64 cfg_idx = 0; @@ -12230,6 +12232,7 @@ rd_frame(void) cfg_node_ptr_list_push(scratch.arena, &immediate_type_views, n->v); } } + access_close(access); } } @@ -12333,7 +12336,7 @@ rd_frame(void) ctx->module_base[0] = module->vaddr_range.min; ctx->frame_base = push_array(scratch.arena, U64, 1); ctx->tls_base = push_array(scratch.arena, U64, 1); - ctx->tls_base[0] = d_query_cached_tls_base_vaddr_from_process_root_rip(process, tls_root_vaddr, rip_vaddr); + ctx->tls_base[0] = d_cached_tls_vaddr_from_thread_module(thread->handle, module->handle, rd_state->frame_eval_memread_endt_us, 0); ctx->cfa = d_query_cached_cfa_from_thread_unwind(thread, unwind_count); } e_select_interpret_ctx(interpret_ctx, eval_dbg_infos_primary->rdi, rip_voff); @@ -16810,7 +16813,7 @@ rd_frame(void) D_EntityArray all_processes = d_entity_array_from_kind(D_EntityKind_Process); // rjf: no processes before this one -> new session - if(process_count_pre_tick == 0) + if(process_count_pre_tick == 0 && all_processes.count != 0) { D_Entity *process = all_processes.v[0]; CFG_Node *target = cfg_node_from_id(process->src_msg_id); diff --git a/src/raddbg/raddbg_main.c b/src/raddbg/raddbg_main.c index b40438bb..64c9e83d 100644 --- a/src/raddbg/raddbg_main.c +++ b/src/raddbg/raddbg_main.c @@ -330,6 +330,9 @@ #include "pdb/pdb_parse.h" #include "pdb/pdb_stringize.h" #include "dwarf/dwarf_inc.h" +#include "eh_frame/eh_frame.h" +#include "eh_frame/eh_frame_dump.h" +#include "unwind/unwind_inc.h" #include "rdi_from_coff/rdi_from_coff.h" #include "rdi_from_elf/rdi_from_elf.h" #include "rdi_from_pdb/rdi_from_pdb.h" @@ -384,6 +387,9 @@ #include "pdb/pdb_parse.c" #include "pdb/pdb_stringize.c" #include "dwarf/dwarf_inc.c" +#include "eh_frame/eh_frame.c" +#include "eh_frame/eh_frame_dump.c" +#include "unwind/unwind_inc.c" #include "rdi_from_coff/rdi_from_coff.c" #include "rdi_from_elf/rdi_from_elf.c" #include "rdi_from_pdb/rdi_from_pdb.c" diff --git a/src/raddbg/tests/raddbg_tests.c b/src/raddbg/tests/raddbg_tests.c index b7a60626..6956d14c 100644 --- a/src/raddbg/tests/raddbg_tests.c +++ b/src/raddbg/tests/raddbg_tests.c @@ -7,7 +7,7 @@ internal String8 rd_test__test_binary_path(Arena *arena, TestCtx *ctx, String8 name) { - String8 path = str8f(arena, "%S/%S/%S%S", ctx->input_data_path, name, name, program_ext_postfix_from_os(OperatingSystem_CURRENT)); + String8 path = str8f(arena, "%S/%S/%S%S", ctx->input_data_path, name, name, program_ext_postfix_from_os(OperatingSystem_CURRENT, 1)); return path; } @@ -15,7 +15,7 @@ internal String8 rd_test__raddbg_path(Arena *arena, CmdLine *cmdline) { String8 raddbg_exe_name = cmd_line_has_flag(cmdline, s("gui")) ? s("raddbg") : s("raddbg_non_graphical"); - String8 raddbg_exe_filename = str8f(arena, "%S/%S%S", get_process_info()->binary_path, raddbg_exe_name, program_ext_postfix_from_os(OperatingSystem_CURRENT)); + String8 raddbg_exe_filename = str8f(arena, "%S/%S%S", get_process_info()->binary_path, raddbg_exe_name, program_ext_postfix_from_os(OperatingSystem_CURRENT, 0)); return raddbg_exe_filename; } diff --git a/src/rdi/rdi_local.c b/src/rdi/rdi_local.c index 23f0dae6..247cd8bc 100644 --- a/src/rdi/rdi_local.c +++ b/src/rdi/rdi_local.c @@ -1401,6 +1401,7 @@ lane_sync(); if(flags & (1ull<<(kind))) ProfScope((char *)rdi_name_title_from_du } lane_sync(); +#undef DumpSubsetKind #undef DumpSubset #undef dumpf #undef dump diff --git a/src/rdi_from_dwarf/rdi_from_dwarf.c b/src/rdi_from_dwarf/rdi_from_dwarf.c index 201b3ca6..f718333a 100644 --- a/src/rdi_from_dwarf/rdi_from_dwarf.c +++ b/src/rdi_from_dwarf/rdi_from_dwarf.c @@ -2752,7 +2752,7 @@ d2r_convert(Arena *arena, D2R_ConvertParams *params) arch = arch_from_elf_machine(bin.hdr.e_machine); image_base = elf_base_addr_from_bin(&bin); - input = dw_input_from_elf_bin(scratch.arena, params->dbg_data, &bin); + input = dw_raw_from_elf_bin(scratch.arena, params->dbg_data, &bin); path_style = PathStyle_UnixAbsolute; g_d2r_shared.binary_sections = e2r_rdi_binary_sections_from_elf_section_table(arena, params->dbg_data, &bin, &bin.shdrs); @@ -2864,16 +2864,16 @@ d2r_convert(Arena *arena, D2R_ConvertParams *params) String8 path; { Temp temp = temp_begin(scratch.arena); - + String8 comp_dir = lookup->vm->header.dir_table.v[0]; PathStyle comp_path_style = path_style_from_str8(comp_dir); PathStyle dir_path_style = path_style_from_str8(lookup->vm->header.dir_table.v[src->dir_idx]); PathStyle file_path_style = path_style_from_str8(src->path); - + String8List dir_path_list = str8_split_path(temp.arena, lookup->vm->header.dir_table.v[src->dir_idx]); String8List file_path_list = str8_split_path(temp.arena, src->path); - + String8List path_list = {0}; if (dir_path_style == PathStyle_Relative) { String8List comp_dir_list = str8_split_path(temp.arena, comp_dir); @@ -2881,15 +2881,15 @@ d2r_convert(Arena *arena, D2R_ConvertParams *params) } str8_list_concat_in_place(&path_list, &dir_path_list); str8_list_concat_in_place(&path_list, &file_path_list); - + PathStyle path_style = PathStyle_SystemAbsolute; if (dir_path_style != PathStyle_Relative) { path_style = dir_path_style; } else if (file_path_style != PathStyle_Relative) { path_style = file_path_style; } else if (comp_path_style != PathStyle_Relative) { path_style = comp_path_style; } - + str8_path_list_resolve_dots_in_place(&path_list, path_style); path = str8_path_list_join_by_style(arena, &path_list, path_style); - + temp_end(temp); } diff --git a/src/rdi_from_dwarf/rdi_from_dwarf_2.c b/src/rdi_from_dwarf/rdi_from_dwarf_2.c index b2fa3672..61ed73bc 100644 --- a/src/rdi_from_dwarf/rdi_from_dwarf_2.c +++ b/src/rdi_from_dwarf/rdi_from_dwarf_2.c @@ -82,9 +82,9 @@ d2r2_convert(Arena *arena, D2R2_ConvertParams *params) ProfScope("gather unit ranges from .debug_info, .debug_aranges") if(lane_idx() == 0) { unit_info_ranges = push_array(scratch.arena, Rng1U64Array, 1); - *unit_info_ranges = dw2_unit_ranges_from_data(scratch.arena, raw->sec[DW_Section_Info].data); + *unit_info_ranges = dw2_unit_ranges_from_data(scratch.arena, raw->sec[DW_SectionKind_Info].data); unit_arange_ranges = push_array(scratch.arena, Rng1U64Array, 1); - *unit_arange_ranges = dw2_unit_ranges_from_data(scratch.arena, raw->sec[DW_Section_ARanges].data); + *unit_arange_ranges = dw2_unit_ranges_from_data(scratch.arena, raw->sec[DW_SectionKind_ARanges].data); } lane_sync_u64(&unit_info_ranges, 0); lane_sync_u64(&unit_arange_ranges, 0); @@ -107,7 +107,7 @@ d2r2_convert(Arena *arena, D2R2_ConvertParams *params) lane_sync_u64(&unit_info_tag_ranges, 0); // rjf: parse all unit headers - String8 data = raw->sec[DW_Section_Info].data; + String8 data = raw->sec[DW_SectionKind_Info].data; Rng1U64 range = lane_range(unit_count); for EachInRange(idx, range) { @@ -135,7 +135,7 @@ d2r2_convert(Arena *arena, D2R2_ConvertParams *params) U64 unit_take_idx_ = 0; U64 *unit_take_idx_ptr = &unit_take_idx_; lane_sync_u64(&unit_take_idx_ptr, 0); - String8 data = raw->sec[DW_Section_ARanges].data; + String8 data = raw->sec[DW_SectionKind_ARanges].data; for(;;) { // rjf: take unit @@ -264,115 +264,7 @@ d2r2_convert(Arena *arena, D2R2_ConvertParams *params) //////////////////////////// //- rjf: build all abbreviation maps, build (unit -> abbrev map) // - U64 abbrev_map_count = 0; - U64 *abbrev_map_off_from_idx_table = 0; - DW2_AbbrevMap *abbrev_map_from_idx_table = 0; - DW2_AbbrevMap **abbrev_map_from_unit_idx_table = 0; - ProfScope("build all abbreviation maps, build (unit -> abbrev map)") - { - //- rjf: gather deduplicated .debug_abbrev offsets, representing beginnings - // of abbreviation tables. we want to do this because many units may refer - // to the same abbreviation table. - // - typedef struct AbbrevOffNode AbbrevOffNode; - struct AbbrevOffNode - { - AbbrevOffNode *order_next; - AbbrevOffNode *hash_next; - U64 off; - U64 idx; - }; - U64 abbrev_off_slots_count = unit_count; - AbbrevOffNode **abbrev_off_slots = 0; - ProfScope("gather deduplicated .debug_abbrev offsets") if(lane_idx() == 0) - { - AbbrevOffNode *abbrev_off_first = 0; - AbbrevOffNode *abbrev_off_last = 0; - abbrev_off_slots = push_array(scratch.arena, AbbrevOffNode *, abbrev_off_slots_count); - for EachIndex(unit_idx, unit_count) - { - U64 abbrev_off = unit_headers[unit_idx].abbrev_off; - U64 hash = u64_hash_from_str8(str8_struct(&abbrev_off)); - U64 slot_idx = hash%abbrev_off_slots_count; - AbbrevOffNode *node = 0; - for(AbbrevOffNode *n = abbrev_off_slots[slot_idx]; n != 0; n = n->hash_next) - { - if(n->off == abbrev_off) - { - node = n; - break; - } - } - if(node == 0) - { - node = push_array(scratch.arena, AbbrevOffNode, 1); - SLLStackPush_N(abbrev_off_slots[slot_idx], node, hash_next); - SLLQueuePush_N(abbrev_off_first, abbrev_off_last, node, order_next); - node->off = abbrev_off; - node->idx = abbrev_map_count; - abbrev_map_count += 1; - } - } - abbrev_map_off_from_idx_table = push_array(scratch.arena, U64, abbrev_map_count); - abbrev_map_from_idx_table = push_array(scratch.arena, DW2_AbbrevMap, abbrev_map_count); - abbrev_map_from_unit_idx_table = push_array(scratch.arena, DW2_AbbrevMap *, unit_count); - { - U64 abbrev_map_idx = 0; - for(AbbrevOffNode *n = abbrev_off_first; n != 0; n = n->order_next) - { - abbrev_map_off_from_idx_table[abbrev_map_idx] = n->off; - abbrev_map_idx += 1; - } - } - } - lane_sync_u64(&abbrev_off_slots, 0); - lane_sync_u64(&abbrev_map_count, 0); - lane_sync_u64(&abbrev_map_off_from_idx_table, 0); - lane_sync_u64(&abbrev_map_from_idx_table, 0); - lane_sync_u64(&abbrev_map_from_unit_idx_table, 0); - - //- rjf: build all unique abbreviation maps - ProfScope("build all unique abbreviation maps") - { - String8 abbrev_data = raw->sec[DW_Section_Abbrev].data; - U64 abbrev_map_take_idx = 0; - U64 *abbrev_map_take_idx_ptr = &abbrev_map_take_idx; - lane_sync_u64(&abbrev_map_take_idx_ptr, 0); - for(;;) - { - U64 abbrev_map_idx = ins_atomic_u64_inc_eval(abbrev_map_take_idx_ptr) - 1; - if(abbrev_map_idx >= abbrev_map_count) - { - break; - } - abbrev_map_from_idx_table[abbrev_map_idx] = dw2_abbrev_map_from_data(scratch.arena, abbrev_data, abbrev_map_off_from_idx_table[abbrev_map_idx]); - } - lane_sync(); - } - - //- rjf: build unit -> abbrev map table - ProfScope("build unit -> abbrev map table") - { - Rng1U64 range = lane_range(unit_count); - for EachInRange(unit_idx, range) - { - U64 abbrev_off = unit_headers[unit_idx].abbrev_off; - U64 hash = u64_hash_from_str8(str8_struct(&abbrev_off)); - U64 slot_idx = hash%abbrev_off_slots_count; - U64 abbrev_map_idx = 0; - for(AbbrevOffNode *n = abbrev_off_slots[slot_idx]; n != 0; n = n->hash_next) - { - if(n->off == abbrev_off) - { - abbrev_map_idx = n->idx; - break; - } - } - abbrev_map_from_unit_idx_table[unit_idx] = &abbrev_map_from_idx_table[abbrev_map_idx]; - } - } - } - lane_sync(); + DW2_UnitAbbrevMapMap unit_abbrev_map_map = dw2_unit_abbrev_map_map_from_data(scratch.arena, raw->sec[DW_SectionKind_Abbrev].data, unit_headers, unit_count); //////////////////////////// //- rjf: parse all unit offsets tables @@ -399,249 +291,62 @@ d2r2_convert(Arena *arena, D2R2_ConvertParams *params) // // thank you, again, DWARF. // - U64 str_offsets_tables_count = 0; - DW2_OffsetTable *str_offsets_tables = 0; - Rng1U64 *str_offsets_tables_ranges = 0; - U64 rnglists_tables_count = 0; - DW2_OffsetTable *rnglists_tables = 0; - Rng1U64 *rnglists_tables_ranges = 0; - U64 addr_tables_count = 0; - DW2_OffsetTable *addr_tables = 0; - Rng1U64 *addr_tables_ranges = 0; - U64 loclists_tables_count = 0; - DW2_OffsetTable *loclists_tables = 0; - Rng1U64 *loclists_tables_ranges = 0; + DW2_OffsetTableSet *offset_tables = 0; ProfScope("parse all offset tables (.debug_rnglists, .debug_str_offsets, .debug_addr, .debug_loclists)") if(lane_idx() == 0) { - struct - { - String8 data; - U64 *tables_count_out; - DW2_OffsetTable **tables_out; - Rng1U64 **tables_ranges_out; - } - tasks[] = - { - {raw->sec[DW_Section_StrOffsets].data, &str_offsets_tables_count, &str_offsets_tables, &str_offsets_tables_ranges}, - {raw->sec[DW_Section_RngLists].data, &rnglists_tables_count, &rnglists_tables, &rnglists_tables_ranges}, - {raw->sec[DW_Section_Addr].data, &addr_tables_count, &addr_tables, &addr_tables_ranges}, - {raw->sec[DW_Section_LocLists].data, &loclists_tables_count, &loclists_tables, &loclists_tables_ranges}, - }; - for EachElement(task_idx, tasks) - { - Temp scratch2 = scratch_begin(&scratch.arena, 1); - String8 data = tasks[task_idx].data; - - //- rjf: gather all tables (loose) - typedef struct TableNode TableNode; - struct TableNode - { - TableNode *next; - DW2_OffsetTable v; - Rng1U64 range; - }; - TableNode *first_table = 0; - TableNode *last_table = 0; - U64 table_count = 0; - for(U64 off = 0; off < data.size;) - { - U64 start_off = off; - DW2_OffsetTable table = {0}; - off += dw2_read_offset_table(data, off, &table); - if(table.entries != 0) - { - TableNode *n = push_array(scratch2.arena, TableNode, 1); - SLLQueuePush(first_table, last_table, n); - n->v = table; - n->range = r1u64(start_off, off); - table_count += 1; - } - if(off == start_off) - { - break; - } - } - - //- rjf: tighten - tasks[task_idx].tables_count_out[0] = table_count; - tasks[task_idx].tables_out[0] = push_array(scratch.arena, DW2_OffsetTable, table_count); - tasks[task_idx].tables_ranges_out[0] = push_array(scratch.arena, Rng1U64, table_count); - { - U64 idx = 0; - for EachNode(n, TableNode, first_table) - { - tasks[task_idx].tables_out[0][idx] = n->v; - tasks[task_idx].tables_ranges_out[0][idx] = n->range; - idx += 1; - } - } - - scratch_end(scratch2); - } + offset_tables = push_array(scratch.arena, DW2_OffsetTableSet, 1); + offset_tables[0] = dw2_offset_table_set_from_raw(scratch.arena, raw); } - lane_sync_u64(&str_offsets_tables_count, 0); - lane_sync_u64(&str_offsets_tables, 0); - lane_sync_u64(&str_offsets_tables_ranges, 0); - lane_sync_u64(&rnglists_tables_count, 0); - lane_sync_u64(&rnglists_tables, 0); - lane_sync_u64(&rnglists_tables_ranges, 0); - lane_sync_u64(&addr_tables_count, 0); - lane_sync_u64(&addr_tables, 0); - lane_sync_u64(&addr_tables_ranges, 0); - lane_sync_u64(&loclists_tables_count, 0); - lane_sync_u64(&loclists_tables, 0); - lane_sync_u64(&loclists_tables_ranges, 0); + lane_sync_u64(&offset_tables, 0); //////////////////////////// - //- rjf: build per-unit parsing contexts + //- rjf: build per-unit parsing contexts, parse compilation unit tags // DW2_ParseCtx *unit_parse_ctxs = 0; + DW2_Tag *unit_root_tags = 0; { if(lane_idx() == 0) { unit_parse_ctxs = push_array(scratch.arena, DW2_ParseCtx, unit_count); + unit_root_tags = push_array(scratch.arena, DW2_Tag, unit_count); } lane_sync_u64(&unit_parse_ctxs, 0); + lane_sync_u64(&unit_root_tags, 0); Rng1U64 range = lane_range(unit_count); for EachInRange(unit_idx, range) { DW2_UnitHeader *hdr = &unit_headers[unit_idx]; DW2_ParseCtx *ctx = &unit_parse_ctxs[unit_idx]; - ctx->raw = raw; - ctx->version = hdr->version; - ctx->format = hdr->format; - ctx->addr_size = hdr->addr_size; - ctx->unit_base_info_off = unit_info_ranges->v[unit_idx].min; - ctx->abbrev_map = abbrev_map_from_unit_idx_table[unit_idx]; - } - } - lane_sync(); - - //////////////////////////// - //- rjf: do initial parse of each unit's root-level tag - // - // an incredible NOTE: this is actually not sufficient. because this tag parse - // is what informs us which .debug_str_offsets table each unit should be associated - // with (via the StrOffsetsBase attribute), we actually don't have the right string - // offset table *before* we parse this, which means we can't resolve some of the - // string attribute values. - // - // so, in another incredible twist of fate, we actually must do this *twice*. first, - // to find *just the StrOffsetsBase*, and then, to actually fully resolve everything. - // - // (all of the above is also true for RngListsBase) - // - DW2_Tag *unit_root_tags__pre_offset_tables = 0; - { - if(lane_idx() == 0) - { - unit_root_tags__pre_offset_tables = push_array(scratch.arena, DW2_Tag, unit_count); - } - lane_sync_u64(&unit_root_tags__pre_offset_tables, 0); - Rng1U64 range = lane_range(unit_count); - for EachInRange(unit_idx, range) - { - dw2_read_tag(scratch.arena, &unit_parse_ctxs[unit_idx], raw->sec[DW_Section_Info].data, unit_info_tag_ranges[unit_idx].min, &unit_root_tags__pre_offset_tables[unit_idx]); - } - } - lane_sync(); - - //////////////////////////// - //- rjf: look up base addresses for each unit - equip to per-unit parsing contexts - // - { - Rng1U64 range = lane_range(unit_count); - for EachInRange(unit_idx, range) - { - DW2_Tag *unit_root_tag = &unit_root_tags__pre_offset_tables[unit_idx]; - DW2_Attrib *low_pc_attrib = dw2_attrib_from_kind(unit_root_tag, DW_AttribKind_LowPc); - U64 low_pc = low_pc_attrib->val.addr; - unit_parse_ctxs[unit_idx].unit_base_addr = low_pc; - } - } - lane_sync(); - - //////////////////////////// - //- rjf: look up offset tables for each unit (.debug_str_offsets, .debug_rnglists, etc.); - // equip to per-unit parsing contexts - // - { - Rng1U64Array rnglists_tables_ranges_array = {rnglists_tables_ranges, rnglists_tables_count}; - Rng1U64Array str_offsets_tables_ranges_array = {str_offsets_tables_ranges, str_offsets_tables_count}; - Rng1U64Array addr_tables_ranges_array = {addr_tables_ranges, addr_tables_count}; - Rng1U64Array loclist_tables_ranges_array = {loclists_tables_ranges, loclists_tables_count}; - Rng1U64 range = lane_range(unit_count); - for EachInRange(unit_idx, range) - { - DW2_Tag *unit_root_tag = &unit_root_tags__pre_offset_tables[unit_idx]; - // rjf: find rnglists table - { - DW2_Attrib *rnglists_base_off_attrib = dw2_attrib_from_kind(unit_root_tag, DW_AttribKind_RngListsBase); - U64 rnglists_base_off = rnglists_base_off_attrib->val.u128.u64[0]; - U64 rnglists_table_num = rng1u64_array_num_from_value__binary_search(&rnglists_tables_ranges_array, rnglists_base_off); - if(0 < rnglists_table_num && rnglists_table_num <= rnglists_tables_ranges_array.count) - { - DW2_OffsetTable *table = &rnglists_tables[rnglists_table_num-1]; - unit_parse_ctxs[unit_idx].rnglists_table = table; - } - } + // rjf: equip initial info - just enough to do a bootstrapping parse of the root tag + dw2_parse_ctx_equip_unit_header(ctx, hdr, unit_info_ranges->v[unit_idx].min); + dw2_parse_ctx_equip_unit_abbrev_map(ctx, &unit_abbrev_map_map, unit_idx); - // rjf: find str offsets table - { - DW2_Attrib *str_offsets_base_off_attrib = dw2_attrib_from_kind(unit_root_tag, DW_AttribKind_StrOffsetsBase); - U64 str_offsets_base_off = str_offsets_base_off_attrib->val.u128.u64[0]; - U64 str_offsets_table_num = rng1u64_array_num_from_value__binary_search(&str_offsets_tables_ranges_array, str_offsets_base_off); - if(0 < str_offsets_table_num && str_offsets_table_num <= str_offsets_tables_ranges_array.count) - { - DW2_OffsetTable *table = &str_offsets_tables[str_offsets_table_num-1]; - unit_parse_ctxs[unit_idx].str_offsets_table = table; - } - } + // rjf: do bootstrapping parse + // + // an incredible NOTE: the above context info is actually not sufficient for general + // parsing, because this "bootstrapping" tag parse is what informs us which + // .debug_str_offsets table each unit should be associated with (via the + // StrOffsetsBase attribute), we actually don't have the right string offset table + // *before* we parse this, which means we can't resolve some of the string attribute + // values. + // + // so, in another incredible twist of fate, we actually must do this *twice*. first, + // to find *just the StrOffsetsBase*, and then, to actually fully resolve everything. + // + // (all of the above is also true for RngListsBase) + // + DW2_Tag root_tag_bootstrap = {0}; + dw2_read_tag(scratch.arena, raw, ctx, raw->sec[DW_SectionKind_Info].data, unit_info_tag_ranges[unit_idx].min, &root_tag_bootstrap); - // rjf: find addr table - { - DW2_Attrib *addr_base_off_attrib = dw2_attrib_from_kind(unit_root_tag, DW_AttribKind_AddrBase); - U64 addr_base_off = addr_base_off_attrib->val.u128.u64[0]; - U64 addr_table_num = rng1u64_array_num_from_value__binary_search(&addr_tables_ranges_array, addr_base_off); - if(0 < addr_table_num && addr_table_num <= addr_tables_ranges_array.count) - { - DW2_OffsetTable *table = &addr_tables[addr_table_num-1]; - unit_parse_ctxs[unit_idx].addr_table = table; - } - } + // rjf: equip info extracted from bootstrapping parse + dw2_parse_ctx_equip_unit_root_tag(ctx, &root_tag_bootstrap, offset_tables); - // rjf: find addr table - { - DW2_Attrib *loclist_base_off_attrib = dw2_attrib_from_kind(unit_root_tag, DW_AttribKind_LocListsBase); - U64 loclist_base_off = loclist_base_off_attrib->val.u128.u64[0]; - U64 loclist_table_num = rng1u64_array_num_from_value__binary_search(&loclist_tables_ranges_array, loclist_base_off); - if(0 < loclist_table_num && loclist_table_num <= loclist_tables_ranges_array.count) - { - DW2_OffsetTable *table = &loclists_tables[loclist_table_num-1]; - unit_parse_ctxs[unit_idx].loclists_table = table; - } - } - } - } - lane_sync(); - - //////////////////////////// - //- rjf: do parse of each unit's root tag AFTER finding the right .debug_str_offsets table - // - // (excellent work everyone - thank you for reminding me why RDI is necessary) - // - DW2_Tag *unit_root_tags = 0; - { - if(lane_idx() == 0) - { - unit_root_tags = push_array(scratch.arena, DW2_Tag, unit_count); - } - lane_sync_u64(&unit_root_tags, 0); - Rng1U64 range = lane_range(unit_count); - for EachInRange(unit_idx, range) - { - dw2_read_tag(scratch.arena, &unit_parse_ctxs[unit_idx], raw->sec[DW_Section_Info].data, unit_info_tag_ranges[unit_idx].min, &unit_root_tags[unit_idx]); + // rjf: do non-bootstrapping parse + dw2_read_tag(scratch.arena, raw, ctx, raw->sec[DW_SectionKind_Info].data, unit_info_tag_ranges[unit_idx].min, &unit_root_tags[unit_idx]); + + // rjf: equip info extracted from non-bootstrapping parse + dw2_parse_ctx_equip_unit_root_tag(ctx, &unit_root_tags[unit_idx], offset_tables); } } lane_sync(); @@ -671,8 +376,8 @@ d2r2_convert(Arena *arena, D2R2_ConvertParams *params) DW2_Tag *unit_root_tag = &unit_root_tags[unit_idx]; DW2_Attrib *stmt_list = dw2_attrib_from_kind(unit_root_tag, DW_AttribKind_StmtList); U64 line_info_off = stmt_list->val.u128.u64[0]; - String8 line_info_data = raw->sec[DW_Section_Line].data; - dw2_read_line_table_header(scratch.arena, ctx, line_info_data, line_info_off, &unit_line_table_headers[unit_idx]); + String8 line_info_data = raw->sec[DW_SectionKind_Line].data; + dw2_read_line_table_header(scratch.arena, raw, ctx, line_info_data, line_info_off, &unit_line_table_headers[unit_idx]); } lane_sync(); } @@ -825,7 +530,7 @@ d2r2_convert(Arena *arena, D2R2_ConvertParams *params) DW2_Tag *unit_root_tag = &unit_root_tags[unit_idx]; DW2_Attrib *stmt_list = dw2_attrib_from_kind(unit_root_tag, DW_AttribKind_StmtList); U64 line_info_off = stmt_list->val.u128.u64[0]; - String8 all_line_info_data = raw->sec[DW_Section_Line].data; + String8 all_line_info_data = raw->sec[DW_SectionKind_Line].data; String8 unit_line_table_data = str8_substr(all_line_info_data, r1u64(line_table_header->line_program_off, line_info_off + line_table_header->total_unit_data_size)); //- rjf: build unit's line table @@ -1295,7 +1000,7 @@ d2r2_convert(Arena *arena, D2R2_ConvertParams *params) //- rjf: reading the next tag DW2_Tag tag = {0}; - off += dw2_read_tag(scratch3.arena, unit_parse_ctx, raw->sec[DW_Section_Info].data, off, &tag); + off += dw2_read_tag(scratch3.arena, raw, unit_parse_ctx, raw->sec[DW_SectionKind_Info].data, off, &tag); //- rjf: do tree navigations if(tag.has_children) @@ -1508,7 +1213,7 @@ d2r2_convert(Arena *arena, D2R2_ConvertParams *params) // rjf: read tag DW2_Tag tag = {0}; - off += dw2_read_tag(tag_scratch.arena, origin_unit_parse_ctx, raw->sec[DW_Section_Info].data, off, &tag); + off += dw2_read_tag(tag_scratch.arena, raw, origin_unit_parse_ctx, raw->sec[DW_SectionKind_Info].data, off, &tag); // rjf: is this one of the tags we'd like to dedup? -> gather if(tag.kind == DW_TagKind_ArrayType || @@ -1667,7 +1372,7 @@ d2r2_convert(Arena *arena, D2R2_ConvertParams *params) // rjf: read tag DW2_Tag tag = {0}; - t_off += dw2_read_tag(tag_scratch.arena, unit_parse_ctx, raw->sec[DW_Section_Info].data, t_off, &tag); + t_off += dw2_read_tag(tag_scratch.arena, raw, unit_parse_ctx, raw->sec[DW_SectionKind_Info].data, t_off, &tag); // rjf: determine if tag should be skipped from hashing B32 should_skip_tag = (tag.kind == DW_TagKind_LexicalBlock || @@ -1691,14 +1396,14 @@ d2r2_convert(Arena *arena, D2R2_ConvertParams *params) for(DW2_AttribNode *n = tag.attribs.first; n != 0; n = n->next) { // rjf: non-reference? -> combine attribute value info - if(n->v.val.kind != DW_Form_RefAddr && - n->v.val.kind != DW_Form_Ref1 && - n->v.val.kind != DW_Form_Ref2 && - n->v.val.kind != DW_Form_Ref4 && - n->v.val.kind != DW_Form_Ref8 && - n->v.val.kind != DW_Form_RefUData && - n->v.val.kind != DW_Form_RefSup4 && - n->v.val.kind != DW_Form_RefSig8 && + if(n->v.val.kind != DW_FormKind_RefAddr && + n->v.val.kind != DW_FormKind_Ref1 && + n->v.val.kind != DW_FormKind_Ref2 && + n->v.val.kind != DW_FormKind_Ref4 && + n->v.val.kind != DW_FormKind_Ref8 && + n->v.val.kind != DW_FormKind_RefUData && + n->v.val.kind != DW_FormKind_RefSup4 && + n->v.val.kind != DW_FormKind_RefSig8 && ((tag.kind != DW_TagKind_SubProgram && tag.kind != DW_TagKind_FormalParameter) || (n->v.attrib_kind != DW_AttribKind_Name && @@ -2090,7 +1795,7 @@ d2r2_convert(Arena *arena, D2R2_ConvertParams *params) // rjf: parse this type's tag U64 read_off = info_off; DW2_Tag tag = {0}; - read_off += dw2_read_tag(scratch2.arena, unit_parse_ctx, raw->sec[DW_Section_Info].data, read_off, &tag); + read_off += dw2_read_tag(scratch2.arena, raw, unit_parse_ctx, raw->sec[DW_SectionKind_Info].data, read_off, &tag); // rjf: find direct types from this type tag typedef struct DirectTypeNode DirectTypeNode; @@ -2146,7 +1851,7 @@ d2r2_convert(Arena *arena, D2R2_ConvertParams *params) // rjf: read child tag DW2_Tag child_tag = {0}; - read_off += dw2_read_tag(scratch2.arena, unit_parse_ctx, raw->sec[DW_Section_Info].data, read_off, &child_tag); + read_off += dw2_read_tag(scratch2.arena, raw, unit_parse_ctx, raw->sec[DW_SectionKind_Info].data, read_off, &child_tag); // rjf: formal parameters -> gather direct types if(depth == 1 && child_tag.kind == DW_TagKind_FormalParameter) @@ -2315,7 +2020,7 @@ d2r2_convert(Arena *arena, D2R2_ConvertParams *params) // rjf: parse root-level tag DW2_Tag tag = {0}; - U64 tag_info_size = dw2_read_tag(temp.arena, unit_parse_ctx, raw->sec[DW_Section_Info].data, info_off, &tag); + U64 tag_info_size = dw2_read_tag(temp.arena, raw, unit_parse_ctx, raw->sec[DW_SectionKind_Info].data, info_off, &tag); // rjf: extract common attributes DW2_Attrib *name_attrib = &dw2_attrib_nil; @@ -2453,7 +2158,7 @@ d2r2_convert(Arena *arena, D2R2_ConvertParams *params) // rjf: read child tag DW2_Tag child_tag = {0}; - off += dw2_read_tag(scratch3.arena, unit_parse_ctx, raw->sec[DW_Section_Info].data, off, &child_tag); + off += dw2_read_tag(scratch3.arena, raw, unit_parse_ctx, raw->sec[DW_SectionKind_Info].data, off, &child_tag); // rjf: gather parameters if(depth == 1 && child_tag.kind == DW_TagKind_FormalParameter) @@ -2654,7 +2359,7 @@ d2r2_convert(Arena *arena, D2R2_ConvertParams *params) { U64 start_off = off; DW2_Tag child_tag = {0}; - off += dw2_read_tag(temp.arena, unit_parse_ctx, raw->sec[DW_Section_Info].data, off, &child_tag); + off += dw2_read_tag(temp.arena, raw, unit_parse_ctx, raw->sec[DW_SectionKind_Info].data, off, &child_tag); if(child_tag.kind == DW_TagKind_SubrangeType) { // rjf: extract bounds attribs @@ -2681,8 +2386,9 @@ d2r2_convert(Arena *arena, D2R2_ConvertParams *params) // rjf: resolve lower bound U64 lower_bound = 0; + if(lower_bound_attrib != &dw2_attrib_nil) { - if(dw_is_form_kind_ref(unit_parse_ctx->version, unit_parse_ctx->ext, lower_bound_attrib->val.kind)) + if(dw_attrib_class_from_form_kind(unit_parse_ctx->version, unit_parse_ctx->exts, lower_bound_attrib->val.kind) & DW_AttribClass_Reference) { log_infof("[.debug_info@0x%I64x] Array type lower bound is a variable; this is not currently supported.\n", start_off); } @@ -2691,6 +2397,13 @@ d2r2_convert(Arena *arena, D2R2_ConvertParams *params) lower_bound = lower_bound_attrib->val.u128.u64[0]; } } + else switch(unit_parse_ctx->language) + { + default:{}break; +#define X(name, code, default_array_lower_bound) case DW_Language_##name:{lower_bound = (default_array_lower_bound);}break; + DW_Language_XList +#undef X + } // rjf: resolve upper bound U64 upper_bound = 0; @@ -2701,7 +2414,7 @@ d2r2_convert(Arena *arena, D2R2_ConvertParams *params) } else if(upper_bound_attrib != &dw2_attrib_nil) { - if(dw_is_form_kind_ref(unit_parse_ctx->version, unit_parse_ctx->ext, upper_bound_attrib->val.kind)) + if(dw_attrib_class_from_form_kind(unit_parse_ctx->version, unit_parse_ctx->exts, upper_bound_attrib->val.kind) & DW_AttribClass_Reference) { log_infof("[.debug_info@0x%I64x] Array type upper bound is a variable; this is not currently supported.\n", start_off); } @@ -2833,7 +2546,7 @@ d2r2_convert(Arena *arena, D2R2_ConvertParams *params) // rjf: read namespace tag DW2_Tag tag = {0}; - dw2_read_tag(scratch2.arena, unit_parse_ctx, raw->sec[DW_Section_Info].data, info_off, &tag); + dw2_read_tag(scratch2.arena, raw, unit_parse_ctx, raw->sec[DW_SectionKind_Info].data, info_off, &tag); DW2_Attrib *name_attrib = dw2_attrib_from_kind(&tag, DW_AttribKind_Name); String8 name = name_attrib->val.string; @@ -2917,7 +2630,7 @@ d2r2_convert(Arena *arena, D2R2_ConvertParams *params) // rjf: parse tag DW2_Tag tag = {0}; U64 tag_parse_off = off; - off += dw2_read_tag(scratch2.arena, unit_parse_ctx, raw->sec[DW_Section_Info].data, off, &tag); + off += dw2_read_tag(scratch2.arena, raw, unit_parse_ctx, raw->sec[DW_SectionKind_Info].data, off, &tag); // rjf: unpack tag attributes DW2_Attrib *name_attrib = &dw2_attrib_nil; @@ -3183,7 +2896,7 @@ d2r2_convert(Arena *arena, D2R2_ConvertParams *params) { U64 voff_base = lopc_attrib->val.addr - base_vaddr; U64 voff_opl = 0; - if(dw_attrib_class_from_form_kind(unit_parse_ctx->version, hipc_attrib->val.kind) & (1<version, unit_parse_ctx->exts, hipc_attrib->val.kind) & (1<val.u128.u64[0]; } @@ -3298,7 +3011,7 @@ d2r2_convert(Arena *arena, D2R2_ConvertParams *params) //- rjf: parse tag // DW2_Tag tag = {0}; - off += dw2_read_tag(scratch2.arena, unit_parse_ctx, raw->sec[DW_Section_Info].data, off, &tag); + off += dw2_read_tag(scratch2.arena, raw, unit_parse_ctx, raw->sec[DW_SectionKind_Info].data, off, &tag); //////////////////////// //- rjf: gather attributes from tag @@ -3365,7 +3078,7 @@ d2r2_convert(Arena *arena, D2R2_ConvertParams *params) { U64 voff_base = lopc_attrib->val.addr - base_vaddr; U64 voff_opl = 0; - if(dw_attrib_class_from_form_kind(unit_parse_ctx->version, hipc_attrib->val.kind) & (1<version, unit_parse_ctx->exts, hipc_attrib->val.kind) & (1<val.u128.u64[0]; } @@ -3467,19 +3180,19 @@ d2r2_convert(Arena *arena, D2R2_ConvertParams *params) switch(attrib->val.kind) { default:{}break; - case DW_Form_ExprLoc: + case DW_FormKind_ExprLoc: { U64 expr_info_off = attrib->val.u128.u64[0]; U64 expr_info_size = attrib->val.u128.u64[1]; - String8 expr = str8_substr(raw->sec[DW_Section_Info].data, r1u64(expr_info_off, expr_info_off+expr_info_size)); + String8 expr = str8_substr(raw->sec[DW_SectionKind_Info].data, r1u64(expr_info_off, expr_info_off+expr_info_size)); DW2_LocNode *n = push_array(scratch2.arena, DW2_LocNode, 1); n->v.expr = expr; n->v.range = r1u64(0, max_U64); SLLQueuePush(locs.first, locs.last, n); locs.count += 1; }break; - case DW_Form_LocListx: // NOTE(rjf): dwarf5+, loclistx -> location list offset table - case DW_Form_SecOffset: // NOTE(rjf): pre-dwarf5, location section offset -> a location list + case DW_FormKind_LocListx: // NOTE(rjf): dwarf5+, loclistx -> location list offset table + case DW_FormKind_SecOffset: // NOTE(rjf): pre-dwarf5, location section offset -> a location list { locs = dw2_loclist_from_form_val(scratch2.arena, unit_parse_ctx, raw, attrib->val); }break; @@ -3550,31 +3263,36 @@ d2r2_convert(Arena *arena, D2R2_ConvertParams *params) DW_ExprOp opcode = 0; expr_off += str8_deserial_read_struct(expr, expr_off, &opcode); + //- rjf: unpack opcode + DW_ExprOpInfo *opcode_info = dw_info_from_expr_op(unit_parse_ctx->version, unit_parse_ctx->exts, opcode); + U64 operands_count = opcode_info->operand_count; + DW_ExprOperandKind operands_kinds[2] = {opcode_info->operand_kinds[0], opcode_info->operand_kinds[1]}; + U64 push_count = opcode_info->push_count; + U64 pop_count = opcode_info->pop_count; + //- rjf: read op operands - U64 operands_count = dw_operand_count_from_expr_op(opcode); - DW_ExprOperandType *operands_types = dw_operand_types_from_expr_opcode(opcode); U64 operand_u64s[2] = {0}; S64 operand_s64s[2] = {0}; String8 operand_str8s[2] = {0}; for EachIndex(operand_idx, operands_count) { - switch(operands_types[operand_idx]) + switch(operands_kinds[operand_idx]) { - case DW_ExprOperandType_Null: + case DW_ExprOperandKind_Null: default:{}break; - case DW_ExprOperandType_U8: {expr_off += str8_deserial_read(expr, expr_off, &operand_u64s[operand_idx], 1, 1);}break; - case DW_ExprOperandType_U16: {expr_off += str8_deserial_read(expr, expr_off, &operand_u64s[operand_idx], 2, 2);}break; - case DW_ExprOperandType_U32: {expr_off += str8_deserial_read(expr, expr_off, &operand_u64s[operand_idx], 4, 4);}break; - case DW_ExprOperandType_U64: {expr_off += str8_deserial_read(expr, expr_off, &operand_u64s[operand_idx], 8, 8);}break; - case DW_ExprOperandType_S8: {expr_off += str8_deserial_read(expr, expr_off, &operand_s64s[operand_idx], 1, 1);}break; - case DW_ExprOperandType_S16: {expr_off += str8_deserial_read(expr, expr_off, &operand_s64s[operand_idx], 2, 2);}break; - case DW_ExprOperandType_S32: {expr_off += str8_deserial_read(expr, expr_off, &operand_s64s[operand_idx], 4, 4);}break; - case DW_ExprOperandType_S64: {expr_off += str8_deserial_read(expr, expr_off, &operand_s64s[operand_idx], 8, 8);}break; - case DW_ExprOperandType_ULEB128: {expr_off += str8_deserial_read_uleb128(expr, expr_off, &operand_u64s[operand_idx]);} break; - case DW_ExprOperandType_SLEB128: {expr_off += str8_deserial_read_sleb128(expr, expr_off, &operand_s64s[operand_idx]);} break; - case DW_ExprOperandType_Addr: {expr_off += str8_deserial_read(expr, expr_off, &operand_u64s[operand_idx], unit_parse_ctx->addr_size, unit_parse_ctx->addr_size);}break; - case DW_ExprOperandType_DwarfUInt: {expr_off += dw2_read_fmt_u64(expr, expr_off, unit_parse_ctx->format, &operand_u64s[operand_idx]);}break; - case DW_ExprOperandType_Block: + case DW_ExprOperandKind_U8: {expr_off += str8_deserial_read(expr, expr_off, &operand_u64s[operand_idx], 1, 1);}break; + case DW_ExprOperandKind_U16: {expr_off += str8_deserial_read(expr, expr_off, &operand_u64s[operand_idx], 2, 2);}break; + case DW_ExprOperandKind_U32: {expr_off += str8_deserial_read(expr, expr_off, &operand_u64s[operand_idx], 4, 4);}break; + case DW_ExprOperandKind_U64: {expr_off += str8_deserial_read(expr, expr_off, &operand_u64s[operand_idx], 8, 8);}break; + case DW_ExprOperandKind_S8: {expr_off += str8_deserial_read(expr, expr_off, &operand_s64s[operand_idx], 1, 1);}break; + case DW_ExprOperandKind_S16: {expr_off += str8_deserial_read(expr, expr_off, &operand_s64s[operand_idx], 2, 2);}break; + case DW_ExprOperandKind_S32: {expr_off += str8_deserial_read(expr, expr_off, &operand_s64s[operand_idx], 4, 4);}break; + case DW_ExprOperandKind_S64: {expr_off += str8_deserial_read(expr, expr_off, &operand_s64s[operand_idx], 8, 8);}break; + case DW_ExprOperandKind_ULEB128: {expr_off += str8_deserial_read_uleb128(expr, expr_off, &operand_u64s[operand_idx]);} break; + case DW_ExprOperandKind_SLEB128: {expr_off += str8_deserial_read_sleb128(expr, expr_off, &operand_s64s[operand_idx]);} break; + case DW_ExprOperandKind_Addr: {expr_off += str8_deserial_read(expr, expr_off, &operand_u64s[operand_idx], unit_parse_ctx->addr_size, unit_parse_ctx->addr_size);}break; + case DW_ExprOperandKind_DwarfUInt: {expr_off += dw2_read_fmt_u64(expr, expr_off, unit_parse_ctx->format, &operand_u64s[operand_idx]);}break; + case DW_ExprOperandKind_Block: { U8 block_size = 0; expr_off += str8_deserial_read_struct(expr, expr_off, &block_size); @@ -3587,7 +3305,6 @@ d2r2_convert(Arena *arena, D2R2_ConvertParams *params) //- rjf: pop stack values D2R2_ExprVal popped_vals[2] = {0}; { - U64 pop_count = dw_pop_count_from_expr_op(opcode); pop_count = Min(pop_count, ArrayCount(popped_vals)); for EachIndex(pop_idx, pop_count) { @@ -3905,7 +3622,7 @@ d2r2_convert(Arena *arena, D2R2_ConvertParams *params) //- rjf: TLS offsets case DW_ExprOp_FormTlsAddress: - case DW_ExprOp_GNU_PushTlsAddress: + case DW_GNU_ExprOp_PushTlsAddress: { rdim_bytecode_push_op(arena, &dst_bytecode, RDI_EvalOp_TLSOff, 0); rdim_bytecode_push_op(arena, &dst_bytecode, RDI_EvalOp_Add, RDI_EvalTypeGroup_U); @@ -3916,7 +3633,7 @@ d2r2_convert(Arena *arena, D2R2_ConvertParams *params) //- rjf: call site value case DW_ExprOp_EntryValue: - case DW_ExprOp_GNU_EntryValue: + case DW_GNU_ExprOp_EntryValue: { // TODO(rjf): expr op entry value ops }break; @@ -3975,24 +3692,24 @@ d2r2_convert(Arena *arena, D2R2_ConvertParams *params) log_infof("[.debug_info@0x%I64x] DWARF expression call operation encountered. This is not supported for location info.\n", start_off); }break; case DW_ExprOp_ImplicitPointer: - case DW_ExprOp_GNU_ImplicitPointer: + case DW_GNU_ExprOp_ImplicitPointer: { dst_bytecode_is_good = 0; log_infof("[.debug_info@0x%I64x] DWARF expression implicit pointer operation encountered. This is not supported for location info.\n", start_off); }break; - case DW_ExprOp_GNU_ParameterRef: + case DW_GNU_ExprOp_ParameterRef: { dst_bytecode_is_good = 0; log_infof("[.debug_info@0x%I64x] DWARF expression GNU_ParameterRef operation encountered. This is not supported for location info.\n", start_off); }break; case DW_ExprOp_DerefType: - case DW_ExprOp_GNU_DerefType: + case DW_GNU_ExprOp_DerefType: { dst_bytecode_is_good = 0; log_infof("[.debug_info@0x%I64x] DWARF expression DerefType operation encountered. This is not supported for location info.\n", start_off); }break; case DW_ExprOp_ConstType: - case DW_ExprOp_GNU_ConstType: + case DW_GNU_ExprOp_ConstType: { dst_bytecode_is_good = 0; log_infof("[.debug_info@0x%I64x] DWARF expression ConstType operation encountered. This is not supported for location info.\n", start_off); @@ -4012,7 +3729,7 @@ d2r2_convert(Arena *arena, D2R2_ConvertParams *params) dst_bytecode_is_good = 0; log_infof("[.debug_info@0x%I64x] DWARF expression Rot (rotate) operation encountered. This is not supported for location info.\n", start_off); }break; - case DW_ExprOp_GNU_UnInit: + case DW_GNU_ExprOp_UnInit: { dst_bytecode_is_good = 0; log_infof("[.debug_info@0x%I64x] DWARF expression GNU_UnInit operation encountered. This is not supported for location info.\n", start_off); @@ -4022,7 +3739,6 @@ d2r2_convert(Arena *arena, D2R2_ConvertParams *params) //- rjf: push values to stack { - U64 push_count = dw_push_count_from_expr_op(opcode); push_count = Min(push_count, ArrayCount(push_vals)); for EachIndex(push_idx, push_count) { diff --git a/src/rdi_from_dwarf/tests/rdi_from_dwarf_tests.c b/src/rdi_from_dwarf/tests/rdi_from_dwarf_tests.c index 8d30e9d9..efd32d87 100644 --- a/src/rdi_from_dwarf/tests/rdi_from_dwarf_tests.c +++ b/src/rdi_from_dwarf/tests/rdi_from_dwarf_tests.c @@ -5,11 +5,13 @@ // // [ ] d2r_types alias size and byte size on __float80 typedef mismatch +#if 0 + internal RDI_Parsed * d2r_rdi_from_dwarf_writer(Arena *arena, DW_Writer *writer) { Temp scratch = scratch_begin(&arena, 1); - + RDI_Parsed *rdi = push_array(arena, RDI_Parsed, 1); String8 raw_coff; @@ -156,19 +158,19 @@ dw_writer_tag_end(writer); \ #define TestBuiltinType(n, bs, r) \ do { \ - RDI_TypeNode *alias = d2rt_type_from_name(rdi, &types_map, n); \ - T_Ok(alias); \ - T_Ok(alias->kind == RDI_TypeKind_Alias); \ - T_Ok(alias->flags == 0); \ - T_Ok(alias->byte_size == bs); \ - RDI_TypeNode *type = rdi_element_from_name_idx(rdi, TypeNodes, alias->user_defined.direct_type_idx); \ - T_Ok(type); \ - T_Ok(type->kind == RDI_TypeKind_##r); \ - T_Ok(type->flags == 0); \ - T_Ok(type->byte_size == alias->byte_size); \ - U64 expected_name_size = 0; \ - U8 *expected_name = rdi_string_from_type_kind(RDI_TypeKind_##r, &expected_name_size); \ - T_Ok(str8_match(str8_from_rdi_string_idx(rdi, type->built_in.name_string_idx), str8(expected_name, expected_name_size), 0)); \ +RDI_TypeNode *alias = d2rt_type_from_name(rdi, &types_map, n); \ +T_Ok(alias); \ +T_Ok(alias->kind == RDI_TypeKind_Alias); \ +T_Ok(alias->flags == 0); \ +T_Ok(alias->byte_size == bs); \ +RDI_TypeNode *type = rdi_element_from_name_idx(rdi, TypeNodes, alias->user_defined.direct_type_idx); \ +T_Ok(type); \ +T_Ok(type->kind == RDI_TypeKind_##r); \ +T_Ok(type->flags == 0); \ +T_Ok(type->byte_size == alias->byte_size); \ +U64 expected_name_size = 0; \ +U8 *expected_name = rdi_string_from_type_kind(RDI_TypeKind_##r, &expected_name_size); \ +T_Ok(str8_match(str8_from_rdi_string_idx(rdi, type->built_in.name_string_idx), str8(expected_name, expected_name_size), 0)); \ } while (0) TestBuiltinType("char", 1, Char8); TestBuiltinType("char8_t", 1, UChar8); @@ -282,32 +284,32 @@ SKIP(d2r_line_table) for EachIndex(k, test_table[i].line_size) { String8 cmdl = str8f(arena, "-voff2line -voff:0x%llx %S", test_table[i].voff + k, t_make_file_path(arena, str8_lit("a.rdi"))); t_invoke_radbin((char *)cmdl.str); - + if (g_last_exit_code != 0) { t_errorf("radbin exited with %llu on \"%S\"\n", (unsigned long long)g_last_exit_code, cmdl); T_Ok(0); } - + if ( ! t_match_linef(&g_output, "%S:%llu", test_table[i].file->path, test_table[i].ln)) { t_errorf("ERROR: conversion produced unexpected output for \"radbin %S\"\n" \ - " Expected: %S:%llu\n" \ - " Got: %S\n", - cmdl, - test_table[i].file->path, (unsigned long long)test_table[i].ln, - g_output); - + " Expected: %S:%llu\n" \ + " Got: %S\n", + cmdl, + test_table[i].file->path, (unsigned long long)test_table[i].ln, + g_output); + if (g_verbose) { t_invoke_radbin("-dump -only:line_tables a.rdi"); t_errorf("\n================================================================================\n"); t_errorf("RDI:\n%S\n", g_output); - + t_errorf("================================================================================\n"); t_invoke_radbin("-dump -only:debug_line a.exe"); t_errorf("DWARF:\n%S\n", g_output); - + g_output = str8_zero(); } - + T_Ok(0); } } @@ -319,7 +321,7 @@ SKIP(d2r_line_table) SKIP(d2r_checksums) { DW_Writer *writer = dw_writer_begin(DW_Format_32Bit, DW_Version_5, DW_CompUnitKind_Compile, Arch_x64); - + B32 is_ok = 0; DW_WriterFile *foo_file = dw_writer_new_file(writer, str8_lit("/mnt/c/devel/foo.c")); @@ -594,4 +596,4 @@ TEST(d2r_general) dw_writer_end(&writer); } - +#endif diff --git a/src/torture/torture_main.c b/src/torture/torture_main.c index b240b3ae..4de5a57a 100644 --- a/src/torture/torture_main.c +++ b/src/torture/torture_main.c @@ -58,11 +58,13 @@ #include "pdb/pdb_parse.h" #include "pdb/pdb_stringize.h" #include "dwarf/dwarf_inc.h" +#include "eh_frame/eh_frame.h" +#include "eh_frame/eh_frame_dump.h" +#include "unwind/unwind_inc.h" #include "llvm/llvm.h" #include "rdi_from_coff/rdi_from_coff.h" #include "rdi_from_elf/rdi_from_elf.h" #include "rdi_from_pdb/rdi_from_pdb.h" -#include "rdi_from_dwarf/rdi_from_dwarf.h" #include "rdi_from_dwarf/rdi_from_dwarf_2.h" #include "obj/obj.h" #include "radbin/radbin.h" @@ -133,11 +135,13 @@ #include "pdb/pdb_parse.c" #include "pdb/pdb_stringize.c" #include "dwarf/dwarf_inc.c" +#include "eh_frame/eh_frame.c" +#include "eh_frame/eh_frame_dump.c" +#include "unwind/unwind_inc.c" #include "llvm/llvm.c" #include "rdi_from_coff/rdi_from_coff.c" #include "rdi_from_elf/rdi_from_elf.c" #include "rdi_from_pdb/rdi_from_pdb.c" -#include "rdi_from_dwarf/rdi_from_dwarf.c" #include "rdi_from_dwarf/rdi_from_dwarf_2.c" #include "obj/obj.c" #include "radbin/radbin.c" diff --git a/src/unwind/generated/unwind.meta.c b/src/unwind/generated/unwind.meta.c new file mode 100644 index 00000000..0378fad9 --- /dev/null +++ b/src/unwind/generated/unwind.meta.c @@ -0,0 +1,5 @@ +// Copyright (c) Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +//- GENERATED CODE + diff --git a/src/unwind/generated/unwind.meta.h b/src/unwind/generated/unwind.meta.h new file mode 100644 index 00000000..fa4831f6 --- /dev/null +++ b/src/unwind/generated/unwind.meta.h @@ -0,0 +1,21 @@ +// Copyright (c) Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +//- GENERATED CODE + +#ifndef UNWIND_META_H +#define UNWIND_META_H + +typedef enum UWND_Unwinder +{ +UWND_Unwinder_Null, +UWND_Unwinder_PEx64, +UWND_Unwinder_EHFrame, +UWND_Unwinder_COUNT, +} UWND_Unwinder; + +#define UWND_Unwinder_XList \ +X(PEx64, pe_x64)\ +X(EHFrame, eh)\ + +#endif // UNWIND_META_H diff --git a/src/unwind/unwind.c b/src/unwind/unwind.c new file mode 100644 index 00000000..4a073e03 --- /dev/null +++ b/src/unwind/unwind.c @@ -0,0 +1,24 @@ +// Copyright (c) Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +//////////////////////////////// +//~ rjf: Generated Code + +#include "unwind/generated/unwind.meta.c" + +//////////////////////////////// +//~ rjf: Abstracted Unwind Step Functions + +internal UWND_StepResult +uwnd_step(UWND_Unwinder unwinder, Arch arch, MemoryMap *memory_map, UWND_ModuleInfo *module_info, U64 tls_vaddr, void *regs, U64 *cfa_out) +{ + UWND_StepResult result = {UWND_StepStatus_Error}; + switch(unwinder) + { + default:{}break; +#define X(name, namespace_lower) case UWND_Unwinder_##name:{result = namespace_lower##_uwnd_step(arch, memory_map, module_info, tls_vaddr, regs, cfa_out);}break; + UWND_Unwinder_XList +#undef X + } + return result; +} diff --git a/src/unwind/unwind.h b/src/unwind/unwind.h new file mode 100644 index 00000000..40bab81d --- /dev/null +++ b/src/unwind/unwind.h @@ -0,0 +1,45 @@ +// Copyright (c) Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef UNWIND_H +#define UNWIND_H + +//////////////////////////////// +//~ rjf: Generated Code + +#include "unwind/generated/unwind.meta.h" + +//////////////////////////////// +//~ rjf: Unwind Artifact Types + +typedef enum UWND_StepStatus +{ + UWND_StepStatus_Error, + UWND_StepStatus_FailedMemoryRead, + UWND_StepStatus_Good, +} +UWND_StepStatus; + +typedef struct UWND_StepResult UWND_StepResult; +struct UWND_StepResult +{ + UWND_StepStatus status; + Rng1U64 missed_read_vaddr_range; +}; + +//////////////////////////////// +//~ rjf: Module Info + +typedef struct UWND_ModuleInfo UWND_ModuleInfo; +struct UWND_ModuleInfo +{ + U64 base_vaddr; + void *unwind_info; +}; + +//////////////////////////////// +//~ rjf: Abstracted Unwind Step Functions + +internal UWND_StepResult uwnd_step(UWND_Unwinder unwinder, Arch arch, MemoryMap *memory_map, UWND_ModuleInfo *module_info, U64 tls_vaddr, void *regs, U64 *cfa_out); + +#endif // UNWIND_H diff --git a/src/unwind/unwind.mdesk b/src/unwind/unwind.mdesk new file mode 100644 index 00000000..2617c1cb --- /dev/null +++ b/src/unwind/unwind.mdesk @@ -0,0 +1,21 @@ +// Copyright (c) Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +@table(name namespace_lower) +UWND_UnwinderTable: +{ + {PEx64 pe_x64 } + {EHFrame eh } +} + +@enum UWND_Unwinder: +{ + Null, + @expand(UWND_UnwinderTable a) `$(a.name)`, + COUNT +} + +@xlist UWND_Unwinder_XList: +{ + @expand(UWND_UnwinderTable a) `$(a.name), $(a.namespace_lower)` +} diff --git a/src/unwind/unwind_inc.c b/src/unwind/unwind_inc.c new file mode 100644 index 00000000..52509304 --- /dev/null +++ b/src/unwind/unwind_inc.c @@ -0,0 +1,10 @@ +// Copyright (c) Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#include "unwind.c" +#if defined(PE_H) && defined(X64_H) +# include "pe/x64/unwind/pe_x64_unwind.c" +#endif +#if defined(EH_FRAME_H) +# include "eh_frame/unwind/eh_frame_unwind.c" +#endif diff --git a/src/unwind/unwind_inc.h b/src/unwind/unwind_inc.h new file mode 100644 index 00000000..4a9b25c3 --- /dev/null +++ b/src/unwind/unwind_inc.h @@ -0,0 +1,15 @@ +// Copyright (c) Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef UNWIND_INC_H +#define UNWIND_INC_H + +#include "unwind.h" +#if defined(PE_H) && defined(X64_H) +# include "pe/x64/unwind/pe_x64_unwind.h" +#endif +#if defined(EH_FRAME_H) +# include "eh_frame/unwind/eh_frame_unwind.h" +#endif + +#endif // UNWIND_INC_H diff --git a/src/win32/base/win32_base.c b/src/win32/base/win32_base.c index b830e42b..1d9f8e1e 100644 --- a/src/win32/base/win32_base.c +++ b/src/win32/base/win32_base.c @@ -316,7 +316,7 @@ commit_memory_large(void *ptr, U64 size) } //////////////////////////////// -//~ rjf: @os_hooks Shared Memory +//~ rjf: @per_os_impl Shared Memory internal SharedMemory shared_memory_alloc(U64 size, String8 name) @@ -476,7 +476,7 @@ thread_detach(Thread thread) } //////////////////////////////// -//~ rjf: @os_hooks Safe Calls +//~ rjf: @per_os_impl Safe Calls internal void safe_call(ThreadEntryPointFunctionType *func, ThreadEntryPointFunctionType *fail_handler, void *ptr) diff --git a/src/win32/demon/win32_demon.c b/src/win32/demon/win32_demon.c index acceaa52..f8c9f6cf 100644 --- a/src/win32/demon/win32_demon.c +++ b/src/win32/demon/win32_demon.c @@ -1217,6 +1217,50 @@ w32_dmn_thread_write_reg_block(Arch arch, HANDLE thread, void *reg_block) return result; } +internal U64 +w32_dmn_thread_stack_base_vaddr_from_tlb_vaddr(HANDLE process, Arch arch, U64 tlb_vaddr) +{ + U64 stack_base_vaddr = 0; + { + switch(arch) + { + case Arch_Null: + case Arch_COUNT: + case Arch_arm64: + case Arch_arm32: + case Arch_x86: + {}break; + case Arch_x64: + { + U64 stack_base_vaddr = tlb_vaddr + 0x8; + w32_dmn_process_read(process, r1u64(stack_base_vaddr, stack_base_vaddr+8), &stack_base_vaddr); + }break; + } + } + return stack_base_vaddr; +} + +internal U64 +w32_dmn_thread_tls_root_vaddr_from_tlb_vaddr(Arch arch, U64 tlb_vaddr) +{ + U64 result = 0; + switch(arch) + { + case Arch_Null: + case Arch_COUNT: + {}break; + case Arch_arm64: + case Arch_arm32: + case Arch_x86: + {}break; + case Arch_x64: + { + result = tlb_vaddr + 88; + }break; + } + return result; +} + //- rjf: remote thread injection internal DWORD @@ -2066,6 +2110,10 @@ dmn_ctrl_run(Arena *arena, DMN_CtrlCtx *ctx, DMN_RunCtrls *ctrls) thread->thread.thread_local_base = tls_base; } + // rjf: unpack thread addresses + U64 stack_base_vaddr = w32_dmn_thread_stack_base_vaddr_from_tlb_vaddr(process_handle, module_info->arch, tls_base); + U64 tls_root_vaddr = w32_dmn_thread_tls_root_vaddr_from_tlb_vaddr(module_info->arch, tls_base); + // rjf: put thread into suspended state, so it matches expected initial state SuspendThread(thread_handle); @@ -2096,11 +2144,13 @@ dmn_ctrl_run(Arena *arena, DMN_CtrlCtx *ctx, DMN_RunCtrls *ctrls) // rjf: create thread { DMN_Event *e = dmn_event_list_push(arena, &events); - e->kind = DMN_EventKind_CreateThread; - e->process = w32_dmn_handle_from_entity(process); - e->thread = w32_dmn_handle_from_entity(thread); - e->arch = module_info->arch; - e->code = evt.dwThreadId; + e->kind = DMN_EventKind_CreateThread; + e->process = w32_dmn_handle_from_entity(process); + e->thread = w32_dmn_handle_from_entity(thread); + e->arch = module_info->arch; + e->code = evt.dwThreadId; + e->stack_pointer = stack_base_vaddr; + e->tls_root_vaddr= tls_root_vaddr; } // rjf: load module @@ -2192,6 +2242,10 @@ dmn_ctrl_run(Arena *arena, DMN_CtrlCtx *ctx, DMN_RunCtrls *ctrls) DWORD sus_result = SuspendThread(thread->handle); (void)sus_result; + // rjf: unpack thread addresses + U64 stack_base_vaddr = w32_dmn_thread_stack_base_vaddr_from_tlb_vaddr(process->handle, thread->arch, (U64)evt.u.CreateThread.lpThreadLocalBase); + U64 tls_root_vaddr = w32_dmn_thread_tls_root_vaddr_from_tlb_vaddr(thread->arch, (U64)evt.u.CreateThread.lpThreadLocalBase); + // rjf: unpack thread name String8 thread_name = {0}; if(w32_dmn_GetThreadDescription != 0) @@ -2213,11 +2267,13 @@ dmn_ctrl_run(Arena *arena, DMN_CtrlCtx *ctx, DMN_RunCtrls *ctrls) { DMN_Event *e = dmn_event_list_push(arena, &events); e->kind = DMN_EventKind_CreateThread; - e->process = w32_dmn_handle_from_entity(process); - e->thread = w32_dmn_handle_from_entity(thread); - e->arch = thread->arch; - e->code = evt.dwThreadId; - e->string = thread_name; + e->process = w32_dmn_handle_from_entity(process); + e->thread = w32_dmn_handle_from_entity(thread); + e->arch = thread->arch; + e->code = evt.dwThreadId; + e->string = thread_name; + e->stack_pointer = stack_base_vaddr; + e->tls_root_vaddr = tls_root_vaddr; } }break; @@ -3207,78 +3263,6 @@ dmn_process_write(DMN_Handle process, Rng1U64 range, void *src) //- rjf: threads -internal Arch -dmn_arch_from_thread(DMN_Handle handle) -{ - Arch arch = Arch_Null; - DMN_AccessScope - { - W32_DMN_Entity *entity = w32_dmn_entity_from_handle(handle); - arch = entity->arch; - } - return arch; -} - -internal U64 -dmn_stack_base_vaddr_from_thread(DMN_Handle handle) -{ - U64 result = 0; - DMN_AccessScope - { - W32_DMN_Entity *thread = w32_dmn_entity_from_handle(handle); - if(thread->kind == W32_DMN_EntityKind_Thread) - { - W32_DMN_Entity *process = thread->parent; - U64 tlb = thread->thread.thread_local_base; - switch(thread->arch) - { - case Arch_Null: - case Arch_COUNT: - {}break; - case Arch_arm64: - case Arch_arm32: - case Arch_x86: - {NotImplemented;}break; - case Arch_x64: - { - U64 stack_base_addr = tlb + 0x8; - w32_dmn_process_read(process->handle, r1u64(stack_base_addr, stack_base_addr+8), &result); - }break; - } - } - } - return result; -} - -internal U64 -dmn_tls_root_vaddr_from_thread(DMN_Handle handle) -{ - U64 result = 0; - DMN_AccessScope - { - W32_DMN_Entity *entity = w32_dmn_entity_from_handle(handle); - if(entity->kind == W32_DMN_EntityKind_Thread) - { - result = entity->thread.thread_local_base; - switch(entity->arch) - { - case Arch_Null: - case Arch_COUNT: - {}break; - case Arch_arm64: - case Arch_arm32: - case Arch_x86: - {NotImplemented;}break; - case Arch_x64: - { - result += 88; - }break; - } - } - } - return result; -} - internal B32 dmn_thread_read_reg_block(DMN_Handle handle, void *reg_block) { @@ -3303,6 +3287,31 @@ dmn_thread_write_reg_block(DMN_Handle handle, void *reg_block) return result; } +internal B32 +dmn_thread_get_module_tls_vaddr(DMN_Handle thread_handle, DMN_Handle module_handle, U64 *vaddr_out) +{ + B32 result = 0; + DMN_AccessScope + { + W32_DMN_Entity *thread = w32_dmn_entity_from_handle(thread_handle); + W32_DMN_Entity *process = thread->parent; + W32_DMN_Entity *module = w32_dmn_entity_from_handle(module_handle); + Arch arch = thread->arch; + U64 addr_size = byte_size_from_arch(arch); + U64 tls_vaddr = w32_dmn_thread_tls_root_vaddr_from_tlb_vaddr(arch, thread->thread.thread_local_base); + U64 tls_addr_array_vaddr = 0; + if(w32_dmn_process_read_struct(process->handle, tls_vaddr, &tls_addr_array_vaddr) == addr_size) + { + U64 tls_ptr_vaddr = tls_addr_array_vaddr + module->module.tls_index*addr_size; + if(w32_dmn_process_read_struct(process->handle, tls_ptr_vaddr, vaddr_out) == addr_size) + { + result = 1; + } + } + } + return result; +} + //- rjf: system process listing internal void diff --git a/src/win32/demon/win32_demon.h b/src/win32/demon/win32_demon.h index dd16020f..5b45f2ce 100644 --- a/src/win32/demon/win32_demon.h +++ b/src/win32/demon/win32_demon.h @@ -205,6 +205,7 @@ struct W32_DMN_Entity { Rng1U64 vaddr_range; U64 address_of_name_pointer; + U64 tls_index; B32 is_main; B32 name_is_unicode; } @@ -349,6 +350,8 @@ internal DMN_ModuleInfo *w32_dmn_module_info_from_process_module(Arena *arena, H //- rjf: threads internal B32 w32_dmn_thread_read_reg_block(Arch arch, HANDLE thread, void *reg_block); internal B32 w32_dmn_thread_write_reg_block(Arch arch, HANDLE thread, void *reg_block); +internal U64 w32_dmn_thread_stack_base_vaddr_from_tlb_vaddr(HANDLE process, Arch arch, U64 tlb_vaddr); +internal U64 w32_dmn_thread_tls_root_vaddr_from_tlb_vaddr(Arch arch, U64 tlb_vaddr); //- rjf: remote thread injection internal DWORD w32_dmn_inject_thread(HANDLE process, U64 start_address); diff --git a/src/win32/window_manager/win32_window_manager.c b/src/win32/window_manager/win32_window_manager.c index 00886719..7b209cd5 100644 --- a/src/win32/window_manager/win32_window_manager.c +++ b/src/win32/window_manager/win32_window_manager.c @@ -815,7 +815,7 @@ w32_wm_monitor_gather_enum_proc(HMONITOR monitor, HDC hdc, LPRECT rect, LPARAM b } //////////////////////////////// -//~ rjf: @os_hooks Main Initialization API (Implemented Per-OS) +//~ rjf: @per_os_impl Main Initialization API (Implemented Per-OS) internal void wm_init(void) @@ -995,7 +995,7 @@ wm_init(void) } //////////////////////////////// -//~ rjf: @os_hooks Graphics System Info (Implemented Per-OS) +//~ rjf: @per_os_impl Graphics System Info (Implemented Per-OS) internal WM_SystemInfo * wm_get_system_info(void) @@ -1004,7 +1004,7 @@ wm_get_system_info(void) } //////////////////////////////// -//~ rjf: @os_hooks Clipboards (Implemented Per-OS) +//~ rjf: @per_os_impl Clipboards (Implemented Per-OS) internal void wm_set_clipboard_text(String8 string) @@ -1053,7 +1053,7 @@ wm_get_clipboard_text(Arena *arena) } //////////////////////////////// -//~ rjf: @os_hooks Windows (Implemented Per-OS) +//~ rjf: @per_os_impl Windows (Implemented Per-OS) internal WM_Window wm_window_open(Rng2F32 rect, WM_WindowFlags flags, String8 title) @@ -1385,7 +1385,7 @@ wm_dpi_from_window(WM_Window handle) } //////////////////////////////// -//~ rjf: @os_hooks External Windows (Implemented Per-OS) +//~ rjf: @per_os_impl External Windows (Implemented Per-OS) internal WM_ExtWindow wm_focused_external_window(void) @@ -1407,7 +1407,7 @@ wm_focus_external_window(WM_ExtWindow handle) } //////////////////////////////// -//~ rjf: @os_hooks Monitors (Implemented Per-OS) +//~ rjf: @per_os_impl Monitors (Implemented Per-OS) internal WM_MonitorArray wm_push_monitors_array(Arena *arena) @@ -1494,7 +1494,7 @@ wm_dpi_from_monitor(WM_Monitor monitor) } //////////////////////////////// -//~ rjf: @os_hooks Events (Implemented Per-OS) +//~ rjf: @per_os_impl Events (Implemented Per-OS) internal void wm_send_wakeup_event(void) @@ -1573,7 +1573,7 @@ wm_mouse_from_window(WM_Window handle) } //////////////////////////////// -//~ rjf: @os_hooks Cursors (Implemented Per-OS) +//~ rjf: @per_os_impl Cursors (Implemented Per-OS) internal void wm_set_cursor(WM_Cursor cursor) @@ -1615,7 +1615,7 @@ hcursor = curs; }break; } //////////////////////////////// -//~ rjf: @os_hooks Native User-Facing Graphical Messages (Implemented Per-OS) +//~ rjf: @per_os_impl Native User-Facing Graphical Messages (Implemented Per-OS) internal void wm_graphical_message(B32 error, String8 title, String8 message) @@ -1651,7 +1651,7 @@ wm_graphical_pick_file(Arena *arena, String8 initial_path) } //////////////////////////////// -//~ rjf: @os_hooks Shell Operations +//~ rjf: @per_os_impl Shell Operations internal void wm_show_in_filesystem_ui(String8 path) diff --git a/src/window_manager/window_manager.h b/src/window_manager/window_manager.h index c17f9c7c..b663d1ec 100644 --- a/src/window_manager/window_manager.h +++ b/src/window_manager/window_manager.h @@ -161,23 +161,23 @@ internal void wm_event_list_concat_in_place(WM_EventList *dst, WM_EventList *to_ internal WM_Event *wm_event_list_push_new(Arena *arena, WM_EventList *evts, WM_EventKind kind); //////////////////////////////// -//~ rjf: @os_hooks Main Initialization API (Implemented Per-OS) +//~ rjf: @per_os_impl Main Initialization API (Implemented Per-OS) internal void wm_init(void); //////////////////////////////// -//~ rjf: @os_hooks Graphics System Info (Implemented Per-OS) +//~ rjf: @per_os_impl Graphics System Info (Implemented Per-OS) internal WM_SystemInfo *wm_get_system_info(void); //////////////////////////////// -//~ rjf: @os_hooks Clipboards (Implemented Per-OS) +//~ rjf: @per_os_impl Clipboards (Implemented Per-OS) internal void wm_set_clipboard_text(String8 string); internal String8 wm_get_clipboard_text(Arena *arena); //////////////////////////////// -//~ rjf: @os_hooks Windows (Implemented Per-OS) +//~ rjf: @per_os_impl Windows (Implemented Per-OS) internal WM_Window wm_window_open(Rng2F32 rect, WM_WindowFlags flags, String8 title); internal void wm_window_close(WM_Window window); @@ -202,13 +202,13 @@ internal Rng2F32 wm_client_rect_from_window(WM_Window window); internal F32 wm_dpi_from_window(WM_Window window); //////////////////////////////// -//~ rjf: @os_hooks External Windows (Implemented Per-OS) +//~ rjf: @per_os_impl External Windows (Implemented Per-OS) internal WM_ExtWindow wm_focused_external_window(void); internal void wm_focus_external_window(WM_ExtWindow ext_window); //////////////////////////////// -//~ rjf: @os_hooks Monitors (Implemented Per-OS) +//~ rjf: @per_os_impl Monitors (Implemented Per-OS) internal WM_MonitorArray wm_push_monitors_array(Arena *arena); internal WM_Monitor wm_primary_monitor(void); @@ -218,7 +218,7 @@ internal Vec2F32 wm_dim_from_monitor(WM_Monitor monitor); internal F32 wm_dpi_from_monitor(WM_Monitor monitor); //////////////////////////////// -//~ rjf: @os_hooks Events (Implemented Per-OS) +//~ rjf: @per_os_impl Events (Implemented Per-OS) internal void wm_send_wakeup_event(void); internal WM_EventList wm_get_events(Arena *arena, B32 wait); @@ -227,18 +227,18 @@ internal B32 wm_key_is_down(WM_Key key); internal Vec2F32 wm_mouse_from_window(WM_Window window); //////////////////////////////// -//~ rjf: @os_hooks Cursors (Implemented Per-OS) +//~ rjf: @per_os_impl Cursors (Implemented Per-OS) internal void wm_set_cursor(WM_Cursor cursor); //////////////////////////////// -//~ rjf: @os_hooks Native User-Facing Graphical Messages (Implemented Per-OS) +//~ rjf: @per_os_impl Native User-Facing Graphical Messages (Implemented Per-OS) internal void wm_graphical_message(B32 error, String8 title, String8 message); internal String8 wm_graphical_pick_file(Arena *arena, String8 initial_path); //////////////////////////////// -//~ rjf: @os_hooks Shell Operations +//~ rjf: @per_os_impl Shell Operations internal void wm_show_in_filesystem_ui(String8 path); internal void wm_open_in_browser(String8 url); diff --git a/src/window_manager/window_manager_stub.c b/src/window_manager/window_manager_stub.c index 060cd161..0bb99c47 100644 --- a/src/window_manager/window_manager_stub.c +++ b/src/window_manager/window_manager_stub.c @@ -1,5 +1,5 @@ //////////////////////////////// -//~ rjf: @os_hooks Main Initialization API (Implemented Per-OS) +//~ rjf: @per_os_impl Main Initialization API (Implemented Per-OS) internal void wm_init(void) @@ -7,7 +7,7 @@ wm_init(void) } //////////////////////////////// -//~ rjf: @os_hooks Graphics System Info (Implemented Per-OS) +//~ rjf: @per_os_impl Graphics System Info (Implemented Per-OS) internal WM_SystemInfo * wm_get_system_info(void) @@ -18,7 +18,7 @@ wm_get_system_info(void) } //////////////////////////////// -//~ rjf: @os_hooks Clipboards (Implemented Per-OS) +//~ rjf: @per_os_impl Clipboards (Implemented Per-OS) internal void wm_set_clipboard_text(String8 string) @@ -32,7 +32,7 @@ wm_get_clipboard_text(Arena *arena) } //////////////////////////////// -//~ rjf: @os_hooks Windows (Implemented Per-OS) +//~ rjf: @per_os_impl Windows (Implemented Per-OS) internal WM_Window wm_window_open(Rng2F32 rect, WM_WindowFlags flags, String8 title) @@ -151,7 +151,7 @@ wm_dpi_from_window(WM_Window window) } //////////////////////////////// -//~ rjf: @os_hooks External Windows (Implemented Per-OS) +//~ rjf: @per_os_impl External Windows (Implemented Per-OS) internal WM_ExtWindow wm_focused_external_window(void) @@ -166,7 +166,7 @@ wm_focus_external_window(WM_ExtWindow handle) } //////////////////////////////// -//~ rjf: @os_hooks Monitors (Implemented Per-OS) +//~ rjf: @per_os_impl Monitors (Implemented Per-OS) internal WM_MonitorArray wm_push_monitors_array(Arena *arena) @@ -209,7 +209,7 @@ wm_dpi_from_monitor(WM_Monitor monitor) } //////////////////////////////// -//~ rjf: @os_hooks Events (Implemented Per-OS) +//~ rjf: @per_os_impl Events (Implemented Per-OS) internal void wm_send_wakeup_event(void) @@ -243,7 +243,7 @@ wm_mouse_from_window(WM_Window window) } //////////////////////////////// -//~ rjf: @os_hooks Cursors (Implemented Per-OS) +//~ rjf: @per_os_impl Cursors (Implemented Per-OS) internal void wm_set_cursor(WM_Cursor cursor) @@ -251,7 +251,7 @@ wm_set_cursor(WM_Cursor cursor) } //////////////////////////////// -//~ rjf: @os_hooks Native User-Facing Graphical Messages (Implemented Per-OS) +//~ rjf: @per_os_impl Native User-Facing Graphical Messages (Implemented Per-OS) internal void wm_graphical_message(B32 error, String8 title, String8 message) @@ -265,7 +265,7 @@ wm_graphical_pick_file(Arena *arena, String8 initial_path) } //////////////////////////////// -//~ rjf: @os_hooks Shell Operations +//~ rjf: @per_os_impl Shell Operations internal void wm_show_in_filesystem_ui(String8 path)