-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathstackwalkerc.h
1306 lines (1141 loc) · 51.8 KB
/
stackwalkerc.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// Windows single header stack walker in C (DbgHelp)
// Copyright (c) 2021, Sepehr Taghdisian.
// Copyright (c) 2005 - 2019, Jochen Kalmbach. All rights reserved.
// Usage:
// #define SW_IMPL
// #include "stackwalkerc.h"
//
// sw_context* ctx = sw_create_context_capture(SW_OPTIONS_ALL, callbacks, NULL);
// sw_show_callstack(ctx);
//
// The usage is super simple, you can see the options/callbacks and check example.cpp
//
// History:
// 1.0.0: Initial version
// 1.0.1: Bugs fixed, taking more sw_option flags into action
// 1.1.0: Added extra userptr to show_callstack_userptr to override the callbacks ptr per-callstack
// 1.2.0: Added fast backtrace implementation for captures in current thread.
// Added utility function sw_load_dbghelp
// Added limiter function sw_set_callstack_limits
// 1.3.0 Added more advanced functions for resolving callstack lazily, sw_capture_current, sw_resolve_callstack
// 1.4.0 Added module cache and reload modules on-demand
// 1.5.0 House cleaning, added sw_set_dbghelp_hintpath, ditched error_msg callback for SW_LOG_ERROR macro
// 1.6.0 [BREAKING] added optional "hash" argument to `sw_capture_current`
// 1.6.1 sw_resolve_callstack skips non-existing symbols with "NA" entries
//
#pragma once
#include <stdbool.h>
#include <stdint.h>
#ifndef SW_API_DECL
#define SW_API_DECL
#endif
#ifndef SW_API_IMPL
#define SW_API_IMPL SW_API_DECL
#endif
#ifndef SW_MAX_NAME_LEN
#define SW_MAX_NAME_LEN 1024
#endif
#ifndef SW_MAX_FRAMES
#define SW_MAX_FRAMES 64
#endif
typedef enum sw_options
{
SW_OPTIONS_NONE = 0,
SW_OPTIONS_SYMBOL = 0x1, // Get symbol names
SW_OPTIONS_SOURCEPOS = 0x2, // Get symbol file+line
SW_OPTIONS_MODULEINFO = 0x4, // Get module information
SW_OPTIONS_VERBOSE = 0xf, // All above options
SW_OPTIONS_SYMBUILDPATH = 0x10, // Generate a good symbol search path
SW_OPTIONS_SYMUSESERVER = 0x20, // Use public microsoft symbol server
SW_OPTIONS_SYMALL = 0x30, // All symbol options
SW_OPTIONS_ALL = 0x3f // All options
} sw_options;
typedef void* sw_sys_handle; // HANDLE
typedef void* sw_exception_pointers; // PEXCEPTION_POINTERS
typedef struct sw_context sw_context;
typedef struct sw_callstack_entry
{
uint64_t offset;
char name[SW_MAX_NAME_LEN];
char und_name[SW_MAX_NAME_LEN];
uint64_t offset_from_symbol;
uint32_t offset_from_line;
uint32_t line;
char line_filename[SW_MAX_NAME_LEN];
uint32_t symbol_type;
const char* symbol_type_str;
char module_name[SW_MAX_NAME_LEN];
uint64_t baseof_image;
char loaded_image_name[SW_MAX_NAME_LEN];
} sw_callstack_entry;
typedef struct sw_callbacks
{
void (*symbol_init)(const char* search_path, uint32_t sym_opts, void* userptr);
void (*load_module)(const char* img, const char* module, uint64_t base_addr, uint32_t size, void* userptr);
void (*callstack_begin)(void* userptr);
void (*callstack_entry)(const sw_callstack_entry* entry, void* userptr);
void (*callstack_end)(void* userptr);
} sw_callbacks;
#ifdef __cplusplus
extern "C" {
#endif
SW_API_DECL sw_context* sw_create_context_capture(uint32_t options, sw_callbacks callbacks, void* userptr);
SW_API_DECL sw_context* sw_create_context_capture_other(uint32_t options, uint32_t process_id,
sw_sys_handle process, sw_callbacks callbacks, void* userptr);
SW_API_DECL sw_context* sw_create_context_exception(uint32_t options,
sw_exception_pointers exp_ptrs,
sw_callbacks callbacks, void* userptr);
SW_API_DECL sw_context* sw_create_context_catch(uint32_t options, sw_callbacks callbacks, void* userptr);
SW_API_DECL void sw_destroy_context(sw_context* ctx);
SW_API_DECL void sw_set_symbol_path(sw_context* ctx, const char* sympath);
SW_API_DECL void sw_set_callstack_limits(sw_context* ctx, uint32_t frames_to_skip, uint32_t frames_to_capture);
SW_API_DECL bool sw_show_callstack_userptr(sw_context* ctx, sw_sys_handle thread_hdl /*=NULL*/, void* callbacks_userptr);
SW_API_DECL bool sw_show_callstack(sw_context* ctx, sw_sys_handle thread_hdl /*=NULL*/);
// manual/advanced functions
SW_API_DECL sw_sys_handle sw_load_dbghelp(void);
SW_API_DECL uint16_t sw_capture_current(sw_context* ctx, void* symbols[SW_MAX_FRAMES], uint32_t* hash);
SW_API_DECL uint16_t sw_resolve_callstack(sw_context* ctx, void* symbols[SW_MAX_FRAMES],
sw_callstack_entry entries[SW_MAX_FRAMES], uint16_t num_entries);
SW_API_DECL void sw_reload_modules(sw_context* ctx);
SW_API_DECL bool sw_get_symbol_module(sw_context* ctx, void* symbol, char module_name[32]);
SW_API_DECL void sw_set_dbghelp_hintpath(const char* path);
#ifdef __cplusplus
}
#endif
#ifdef SW_IMPL
#ifndef _WIN32
#error "Platforms other than Windows are not supported"
#endif
#define WIN32_LEAN_AND_MEAN
#pragma warning(push)
#pragma warning(disable : 5105)
#include <windows.h>
#pragma warning(pop)
#include <malloc.h> // alloca, malloc
#include <string.h> // strlen, strcat_s
#ifndef SW_ASSERT
# include <assert.h>
# define SW_ASSERT(e) assert(e)
#endif
#ifndef SW_LOG_ERROR
# include <stdio.h>
# define SW_LOG_ERROR(err_fmt, ...) printf(err_fmt "\n", ##__VA_ARGS__)
#endif
#ifndef SW_MALLOC
# define SW_MALLOC(size) malloc(size)
# define SW_FREE(ptr) free(ptr)
#endif
#define _SW_UNUSED(x) (void)(x)
#ifndef _SW_PRIVATE
# if defined(__GNUC__) || defined(__clang__)
# define _SW_PRIVATE __attribute__((unused)) static
# else
# define _SW_PRIVATE static
# endif
#endif
_SW_PRIVATE char* sw__strcpy(char* dst, size_t dst_sz, const char* src)
{
SW_ASSERT(dst);
SW_ASSERT(src);
SW_ASSERT(dst_sz > 0);
const size_t len = strlen(src);
const size_t _max = dst_sz - 1;
const size_t num = (len < _max ? len : _max);
memcpy(dst, src, num);
dst[num] = '\0';
return dst;
}
#pragma pack(push, 8)
#include <DbgHelp.h>
typedef struct _IMAGEHLP_MODULE64_V3
{
DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_MODULE64)
DWORD64 BaseOfImage; // base load address of module
DWORD ImageSize; // virtual size of the loaded module
DWORD TimeDateStamp; // date/time stamp from pe header
DWORD CheckSum; // checksum from the pe header
DWORD NumSyms; // number of symbols in the symbol table
SYM_TYPE SymType; // type of symbols loaded
CHAR ModuleName[32]; // module name
CHAR ImageName[256]; // image name
CHAR LoadedImageName[256]; // symbol file name
// new elements: 07-Jun-2002
CHAR LoadedPdbName[256]; // pdb file name
DWORD CVSig; // Signature of the CV record in the debug directories
CHAR CVData[MAX_PATH * 3]; // Contents of the CV record
DWORD PdbSig; // Signature of PDB
GUID PdbSig70; // Signature of PDB (VC 7 and up)
DWORD PdbAge; // DBI age of pdb
BOOL PdbUnmatched; // loaded an unmatched pdb
BOOL DbgUnmatched; // loaded an unmatched dbg
BOOL LineNumbers; // we have line number information
BOOL GlobalSymbols; // we have internal symbol information
BOOL TypeInfo; // we have type information
// new elements: 17-Dec-2003
BOOL SourceIndexed; // pdb supports source server
BOOL Publics; // contains public symbols
} IMAGEHLP_MODULE64_V3, *PIMAGEHLP_MODULE64_V3;
typedef struct _IMAGEHLP_MODULE64_V2
{
DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_MODULE64)
DWORD64 BaseOfImage; // base load address of module
DWORD ImageSize; // virtual size of the loaded module
DWORD TimeDateStamp; // date/time stamp from pe header
DWORD CheckSum; // checksum from the pe header
DWORD NumSyms; // number of symbols in the symbol table
SYM_TYPE SymType; // type of symbols loaded
CHAR ModuleName[32]; // module name
CHAR ImageName[256]; // image name
CHAR LoadedImageName[256]; // symbol file name
} IMAGEHLP_MODULE64_V2, *PIMAGEHLP_MODULE64_V2;
#pragma pack(pop)
typedef BOOL(__stdcall* SymCleanup_t)(IN HANDLE process);
typedef PVOID(__stdcall* SymFunctionTableAccess64_t)(HANDLE process, DWORD64 AddrBase);
typedef BOOL(__stdcall* SymGetLineFromAddr64_t)(IN HANDLE process,
IN DWORD64 dwAddr,
OUT PDWORD pdwDisplacement,
OUT PIMAGEHLP_LINE64 line);
typedef DWORD64(__stdcall* SymGetModuleBase64_t)(IN HANDLE process, IN DWORD64 dwAddr);
typedef BOOL(__stdcall* SymGetModuleInfo64_t)(IN HANDLE process,
IN DWORD64 dwAddr,
OUT IMAGEHLP_MODULE64_V3* ModuleInfo);
typedef DWORD(__stdcall* SymGetOptions_t)(VOID);
typedef BOOL(__stdcall* SymGetSymFromAddr64_t)(IN HANDLE process,
IN DWORD64 dwAddr,
OUT PDWORD64 pdwDisplacement,
OUT PIMAGEHLP_SYMBOL64 Symbol);
typedef BOOL(__stdcall* SymInitialize_t)(IN HANDLE process, IN LPCSTR UserSearchPath, IN BOOL fInvadeProcess);
typedef DWORD64(__stdcall* SymLoadModule64_t)(IN HANDLE process,
IN HANDLE hFile,
IN LPCSTR ImageName,
IN LPCSTR ModuleName,
IN DWORD64 BaseOfDll,
IN DWORD SizeOfDll);
typedef BOOL(__stdcall* SymUnloadModule64_t)(IN HANDLE hProcess, IN DWORD64 BaseOfDll);
typedef DWORD(__stdcall* SymSetOptions_t)(IN DWORD SymOptions);
typedef BOOL(__stdcall* StackWalk64_t)(DWORD MachineType,
HANDLE process,
HANDLE hThread,
LPSTACKFRAME64 StackFrame,
PVOID ContextRecord,
PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine,
PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine,
PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine,
PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress);
typedef DWORD(__stdcall WINAPI* UnDecorateSymbolName_t)(PCSTR DecoratedName,
PSTR UnDecoratedName,
DWORD UndecoratedLength,
DWORD Flags);
typedef BOOL(__stdcall WINAPI* SymGetSearchPath_t)(HANDLE process, PSTR SearchPath, DWORD SearchPathLength);
typedef BOOL(__stdcall WINAPI* EnumerateLoadedModules64_t)(HANDLE hProcess, PENUMLOADED_MODULES_CALLBACK64 EnumLoadedModulesCallback, PVOID UserContext);
typedef BOOL(__stdcall* ReadProcessMemoryRoutine_t)(
HANDLE process,
DWORD64 qwBaseAddress,
PVOID lpBuffer,
DWORD nSize,
LPDWORD lpNumberOfBytesRead,
LPVOID pUserData); // optional data, which was passed in "show_callstack"
// **************************************** ToolHelp32 ************************
#define MAX_MODULE_NAME32 255
#define TH32CS_SNAPMODULE 0x00000008
#pragma pack(push, 8)
typedef struct tagMODULEENTRY32
{
DWORD dwSize;
DWORD th32ModuleID; // This module
DWORD th32ProcessID; // owning process
DWORD GlblcntUsage; // Global usage count on the module
DWORD ProccntUsage; // module usage count in th32ProcessID's context
BYTE *modBaseAddr; // Base address of module in th32ProcessID's context
DWORD modBaseSize; // Size in bytes of module starting at modBaseAddr
HMODULE hModule; // The hModule of this module in th32ProcessID's context
char szModule[MAX_MODULE_NAME32 + 1];
char szExePath[MAX_PATH];
} MODULEENTRY32;
typedef MODULEENTRY32 *PMODULEENTRY32;
typedef MODULEENTRY32 *LPMODULEENTRY32;
#pragma pack(pop)
// **************************************** PSAPI ************************
typedef struct _MODULEINFO
{
LPVOID lpBaseOfDll;
DWORD SizeOfImage;
LPVOID EntryPoint;
} MODULEINFO, *LPMODULEINFO;
// Normally it should be enough to use 'CONTEXT_FULL' (better would be 'CONTEXT_ALL')
#define USED_CONTEXT_FLAGS CONTEXT_FULL
// only available on msvc2015+ (_MSC_VER >= 1900)
#ifdef __cplusplus
extern "C" {
#endif
void** __cdecl __current_exception_context();
#ifdef __cplusplus
}
#endif
typedef struct sw_module_cache_item
{
char name[32];
DWORD64 base_addr;
} sw_module_cache_item;
typedef struct sw_context_internal
{
sw_context* parent;
CONTEXT ctx;
HMODULE dbg_help;
HANDLE process;
SymCleanup_t fSymCleanup;
SymFunctionTableAccess64_t fSymFunctionTableAccess64;
SymGetModuleBase64_t fSymGetModuleBase64;
SymGetModuleInfo64_t fSymGetModuleInfo64;
SymGetOptions_t fSymGetOptions;
SymGetSymFromAddr64_t fSymGetSymFromAddr64;
SymGetLineFromAddr64_t fSymGetLineFromAddr64;
SymInitialize_t fSymInitialize;
SymLoadModule64_t fSymLoadModule64;
SymUnloadModule64_t fSymUnloadModule64;
SymSetOptions_t fSymSetOptions;
StackWalk64_t fStackWalk64;
UnDecorateSymbolName_t fUnDecorateSymbolName;
SymGetSearchPath_t fSymGetSearchPath;
EnumerateLoadedModules64_t fEnumerateLoadedModules64;
CRITICAL_SECTION modules_cs;
uint32_t num_modules;
uint32_t max_modules;
sw_module_cache_item* modules;
} sw_context_internal;
typedef struct sw_context
{
sw_callbacks callbacks;
void* callbacks_userptr;
sw_sys_handle process;
uint32_t process_id;
bool modules_loaded;
bool reload_modules;
bool fatal_error; // cannot recover anymore
char sympath[SW_MAX_NAME_LEN];
uint32_t options;
uint32_t max_recursion;
sw_context_internal internal;
uint32_t frames_to_skip;
uint32_t frames_to_capture;
} sw_context;
static char sw__dbghelp_hintpath[SW_MAX_NAME_LEN];
_SW_PRIVATE bool sw__get_module_info(sw_context_internal* ctxi, HANDLE process, DWORD64 baseAddr, IMAGEHLP_MODULE64_V3* modinfo)
{
memset(modinfo, 0, sizeof(IMAGEHLP_MODULE64_V3));
if (ctxi->fSymGetModuleInfo64 == NULL) {
SetLastError(ERROR_DLL_INIT_FAILED);
return false;
}
// First try to use the larger ModuleInfo-Structure
modinfo->SizeOfStruct = sizeof(IMAGEHLP_MODULE64_V3);
char data[4096]; // reserve enough memory, so the bug in v6.3.5.1 does not lead to memory-overwrites...
memcpy(data, modinfo, sizeof(IMAGEHLP_MODULE64_V3));
static bool use_v3_version = true;
if (use_v3_version) {
if (ctxi->fSymGetModuleInfo64(process, baseAddr, (IMAGEHLP_MODULE64_V3*)data) != FALSE) {
// only copy as much memory as is reserved...
memcpy(modinfo, data, sizeof(IMAGEHLP_MODULE64_V3));
modinfo->SizeOfStruct = sizeof(IMAGEHLP_MODULE64_V3);
return true;
}
use_v3_version = false; // to prevent unnecessary calls with the larger struct...
}
// could not retrieve the bigger structure, try with the smaller one (as defined in VC7.1)...
modinfo->SizeOfStruct = sizeof(IMAGEHLP_MODULE64_V2);
memcpy(data, modinfo, sizeof(IMAGEHLP_MODULE64_V2));
if (ctxi->fSymGetModuleInfo64(process, baseAddr, (IMAGEHLP_MODULE64_V3*)data) != FALSE) {
// only copy as much memory as is reserved...
memcpy(modinfo, data, sizeof(IMAGEHLP_MODULE64_V2));
modinfo->SizeOfStruct = sizeof(IMAGEHLP_MODULE64_V2);
return true;
}
SetLastError(ERROR_DLL_INIT_FAILED);
return false;
}
_SW_PRIVATE bool sw__get_module_info_csentry(sw_context_internal* ctxi, HANDLE process, DWORD64 baseAddr,
sw_callstack_entry* cs_entry)
{
IMAGEHLP_MODULE64_V3 module;
bool r = sw__get_module_info(ctxi, process, baseAddr, &module);
if (r) {
SW_ASSERT(cs_entry);
cs_entry->symbol_type = module.SymType;
switch (module.SymType) {
case SymNone: cs_entry->symbol_type_str = "-nosymbols-"; break;
case SymCoff: cs_entry->symbol_type_str = "COFF"; break;
case SymCv: cs_entry->symbol_type_str = "CV"; break;
case SymPdb: cs_entry->symbol_type_str = "PDB"; break;
case SymExport: cs_entry->symbol_type_str = "-exported-"; break;
case SymDeferred: cs_entry->symbol_type_str = "-deferred-"; break;
case SymSym: cs_entry->symbol_type_str = "SYM"; break;
#if API_VERSION_NUMBER >= 9
case SymDia: cs_entry->symbol_type_str = "DIA"; break;
#endif
case 8: cs_entry->symbol_type_str = "Virtual"; break;
default: cs_entry->symbol_type_str = NULL; break;
}
sw__strcpy(cs_entry->module_name, sizeof(cs_entry->module_name), module.ModuleName);
cs_entry->baseof_image = module.BaseOfImage;
sw__strcpy(cs_entry->loaded_image_name, sizeof(cs_entry->module_name), module.LoadedImageName);
} else {
sw__strcpy(cs_entry->module_name, sizeof(cs_entry->module_name), "[NA]");
sw__strcpy(cs_entry->loaded_image_name, sizeof(cs_entry->module_name), "[NA]");
}
return r;
}
static BOOL sw__enum_loaded_modules_cb(PCSTR module_path, ULONGLONG module_base, ULONG module_size, PVOID userptr)
{
// resolve module name
char module_name[SW_MAX_NAME_LEN];
const char* backslash = strrchr(module_path, '\\');
if (backslash) {
sw__strcpy(module_name, sizeof(module_name), backslash+1);
} else {
sw__strcpy(module_name, sizeof(module_name), module_path);
}
sw_context_internal* ctxi = (sw_context_internal*)userptr;
EnterCriticalSection(&ctxi->modules_cs);
// don't Load modules that are already in the cache
for (uint32_t mi = 0; mi < ctxi->num_modules; mi++) {
if (strcmp(ctxi->modules[mi].name, module_name) == 0) {
LeaveCriticalSection(&ctxi->modules_cs);
return ERROR_SUCCESS;
}
}
LeaveCriticalSection(&ctxi->modules_cs);
DWORD64 mod_addr = ctxi->fSymLoadModule64(ctxi->process, NULL, module_path, module_name, module_base, module_size);
if (mod_addr == 0) {
return FALSE;
} else {
EnterCriticalSection(&ctxi->modules_cs);
// Add the module to cache
if (ctxi->num_modules == ctxi->max_modules) {
ctxi->max_modules <<= 1;
sw_module_cache_item* modules = (sw_module_cache_item*)SW_MALLOC(ctxi->max_modules*sizeof(sw_module_cache_item));
if (!modules) {
LeaveCriticalSection(&ctxi->modules_cs);
SW_ASSERT(0);
return 0;
}
memcpy(modules, ctxi->modules, ctxi->num_modules*sizeof(sw_module_cache_item));
SW_FREE(ctxi->modules);
ctxi->modules = modules;
}
sw_module_cache_item* mcache = &ctxi->modules[ctxi->num_modules++];
sw__strcpy(mcache->name, sizeof(mcache->name), module_name);
mcache->base_addr = mod_addr;
LeaveCriticalSection(&ctxi->modules_cs);
}
if (ctxi->parent->callbacks.load_module) {
ctxi->parent->callbacks.load_module(module_path, module_name, mod_addr, module_size, ctxi->parent->callbacks_userptr);
}
return TRUE;
}
_SW_PRIVATE bool sw__get_module_list_TH32(sw_context_internal* ctxi, DWORD pid)
{
typedef HANDLE(__stdcall * CreateToolhelp32Snapshot_t)(DWORD dwFlags, DWORD th32ProcessID);
typedef BOOL(__stdcall * Module32First_t)(HANDLE hSnapshot, LPMODULEENTRY32 lpme);
typedef BOOL(__stdcall * Module32Next_t)(HANDLE hSnapshot, LPMODULEENTRY32 lpme);
// try both dlls...
const char* dllname[] = { "kernel32.dll", "tlhelp32.dll" };
HINSTANCE hToolhelp = NULL;
CreateToolhelp32Snapshot_t fCreateToolhelp32Snapshot = NULL;
Module32First_t fModule32First = NULL;
Module32Next_t fModule32Next = NULL;
HANDLE hSnap;
MODULEENTRY32 me;
me.dwSize = sizeof(me);
BOOL keepGoing;
size_t i;
for (i = 0; i < (sizeof(dllname) / sizeof(dllname[0])); i++) {
hToolhelp = LoadLibraryA(dllname[i]);
if (hToolhelp == NULL)
continue;
fCreateToolhelp32Snapshot = (CreateToolhelp32Snapshot_t)GetProcAddress(hToolhelp, "CreateToolhelp32Snapshot");
fModule32First = (Module32First_t)GetProcAddress(hToolhelp, "Module32First");
fModule32Next = (Module32Next_t)GetProcAddress(hToolhelp, "Module32Next");
if ((fCreateToolhelp32Snapshot != NULL) && (fModule32First != NULL) && (fModule32Next != NULL))
break; // found the functions!
FreeLibrary(hToolhelp);
hToolhelp = NULL;
}
if (hToolhelp == NULL)
return FALSE;
hSnap = fCreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid);
if (hSnap == (HANDLE)-1) {
FreeLibrary(hToolhelp);
return FALSE;
}
keepGoing = !!fModule32First(hSnap, &me);
int cnt = 0;
while (keepGoing) {
sw__enum_loaded_modules_cb(me.szExePath, (DWORD64)me.modBaseAddr, me.modBaseSize, ctxi);
cnt++;
keepGoing = !!fModule32Next(hSnap, &me);
}
CloseHandle(hSnap);
FreeLibrary(hToolhelp);
return (cnt <= 0) ? false : true;
} // sw__get_module_list_TH32
_SW_PRIVATE bool sw__load_modules_internal(sw_context_internal* ctxi, HANDLE process, DWORD process_id)
{
if (!sw__get_module_list_TH32(ctxi, process_id)) {
return ctxi->fEnumerateLoadedModules64(process, sw__enum_loaded_modules_cb, ctxi) ? true : false;
}
return true;
}
_SW_PRIVATE PCONTEXT get_current_exception_context()
{
PCONTEXT* pctx = (PCONTEXT*)__current_exception_context();
return pctx ? *pctx : NULL;
}
_SW_PRIVATE sw_context* sw__create_context(uint32_t options, uint32_t process_id, sw_sys_handle process,
PCONTEXT context_win, sw_callbacks callbacks, void* userptr)
{
SW_ASSERT(process);
sw_context* ctx = (sw_context*)SW_MALLOC(sizeof(sw_context));
memset(ctx, 0x0, sizeof(sw_context));
ctx->options = options;
ctx->max_recursion = 1000;
ctx->process_id = process_id;
ctx->process = process;
ctx->callbacks = callbacks;
ctx->callbacks_userptr = userptr;
ctx->internal.process = process;
ctx->internal.parent = ctx;
ctx->internal.ctx.ContextFlags = 0;
if (context_win)
ctx->internal.ctx = *context_win;
ctx->frames_to_skip = 0;
ctx->frames_to_capture = 0xffffffff;
return ctx;
}
SW_API_IMPL sw_sys_handle sw_load_dbghelp()
{
HMODULE dbg_help = NULL;
// Dynamically load the Entry-Points for dbghelp.dll:
// First try to load the newest one from
TCHAR temp[4096];
if (sw__dbghelp_hintpath[0]) {
sw__strcpy(temp, sizeof(temp), sw__dbghelp_hintpath);
strcat_s(temp, sizeof(temp), "\\dbghelp.dll");
if (GetFileAttributes(temp) != INVALID_FILE_ATTRIBUTES) {
dbg_help = LoadLibraryA(temp);
}
}
// ".local" file does not exist, so we can try to load the dbghelp.dll from the
// "Debugging Tools for Windows" Ok, first try the new path according to the
// architecture:
#ifdef _M_IX86
if ((dbg_help == NULL) && (GetEnvironmentVariableA("ProgramFiles", temp, sizeof(temp)) > 0)) {
strcat_s(temp, sizeof(temp), "\\Debugging Tools for Windows (x86)\\dbghelp.dll");
// now check if the file exists:
if (GetFileAttributes(temp) != INVALID_FILE_ATTRIBUTES) {
dbg_help = LoadLibraryA(temp);
}
}
#elif _M_X64
if ((dbg_help == NULL) && (GetEnvironmentVariableA("ProgramFiles", temp, sizeof(temp)) > 0)) {
strcat_s(temp, sizeof(temp), "\\Debugging Tools for Windows (x64)\\dbghelp.dll");
// now check if the file exists:
if (GetFileAttributes(temp) != INVALID_FILE_ATTRIBUTES) {
dbg_help = LoadLibraryA(temp);
}
}
#elif _M_IA64
if ((dbg_help == NULL) &&
(GetEnvironmentVariableA("ProgramFiles", temp, sizeof(temp)) > 0)) {
strcat_s(temp, sizeof(temp), "\\Debugging Tools for Windows (ia64)\\dbghelp.dll");
// now check if the file exists:
if (GetFileAttributes(temp) != INVALID_FILE_ATTRIBUTES) {
dbg_help = LoadLibraryA(temp);
}
}
#endif
// If still not found, try the old directories...
if ((dbg_help == NULL) && (GetEnvironmentVariableA("ProgramFiles", temp, sizeof(temp)) > 0)) {
strcat_s(temp, sizeof(temp), "\\Debugging Tools for Windows\\dbghelp.dll");
// now check if the file exists:
if (GetFileAttributes(temp) != INVALID_FILE_ATTRIBUTES) {
dbg_help = LoadLibraryA(temp);
}
}
#if defined _M_X64 || defined _M_IA64
// Still not found? Then try to load the (old) 64-Bit version:
if ((dbg_help == NULL) &&
(GetEnvironmentVariableA("ProgramFiles", temp, sizeof(temp)) > 0)) {
strcat_s(temp, sizeof(temp), "\\Debugging Tools for Windows 64-Bit\\dbghelp.dll");
if (GetFileAttributes(temp) != INVALID_FILE_ATTRIBUTES) {
dbg_help = LoadLibraryA(temp);
}
}
#endif
if (dbg_help == NULL) // if not already loaded, try to load a default-one
dbg_help = LoadLibraryA("dbghelp.dll");
return dbg_help;
}
_SW_PRIVATE bool sw__init_internal(sw_context_internal* ctxi, const char* sympath)
{
ctxi->dbg_help = (HMODULE)sw_load_dbghelp();
if (ctxi->dbg_help == NULL) {
return false;
}
ctxi->fSymInitialize = (SymInitialize_t)GetProcAddress(ctxi->dbg_help, "SymInitialize");
ctxi->fSymCleanup = (SymCleanup_t)GetProcAddress(ctxi->dbg_help, "SymCleanup");
ctxi->fStackWalk64 = (StackWalk64_t)GetProcAddress(ctxi->dbg_help, "StackWalk64");
ctxi->fSymGetOptions = (SymGetOptions_t)GetProcAddress(ctxi->dbg_help, "SymGetOptions");
ctxi->fSymSetOptions = (SymSetOptions_t)GetProcAddress(ctxi->dbg_help, "SymSetOptions");
ctxi->fSymFunctionTableAccess64 = (SymFunctionTableAccess64_t)GetProcAddress(ctxi->dbg_help, "SymFunctionTableAccess64");
ctxi->fSymGetLineFromAddr64 = (SymGetLineFromAddr64_t)GetProcAddress(ctxi->dbg_help, "SymGetLineFromAddr64");
ctxi->fSymGetModuleBase64 = (SymGetModuleBase64_t)GetProcAddress(ctxi->dbg_help, "SymGetModuleBase64");
ctxi->fSymGetModuleInfo64 = (SymGetModuleInfo64_t)GetProcAddress(ctxi->dbg_help, "SymGetModuleInfo64");
ctxi->fSymGetSymFromAddr64 = (SymGetSymFromAddr64_t)GetProcAddress(ctxi->dbg_help, "SymGetSymFromAddr64");
ctxi->fUnDecorateSymbolName = (UnDecorateSymbolName_t)GetProcAddress(ctxi->dbg_help, "UnDecorateSymbolName");
ctxi->fSymLoadModule64 = (SymLoadModule64_t)GetProcAddress(ctxi->dbg_help, "SymLoadModule64");
ctxi->fSymUnloadModule64 = (SymUnloadModule64_t)GetProcAddress(ctxi->dbg_help, "SymUnloadModule64");
ctxi->fSymGetSearchPath = (SymGetSearchPath_t)GetProcAddress(ctxi->dbg_help, "SymGetSearchPath");
ctxi->fEnumerateLoadedModules64 = (EnumerateLoadedModules64_t)GetProcAddress(ctxi->dbg_help, "EnumerateLoadedModules64");
if (ctxi->fSymCleanup == NULL || ctxi->fSymFunctionTableAccess64 == NULL || ctxi->fSymGetModuleBase64 == NULL ||
ctxi->fSymGetModuleInfo64 == NULL || ctxi->fSymGetOptions == NULL || ctxi->fSymGetSymFromAddr64 == NULL ||
ctxi->fSymInitialize == NULL || ctxi->fSymSetOptions == NULL || ctxi->fStackWalk64 == NULL ||
ctxi->fUnDecorateSymbolName == NULL || ctxi->fSymLoadModule64 == NULL || ctxi->fSymUnloadModule64 == NULL ||
ctxi->fEnumerateLoadedModules64 == NULL)
{
FreeLibrary(ctxi->dbg_help);
ctxi->dbg_help = NULL;
ctxi->fSymCleanup = NULL;
return false;
}
// SymInitialize
if (ctxi->fSymInitialize(ctxi->process, sympath, FALSE) == FALSE) {
SW_LOG_ERROR("SymInitialize failed (ErrorCode=%lu)", GetLastError());
return false;
}
DWORD sym_opts = ctxi->fSymGetOptions(); // SymGetOptions
sym_opts |= SYMOPT_LOAD_LINES;
sym_opts |= SYMOPT_FAIL_CRITICAL_ERRORS;
sym_opts = ctxi->fSymSetOptions(sym_opts);
char buf[SW_MAX_NAME_LEN] = { 0 };
if (ctxi->fSymGetSearchPath != NULL) {
if (ctxi->fSymGetSearchPath(ctxi->process, buf, SW_MAX_NAME_LEN) == FALSE)
SW_LOG_ERROR("SymGetSearchPath failed (ErrorCode=%lu)", GetLastError());
}
if (ctxi->parent->callbacks.symbol_init) {
ctxi->parent->callbacks.symbol_init(buf, sym_opts, ctxi->parent->callbacks_userptr);
}
// initialize module cache
InitializeCriticalSection(&ctxi->modules_cs);
ctxi->max_modules = 32;
ctxi->modules = (sw_module_cache_item*)SW_MALLOC(ctxi->max_modules*sizeof(sw_module_cache_item));
if (!ctxi->modules) {
return false;
}
return true;
}
_SW_PRIVATE bool sw__load_modules(sw_context* ctx)
{
if (ctx->fatal_error)
return false;
if (ctx->modules_loaded)
return true;
// Build the sym-path:
char sympath[SW_MAX_NAME_LEN]; sympath[0] = 0;
if ((ctx->options & SW_OPTIONS_SYMBUILDPATH) != 0) {
// Now first add the (optional) provided sympath:
if (ctx->sympath[0]) {
strcat_s(sympath, sizeof(sympath), ctx->sympath);
strcat_s(sympath, sizeof(sympath), ";");
}
strcat_s(sympath, sizeof(sympath), ".;");
char temp[1024];
// Now add the current directory:
if (GetCurrentDirectoryA(sizeof(temp), temp) > 0) {
temp[sizeof(temp) - 1] = 0;
strcat_s(sympath, sizeof(sympath), temp);
strcat_s(sympath, sizeof(sympath), ";");
}
// Now add the path for the main-module:
if (GetModuleFileNameA(NULL, temp, sizeof(temp)) > 0) {
temp[sizeof(temp) - 1] = 0;
for (char* p = (temp + strlen(temp) - 1); p >= temp; --p) {
// locate the rightmost path separator
if ((*p == '\\') || (*p == '/') || (*p == ':')) {
*p = 0;
break;
}
} // for (search for path separator...)
if (strlen(temp) > 0) {
strcat_s(sympath, sizeof(sympath), temp);
strcat_s(sympath, sizeof(sympath), ";");
}
}
if (GetEnvironmentVariableA("_NT_SYMBOL_PATH", temp, sizeof(temp)) > 0) {
temp[sizeof(temp) - 1] = 0;
strcat_s(sympath, sizeof(sympath), temp);
strcat_s(sympath, sizeof(sympath), ";");
}
if (GetEnvironmentVariableA("_NT_ALTERNATE_SYMBOL_PATH", temp, sizeof(temp)) > 0) {
temp[sizeof(temp) - 1] = 0;
strcat_s(sympath, sizeof(sympath), temp);
strcat_s(sympath, sizeof(sympath), ";");
}
if (GetEnvironmentVariableA("SYSTEMROOT", temp, sizeof(temp)) > 0) {
temp[sizeof(temp) - 1] = 0;
strcat_s(sympath, sizeof(sympath), temp);
strcat_s(sympath, sizeof(sympath), ";");
// also add the "system32"-directory:
strcat_s(temp, sizeof(temp), "\\system32");
strcat_s(sympath, sizeof(sympath), temp);
strcat_s(sympath, sizeof(sympath), ";");
}
if ((ctx->options & SW_OPTIONS_SYMUSESERVER) != 0) {
if (GetEnvironmentVariableA("SYSTEMDRIVE", temp, sizeof(temp)) > 0) {
temp[sizeof(temp) - 1] = 0;
strcat_s(sympath, sizeof(sympath), "SRV*");
strcat_s(sympath, sizeof(sympath), temp);
strcat_s(sympath, sizeof(sympath), "\\websymbols");
strcat_s(sympath, sizeof(sympath), "*https://msdl.microsoft.com/download/symbols;");
} else
strcat_s(sympath, sizeof(sympath),
"SRV*c:\\websymbols*https://msdl.microsoft.com/download/symbols;");
}
} // if SW_OPTIONS_SYMBUILDPATH
// First Init the whole stuff...
if (!sw__init_internal(&ctx->internal, sympath)) {
SW_LOG_ERROR("Error initializing dbghelp.dll");
SetLastError(ERROR_DLL_INIT_FAILED);
ctx->fatal_error = true;
return false;
}
if (sw__load_modules_internal(&ctx->internal, ctx->process, ctx->process_id)) {
ctx->modules_loaded = true;
}
return ctx->modules_loaded;
}
SW_API_IMPL sw_context* sw_create_context_capture(uint32_t options, sw_callbacks callbacks, void* userptr)
{
return sw__create_context(options, GetCurrentProcessId(), GetCurrentProcess(), NULL, callbacks, userptr);
}
SW_API_IMPL sw_context* sw_create_context_capture_other(uint32_t options, uint32_t process_id,
sw_sys_handle process, sw_callbacks callbacks,
void* userptr)
{
SW_ASSERT(process_id);
SW_ASSERT(process);
return sw__create_context(options, process_id, process, NULL, callbacks, userptr);
}
SW_API_IMPL sw_context* sw_create_context_exception(uint32_t options,
sw_exception_pointers exp_ptrs,
sw_callbacks callbacks, void* userptr)
{
SW_ASSERT(exp_ptrs);
PCONTEXT ctx_win = ((PEXCEPTION_POINTERS)exp_ptrs)->ContextRecord;
return sw__create_context(options, GetCurrentProcessId(), GetCurrentProcess(), ctx_win, callbacks, userptr);
}
SW_API_IMPL sw_context* sw_create_context_catch(uint32_t options, sw_callbacks callbacks, void* userptr)
{
PCONTEXT ctx_win = get_current_exception_context();
SW_ASSERT(ctx_win);
return sw__create_context(options, GetCurrentProcessId(), GetCurrentProcess(), ctx_win, callbacks, userptr);
}
SW_API_IMPL void sw_destroy_context(sw_context* ctx)
{
if (ctx) {
SW_FREE(ctx->internal.modules);
DeleteCriticalSection(&ctx->internal.modules_cs);
if (ctx->internal.fSymCleanup)
ctx->internal.fSymCleanup(ctx->process);
if (ctx->internal.dbg_help)
FreeLibrary(ctx->internal.dbg_help);
memset(ctx, 0x0, sizeof(*ctx));
SW_FREE(ctx);
}
}
SW_API_IMPL void sw_set_symbol_path(sw_context* ctx, const char* sympath)
{
SW_ASSERT(ctx);
SW_ASSERT(sympath);
sw__strcpy(ctx->sympath, sizeof(ctx->sympath), sympath);
ctx->options |= SW_OPTIONS_SYMBUILDPATH;
}
SW_API_IMPL void sw_set_callstack_limits(sw_context* ctx, uint32_t frames_to_skip, uint32_t frames_to_capture)
{
ctx->frames_to_skip = frames_to_skip;
ctx->frames_to_capture = frames_to_capture;
}
// The following is used to pass the "userData"-Pointer to the user-provided readMemoryFunction
// This has to be done due to a problem with the "hProcess"-parameter in x64...
// Because this class is in no case multi-threading-enabled (because of the limitations
// of dbghelp.dll) it is "safe" to use a static-variable
static ReadProcessMemoryRoutine_t s_read_mem_func = NULL;
static LPVOID s_read_mem_func_userdata = NULL;
_SW_PRIVATE BOOL __stdcall sw__read_proc_mem(HANDLE hProcess,
DWORD64 qwBaseAddress,
PVOID lpBuffer,
DWORD nSize,
LPDWORD lpNumberOfBytesRead)
{
if (s_read_mem_func == NULL) {
SIZE_T st;
BOOL ret = ReadProcessMemory(hProcess, (LPVOID)qwBaseAddress, lpBuffer, nSize, &st);
*lpNumberOfBytesRead = (DWORD)st;
return ret;
} else {
return s_read_mem_func(hProcess, qwBaseAddress, lpBuffer, nSize, lpNumberOfBytesRead, s_read_mem_func_userdata);
}
}
static bool sw_show_callstack_fast(sw_context* ctx, void* callbacks_userptr)
{
sw_callstack_entry cs_entry;
IMAGEHLP_LINE64 line;
uint32_t max_frames = ctx->frames_to_capture != 0xffffffff ? ctx->frames_to_capture : SW_MAX_FRAMES;
max_frames = max_frames > SW_MAX_FRAMES ? SW_MAX_FRAMES : max_frames;
void** backtrace = (void**)alloca(sizeof(void*) * max_frames);
if (!backtrace) {
return false;
}
uint32_t frames_to_skip = ctx->frames_to_skip > 2 ? ctx->frames_to_skip : 2;
USHORT num_captures = RtlCaptureStackBackTrace(frames_to_skip, max_frames, backtrace, NULL);
if (num_captures == 0) {
return true;
}
IMAGEHLP_SYMBOL64* symbol = (IMAGEHLP_SYMBOL64*)alloca(sizeof(IMAGEHLP_SYMBOL64) + SW_MAX_NAME_LEN);
if (!symbol) {
return false;
}
memset(symbol, 0, sizeof(IMAGEHLP_SYMBOL64) + SW_MAX_NAME_LEN);
symbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
symbol->MaxNameLength = SW_MAX_NAME_LEN;
if (ctx->callbacks.callstack_begin) {
ctx->callbacks.callstack_begin(callbacks_userptr);
}
for (USHORT i = 0; i < num_captures; i++) {
memset(&cs_entry, 0x0, sizeof(cs_entry));
if ((ctx->options & SW_OPTIONS_SYMBOL) && ctx->internal.fSymGetSymFromAddr64 != NULL) {
if (ctx->internal.fSymGetSymFromAddr64(ctx->process, (DWORD64)backtrace[i], &(cs_entry.offset_from_symbol), symbol) != FALSE) {
sw__strcpy(cs_entry.name, sizeof(cs_entry.name), symbol->Name);
sw__strcpy(cs_entry.und_name, sizeof(cs_entry.und_name), symbol->Name);
} else {
DWORD gle = GetLastError();
if (gle == ERROR_INVALID_ADDRESS)
break;
else if (gle == ERROR_MOD_NOT_FOUND)
continue;
SW_LOG_ERROR("SymGetSymFromAddr64 failed (ErrorCode=%lu)", gle);
break;
}
}
// show line number info, NT5.0-method (SymGetLineFromAddr64())
if ((ctx->options && SW_OPTIONS_SOURCEPOS) && ctx->internal.fSymGetLineFromAddr64 != NULL) { // yes, we have SymGetLineFromAddr64()
if (ctx->internal.fSymGetLineFromAddr64(ctx->process, (DWORD64)backtrace[i], (PDWORD)&(cs_entry.offset_from_line), &line) != FALSE) {
cs_entry.line = line.LineNumber;
sw__strcpy(cs_entry.line_filename, SW_MAX_NAME_LEN, line.FileName);
} else {
DWORD gle = GetLastError();
if (gle == ERROR_INVALID_ADDRESS)
break;
else if (gle == ERROR_MOD_NOT_FOUND)
continue;
SW_LOG_ERROR("SymGetLineFromAddr64 failed (ErrorCode=%lu)", gle);
break;
}
} // yes, we have SymGetLineFromAddr64()
// show module info (SymGetModuleInfo64())
if (ctx->options & SW_OPTIONS_MODULEINFO) {
if (!sw__get_module_info_csentry(&ctx->internal, ctx->process, (uint64_t)backtrace[i], &cs_entry)) { // got module info OK
SW_LOG_ERROR("SymGetModuleInfo64 failed (ErrorCode=%lu)", GetLastError());
}
}
if (ctx->callbacks.callstack_entry) {
ctx->callbacks.callstack_entry(&cs_entry, callbacks_userptr);
}
}
if (ctx->callbacks.callstack_end) {
ctx->callbacks.callstack_end(callbacks_userptr);
}
return true;
}
SW_API_IMPL bool sw_show_callstack_userptr(sw_context* ctx, sw_sys_handle thread_hdl, void* callbacks_userptr)
{
CONTEXT c;
callbacks_userptr = callbacks_userptr ? callbacks_userptr : ctx->callbacks_userptr;
if (thread_hdl == NULL) {
thread_hdl = GetCurrentThread();
}
if (!ctx->modules_loaded) {
sw__load_modules(ctx);
}
if (ctx->reload_modules) {
sw__load_modules_internal(&ctx->internal, ctx->process, ctx->process_id);
ctx->reload_modules = false;
}