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.

This commit is contained in:
Ryan Fleury
2026-06-02 12:11:13 -07:00
parent 541d3afefa
commit d73ce364b9
88 changed files with 9899 additions and 4723 deletions
+1 -1
View File
@@ -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, },
+12 -1
View File
@@ -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"
}
+1 -1
View File
@@ -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;
{
+1 -1
View File
@@ -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);
+46 -1
View File
@@ -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
//
+18 -1
View File
@@ -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
+1
View File
@@ -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"
+1
View File
@@ -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"
-6
View File
@@ -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
+36
View File
@@ -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;
}
+38
View File
@@ -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
+7 -1
View File
@@ -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);
+72 -27
View File
@@ -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
+3 -3
View File
@@ -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
+3 -9
View File
@@ -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();
+1
View File
@@ -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;
};
+325 -121
View File
@@ -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;
+39 -31
View File
@@ -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
+4 -3
View File
@@ -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);
+448 -764
View File
File diff suppressed because it is too large Load Diff
+54 -1798
View File
File diff suppressed because it is too large Load Diff
+1338 -16
View File
File diff suppressed because it is too large Load Diff
-62
View File
@@ -1,62 +0,0 @@
// 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 = &section_table[idx];
String8 name = coff_name_from_section_header(string_table, header);
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;
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 (U64 i = 0; i < section_count; ++i) {
COFF_SectionHeader *header = &section_table[i];
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);
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;
}
+4 -4
View File
@@ -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);
+718
View File
@@ -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, &reg_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, &reg_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, &reg_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, &reg_idx);
off += str8_deserial_read_sleb128(data, off, &reg_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, &reg_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;
}
+17
View File
@@ -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
-108
View File
@@ -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;
}
-3
View File
@@ -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
+393
View File
@@ -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;
}
+66
View File
@@ -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
-66
View File
@@ -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;
}
-18
View File
@@ -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
+16 -12
View File
@@ -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
+16 -12
View File
@@ -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
-42
View File
@@ -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.
-------------------------------------------------------------------------------
+3 -11
View File
@@ -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;
-18
View File
@@ -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);
+611 -122
View File
File diff suppressed because it is too large Load Diff
+172 -7
View File
@@ -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
+45
View File
@@ -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 = &section_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 = &section_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;
}
@@ -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
@@ -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;
}
@@ -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
+2
View File
@@ -0,0 +1,2 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
+44
View File
@@ -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
+231 -1
View File
@@ -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;
}
+20
View File
@@ -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
-141
View File
@@ -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;
}
-470
View File
@@ -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;
}
+69
View File
@@ -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
File diff suppressed because it is too large Load Diff
+82 -2
View File
@@ -1,6 +1,8 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#if 0
internal U64
t_dw_test_uleb128(U64 v, U64 expected_length)
{
@@ -63,6 +65,85 @@ Test(test_leb128)
}
}
internal B32
dw_test__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 B32
dwt_tags_must_match(DW_WriterTag *writer_tag, DW_TagNode *reader_tag)
{
@@ -82,7 +163,7 @@ dwt_tags_must_match(DW_WriterTag *writer_tag, DW_TagNode *reader_tag)
while (writer_attrib) {
if (writer_attrib->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
+650
View File
@@ -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;
}
@@ -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);
+152
View File
@@ -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;
}
@@ -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,
+696
View File
@@ -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, &reg);
off += str8_deserial_read_uleb128(inst_data, off, &reg_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, &reg);
off += str8_deserial_read_sleb128(inst_data, off, &reg_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, &reg);
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, &reg);
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, &reg);
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, &reg);
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, &reg);
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, &reg);
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, &reg);
off += str8_deserial_read_uleb128(inst_data, off, &reg_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, &reg);
off += str8_deserial_read_sleb128(inst_data, off, &reg_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, &reg);
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, &reg_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, &reg_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, &reg);
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, &reg_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, &reg_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;
}
+22
View File
@@ -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
+1 -1
View File
@@ -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)
+61 -41
View File
@@ -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
+2
View File
@@ -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
+201 -201
View File
@@ -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)
+17 -1
View File
@@ -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);
+936
View File
@@ -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 *)&regs + 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 *)&regs + 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;
}
+28
View File
@@ -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
+7 -4
View File
@@ -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();
+14 -2
View File
@@ -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"
+7 -4
View File
@@ -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);
+6
View File
@@ -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"
+2 -2
View File
@@ -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;
}
+1
View File
@@ -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
+7 -7
View File
@@ -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);
}
+106 -390
View File
@@ -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<<DW_AttribClass_Address))
if(dw_attrib_class_from_form_kind(unit_parse_ctx->version, unit_parse_ctx->exts, hipc_attrib->val.kind) & (1<<DW_AttribClass_Address))
{
voff_opl = voff_base + hipc_attrib->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<<DW_AttribClass_Address))
if(dw_attrib_class_from_form_kind(unit_parse_ctx->version, unit_parse_ctx->exts, hipc_attrib->val.kind) & (1<<DW_AttribClass_Address))
{
voff_opl = voff_base + hipc_attrib->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)
{
+29 -27
View File
@@ -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
+6 -2
View File
@@ -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"
+5
View File
@@ -0,0 +1,5 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
//- GENERATED CODE
+21
View File
@@ -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
+24
View File
@@ -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;
}
+45
View File
@@ -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
+21
View File
@@ -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)`
}
+10
View File
@@ -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
+15
View File
@@ -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
+2 -2
View File
@@ -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)
+91 -82
View File
@@ -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
+3
View File
@@ -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);
+10 -10
View File
@@ -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)
+10 -10
View File
@@ -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);
+10 -10
View File
@@ -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)