mirror of
https://github.com/Ed94/raddebugger.git
synced 2026-08-14 17:28:07 +00:00
WIP add support for GNU LibC TLS model
Unlike on Windows, TLS index isn't part of the module format. glibc stores it in the opaque link_map struct. There are many different heuristics a debugger can use to locate it but most of them are unreliable and prone to errors, or don't work when debugger attaches to a process. We adopt the newer approach of looking for special symbols in glibc that provide TLS index offset. These symbols were added in 2021, so programs linked with older glibc won't have TLS support.
This commit is contained in:
+31
-57
@@ -60,6 +60,20 @@ ctrl_exception_kind_from_dmn(DMN_ExceptionKind kind)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal CTRL_TlsModel
|
||||||
|
ctrl_dynamic_linker_type_from_dmn(DMN_TlsModel type)
|
||||||
|
{
|
||||||
|
CTRL_TlsModel result = CTRL_TlsModel_Null;
|
||||||
|
switch(type)
|
||||||
|
{
|
||||||
|
default:{}break;
|
||||||
|
case DMN_TlsModel_Null: {result = CTRL_TlsModel_Null;}break;
|
||||||
|
case DMN_TlsModel_WinodwsNt: {result = CTRL_TlsModel_WinodwsNt;}break;
|
||||||
|
case DMN_TlsModel_Gnu: {result = CTRL_TlsModel_Gnu;}break;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
internal String8
|
internal String8
|
||||||
ctrl_string_from_event_kind(CTRL_EventKind kind)
|
ctrl_string_from_event_kind(CTRL_EventKind kind)
|
||||||
{
|
{
|
||||||
@@ -616,6 +630,9 @@ ctrl_serialized_string_from_event(Arena *arena, CTRL_Event *event, U64 max)
|
|||||||
str8_serial_push_struct(scratch.arena, &srl, &event->exception_code);
|
str8_serial_push_struct(scratch.arena, &srl, &event->exception_code);
|
||||||
str8_serial_push_struct(scratch.arena, &srl, &event->rgba);
|
str8_serial_push_struct(scratch.arena, &srl, &event->rgba);
|
||||||
str8_serial_push_struct(scratch.arena, &srl, &event->bp_flags);
|
str8_serial_push_struct(scratch.arena, &srl, &event->bp_flags);
|
||||||
|
str8_serial_push_struct(scratch.arena, &srl, &event->tls_model);
|
||||||
|
str8_serial_push_struct(scratch.arena, &srl, &event->tls_index);
|
||||||
|
str8_serial_push_struct(scratch.arena, &srl, &event->tls_offset);
|
||||||
String8 string = event->string;
|
String8 string = event->string;
|
||||||
string.size = Min(string.size, max-srl.total_size);
|
string.size = Min(string.size, max-srl.total_size);
|
||||||
str8_serial_push_struct(scratch.arena, &srl, &string.size);
|
str8_serial_push_struct(scratch.arena, &srl, &string.size);
|
||||||
@@ -649,6 +666,9 @@ ctrl_event_from_serialized_string(Arena *arena, String8 string)
|
|||||||
read_off += str8_deserial_read_struct(string, read_off, &event.exception_code);
|
read_off += str8_deserial_read_struct(string, read_off, &event.exception_code);
|
||||||
read_off += str8_deserial_read_struct(string, read_off, &event.rgba);
|
read_off += str8_deserial_read_struct(string, read_off, &event.rgba);
|
||||||
read_off += str8_deserial_read_struct(string, read_off, &event.bp_flags);
|
read_off += str8_deserial_read_struct(string, read_off, &event.bp_flags);
|
||||||
|
read_off += str8_deserial_read_struct(string, read_off, &event.tls_model);
|
||||||
|
read_off += str8_deserial_read_struct(string, read_off, &event.tls_index);
|
||||||
|
read_off += str8_deserial_read_struct(string, read_off, &event.tls_offset);
|
||||||
read_off += str8_deserial_read_struct(string, read_off, &event.string.size);
|
read_off += str8_deserial_read_struct(string, read_off, &event.string.size);
|
||||||
event.string.str = push_array_no_zero(arena, U8, event.string.size);
|
event.string.str = push_array_no_zero(arena, U8, event.string.size);
|
||||||
read_off += str8_deserial_read(string, read_off, event.string.str, event.string.size, 1);
|
read_off += str8_deserial_read(string, read_off, event.string.str, event.string.size, 1);
|
||||||
@@ -1249,6 +1269,7 @@ ctrl_entity_store_apply_events(CTRL_EntityCtxRWStore *store, CTRL_EventList *lis
|
|||||||
{
|
{
|
||||||
CTRL_Entity *machine = ctrl_entity_from_handle(&store->ctx, ctrl_handle_make(event->entity.machine_id, dmn_handle_zero()));
|
CTRL_Entity *machine = ctrl_entity_from_handle(&store->ctx, ctrl_handle_make(event->entity.machine_id, dmn_handle_zero()));
|
||||||
CTRL_Entity *process = ctrl_entity_alloc(store, machine, CTRL_EntityKind_Process, event->arch, event->entity, (U64)event->entity_id);
|
CTRL_Entity *process = ctrl_entity_alloc(store, machine, CTRL_EntityKind_Process, event->arch, event->entity, (U64)event->entity_id);
|
||||||
|
process->tls_model = event->tls_model;
|
||||||
}break;
|
}break;
|
||||||
case CTRL_EventKind_EndProc:
|
case CTRL_EventKind_EndProc:
|
||||||
{
|
{
|
||||||
@@ -1370,6 +1391,8 @@ ctrl_entity_store_apply_events(CTRL_EntityCtxRWStore *store, CTRL_EventList *lis
|
|||||||
ctrl_entity_equip_string(store, module, event->string);
|
ctrl_entity_equip_string(store, module, event->string);
|
||||||
module->timestamp = event->timestamp;
|
module->timestamp = event->timestamp;
|
||||||
module->vaddr_range = event->vaddr_rng;
|
module->vaddr_range = event->vaddr_rng;
|
||||||
|
module->tls_index = event->tls_index;
|
||||||
|
module->tls_offset = event->tls_offset;
|
||||||
CTRL_Entity *first_module = ctrl_entity_child_from_kind(process, CTRL_EntityKind_Module);
|
CTRL_Entity *first_module = ctrl_entity_child_from_kind(process, CTRL_EntityKind_Module);
|
||||||
if(first_module == module)
|
if(first_module == module)
|
||||||
{
|
{
|
||||||
@@ -1723,26 +1746,6 @@ ctrl_entry_point_voff_from_module(CTRL_Handle module_handle)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal Rng1U64
|
|
||||||
ctrl_tls_vaddr_range_from_module(CTRL_Handle module_handle)
|
|
||||||
{
|
|
||||||
Rng1U64 result = {0};
|
|
||||||
U64 hash = ctrl_hash_from_handle(module_handle);
|
|
||||||
U64 slot_idx = hash%ctrl_state->module_image_info_cache.slots_count;
|
|
||||||
U64 stripe_idx = slot_idx%ctrl_state->module_image_info_cache.stripes_count;
|
|
||||||
CTRL_ModuleImageInfoCacheSlot *slot = &ctrl_state->module_image_info_cache.slots[slot_idx];
|
|
||||||
CTRL_ModuleImageInfoCacheStripe *stripe = &ctrl_state->module_image_info_cache.stripes[stripe_idx];
|
|
||||||
MutexScopeR(stripe->rw_mutex) for(CTRL_ModuleImageInfoCacheNode *n = slot->first; n != 0; n = n->next)
|
|
||||||
{
|
|
||||||
if(ctrl_handle_match(n->module, module_handle))
|
|
||||||
{
|
|
||||||
result = n->tls_vaddr_range;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal String8
|
internal String8
|
||||||
ctrl_initial_debug_info_path_from_module(Arena *arena, CTRL_Handle module_handle)
|
ctrl_initial_debug_info_path_from_module(Arena *arena, CTRL_Handle module_handle)
|
||||||
{
|
{
|
||||||
@@ -3734,7 +3737,6 @@ ctrl_thread__module_open(CTRL_Handle process, CTRL_Handle module, Rng1U64 vaddr_
|
|||||||
|
|
||||||
Arena *arena = arena_alloc();
|
Arena *arena = arena_alloc();
|
||||||
U64 entry_point_voff = 0;
|
U64 entry_point_voff = 0;
|
||||||
Rng1U64 tls_vaddr_range = {0};
|
|
||||||
U32 rdi_dbg_time = 0;
|
U32 rdi_dbg_time = 0;
|
||||||
Guid rdi_dbg_guid = {0};
|
Guid rdi_dbg_guid = {0};
|
||||||
String8 exe_dbg_path = {0};
|
String8 exe_dbg_path = {0};
|
||||||
@@ -3855,34 +3857,6 @@ ctrl_thread__module_open(CTRL_Handle process, CTRL_Handle module, Rng1U64 vaddr_
|
|||||||
dmn_process_read(process.dmn_handle, r1u64(vaddr_range.min + pdatas_voff_range.min, vaddr_range.min + pdatas_voff_range.max), pdatas);
|
dmn_process_read(process.dmn_handle, r1u64(vaddr_range.min + pdatas_voff_range.min, vaddr_range.min + pdatas_voff_range.max), pdatas);
|
||||||
}
|
}
|
||||||
|
|
||||||
// rjf: extract tls header
|
|
||||||
PE_TLSHeader64 tls_header = {0};
|
|
||||||
if(data_dir_count > PE_DataDirectoryIndex_TLS)
|
|
||||||
{
|
|
||||||
PE_DataDirectory dir = {0};
|
|
||||||
dmn_process_read_struct(process.dmn_handle, vaddr_range.min + opt_ext_off_range.min + reported_data_dir_offset + sizeof(PE_DataDirectory)*PE_DataDirectoryIndex_TLS, &dir);
|
|
||||||
Rng1U64 tls_voff_range = r1u64((U64)dir.virt_off, (U64)dir.virt_off + (U64)dir.virt_size);
|
|
||||||
switch(file_header.machine)
|
|
||||||
{
|
|
||||||
default:{}break;
|
|
||||||
case COFF_MachineType_X86:
|
|
||||||
{
|
|
||||||
PE_TLSHeader32 tls_header32 = {0};
|
|
||||||
dmn_process_read_struct(process.dmn_handle, vaddr_range.min + tls_voff_range.min, &tls_header32);
|
|
||||||
tls_header.raw_data_start = (U64)tls_header32.raw_data_start;
|
|
||||||
tls_header.raw_data_end = (U64)tls_header32.raw_data_end;
|
|
||||||
tls_header.index_address = (U64)tls_header32.index_address;
|
|
||||||
tls_header.callbacks_address = (U64)tls_header32.callbacks_address;
|
|
||||||
tls_header.zero_fill_size = (U64)tls_header32.zero_fill_size;
|
|
||||||
tls_header.characteristics = (U64)tls_header32.characteristics;
|
|
||||||
}break;
|
|
||||||
case COFF_MachineType_X64:
|
|
||||||
{
|
|
||||||
dmn_process_read_struct(process.dmn_handle, vaddr_range.min + tls_voff_range.min, &tls_header);
|
|
||||||
}break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// rjf: extract sections
|
// rjf: extract sections
|
||||||
U64 sec_array_off = opt_ext_off_range.max;
|
U64 sec_array_off = opt_ext_off_range.max;
|
||||||
U64 sec_count = file_header.section_count;
|
U64 sec_count = file_header.section_count;
|
||||||
@@ -3892,9 +3866,6 @@ ctrl_thread__module_open(CTRL_Handle process, CTRL_Handle module, Rng1U64 vaddr_
|
|||||||
// rjf: grab entry point vaddr
|
// rjf: grab entry point vaddr
|
||||||
entry_point_voff = entry_point;
|
entry_point_voff = entry_point;
|
||||||
|
|
||||||
// rjf: calculate TLS vaddr range
|
|
||||||
tls_vaddr_range = r1u64(tls_header.index_address, tls_header.index_address+sizeof(U32));
|
|
||||||
|
|
||||||
// rjf: grab data about debug info
|
// rjf: grab data about debug info
|
||||||
if(data_dir_count > PE_DataDirectoryIndex_DEBUG)
|
if(data_dir_count > PE_DataDirectoryIndex_DEBUG)
|
||||||
{
|
{
|
||||||
@@ -4469,11 +4440,12 @@ ctrl_thread__next_dmn_event(Arena *arena, DMN_CtrlCtx *ctrl_ctx, CTRL_Msg *msg,
|
|||||||
case DMN_EventKind_CreateProcess:
|
case DMN_EventKind_CreateProcess:
|
||||||
{
|
{
|
||||||
CTRL_Event *out_evt = ctrl_event_list_push(scratch.arena, &evts);
|
CTRL_Event *out_evt = ctrl_event_list_push(scratch.arena, &evts);
|
||||||
out_evt->kind = CTRL_EventKind_NewProc;
|
out_evt->kind = CTRL_EventKind_NewProc;
|
||||||
out_evt->msg_id = msg->msg_id;
|
out_evt->msg_id = msg->msg_id;
|
||||||
out_evt->entity = ctrl_handle_make(CTRL_MachineID_Local, event->process);
|
out_evt->entity = ctrl_handle_make(CTRL_MachineID_Local, event->process);
|
||||||
out_evt->arch = event->arch;
|
out_evt->arch = event->arch;
|
||||||
out_evt->entity_id = event->code;
|
out_evt->entity_id = event->code;
|
||||||
|
out_evt->tls_model = ctrl_dynamic_linker_type_from_dmn(event->tls_model);
|
||||||
ctrl_state->process_counter += 1;
|
ctrl_state->process_counter += 1;
|
||||||
}break;
|
}break;
|
||||||
case DMN_EventKind_CreateThread:
|
case DMN_EventKind_CreateThread:
|
||||||
@@ -4508,6 +4480,8 @@ ctrl_thread__next_dmn_event(Arena *arena, DMN_CtrlCtx *ctrl_ctx, CTRL_Msg *msg,
|
|||||||
out_evt1->rip_vaddr = event->address;
|
out_evt1->rip_vaddr = event->address;
|
||||||
out_evt1->timestamp = exe_timestamp;
|
out_evt1->timestamp = exe_timestamp;
|
||||||
out_evt1->string = module_path;
|
out_evt1->string = module_path;
|
||||||
|
out_evt1->tls_index = event->tls_index;
|
||||||
|
out_evt1->tls_offset = event->tls_offset;
|
||||||
CTRL_Event *out_evt2 = ctrl_event_list_push(scratch.arena, &evts);
|
CTRL_Event *out_evt2 = ctrl_event_list_push(scratch.arena, &evts);
|
||||||
String8 initial_debug_info_path = ctrl_initial_debug_info_path_from_module(scratch.arena, module_handle);
|
String8 initial_debug_info_path = ctrl_initial_debug_info_path_from_module(scratch.arena, module_handle);
|
||||||
U64 debug_info_timestamp = os_properties_from_file_path(initial_debug_info_path).modified;
|
U64 debug_info_timestamp = os_properties_from_file_path(initial_debug_info_path).modified;
|
||||||
|
|||||||
+19
-3
@@ -59,6 +59,17 @@ struct CTRL_UserBreakpointList
|
|||||||
U64 count;
|
U64 count;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
//~ Dynamic Linker Types
|
||||||
|
|
||||||
|
typedef U32 CTRL_TlsModel;
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
CTRL_TlsModel_Null,
|
||||||
|
CTRL_TlsModel_WinodwsNt,
|
||||||
|
CTRL_TlsModel_Gnu
|
||||||
|
};
|
||||||
|
|
||||||
////////////////////////////////
|
////////////////////////////////
|
||||||
//~ rjf: Entity Handle Types
|
//~ rjf: Entity Handle Types
|
||||||
|
|
||||||
@@ -119,6 +130,9 @@ struct CTRL_Entity
|
|||||||
U64 timestamp;
|
U64 timestamp;
|
||||||
CTRL_UserBreakpointFlags bp_flags;
|
CTRL_UserBreakpointFlags bp_flags;
|
||||||
String8 string;
|
String8 string;
|
||||||
|
CTRL_TlsModel tls_model;
|
||||||
|
U64 tls_index;
|
||||||
|
U64 tls_offset;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct CTRL_EntityNode CTRL_EntityNode;
|
typedef struct CTRL_EntityNode CTRL_EntityNode;
|
||||||
@@ -512,11 +526,14 @@ struct CTRL_Event
|
|||||||
U64 rip_vaddr;
|
U64 rip_vaddr;
|
||||||
U64 stack_base;
|
U64 stack_base;
|
||||||
U64 tls_root;
|
U64 tls_root;
|
||||||
|
U64 tls_index;
|
||||||
|
U64 tls_offset;
|
||||||
U64 timestamp;
|
U64 timestamp;
|
||||||
U32 exception_code;
|
U32 exception_code;
|
||||||
U32 rgba;
|
U32 rgba;
|
||||||
CTRL_UserBreakpointFlags bp_flags;
|
CTRL_UserBreakpointFlags bp_flags;
|
||||||
String8 string;
|
String8 string;
|
||||||
|
CTRL_TlsModel tls_model;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct CTRL_EventNode CTRL_EventNode;
|
typedef struct CTRL_EventNode CTRL_EventNode;
|
||||||
@@ -603,7 +620,6 @@ struct CTRL_ModuleImageInfoCacheNode
|
|||||||
EH_FrameHdr eh_frame_hdr;
|
EH_FrameHdr eh_frame_hdr;
|
||||||
EH_PtrCtx eh_ptr_ctx;
|
EH_PtrCtx eh_ptr_ctx;
|
||||||
U64 entry_point_voff;
|
U64 entry_point_voff;
|
||||||
Rng1U64 tls_vaddr_range;
|
|
||||||
String8 initial_debug_info_path;
|
String8 initial_debug_info_path;
|
||||||
Rng1U64 raddbg_section_voff_range;
|
Rng1U64 raddbg_section_voff_range;
|
||||||
String8 raddbg_data;
|
String8 raddbg_data;
|
||||||
@@ -776,6 +792,7 @@ internal U64 ctrl_hash_from_string(String8 string);
|
|||||||
internal U64 ctrl_hash_from_handle(CTRL_Handle handle);
|
internal U64 ctrl_hash_from_handle(CTRL_Handle handle);
|
||||||
internal CTRL_EventCause ctrl_event_cause_from_dmn_event_kind(DMN_EventKind event_kind);
|
internal CTRL_EventCause ctrl_event_cause_from_dmn_event_kind(DMN_EventKind event_kind);
|
||||||
internal CTRL_ExceptionKind ctrl_exception_kind_from_dmn(DMN_ExceptionKind kind);
|
internal CTRL_ExceptionKind ctrl_exception_kind_from_dmn(DMN_ExceptionKind kind);
|
||||||
|
internal CTRL_TlsModel ctrl_dynamic_linker_type_from_dmn(DMN_TlsModel type);
|
||||||
internal String8 ctrl_string_from_event_kind(CTRL_EventKind kind);
|
internal String8 ctrl_string_from_event_kind(CTRL_EventKind kind);
|
||||||
internal String8 ctrl_string_from_msg_kind(CTRL_MsgKind kind);
|
internal String8 ctrl_string_from_msg_kind(CTRL_MsgKind kind);
|
||||||
internal CTRL_EntityKind ctrl_entity_kind_from_string(String8 string);
|
internal CTRL_EntityKind ctrl_entity_kind_from_string(String8 string);
|
||||||
@@ -916,7 +933,6 @@ internal B32 ctrl_thread_write_reg_block(CTRL_Handle thread, void *block);
|
|||||||
//- rjf: cache lookups
|
//- rjf: cache lookups
|
||||||
internal PE_IntelPdata *ctrl_intel_pdata_from_module_voff(Arena *arena, CTRL_Handle module_handle, U64 voff);
|
internal PE_IntelPdata *ctrl_intel_pdata_from_module_voff(Arena *arena, CTRL_Handle module_handle, U64 voff);
|
||||||
internal U64 ctrl_entry_point_voff_from_module(CTRL_Handle module_handle);
|
internal U64 ctrl_entry_point_voff_from_module(CTRL_Handle module_handle);
|
||||||
internal Rng1U64 ctrl_tls_vaddr_range_from_module(CTRL_Handle module_handle);
|
|
||||||
internal String8 ctrl_initial_debug_info_path_from_module(Arena *arena, CTRL_Handle module_handle);
|
internal String8 ctrl_initial_debug_info_path_from_module(Arena *arena, CTRL_Handle module_handle);
|
||||||
internal String8 ctrl_raddbg_data_from_module(Arena *arena, CTRL_Handle module_handle);
|
internal String8 ctrl_raddbg_data_from_module(Arena *arena, CTRL_Handle module_handle);
|
||||||
|
|
||||||
@@ -993,7 +1009,7 @@ internal void ctrl_thread__module_close(CTRL_Handle process, CTRL_Handle module,
|
|||||||
//- rjf: attached process running/event gathering
|
//- rjf: attached process running/event gathering
|
||||||
internal DMN_Event *ctrl_thread__next_dmn_event(Arena *arena, DMN_CtrlCtx *ctrl_ctx, CTRL_Msg *msg, DMN_RunCtrls *run_ctrls, CTRL_Spoof *spoof);
|
internal DMN_Event *ctrl_thread__next_dmn_event(Arena *arena, DMN_CtrlCtx *ctrl_ctx, CTRL_Msg *msg, DMN_RunCtrls *run_ctrls, CTRL_Spoof *spoof);
|
||||||
|
|
||||||
//- rjf: eval helpers
|
//- rjf: eval helpers
|
||||||
internal U64 ctrl_eval_space_gen(E_Space space);
|
internal U64 ctrl_eval_space_gen(E_Space space);
|
||||||
internal B32 ctrl_eval_space_read(E_Space space, void *out, Rng1U64 vaddr_range);
|
internal B32 ctrl_eval_space_read(E_Space space, void *out, Rng1U64 vaddr_range);
|
||||||
|
|
||||||
|
|||||||
@@ -984,80 +984,68 @@ d_tls_base_vaddr_from_process_root_rip(CTRL_Entity *process, U64 root_vaddr, U64
|
|||||||
{
|
{
|
||||||
ProfBeginFunction();
|
ProfBeginFunction();
|
||||||
U64 base_vaddr = 0;
|
U64 base_vaddr = 0;
|
||||||
Temp scratch = scratch_begin(0, 0);
|
|
||||||
if(!d_ctrl_targets_running())
|
if(!d_ctrl_targets_running())
|
||||||
{
|
{
|
||||||
|
Temp scratch = scratch_begin(0, 0);
|
||||||
|
|
||||||
//- rjf: unpack module info
|
//- rjf: unpack module info
|
||||||
CTRL_Entity *module = ctrl_module_from_process_vaddr(process, rip_vaddr);
|
CTRL_Entity *module = ctrl_module_from_process_vaddr(process, rip_vaddr);
|
||||||
Rng1U64 tls_vaddr_range = ctrl_tls_vaddr_range_from_module(module->handle);
|
U64 addr_size = byte_size_from_arch(process->arch);
|
||||||
U64 addr_size = bit_size_from_arch(process->arch)/8;
|
|
||||||
|
switch(process->tls_model)
|
||||||
//- rjf: read module's TLS index
|
|
||||||
U64 tls_index = 0;
|
|
||||||
if(addr_size != 0)
|
|
||||||
{
|
{
|
||||||
CTRL_ProcessMemorySlice tls_index_slice = ctrl_process_memory_slice_from_vaddr_range(scratch.arena, process->handle, tls_vaddr_range, 0, 0);
|
case CTRL_TlsModel_Null: {}break;
|
||||||
if(tls_index_slice.data.size >= addr_size)
|
case CTRL_TlsModel_WinodwsNt:
|
||||||
{
|
{
|
||||||
tls_index = *(U64 *)tls_index_slice.data.str;
|
// read thread local base pointer out of TEB
|
||||||
}
|
U64 thread_local_base = root_vaddr;
|
||||||
}
|
U64 tls_addr_array = 0;
|
||||||
|
CTRL_ProcessMemorySlice tls_addr_array_slice = ctrl_process_memory_slice_from_vaddr_range(scratch.arena, process->handle, r1u64(thread_local_base, thread_local_base + addr_size), 0, 0);
|
||||||
//- rjf: PE path
|
String8 tls_array_vaddr_data = tls_addr_array_slice.data;
|
||||||
if(addr_size != 0)
|
if(tls_array_vaddr_data.size == addr_size)
|
||||||
{
|
|
||||||
U64 thread_info_addr = root_vaddr;
|
|
||||||
U64 tls_addr_off = tls_index*addr_size;
|
|
||||||
U64 tls_addr_array = 0;
|
|
||||||
CTRL_ProcessMemorySlice tls_addr_array_slice = ctrl_process_memory_slice_from_vaddr_range(scratch.arena, process->handle, r1u64(thread_info_addr, thread_info_addr+addr_size), 0, 0);
|
|
||||||
String8 tls_addr_array_data = tls_addr_array_slice.data;
|
|
||||||
if(tls_addr_array_data.size >= 8)
|
|
||||||
{
|
|
||||||
MemoryCopy(&tls_addr_array, tls_addr_array_data.str, sizeof(U64));
|
|
||||||
}
|
|
||||||
CTRL_ProcessMemorySlice result_slice = ctrl_process_memory_slice_from_vaddr_range(scratch.arena, process->handle, r1u64(tls_addr_array + tls_addr_off, tls_addr_array + tls_addr_off + addr_size), 0, 0);
|
|
||||||
String8 result_data = result_slice.data;
|
|
||||||
if(result_data.size >= 8)
|
|
||||||
{
|
|
||||||
MemoryCopy(&base_vaddr, result_data.str, sizeof(U64));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//- rjf: non-PE path (not implemented)
|
|
||||||
#if 0
|
|
||||||
if(!bin_is_pe)
|
|
||||||
{
|
|
||||||
// TODO(rjf): not supported. old code from the prototype that Nick had sketched out:
|
|
||||||
// TODO(nick): This code works only if the linked c runtime library is glibc.
|
|
||||||
// Implement CRT detection here.
|
|
||||||
|
|
||||||
U64 dtv_addr = UINT64_MAX;
|
|
||||||
demon_read_memory(process->demon_handle, &dtv_addr, thread_info_addr, addr_size);
|
|
||||||
|
|
||||||
/*
|
|
||||||
union delta_thread_vector
|
|
||||||
{
|
{
|
||||||
size_t counter;
|
U64 tls_array_vaddr = 0;
|
||||||
struct
|
MemoryCopyStr8(&tls_array_vaddr, tls_array_vaddr_data);
|
||||||
|
|
||||||
|
// read thread local storage pointer (array of TLS pointers, one per module)
|
||||||
|
U64 tls_ptr_vaddr = tls_array_vaddr + module->tls_index * addr_size;
|
||||||
|
CTRL_ProcessMemorySlice result_slice = ctrl_process_memory_slice_from_vaddr_range(scratch.arena, process->handle, r1u64(tls_ptr_vaddr, tls_ptr_vaddr + addr_size), 0, 0);
|
||||||
|
String8 result_data = result_slice.data;
|
||||||
|
if(result_data.size == addr_size)
|
||||||
{
|
{
|
||||||
void *value;
|
MemoryCopyStr8(&base_vaddr, result_data);
|
||||||
void *to_free;
|
}
|
||||||
} pointer;
|
}
|
||||||
};
|
}break;
|
||||||
*/
|
case CTRL_TlsModel_Gnu:
|
||||||
|
|
||||||
U64 dtv_size = 16;
|
|
||||||
U64 dtv_count = 0;
|
|
||||||
demon_read_memory(process->demon_handle, &dtv_count, dtv_addr - dtv_size, addr_size);
|
|
||||||
|
|
||||||
if (tls_index > 0 && tls_index < dtv_count)
|
|
||||||
{
|
{
|
||||||
demon_read_memory(process->demon_handle, &result, dtv_addr + dtv_size*tls_index, addr_size);
|
if(module->tls_index > 0) // zero is reserved for the generation counter
|
||||||
}
|
{
|
||||||
|
// read dynamic thread vector pointer (one per dynamic module)
|
||||||
|
U64 dtv_base = root_vaddr;
|
||||||
|
U64 dtv_size = addr_size * 2; // union dtv { size_t counter; struct dtv_pointer { void *val, *to_free; }; };
|
||||||
|
U64 dtv_pointer_vaddr = dtv_base + module->tls_index * dtv_size;
|
||||||
|
CTRL_ProcessMemorySlice dtv_pointer_slice = ctrl_process_memory_slice_from_vaddr_range(scratch.arena, process->handle, r1u64(dtv_pointer_vaddr, dtv_pointer_vaddr + dtv_size), 0, 0);
|
||||||
|
String8 dtv_pointer_data = dtv_pointer_slice.data;
|
||||||
|
if(dtv_pointer_data.size == dtv_size)
|
||||||
|
{
|
||||||
|
U64 dtv_pointer = 0;
|
||||||
|
MemoryCopyStr8(&dtv_pointer, dtv_pointer_data);
|
||||||
|
|
||||||
|
// verify that TLS block was allocated
|
||||||
|
U64 tls_dtv_unallocated = addr_size == 4 ? max_U32 : max_U64;
|
||||||
|
if(dtv_pointer != tls_dtv_unallocated)
|
||||||
|
{
|
||||||
|
base_vaddr = dtv_pointer + module->tls_offset; // add tls_offset because DT_NEEDED modules share TLS block with the main exe
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}break;
|
||||||
|
default: {InvalidPath;}break;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
scratch_end(scratch);
|
||||||
}
|
}
|
||||||
scratch_end(scratch);
|
|
||||||
ProfEnd();
|
ProfEnd();
|
||||||
return base_vaddr;
|
return base_vaddr;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,17 @@ struct DMN_CtrlCtx
|
|||||||
U64 u64[1];
|
U64 u64[1];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
//~ Dynamic Linker Types
|
||||||
|
|
||||||
|
typedef U32 DMN_TlsModel;
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
DMN_TlsModel_Null,
|
||||||
|
DMN_TlsModel_WinodwsNt,
|
||||||
|
DMN_TlsModel_Gnu
|
||||||
|
};
|
||||||
|
|
||||||
////////////////////////////////
|
////////////////////////////////
|
||||||
//~ rjf: Handle Types
|
//~ rjf: Handle Types
|
||||||
|
|
||||||
@@ -82,6 +93,9 @@ struct DMN_Event
|
|||||||
U64 stack_pointer;
|
U64 stack_pointer;
|
||||||
U64 user_data;
|
U64 user_data;
|
||||||
B32 exception_repeated;
|
B32 exception_repeated;
|
||||||
|
U64 tls_index;
|
||||||
|
U64 tls_offset;
|
||||||
|
DMN_TlsModel tls_model;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct DMN_EventNode DMN_EventNode;
|
typedef struct DMN_EventNode DMN_EventNode;
|
||||||
|
|||||||
@@ -1401,6 +1401,42 @@ dmn_lnx_handle_not_attached(Arena *arena, DMN_EventList *events)
|
|||||||
e->error_kind = DMN_ErrorKind_NotAttached;
|
e->error_kind = DMN_ErrorKind_NotAttached;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal DMN_LNX_Entity *
|
||||||
|
dmn_lnx_handle_create_thread(Arena *arena, DMN_EventList *events, DMN_LNX_Entity *process, pid_t tid)
|
||||||
|
{
|
||||||
|
DMN_LNX_Entity *thread = dmn_lnx_entity_alloc(process, DMN_LNX_EntityKind_Thread);
|
||||||
|
thread->id = tid;
|
||||||
|
thread->arch = process->arch;
|
||||||
|
thread->reg_block = push_array(process->arena, U8, regs_block_size_from_arch(process->arch));
|
||||||
|
thread->thread_state = DMN_LNX_ThreadState_Stopped;
|
||||||
|
thread->is_reg_block_dirty = !dmn_lnx_thread_read_reg_block(thread);
|
||||||
|
hash_table_push_u64_raw(dmn_lnx_state->arena, dmn_lnx_state->tid_ht, thread->id, thread);
|
||||||
|
|
||||||
|
if(!thread->is_reg_block_dirty)
|
||||||
|
{
|
||||||
|
switch(thread->arch)
|
||||||
|
{
|
||||||
|
case Arch_Null: break;
|
||||||
|
case Arch_x64: thread->thread_local_base = ((REGS_RegBlockX64 *)thread->reg_block)->fsbase.u64; break;
|
||||||
|
case Arch_x86:
|
||||||
|
case Arch_arm32:
|
||||||
|
case Arch_arm64: { NotImplemented; } break;
|
||||||
|
default: { InvalidPath; } break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DMN_Event *e = dmn_event_list_push(arena, events);
|
||||||
|
e->kind = DMN_EventKind_CreateThread;
|
||||||
|
e->process = dmn_lnx_handle_from_entity(process);
|
||||||
|
e->thread = dmn_lnx_handle_from_entity(thread);
|
||||||
|
e->arch = thread->arch;
|
||||||
|
e->code = thread->id;
|
||||||
|
|
||||||
|
process->thread_count += 1;
|
||||||
|
|
||||||
|
return thread;
|
||||||
|
}
|
||||||
|
|
||||||
internal void
|
internal void
|
||||||
dmn_lnx_handle_create_process(Arena *arena, DMN_EventList *events, pid_t pid)
|
dmn_lnx_handle_create_process(Arena *arena, DMN_EventList *events, pid_t pid)
|
||||||
{
|
{
|
||||||
@@ -1529,32 +1565,17 @@ dmn_lnx_handle_create_process(Arena *arena, DMN_EventList *events, pid_t pid)
|
|||||||
// push create process event
|
// push create process event
|
||||||
{
|
{
|
||||||
DMN_Event *e = dmn_event_list_push(arena, events);
|
DMN_Event *e = dmn_event_list_push(arena, events);
|
||||||
e->kind = DMN_EventKind_CreateProcess;
|
e->kind = DMN_EventKind_CreateProcess;
|
||||||
e->process = dmn_lnx_handle_from_entity(process);
|
e->process = dmn_lnx_handle_from_entity(process);
|
||||||
e->arch = process->arch;
|
e->arch = process->arch;
|
||||||
e->code = pid;
|
e->code = pid;
|
||||||
|
e->tls_model = DMN_TlsModel_Gnu; // TODO: use dynamic linker path to figure out correct enum here
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// init main thread
|
// init main thread
|
||||||
//
|
//
|
||||||
DMN_LNX_Entity *thread = dmn_lnx_entity_alloc(process, DMN_LNX_EntityKind_Thread);
|
DMN_LNX_Entity *thread = dmn_lnx_handle_create_thread(arena, events, process, pid);
|
||||||
thread->id = pid;
|
|
||||||
thread->arch = process->arch;
|
|
||||||
thread->is_main_thread = 1;
|
|
||||||
thread->thread_state = DMN_LNX_ThreadState_Stopped;
|
|
||||||
thread->reg_block = push_array(process->arena, U8, regs_block_size_from_arch(process->arch));
|
|
||||||
thread->is_reg_block_dirty = !dmn_lnx_thread_read_reg_block(thread);
|
|
||||||
hash_table_push_u64_raw(dmn_lnx_state->arena, dmn_lnx_state->tid_ht, thread->id, thread);
|
|
||||||
// push create thread event
|
|
||||||
{
|
|
||||||
DMN_Event *e = dmn_event_list_push(arena, events);
|
|
||||||
e->kind = DMN_EventKind_CreateThread;
|
|
||||||
e->process = dmn_lnx_handle_from_entity(process);
|
|
||||||
e->thread = dmn_lnx_handle_from_entity(thread);
|
|
||||||
e->arch = thread->arch;
|
|
||||||
e->code = thread->id;
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// init main module
|
// init main module
|
||||||
@@ -1580,6 +1601,8 @@ dmn_lnx_handle_create_process(Arena *arena, DMN_EventList *events, pid_t pid)
|
|||||||
e->string = dmn_lnx_read_string(arena, process->fd, auxv.execfn);
|
e->string = dmn_lnx_read_string(arena, process->fd, auxv.execfn);
|
||||||
e->elf_phdr_vrange = r1u64(auxv.phdr, auxv.phdr + auxv.phent * auxv.phnum);
|
e->elf_phdr_vrange = r1u64(auxv.phdr, auxv.phdr + auxv.phent * auxv.phnum);
|
||||||
e->elf_phdr_entsize = auxv.phent;
|
e->elf_phdr_entsize = auxv.phent;
|
||||||
|
e->tls_index = 1;
|
||||||
|
e->tls_offset = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1643,30 +1666,6 @@ dmn_lnx_handle_exit_process(Arena *arena, DMN_EventList *events, pid_t pid)
|
|||||||
dmn_lnx_state->active_process_count -= 1;
|
dmn_lnx_state->active_process_count -= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void
|
|
||||||
dmn_lnx_handle_create_thread(Arena *arena, DMN_EventList *events, pid_t tid, pid_t new_tid)
|
|
||||||
{
|
|
||||||
DMN_LNX_Entity *thread = dmn_lnx_thread_from_pid(tid);
|
|
||||||
DMN_LNX_Entity *process = thread->parent;
|
|
||||||
|
|
||||||
DMN_LNX_Entity *new_thread = dmn_lnx_entity_alloc(process, DMN_LNX_EntityKind_Thread);
|
|
||||||
new_thread->id = new_tid;
|
|
||||||
new_thread->arch = process->arch;
|
|
||||||
new_thread->reg_block = push_array(process->arena, U8, regs_block_size_from_arch(process->arch));
|
|
||||||
new_thread->thread_state = DMN_LNX_ThreadState_Stopped;
|
|
||||||
new_thread->is_reg_block_dirty = !dmn_lnx_thread_read_reg_block(new_thread);
|
|
||||||
hash_table_push_u64_raw(dmn_lnx_state->arena, dmn_lnx_state->tid_ht, new_thread->id, new_thread);
|
|
||||||
|
|
||||||
DMN_Event *e = dmn_event_list_push(arena, events);
|
|
||||||
e->kind = DMN_EventKind_CreateThread;
|
|
||||||
e->process = dmn_lnx_handle_from_entity(process);
|
|
||||||
e->thread = dmn_lnx_handle_from_entity(new_thread);
|
|
||||||
e->arch = new_thread->arch;
|
|
||||||
e->code = new_thread->id;
|
|
||||||
|
|
||||||
process->thread_count += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void
|
internal void
|
||||||
dmn_lnx_handle_exit_thread(Arena *arena, DMN_EventList *events, pid_t tid, U64 exit_code)
|
dmn_lnx_handle_exit_thread(Arena *arena, DMN_EventList *events, pid_t tid, U64 exit_code)
|
||||||
{
|
{
|
||||||
@@ -1716,14 +1715,27 @@ dmn_lnx_handle_load_module(Arena *arena, DMN_EventList *events, DMN_LNX_Entity *
|
|||||||
DMN_LNX_PhdrInfo module_phdr_info = dmn_lnx_phdr_info_from_memory(process->fd, module_ehdr.e_ident[ELF_Identifier_Class], module_rebase, module_phdr_vaddr, module_ehdr.e_phentsize, module_ehdr.e_phnum);
|
DMN_LNX_PhdrInfo module_phdr_info = dmn_lnx_phdr_info_from_memory(process->fd, module_ehdr.e_ident[ELF_Identifier_Class], module_rebase, module_phdr_vaddr, module_ehdr.e_phentsize, module_ehdr.e_phnum);
|
||||||
String8 module_name = dmn_lnx_read_string(process->arena, process->fd, map.name_vaddr);
|
String8 module_name = dmn_lnx_read_string(process->arena, process->fd, map.name_vaddr);
|
||||||
|
|
||||||
|
// read TLS index and TLS offset
|
||||||
|
U64 tls_index = max_U64;
|
||||||
|
U64 tls_offset = max_U64;
|
||||||
|
if(dmn_lnx_state->is_tls_detected)
|
||||||
|
{
|
||||||
|
Rng1U64 tls_modid_range = r1u64(dmn_lnx_state->tls_modid_desc.offset, dmn_lnx_state->tls_modid_desc.offset + dmn_lnx_state->tls_modid_desc.bit_size / 8);
|
||||||
|
Rng1U64 tls_offset_range = r1u64(dmn_lnx_state->tls_offset_desc.offset, dmn_lnx_state->tls_offset_desc.offset + dmn_lnx_state->tls_offset_desc.bit_size / 8);
|
||||||
|
tls_modid_range = shift_1u64(tls_modid_range, map_vaddr);
|
||||||
|
tls_offset_range = shift_1u64(tls_offset_range, map_vaddr);
|
||||||
|
if(!dmn_lnx_read(process->fd, tls_modid_range, &tls_index)) { Assert(0 && "failed to read TLS index"); }
|
||||||
|
if(!dmn_lnx_read(process->fd, tls_offset_range, &tls_offset)) { Assert(0 && "failed to read TLS offset"); }
|
||||||
|
}
|
||||||
|
|
||||||
// fill out module
|
// fill out module
|
||||||
module = dmn_lnx_entity_alloc(process, DMN_LNX_EntityKind_Module);
|
module = dmn_lnx_entity_alloc(process, DMN_LNX_EntityKind_Module);
|
||||||
module->id = map.name_vaddr;
|
module->id = map.name_vaddr;
|
||||||
module->base_vaddr = map.addr_vaddr;
|
module->base_vaddr = map.addr_vaddr;
|
||||||
|
|
||||||
// push load event
|
|
||||||
if(!str8_match(module_name, str8_lit("linux-vdso.so.1"), 0))
|
if(!str8_match(module_name, str8_lit("linux-vdso.so.1"), 0))
|
||||||
{
|
{
|
||||||
|
// push load event
|
||||||
DMN_Event *e = dmn_event_list_push(arena, events);
|
DMN_Event *e = dmn_event_list_push(arena, events);
|
||||||
e->kind = DMN_EventKind_LoadModule;
|
e->kind = DMN_EventKind_LoadModule;
|
||||||
e->process = dmn_lnx_handle_from_entity(process);
|
e->process = dmn_lnx_handle_from_entity(process);
|
||||||
@@ -1734,6 +1746,8 @@ dmn_lnx_handle_load_module(Arena *arena, DMN_EventList *events, DMN_LNX_Entity *
|
|||||||
e->string = module_name;
|
e->string = module_name;
|
||||||
e->elf_phdr_vrange = r1u64(module_phdr_vaddr, module_phdr_vaddr + module_ehdr.e_phentsize * module_ehdr.e_phnum);
|
e->elf_phdr_vrange = r1u64(module_phdr_vaddr, module_phdr_vaddr + module_ehdr.e_phentsize * module_ehdr.e_phnum);
|
||||||
e->elf_phdr_entsize = module_ehdr.e_phentsize;
|
e->elf_phdr_entsize = module_ehdr.e_phentsize;
|
||||||
|
e->tls_index = tls_index;
|
||||||
|
e->tls_offset = tls_offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
// create mapping for base -> module
|
// create mapping for base -> module
|
||||||
@@ -2007,19 +2021,40 @@ internal void
|
|||||||
dmn_init(void)
|
dmn_init(void)
|
||||||
{
|
{
|
||||||
local_persist B32 was_inited;
|
local_persist B32 was_inited;
|
||||||
local_persist DMN_LNX_State state;
|
if(!was_inited)
|
||||||
AssertAlways(!was_inited);
|
{
|
||||||
was_inited = 1;
|
was_inited = 1;
|
||||||
|
|
||||||
dmn_lnx_state = &state;
|
local_persist DMN_LNX_State state;
|
||||||
dmn_lnx_state->arena = arena_alloc();
|
dmn_lnx_state = &state;
|
||||||
dmn_lnx_state->access_mutex = mutex_alloc();
|
|
||||||
dmn_lnx_state->entities_arena = arena_alloc(.reserve_size = GB(32), .commit_size = KB(64), .flags = ArenaFlag_NoChain);
|
dmn_lnx_state->arena = arena_alloc();
|
||||||
dmn_lnx_state->entities_base = push_array(dmn_lnx_state->entities_arena, DMN_LNX_Entity, 0);
|
dmn_lnx_state->access_mutex = mutex_alloc();
|
||||||
dmn_lnx_state->tid_ht = hash_table_init(dmn_lnx_state->arena, 0x2000);
|
dmn_lnx_state->entities_arena = arena_alloc(.reserve_size = GB(32), .commit_size = KB(64), .flags = ArenaFlag_NoChain);
|
||||||
dmn_lnx_state->pid_ht = hash_table_init(dmn_lnx_state->arena, 0x400);
|
dmn_lnx_state->entities_base = push_array(dmn_lnx_state->entities_arena, DMN_LNX_Entity, 0);
|
||||||
dmn_lnx_state->halter_mutex = mutex_alloc();
|
dmn_lnx_state->tid_ht = hash_table_init(dmn_lnx_state->arena, 0x2000);
|
||||||
dmn_lnx_entity_alloc(dmn_lnx_nil_entity, DMN_LNX_EntityKind_Root);
|
dmn_lnx_state->pid_ht = hash_table_init(dmn_lnx_state->arena, 0x400);
|
||||||
|
dmn_lnx_state->halter_mutex = mutex_alloc();
|
||||||
|
dmn_lnx_entity_alloc(dmn_lnx_nil_entity, DMN_LNX_EntityKind_Root);
|
||||||
|
|
||||||
|
// find offsets of TLS index and TLS offset in the link_map struct
|
||||||
|
//
|
||||||
|
// TODO: assuming that target is using same libc version as debugger
|
||||||
|
{
|
||||||
|
DMN_LNX_DbDesc *tls_modid_desc = dlsym(RTLD_DEFAULT, "_thread_db_link_map_l_tls_modid");
|
||||||
|
DMN_LNX_DbDesc *tls_offset_desc = dlsym(RTLD_DEFAULT, "_thread_db_link_map_l_tls_offset");
|
||||||
|
if(tls_modid_desc && tls_offset_desc)
|
||||||
|
{
|
||||||
|
if(tls_modid_desc->bit_size <= 64 && tls_offset_desc->bit_size <= 64)
|
||||||
|
{
|
||||||
|
dmn_lnx_state->tls_modid_desc = *tls_modid_desc;
|
||||||
|
dmn_lnx_state->tls_offset_desc = *tls_offset_desc;
|
||||||
|
dmn_lnx_state->is_tls_detected = 1;
|
||||||
|
}
|
||||||
|
else { Assert(0 && "invalid TLS desc"); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////
|
////////////////////////////////
|
||||||
@@ -2518,7 +2553,7 @@ dmn_ctrl_run(Arena *arena, DMN_CtrlCtx *ctx, DMN_RunCtrls *ctrls)
|
|||||||
pid_t new_pid;
|
pid_t new_pid;
|
||||||
if(OS_LNX_RETRY_ON_EINTR(ptrace(PTRACE_GETEVENTMSG, wait_id, 0, &new_pid)) >= 0)
|
if(OS_LNX_RETRY_ON_EINTR(ptrace(PTRACE_GETEVENTMSG, wait_id, 0, &new_pid)) >= 0)
|
||||||
{
|
{
|
||||||
dmn_lnx_handle_create_thread(arena, &events, wait_id, new_pid);
|
dmn_lnx_handle_create_thread(arena, &events, dmn_lnx_thread_from_pid(wait_id), new_pid);
|
||||||
}
|
}
|
||||||
else { Assert(0 && "failed to get new tid"); }
|
else { Assert(0 && "failed to get new tid"); }
|
||||||
}break;
|
}break;
|
||||||
@@ -2742,7 +2777,33 @@ dmn_stack_base_vaddr_from_thread(DMN_Handle handle)
|
|||||||
internal U64
|
internal U64
|
||||||
dmn_tls_root_vaddr_from_thread(DMN_Handle handle)
|
dmn_tls_root_vaddr_from_thread(DMN_Handle handle)
|
||||||
{
|
{
|
||||||
return 0;
|
U64 tls_root_vaddr = max_U64;
|
||||||
|
DMN_AccessScope
|
||||||
|
{
|
||||||
|
DMN_LNX_Entity *thread = dmn_lnx_entity_from_handle(handle);
|
||||||
|
switch (thread->arch)
|
||||||
|
{
|
||||||
|
case Arch_Null: {} break;
|
||||||
|
case Arch_x64:
|
||||||
|
{
|
||||||
|
DMN_LNX_Entity *process = thread->parent;
|
||||||
|
REGS_RegBlockX64 *reg_block = thread->reg_block;
|
||||||
|
U64 dtv_pointer = 0;
|
||||||
|
if(dmn_lnx_read_struct(process->fd, reg_block->fsbase.u64 + 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
|
internal B32
|
||||||
|
|||||||
@@ -61,6 +61,15 @@ struct DMN_LNX_UserX64
|
|||||||
};
|
};
|
||||||
StaticAssert(sizeof(DMN_LNX_UserX64) == 912, g_dmn_lnx_user_x64_size_check);
|
StaticAssert(sizeof(DMN_LNX_UserX64) == 912, g_dmn_lnx_user_x64_size_check);
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
U32 bit_size;
|
||||||
|
U32 count;
|
||||||
|
U32 offset;
|
||||||
|
} DMN_LNX_DbDesc;
|
||||||
|
|
||||||
////////////////////////////////
|
////////////////////////////////
|
||||||
//~ SDT Probes
|
//~ SDT Probes
|
||||||
|
|
||||||
@@ -211,6 +220,7 @@ struct DMN_LNX_Entity
|
|||||||
B32 pass_through_signal;
|
B32 pass_through_signal;
|
||||||
U64 pass_through_signo;
|
U64 pass_through_signo;
|
||||||
DMN_LNX_ThreadState thread_state;
|
DMN_LNX_ThreadState thread_state;
|
||||||
|
U64 thread_local_base;
|
||||||
|
|
||||||
// module
|
// module
|
||||||
U64 base_vaddr;
|
U64 base_vaddr;
|
||||||
@@ -292,6 +302,11 @@ struct DMN_LNX_State
|
|||||||
U64 halt_code;
|
U64 halt_code;
|
||||||
U64 halt_user_data;
|
U64 halt_user_data;
|
||||||
B32 is_halting;
|
B32 is_halting;
|
||||||
|
|
||||||
|
// TLS
|
||||||
|
B32 is_tls_detected;
|
||||||
|
DMN_LNX_DbDesc tls_modid_desc;
|
||||||
|
DMN_LNX_DbDesc tls_offset_desc;
|
||||||
};
|
};
|
||||||
|
|
||||||
////////////////////////////////
|
////////////////////////////////
|
||||||
|
|||||||
@@ -465,7 +465,7 @@ dmn_w32_image_info_from_process_base_vaddr(HANDLE process, U64 base_vaddr)
|
|||||||
U32 pe_offset = 0;
|
U32 pe_offset = 0;
|
||||||
{
|
{
|
||||||
U64 dos_magic_off = base_vaddr;
|
U64 dos_magic_off = base_vaddr;
|
||||||
U16 dos_magic = 0;
|
U16 dos_magic = 0;
|
||||||
dmn_w32_process_read_struct(process, dos_magic_off, &dos_magic);
|
dmn_w32_process_read_struct(process, dos_magic_off, &dos_magic);
|
||||||
if(dos_magic == PE_DOS_MAGIC)
|
if(dos_magic == PE_DOS_MAGIC)
|
||||||
{
|
{
|
||||||
@@ -475,56 +475,107 @@ dmn_w32_image_info_from_process_base_vaddr(HANDLE process, U64 base_vaddr)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// rjf: get COFF header
|
// rjf: get COFF header
|
||||||
B32 got_coff_header = 0;
|
B32 got_coff_header = 0;
|
||||||
U64 coff_header_off = 0;
|
U64 coff_header_off = 0;
|
||||||
COFF_FileHeader coff_header = {0};
|
COFF_FileHeader coff_header = {0};
|
||||||
if(pe_offset > 0)
|
if(pe_offset > 0)
|
||||||
{
|
{
|
||||||
U64 pe_magic_off = base_vaddr + pe_offset;
|
U64 pe_magic_off = base_vaddr + pe_offset;
|
||||||
U32 pe_magic = 0;
|
U32 pe_magic = 0;
|
||||||
dmn_w32_process_read_struct(process, pe_magic_off, &pe_magic);
|
dmn_w32_process_read_struct(process, pe_magic_off, &pe_magic);
|
||||||
if(pe_magic == PE_MAGIC)
|
if(pe_magic == PE_MAGIC)
|
||||||
{
|
{
|
||||||
coff_header_off = pe_magic_off + sizeof(pe_magic);
|
coff_header_off = pe_magic_off + sizeof(pe_magic);
|
||||||
if(dmn_w32_process_read_struct(process, coff_header_off, &coff_header))
|
got_coff_header = dmn_w32_process_read_struct(process, coff_header_off, &coff_header);
|
||||||
{
|
|
||||||
got_coff_header = 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// rjf: get arch and size
|
|
||||||
DMN_W32_ImageInfo result = zero_struct;
|
DMN_W32_ImageInfo result = zero_struct;
|
||||||
if(got_coff_header)
|
if(got_coff_header)
|
||||||
{
|
{
|
||||||
U64 optional_size_off = 0;
|
U64 optional_off = coff_header_off + sizeof(COFF_FileHeader);
|
||||||
Arch arch = Arch_Null;
|
|
||||||
switch(coff_header.machine)
|
Arch arch = arch_from_coff_machine(coff_header.machine);
|
||||||
|
U64 image_size = 0;
|
||||||
|
U64 data_dir_count = 0;
|
||||||
|
U64 data_dir_off = max_U64;
|
||||||
|
U64 tls_index = max_U64;
|
||||||
|
|
||||||
|
// parse optional header
|
||||||
|
if(pe_has_plus_header(coff_header.machine))
|
||||||
{
|
{
|
||||||
case COFF_MachineType_X86:
|
PE_OptionalHeader32Plus opt_header = {0};
|
||||||
|
if(dmn_w32_process_read_struct(process, optional_off, &opt_header))
|
||||||
{
|
{
|
||||||
arch = Arch_x86;
|
image_size = opt_header.sizeof_image;
|
||||||
optional_size_off = OffsetOf(PE_OptionalHeader32, sizeof_image);
|
data_dir_count = opt_header.data_dir_count;
|
||||||
}break;
|
data_dir_off = optional_off + sizeof(opt_header);
|
||||||
case COFF_MachineType_X64:
|
|
||||||
{
|
|
||||||
arch = Arch_x64;
|
|
||||||
optional_size_off = OffsetOf(PE_OptionalHeader32Plus, sizeof_image);
|
|
||||||
}break;
|
|
||||||
default:
|
|
||||||
{}break;
|
|
||||||
}
|
|
||||||
if(arch != Arch_Null)
|
|
||||||
{
|
|
||||||
U64 optional_off = coff_header_off + sizeof(coff_header);
|
|
||||||
U32 size = 0;
|
|
||||||
if(dmn_w32_process_read_struct(process, optional_off+optional_size_off, &size) >= sizeof(size))
|
|
||||||
{
|
|
||||||
result.arch = arch;
|
|
||||||
result.size = size;
|
|
||||||
}
|
}
|
||||||
|
else { Assert(0 && "failed to read optional header"); }
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
PE_OptionalHeader32 opt_header = {0};
|
||||||
|
if(dmn_w32_process_read_struct(process, optional_off, &opt_header))
|
||||||
|
{
|
||||||
|
image_size = opt_header.sizeof_image;
|
||||||
|
data_dir_count = opt_header.data_dir_count;
|
||||||
|
data_dir_off = optional_off + sizeof(opt_header);
|
||||||
|
}
|
||||||
|
else { Assert(0 && "failed to read optional header"); }
|
||||||
|
}
|
||||||
|
|
||||||
|
// extract TLS index
|
||||||
|
if(PE_DataDirectoryIndex_TLS < data_dir_count)
|
||||||
|
{
|
||||||
|
U64 tls_dir_vaddr = data_dir_off + PE_DataDirectoryIndex_TLS * sizeof(PE_DataDirectory);
|
||||||
|
PE_DataDirectory tls_dir = {0};
|
||||||
|
if(dmn_w32_process_read_struct(process, tls_dir_vaddr, &tls_dir))
|
||||||
|
{
|
||||||
|
if(pe_has_plus_header(coff_header.machine))
|
||||||
|
{
|
||||||
|
if(tls_dir.virt_size == sizeof(PE_TLSHeader64))
|
||||||
|
{
|
||||||
|
PE_TLSHeader64 tls_header = {0};
|
||||||
|
if(dmn_w32_process_read_struct(process, base_vaddr + tls_dir.virt_off, &tls_header))
|
||||||
|
{
|
||||||
|
U64 tls_index64 = 0;
|
||||||
|
if(dmn_w32_process_read_struct(process, tls_header.index_address, &tls_index64))
|
||||||
|
{
|
||||||
|
tls_index = tls_index64;
|
||||||
|
}
|
||||||
|
else { Assert(0 && "failed to read TLS Index 64"); }
|
||||||
|
}
|
||||||
|
else { Assert(0 && "failed to read TLS Header 64"); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(tls_dir.virt_size == sizeof(PE_TLSHeader32))
|
||||||
|
{
|
||||||
|
PE_TLSHeader32 tls_header = {0};
|
||||||
|
if(dmn_w32_process_read_struct(process, base_vaddr + tls_dir.virt_off, &tls_header))
|
||||||
|
{
|
||||||
|
U32 tls_index32 = 0;
|
||||||
|
if(dmn_w32_process_read_struct(process, tls_header.index_address, &tls_index32))
|
||||||
|
{
|
||||||
|
tls_index = tls_index32;
|
||||||
|
}
|
||||||
|
else { Assert(0 && "failed to read TLS Index 32"); }
|
||||||
|
}
|
||||||
|
else { Assert(0 && "failed to read TLS Header32"); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else { Assert(0 && "failed to read TLS directory"); }
|
||||||
|
}
|
||||||
|
|
||||||
|
// fill out result
|
||||||
|
result.arch = arch;
|
||||||
|
result.size = image_size;
|
||||||
|
result.tls_index = tls_index;
|
||||||
}
|
}
|
||||||
|
else { Assert(0 && "failed to find COFF file header"); }
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -2003,10 +2054,11 @@ dmn_ctrl_run(Arena *arena, DMN_CtrlCtx *ctx, DMN_RunCtrls *ctrls)
|
|||||||
// rjf: create process
|
// rjf: create process
|
||||||
{
|
{
|
||||||
DMN_Event *e = dmn_event_list_push(arena, &events);
|
DMN_Event *e = dmn_event_list_push(arena, &events);
|
||||||
e->kind = DMN_EventKind_CreateProcess;
|
e->kind = DMN_EventKind_CreateProcess;
|
||||||
e->process = dmn_w32_handle_from_entity(process);
|
e->process = dmn_w32_handle_from_entity(process);
|
||||||
e->arch = image_info.arch;
|
e->arch = image_info.arch;
|
||||||
e->code = evt.dwProcessId;
|
e->code = evt.dwProcessId;
|
||||||
|
e->tls_model = DMN_TlsModel_WinodwsNt;
|
||||||
}
|
}
|
||||||
|
|
||||||
// rjf: create thread
|
// rjf: create thread
|
||||||
@@ -2022,13 +2074,14 @@ dmn_ctrl_run(Arena *arena, DMN_CtrlCtx *ctx, DMN_RunCtrls *ctrls)
|
|||||||
// rjf: load module
|
// rjf: load module
|
||||||
{
|
{
|
||||||
DMN_Event *e = dmn_event_list_push(arena, &events);
|
DMN_Event *e = dmn_event_list_push(arena, &events);
|
||||||
e->kind = DMN_EventKind_LoadModule;
|
e->kind = DMN_EventKind_LoadModule;
|
||||||
e->process = dmn_w32_handle_from_entity(process);
|
e->process = dmn_w32_handle_from_entity(process);
|
||||||
e->module = dmn_w32_handle_from_entity(module);
|
e->module = dmn_w32_handle_from_entity(module);
|
||||||
e->arch = image_info.arch;
|
e->arch = image_info.arch;
|
||||||
e->address = module_base;
|
e->address = module_base;
|
||||||
e->size = image_info.size;
|
e->size = image_info.size;
|
||||||
e->string = dmn_w32_full_path_from_module(arena, module);
|
e->string = dmn_w32_full_path_from_module(arena, module);
|
||||||
|
e->tls_index = image_info.tls_index;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}break;
|
}break;
|
||||||
@@ -2195,13 +2248,14 @@ dmn_ctrl_run(Arena *arena, DMN_CtrlCtx *ctx, DMN_RunCtrls *ctrls)
|
|||||||
// rjf: generate event
|
// rjf: generate event
|
||||||
{
|
{
|
||||||
DMN_Event *e = dmn_event_list_push(arena, &events);
|
DMN_Event *e = dmn_event_list_push(arena, &events);
|
||||||
e->kind = DMN_EventKind_LoadModule;
|
e->kind = DMN_EventKind_LoadModule;
|
||||||
e->process = dmn_w32_handle_from_entity(process);
|
e->process = dmn_w32_handle_from_entity(process);
|
||||||
e->module = dmn_w32_handle_from_entity(module);
|
e->module = dmn_w32_handle_from_entity(module);
|
||||||
e->arch = module->arch;
|
e->arch = module->arch;
|
||||||
e->address = module_base;
|
e->address = module_base;
|
||||||
e->size = image_info.size;
|
e->size = image_info.size;
|
||||||
e->string = dmn_w32_full_path_from_module(arena, module);
|
e->string = dmn_w32_full_path_from_module(arena, module);
|
||||||
|
e->tls_index = image_info.tls_index;
|
||||||
}
|
}
|
||||||
}break;
|
}break;
|
||||||
|
|
||||||
|
|||||||
@@ -255,6 +255,7 @@ struct DMN_W32_ImageInfo
|
|||||||
{
|
{
|
||||||
Arch arch;
|
Arch arch;
|
||||||
U32 size;
|
U32 size;
|
||||||
|
U64 tls_index;
|
||||||
};
|
};
|
||||||
|
|
||||||
////////////////////////////////
|
////////////////////////////////
|
||||||
|
|||||||
Reference in New Issue
Block a user