GCC Code Coverage Report


Directory: src/gate/
File: src/gate/callstacks.c
Date: 2025-12-12 23:40:09
Exec Total Coverage
Lines: 92 116 79.3%
Functions: 11 11 100.0%
Branches: 23 42 54.8%

Line Branch Exec Source
1 /* GATE PROJECT LICENSE:
2 +----------------------------------------------------------------------------+
3 | Copyright(c) 2018-2025, Stefan Meislinger <sm@opengate.at> |
4 | All rights reserved. |
5 | |
6 | Redistribution and use in source and binary forms, with or without |
7 | modification, are permitted provided that the following conditions are met:|
8 | |
9 | 1. Redistributions of source code must retain the above copyright notice, |
10 | this list of conditions and the following disclaimer. |
11 | 2. Redistributions in binary form must reproduce the above copyright |
12 | notice, this list of conditions and the following disclaimer in the |
13 | documentation and/or other materials provided with the distribution. |
14 | |
15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"|
16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
18 | ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
19 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
20 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
21 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
22 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
23 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
24 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
25 | THE POSSIBILITY OF SUCH DAMAGE. |
26 +----------------------------------------------------------------------------+
27 */
28
29 #include "gate/callstacks.h"
30 #include "gate/strings.h"
31 #include "gate/results.h"
32 #include "gate/platforms.h"
33
34
35 #if defined(GATE_SYS_WINCE)
36 # define GATE_CALLSTACKS_WINCE_TRACE_IMPL 1
37 # define GATE_CALLSTACKS_DEFAULT_SETJMP 1
38 # define GATE_CALLSTACKS_WIN32_CALLSTACK_IMPL
39 #elif defined(GATE_SYS_WASM)
40 # define GATE_CALLSTACKS_NO_IMPL 1
41 # define GATE_CALLSTACKS_NO_FORK_IMPL 1
42 #elif defined(GATE_SYS_DOS)
43 # define GATE_CALLSTACKS_DEFAULT_SETJMP 1
44 # define GATE_CALLSTACKS_DOS_IMPL 1
45 #elif defined(GATE_SYS_WIN) && !defined(GATE_SYS_WIN16)
46 # define GATE_CALLSTACKS_WIN32_TRACE_DBG_IMPL 1
47 # define GATE_CALLSTACKS_DEFAULT_SETJMP 1
48 # define GATE_CALLSTACKS_WIN32_CALLSTACK_IMPL
49 #elif defined(GATE_SYS_BEOS)
50 # define GATE_CALLSTACKS_NO_IMPL 1
51 # define GATE_CALLSTACKS_DEFAULT_SETJMP 1
52 #elif defined(GATE_SYS_ANDROID)
53 # define GATE_CALLSTACKS_NO_IMPL 1
54 # define GATE_CALLSTACKS_DEFAULT_SETJMP 1
55 #elif defined(GATE_SYS_POSIX)
56 # define GATE_CALLSTACKS_POSIX_IMPL 1
57 #elif defined(GATE_SYS_EFI)
58 # define GATE_CALLSTACKS_EFI_IMPL 1
59 # define GATE_CALLSTACKS_DEFAULT_SETJMP 1
60 #else
61 # define GATE_CALLSTACKS_NO_IMPL 1
62 # define GATE_CALLSTACKS_DEFAULT_SETJMP 1
63 #endif
64
65
66 #if defined(GATE_CALLSTACKS_NO_FORK_IMPL)
67 gate_result_t gate_callstack_fork(gate_callstack_jmpbuf_t* jmpbuffer,
68 gate_entrypoint_t entry, void* param,
69 gate_entrypoint_t branch, void* branch_param)
70 {
71 return GATE_RESULT_NOTIMPLEMENTED;
72 }
73
74 void gate_callstack_jump(gate_callstack_jmpbuf_t* jmpbuffer)
75 {
76 }
77
78 #endif /* GATE_CALLSTACKS_NO_FORK_IMPL */
79
80 #if defined(GATE_CALLSTACKS_DEFAULT_SETJMP)
81
82 #if defined(GATE_SYS_EFI) && defined(GATE_COMPILER_MSVC) && (_MSC_VER < 1930)
83 typedef void* jmp_buf[48];
84 #define setjmp _setjmp
85 extern int setjmp(jmp_buf buf);
86 extern void longjmp(jmp_buf buf, int n);
87 #else
88 #include <setjmp.h>
89 #endif
90
91 typedef struct gate_native_jmp_buf_class
92 {
93 jmp_buf buf;
94 } gate_native_jmp_buf_t;
95
96 gate_result_t gate_callstack_fork(gate_callstack_jmpbuf_t* jmpbuffer,
97 gate_entrypoint_t entry, void* param,
98 gate_entrypoint_t branch, void* branch_param)
99 {
100 gate_result_t ret = GATE_RESULT_OK;
101 gate_native_jmp_buf_t* ptr_buf = (gate_native_jmp_buf_t*)jmpbuffer;
102 if (0 == setjmp(ptr_buf->buf))
103 {
104 if (entry)
105 {
106 ret = entry(param);
107 }
108 }
109 else
110 {
111 if (branch)
112 {
113 ret = branch(branch_param);
114 }
115 }
116 gate_mem_clear(jmpbuffer, sizeof(gate_callstack_jmpbuf_t));
117 return ret;
118 }
119
120 void gate_callstack_jump(gate_callstack_jmpbuf_t* jmpbuffer)
121 {
122 gate_native_jmp_buf_t* ptr_buf = (gate_native_jmp_buf_t*)jmpbuffer;
123 longjmp(ptr_buf->buf, 1);
124 }
125
126 #endif /* GATE_CALLSTACKS_DEFAULT_SETJMP */
127
128
129
130
131 #if defined(GATE_CALLSTACKS_WINCE_TRACE_IMPL)
132
133 #include "gate/libraries.h"
134
135 #include <windows.h>
136 #if defined(GATE_COMPILER_MSVC)
137 #include <Kfuncs.h>
138 #endif
139
140 typedef struct _CallSnapshot
141 {
142 DWORD dwReturnAddr;
143 } CallSnapshot;
144
145 typedef ULONG(*GetThreadCallStack_t)(
146 HANDLE hThrd,
147 ULONG dwMaxFrames,
148 LPVOID lpFrames[],
149 DWORD dwFlags,
150 DWORD dwSkip
151 );
152
153
154 static gate_library_t gate_callstack_lib = NULL;
155 static GetThreadCallStack_t GetThreadCallStackFunction = NULL;
156
157 static gate_bool_t gate_callstack_init()
158 {
159 static gate_string_t const lib_binary = { "coredll.dll", 11, NULL };
160 gate_result_t ret;
161
162 if (gate_callstack_lib == NULL)
163 {
164 ret = gate_library_open(&lib_binary, &gate_callstack_lib, 0);
165 if (GATE_FAILED(ret))
166 {
167 return false;
168 }
169 }
170 if (GetThreadCallStackFunction == NULL)
171 {
172 ret = gate_library_get_function_name(gate_callstack_lib, "GetThreadCallStack", &GetThreadCallStackFunction);
173 if (GATE_FAILED(ret))
174 {
175 return false;
176 }
177 }
178
179 return true;
180 }
181
182 gate_result_t gate_callstack_trace(gate_callstack_address_t* addresses, gate_size_t addresses_count, gate_size_t* addresses_used)
183 {
184 HANDLE hThread = GetCurrentThread();
185 CallSnapshot stack_addresses[1024];
186 DWORD dwCount, dwIndex;
187
188 if (!gate_callstack_init())
189 {
190 return GATE_RESULT_NOTSUPPORTED;
191 }
192
193 dwCount = GetThreadCallStackFunction(hThread, sizeof(stack_addresses) / sizeof(stack_addresses[0]),
194 (void**)&stack_addresses[0], 0, 0);
195
196 if (dwCount > addresses_count)
197 {
198 dwCount = addresses_count;
199 }
200 for (dwIndex = 0; dwIndex != dwCount; ++dwIndex)
201 {
202 addresses[dwIndex] = (gate_callstack_address_t)stack_addresses[dwIndex].dwReturnAddr;
203 }
204 if (addresses_used != NULL)
205 {
206 *addresses_used = dwCount;
207 }
208 return GATE_RESULT_OK;
209 }
210 gate_result_t gate_callstack_print(gate_callstack_address_t address, char* buffer, gate_size_t buffer_length, gate_size_t* buffer_used)
211 {
212 gate_result_t ret = GATE_RESULT_NOTIMPLEMENTED;
213
214 return ret;
215 }
216
217 #endif /* GATE_CALLSTACKS_WINCE_IMPL */
218
219
220
221
222
223 #if defined(GATE_CALLSTACKS_WIN32_TRACE_DBG_IMPL)
224
225 #include "gate/libraries.h"
226
227 #define WIN32_LEAN_AND_MEAN
228 #include <tchar.h>
229 #include <windows.h>
230
231 #include "gate/platforms.h"
232
233 static gate_library_t gate_win32_dbghlp_module = NULL;
234
235 #define MAX_SYM_NAME 2000
236
237 typedef enum
238 {
239 SymNone = 0,
240 SymCoff,
241 SymCv,
242 SymPdb,
243 SymExport,
244 SymDeferred,
245 SymSym, // .sym file
246 SymDia,
247 SymVirtual,
248 NumSymTypes
249 } SYM_TYPE;
250
251 typedef struct _IMAGEHLP_MODULE64
252 {
253 DWORD SizeOfStruct; /* set to sizeof(IMAGEHLP_MODULE64) */
254 DWORD64 BaseOfImage; /* base load address of module */
255 DWORD ImageSize; /* virtual size of the loaded module */
256 DWORD TimeDateStamp; /* date/time stamp from pe header */
257 DWORD CheckSum; /* checksum from the pe header */
258 DWORD NumSyms; /* number of symbols in the symbol table */
259 SYM_TYPE SymType; /* type of symbols loaded */
260 CHAR ModuleName[32]; /* module name */
261 CHAR ImageName[256]; /* image name */
262 /* new elements: 07-Jun-2002 */
263 CHAR LoadedImageName[256]; /* symbol file name */
264 CHAR LoadedPdbName[256]; /* pdb file name */
265 DWORD CVSig; /* Signature of the CV record in the debug directories */
266 CHAR CVData[MAX_PATH * 3]; /* Contents of the CV record */
267 DWORD PdbSig; /* Signature of PDB */
268 GUID PdbSig70; /* Signature of PDB (VC 7 and up) */
269 DWORD PdbAge; /* DBI age of pdb */
270 BOOL PdbUnmatched; /* loaded an unmatched pdb */
271 BOOL DbgUnmatched; /* loaded an unmatched dbg */
272 BOOL LineNumbers; /* we have line number information */
273 BOOL GlobalSymbols; /* we have internal symbol information */
274 BOOL TypeInfo; /* we have type information */
275 } IMAGEHLP_MODULE64, * PIMAGEHLP_MODULE64;
276
277 typedef PVOID(WINAPI* PtrSymFunctionTableAccess64)(HANDLE hProcess, DWORD64 AddrBase);
278 static PtrSymFunctionTableAccess64 SymFunctionTableAccess64 = NULL;
279
280 typedef DWORD64(WINAPI* PtrSymGetModuleBase64)(HANDLE hProcess, DWORD64 qwAddr);
281 static PtrSymGetModuleBase64 SymGetModuleBase64 = NULL;
282
283 typedef enum
284 {
285 AddrMode1616,
286 AddrMode1632,
287 AddrModeReal,
288 AddrModeFlat
289 } ADDRESS_MODE;
290
291 typedef struct _tagADDRESS64
292 {
293 DWORD64 Offset;
294 WORD Segment;
295 ADDRESS_MODE Mode;
296 } ADDRESS64, * LPADDRESS64;
297
298
299 typedef BOOL(__stdcall* PREAD_PROCESS_MEMORY_ROUTINE64)(
300 HANDLE hProcess,
301 DWORD64 qwBaseAddress,
302 PVOID lpBuffer,
303 DWORD nSize,
304 LPDWORD lpNumberOfBytesRead
305 );
306
307 typedef PVOID(__stdcall* PFUNCTION_TABLE_ACCESS_ROUTINE64)(
308 HANDLE hProcess,
309 DWORD64 AddrBase
310 );
311
312 typedef DWORD64(__stdcall* PGET_MODULE_BASE_ROUTINE64)(
313 HANDLE hProcess,
314 DWORD64 Address
315 );
316
317 typedef DWORD64(__stdcall* PTRANSLATE_ADDRESS_ROUTINE64)(
318 HANDLE hProcess,
319 HANDLE hThread,
320 LPADDRESS64 lpaddr
321 );
322
323 typedef struct _KDHELP64
324 {
325 DWORD64 Thread;
326 DWORD ThCallbackStack;
327 DWORD ThCallbackBStore;
328 DWORD NextCallback;
329 DWORD FramePointer;
330 DWORD64 KiCallUserMode;
331 DWORD64 KeUserCallbackDispatcher;
332 DWORD64 SystemRangeStart;
333 DWORD64 Reserved[8];
334 } KDHELP64, * PKDHELP64;
335
336 typedef struct _tagSTACKFRAME64
337 {
338 ADDRESS64 AddrPC;
339 ADDRESS64 AddrReturn;
340 ADDRESS64 AddrFrame;
341 ADDRESS64 AddrStack;
342 ADDRESS64 AddrBStore;
343 PVOID FuncTableEntry;
344 DWORD64 Params[4];
345 BOOL Far;
346 BOOL Virtual;
347 DWORD64 Reserved[3];
348 KDHELP64 KdHelp;
349 } STACKFRAME64, * LPSTACKFRAME64;
350
351 typedef BOOL(WINAPI* PtrSymInitialize)(HANDLE, PCSTR, BOOL);
352 static PtrSymInitialize SymInitialize = NULL;
353
354 typedef BOOL(WINAPI* PtrSymCleanup)(HANDLE);
355 static PtrSymCleanup SymCleanup = NULL;
356
357 typedef struct _IMAGEHLP_SYMBOL64
358 {
359 DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_SYMBOL64)
360 DWORD64 Address; // virtual address including dll base address
361 DWORD Size; // estimated size of symbol, can be zero
362 DWORD Flags; // info about the symbols, see the SYMF defines
363 DWORD MaxNameLength; // maximum size of symbol name in 'Name'
364 CHAR Name[1]; // symbol name (null terminated string)
365 } IMAGEHLP_SYMBOL64, * PIMAGEHLP_SYMBOL64;
366
367 typedef BOOL(WINAPI* PtrStackWalk64)(
368 DWORD MachineType,
369 HANDLE hProcess,
370 HANDLE hThread,
371 LPSTACKFRAME64 StackFrame,
372 PVOID ContextRecord,
373 PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine,
374 PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine,
375 PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine,
376 PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress
377 );
378 static PtrStackWalk64 StackWalk64 = NULL;
379
380
381 typedef BOOL(WINAPI* PtrSymGetModuleInfo64)(HANDLE hProcess, DWORD64 qwAddr, PIMAGEHLP_MODULE64 ModuleInfo);
382 static PtrSymGetModuleInfo64 SymGetModuleInfo64 = NULL;
383
384 typedef BOOL(WINAPI* PtrSymGetSymFromAddr64)(HANDLE hProcess, DWORD64 qwAddr, PDWORD64 pdwDisplacement, PIMAGEHLP_SYMBOL64 Symbol);
385 static PtrSymGetSymFromAddr64 SymGetSymFromAddr64 = NULL;
386
387 static gate_bool_t gate_callstack_load_api(void* ptr_to_func_ptr, char const* func_name)
388 {
389 gate_result_t ret;
390
391 if ((*(void**)ptr_to_func_ptr) == NULL)
392 {
393 ret = gate_library_get_function_name(gate_win32_dbghlp_module, func_name, ptr_to_func_ptr);
394 if (GATE_FAILED(ret))
395 {
396 return false;
397 }
398 }
399 return true;
400 }
401
402 static gate_bool_t gate_callstack_init()
403 {
404 static gate_string_t const str_dbghelp = { "Dbghelp.dll", 11, NULL };
405 gate_result_t ret;
406
407 if (gate_win32_dbghlp_module == NULL)
408 {
409 ret = gate_library_open(&str_dbghelp, &gate_win32_dbghlp_module, 0);
410 if (GATE_FAILED(ret))
411 {
412 return false;
413 }
414 }
415
416 ret = true;
417 ret &= gate_callstack_load_api(&SymInitialize, "SymInitialize");
418 ret &= gate_callstack_load_api(&SymCleanup, "SymCleanup");
419 ret &= gate_callstack_load_api(&StackWalk64, "StackWalk64");
420 ret &= gate_callstack_load_api(&SymGetModuleInfo64, "SymGetModuleInfo64");
421 ret &= gate_callstack_load_api(&SymGetSymFromAddr64, "SymGetSymFromAddr64");
422 ret &= gate_callstack_load_api(&SymFunctionTableAccess64, "SymFunctionTableAccess64");
423 ret &= gate_callstack_load_api(&SymGetModuleBase64, "SymGetModuleBase64");
424 ret &= gate_callstack_load_api(&SymInitialize, "SymInitialize");
425 ret &= gate_callstack_load_api(&SymInitialize, "SymInitialize");
426 ret &= gate_callstack_load_api(&SymInitialize, "SymInitialize");
427
428 return ret;
429 }
430
431 #ifndef IMAGE_FILE_MACHINE_I386
432 #define IMAGE_FILE_MACHINE_I386 0x014c
433 #endif
434
435 #ifndef IMAGE_FILE_MACHINE_AMD64
436 #define IMAGE_FILE_MACHINE_AMD64 0x8664
437 #endif
438
439 #if defined(GATE_COMPILER_MSVC) || defined(GATE_COMPILER_CLANG)
440 # define GATE_WINAPI_CPU_CONTEXT CONTEXT
441 # if (GATE_ARCH == GATE_ARCH_X86X64)
442 # define MACHINE_FLAG IMAGE_FILE_MACHINE_AMD64
443 # define GATE_DEBUG_GETFRAME(frame, ctx) do { \
444 gate_platform.Win32RtlCaptureContext(&ctx); \
445 frame.AddrFrame.Mode = AddrModeFlat; \
446 frame.AddrFrame.Offset = (DWORD64)ctx.Rbp; \
447 frame.AddrStack.Mode = AddrModeFlat; \
448 frame.AddrStack.Offset = (DWORD64)ctx.Rsp; \
449 frame.AddrPC.Mode = AddrModeFlat; \
450 frame.AddrPC.Offset = (DWORD64)ctx.Rip; \
451 } while(0)
452 # elif (GATE_ARCH == GATE_ARCH_ARM64)
453 # define MACHINE_FLAG IMAGE_FILE_MACHINE_ARM64
454 # define GATE_DEBUG_GETFRAME(frame, ctx) do { \
455 gate_platform.Win32RtlCaptureContext(&ctx); \
456 frame.AddrFrame.Mode = AddrModeFlat; \
457 frame.AddrFrame.Offset = (DWORD64)ctx.Fp; \
458 frame.AddrStack.Mode = AddrModeFlat; \
459 frame.AddrStack.Offset = (DWORD64)ctx.Sp; \
460 frame.AddrPC.Mode = AddrModeFlat; \
461 frame.AddrPC.Offset = (DWORD64)ctx.Pc; \
462 frame.AddrReturn.Mode = AddrModeFlat; \
463 frame.AddrReturn.Offset = (DWORD64)ctx.Lr; \
464 } while(0)
465 # elif (GATE_ARCH == GATE_ARCH_ARM32)
466 # define MACHINE_FLAG IMAGE_FILE_MACHINE_ARM
467 # define GATE_DEBUG_GETFRAME(frame, ctx) do { \
468 gate_platform.Win32RtlCaptureContext(&ctx); \
469 frame.AddrFrame.Mode = AddrModeFlat; \
470 frame.AddrFrame.Offset = (DWORD64)ctx.R12; \
471 frame.AddrStack.Mode = AddrModeFlat; \
472 frame.AddrStack.Offset = (DWORD64)ctx.Sp; \
473 frame.AddrPC.Mode = AddrModeFlat; \
474 frame.AddrPC.Offset = (DWORD64)ctx.Pc; \
475 } while(0)
476 # elif (GATE_ARCH == GATE_ARCH_X86IA32)
477 # define MACHINE_FLAG IMAGE_FILE_MACHINE_I386
478 # define GATE_DEBUG_GETFRAME(frame, ctx) do { \
479 size_t * ptrFrame = 0; \
480 size_t ptrStack = 0; \
481 _asm { mov ptrFrame, ebp } \
482 _asm { mov ptrStack, esp } \
483 ctx.Ebp = (DWORD)(size_t)(ptrFrame); \
484 ctx.Esp = (DWORD)ptrStack; \
485 ctx.Eip = (DWORD)*(ptrFrame + 1); \
486 frame.AddrFrame.Mode = AddrModeFlat; \
487 frame.AddrFrame.Offset = (DWORD64)ctx.Ebp; \
488 frame.AddrStack.Mode = AddrModeFlat; \
489 frame.AddrStack.Offset = (DWORD64)ctx.Esp; \
490 frame.AddrPC.Mode = AddrModeFlat; \
491 frame.AddrPC.Offset = (DWORD64)ctx.Eip; \
492 } while(0)
493 # else
494 # define GATE_DEBUG_GETFRAME(frame, ctx) do { \
495 frame.AddrFrame.Mode = AddrModeFlat; \
496 frame.AddrFrame.Offset = (DWORD64)0; \
497 frame.AddrStack.Mode = AddrModeFlat; \
498 frame.AddrStack.Offset = (DWORD64)0; \
499 frame.AddrPC.Mode = AddrModeFlat; \
500 frame.AddrPC.Offset = (DWORD64)0; \
501 } while(0)
502 # endif
503 #elif defined(GATE_COMPILER_GCC)
504 # define GATE_WINAPI_CPU_CONTEXT CONTEXT
505 # if defined(GATE_SYS_WIN64)
506 # define MACHINE_FLAG IMAGE_FILE_MACHINE_AMD64
507 # define GATE_DEBUG_GETFRAME(frame, ctx) do { \
508 gate_platform.Win32RtlCaptureContext(&ctx); \
509 frame.AddrFrame.Mode = AddrModeFlat; \
510 frame.AddrFrame.Offset = (DWORD64)ctx.Rbp; \
511 frame.AddrStack.Mode = AddrModeFlat; \
512 frame.AddrStack.Offset = (DWORD64)ctx.Rsp; \
513 frame.AddrPC.Mode = AddrModeFlat; \
514 frame.AddrPC.Offset = (DWORD64)ctx.Rip; \
515 } while(0)
516 # else
517 # define MACHINE_FLAG IMAGE_FILE_MACHINE_I386
518 # define GATE_DEBUG_GETFRAME(frame, ctx) do { \
519 size_t * ptrFrame = 0; \
520 size_t ptrStack = 0; \
521 asm ( "movl %%ebp, %0;" : "=r" ( ptrFrame ) ); \
522 asm ( "movl %%esp, %0;" : "=r" ( ptrStack ) ); \
523 ctx.Ebp = (DWORD)(size_t)(ptrFrame); \
524 ctx.Esp = (DWORD)ptrStack; \
525 ctx.Eip = (DWORD)*(ptrFrame + 1); \
526 frame.AddrFrame.Mode = AddrModeFlat; \
527 frame.AddrFrame.Offset = (DWORD64)ctx.Ebp; \
528 frame.AddrStack.Mode = AddrModeFlat; \
529 frame.AddrStack.Offset = (DWORD64)ctx.Esp; \
530 frame.AddrPC.Mode = AddrModeFlat; \
531 frame.AddrPC.Offset = (DWORD64)ctx.Eip; \
532 } while(0)
533 # endif
534 #elif defined(GATE_COMPILER_WATCOM)
535 # define GATE_WINAPI_CPU_CONTEXT CONTEXT
536 # if (GATE_ARCH == GATE_ARCH_X86IA32)
537 # define MACHINE_FLAG IMAGE_FILE_MACHINE_I386
538 # define GATE_DEBUG_GETFRAME(frame, ctx) do { \
539 size_t * ptrFrame = 0; \
540 size_t ptrStack = 0; \
541 ctx.Ebp = (DWORD)(size_t)(ptrFrame); \
542 ctx.Esp = (DWORD)ptrStack; \
543 ctx.Eip = (DWORD)*(ptrFrame + 1); \
544 frame.AddrFrame.Mode = AddrModeFlat; \
545 frame.AddrFrame.Offset = (DWORD64)ctx.Ebp; \
546 frame.AddrStack.Mode = AddrModeFlat; \
547 frame.AddrStack.Offset = (DWORD64)ctx.Esp; \
548 frame.AddrPC.Mode = AddrModeFlat; \
549 frame.AddrPC.Offset = (DWORD64)ctx.Eip; \
550 } while(0)
551 # endif
552 #elif defined(GATE_COMPILER_TCC)
553 typedef struct
554 {
555 DWORD ControlWord;
556 DWORD StatusWord;
557 DWORD TagWord;
558 DWORD ErrorOffset;
559 DWORD ErrorSelector;
560 DWORD DataOffset;
561 DWORD DataSelector;
562 BYTE RegisterArea[80];
563 DWORD Spare0;
564 } WIN32_CPU_FLOATING_SAVE_AREA;
565
566 typedef struct
567 {
568 DWORD ContextFlags;
569
570 DWORD Dr0;
571 DWORD Dr1;
572 DWORD Dr2;
573 DWORD Dr3;
574 DWORD Dr6;
575 DWORD Dr7;
576
577 WIN32_CPU_FLOATING_SAVE_AREA FloatSave;
578
579 DWORD SegGs;
580 DWORD SegFs;
581 DWORD SegEs;
582 DWORD SegDs;
583
584 DWORD Edi;
585 DWORD Esi;
586 DWORD Ebx;
587 DWORD Edx;
588 DWORD Ecx;
589 DWORD Eax;
590
591 DWORD Ebp;
592 DWORD Eip;
593 DWORD SegCs;
594 DWORD EFlags;
595 DWORD Esp;
596 DWORD SegSs;
597
598 BYTE ExtendedRegisters[512];
599 } WIN32_CPU_CONTEXT;
600
601 #define GATE_WINAPI_CPU_CONTEXT WIN32_CPU_CONTEXT
602 # if (GATE_ARCH == GATE_ARCH_X86IA32)
603 # define MACHINE_FLAG IMAGE_FILE_MACHINE_I386
604 # define GATE_DEBUG_GETFRAME(frame, ctx) do { \
605 size_t * ptrFrame = 0; \
606 size_t ptrStack = 0; \
607 ctx.Ebp = (DWORD)(size_t)(ptrFrame); \
608 ctx.Esp = (DWORD)ptrStack; \
609 ctx.Eip = (DWORD)*(ptrFrame + 1); \
610 frame.AddrFrame.Mode = AddrModeFlat; \
611 frame.AddrFrame.Offset = (DWORD64)ctx.Ebp; \
612 frame.AddrStack.Mode = AddrModeFlat; \
613 frame.AddrStack.Offset = (DWORD64)ctx.Esp; \
614 frame.AddrPC.Mode = AddrModeFlat; \
615 frame.AddrPC.Offset = (DWORD64)ctx.Eip; \
616 } while(0)
617 # endif
618
619 #endif
620
621
622 gate_result_t gate_callstack_trace(gate_callstack_address_t* addresses, gate_size_t addresses_count, gate_size_t* addresses_used)
623 {
624 gate_result_t ret;
625 HANDLE hThread = GetCurrentThread();
626 HANDLE hProcess = NULL;
627 STACKFRAME64 frame;
628 GATE_WINAPI_CPU_CONTEXT ctx;
629
630 DWORD dwMachineFlag = MACHINE_FLAG;
631 LPCONTEXT pctx = NULL;
632 gate_size_t frames_found = 0;
633 BOOL symcleanup_required = FALSE;
634
635 do
636 {
637 if (!gate_callstack_init())
638 {
639 ret = GATE_RESULT_NOTSUPPORTED;
640 break;
641 }
642
643 hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, GetCurrentProcessId());
644 if (hProcess == NULL)
645 {
646 ret = GATE_RESULT_OUTOFRESOURCES;
647 break;
648 }
649
650 symcleanup_required = SymInitialize(hProcess, NULL, TRUE);
651
652 gate_mem_clear(&frame, sizeof(frame));
653 gate_mem_clear(&ctx, sizeof(ctx));
654
655 GATE_DEBUG_GETFRAME(frame, ctx);
656
657 #if (GATE_ARCH == GATE_ARCH_ARM64)
658 dwMachineFlag = IMAGE_FILE_MACHINE_ARM64;
659 pctx = &ctx;
660 #elif (GATE_ARCH == GATE_ARCH_X86X64)
661 dwMachineFlag = IMAGE_FILE_MACHINE_AMD64;
662 pctx = &ctx;
663 #endif
664 if (!StackWalk64(dwMachineFlag, hProcess, hThread, &frame, pctx, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL))
665 {
666 ret = GATE_RESULT_FAILED;
667 break;
668 }
669 ret = GATE_RESULT_OK;
670 while (addresses_count-- != 0)
671 {
672 if (StackWalk64(dwMachineFlag, hProcess, hThread, &frame, pctx, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL))
673 {
674 *addresses = (gate_callstack_address_t)(gate_uintptr_t)frame.AddrPC.Offset;
675 ++frames_found;
676 ++addresses;
677 }
678 else
679 {
680 break;
681 }
682 }
683 if (addresses_used != 0)
684 {
685 *addresses_used = frames_found;
686 }
687 } while (0);
688
689 if (symcleanup_required)
690 {
691 SymCleanup(hProcess);
692 }
693
694 if (hProcess != NULL)
695 {
696 CloseHandle(hProcess);
697 }
698
699 return ret;
700 }
701 gate_result_t gate_callstack_print(gate_callstack_address_t address, char* buffer, gate_size_t buffer_length, gate_size_t* buffer_used)
702 {
703 gate_result_t ret = GATE_RESULT_FAILED;
704 HANDLE hProcess = NULL;
705 IMAGEHLP_MODULE64 mod64;
706 DWORD64 dwAddr;
707 DWORD64 dwDisp = 0;
708 char bufSym[sizeof(IMAGEHLP_SYMBOL64) + MAX_SYM_NAME];
709 IMAGEHLP_SYMBOL64* pSym = (IMAGEHLP_SYMBOL64*)&bufSym[0];
710 gate_size_t len;
711
712 do
713 {
714 if (!gate_callstack_init())
715 {
716 ret = GATE_RESULT_NOTSUPPORTED;
717 break;
718 }
719
720 hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, GetCurrentProcessId());
721 if (hProcess == NULL)
722 {
723 ret = GATE_RESULT_OUTOFRESOURCES;
724 break;
725 }
726
727 gate_mem_clear(&mod64, sizeof(mod64));
728 mod64.SizeOfStruct = sizeof(mod64);
729 dwAddr = (DWORD64)(gate_uintptr_t)address;
730 SymGetModuleInfo64(hProcess, dwAddr, &mod64);
731 gate_mem_clear(pSym, sizeof(IMAGEHLP_SYMBOL64) + MAX_SYM_NAME);
732 pSym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
733 pSym->MaxNameLength = MAX_SYM_NAME;
734
735 if (SymGetSymFromAddr64(hProcess, dwAddr, &dwDisp, pSym))
736 {
737 len = gate_str_print_text(buffer, buffer_length, pSym->Name, gate_str_length(pSym->Name));
738 if (buffer_used != NULL)
739 {
740 *buffer_used = len;
741 }
742 ret = GATE_RESULT_OK;
743 }
744 else
745 {
746 gate_str_print_hex_uint64(buffer, buffer_length, (gate_uint64_t)(gate_uintptr_t)address, false);
747 ret = GATE_RESULT_OK_PARTIAL;
748 }
749 } while (0);
750
751 if (hProcess != NULL)
752 {
753 CloseHandle(hProcess);
754 }
755
756 return ret;
757 }
758
759 #endif /* GATE_CALLSTACKS_WIN32_DBG_IMPL */
760
761
762 #ifdef GATE_CALLSTACKS_WIN32_CALLSTACK_IMPL
763
764 #include "gate/platform/windows/win32callstack.h"
765
766 #include "gate/platforms.h"
767 #include <windows.h>
768
769
770 #if defined(GATE_COMPILE_ASM_SUPPORT)
771
772 #ifndef MEM_COMMIT
773 #define MEM_COMMIT 0x1000
774 #endif
775
776 #ifndef PAGE_READWRITE
777 #define PAGE_READWRITE 0x04
778 #endif
779
780 #ifndef PAGE_EXECUTE_READ
781 #define PAGE_EXECUTE_READ 0x20
782 #endif
783
784 #ifndef PAGE_EXECUTE_READWRITE
785 #define PAGE_EXECUTE_READWRITE 0x40
786 #endif
787
788 #ifndef MEM_RELEASE
789 #define MEM_RELEASE 0x8000
790 #endif
791
792 gate_result_t gate_callstack_create(gate_size_t* ptr_stacksize, void** ptr_callstack)
793 {
794 gate_result_t ret = GATE_RESULT_FAILED;
795 LPVOID stack_buffer = NULL;
796 DWORD old_protect = 0;
797 gate_size_t stacksize;
798 do
799 {
800 if (!ptr_callstack || !ptr_stacksize)
801 {
802 ret = GATE_RESULT_INVALIDARG;
803 break;
804 }
805 stacksize = *ptr_stacksize;
806 stacksize &= ~((gate_size_t)0xff);
807 if (stacksize == 0)
808 {
809 stacksize = 256 * 1024;
810 }
811 if (stacksize < 4096)
812 {
813 stacksize = 4096;
814 }
815 stack_buffer = VirtualAlloc(NULL, stacksize, MEM_COMMIT, PAGE_READWRITE);
816 if (NULL == stack_buffer)
817 {
818 break;
819 }
820 if (!VirtualProtect(stack_buffer, stacksize, PAGE_EXECUTE_READWRITE, &old_protect))
821 {
822 VirtualFree(stack_buffer, 0, MEM_RELEASE);
823 }
824
825 ret = GATE_RESULT_OK;
826 *ptr_stacksize = stacksize;
827 *ptr_callstack = stack_buffer;
828
829 } while (0);
830 return ret;
831 }
832 gate_result_t gate_callstack_destroy(void* ptr_callstack, gate_size_t stacksize)
833 {
834 gate_result_t ret = GATE_RESULT_FAILED;
835 if (ptr_callstack)
836 {
837 if (VirtualFree((LPVOID)ptr_callstack, 0, MEM_RELEASE))
838 {
839 ret = GATE_RESULT_OK;
840 }
841 }
842 return ret;
843 }
844
845
846 gate_result_t gate_callstack_run(void* ptr_callstack, gate_size_t stacksize,
847 gate_callstack_context_t* ptr_caller_ctx_to_save,
848 gate_entrypoint_t entry_func, void* param,
849 gate_result_t* ptr_returnvalue)
850 {
851 gate_result_t ret = GATE_RESULT_FAILED;
852 gate_size_t stack_addr = (gate_size_t)ptr_callstack;
853 gate_callstack_context_t dummy_context = GATE_INIT_EMPTY;
854 void* entry_ret = NULL;
855
856 #if !defined(GATE_SYS_WINCE)
857 gate_size_t stack_top = stack_addr + (stacksize - 4);
858 stack_top -= (stack_top & 0x0f);
859 entry_ret = gate_win32_callstack_create((void*)stack_top,
860 (ptr_caller_ctx_to_save != NULL) ? ptr_caller_ctx_to_save : &dummy_context,
861 entry_func, param);
862 ret = GATE_RESULT_OK;
863 #endif
864 if (ptr_returnvalue)
865 {
866 *ptr_returnvalue = (gate_result_t)(gate_intptr_t)entry_ret;
867 }
868 return ret;
869 }
870
871 gate_result_t gate_callstack_switch(gate_callstack_context_t* current_to_save,
872 gate_callstack_context_t* next_to_load)
873 {
874 gate_result_t ret = GATE_RESULT_NOTIMPLEMENTED;
875 #if !defined(GATE_SYS_WINCE)
876 if (NULL == gate_win32_callstack_switch(current_to_save, next_to_load))
877 {
878 ret = GATE_RESULT_FAILED;
879 }
880 else
881 {
882 ret = GATE_RESULT_OK;
883 }
884 #endif
885 return ret;
886 }
887
888 #else /* GATE_COMPILE_ASM_SUPPORT */
889
890 /* use FIBERs if no native ASM support is present */
891
892 typedef struct gate_callstack_fiber_class
893 {
894 LPVOID fiber;
895 LPVOID parent;
896 gate_entrypoint_t entrypoint;
897 void* param;
898 gate_result_t result;
899
900 } gate_callstack_fiber_t;
901
902
903 static VOID WINAPI gate_callstack_fiber_dispatcher(LPVOID lpFiberParameter)
904 {
905 gate_callstack_fiber_t* ptr_fiber = (gate_callstack_fiber_t*)lpFiberParameter;
906 if (ptr_fiber && ptr_fiber->entrypoint)
907 {
908 ptr_fiber->result = ptr_fiber->entrypoint(ptr_fiber->param);
909 gate_win32_fibers()->Win32SwitchToFiber(ptr_fiber->parent);
910 }
911 }
912
913
914 gate_result_t gate_callstack_create(gate_size_t* ptr_stacksize, void** ptr_callstack)
915 {
916 gate_result_t ret = GATE_RESULT_FAILED;
917 gate_callstack_fiber_t* ptr_fiber = NULL;
918 gate_size_t stacksize = 0;
919 gate_win32_fibers_t* fibers = NULL;
920
921 do
922 {
923 if (!ptr_callstack || !ptr_stacksize)
924 {
925 ret = GATE_RESULT_INVALIDARG;
926 break;
927 }
928 stacksize = *ptr_stacksize;
929 stacksize &= ~((gate_size_t)0xff);
930 if (stacksize == 0)
931 {
932 stacksize = 256 * 1024;
933 }
934 if (stacksize < 4096)
935 {
936 stacksize = 4096;
937 }
938
939 fibers = gate_win32_fibers();
940
941 if (!fibers)
942 {
943 ret = GATE_RESULT_NOTSUPPORTED;
944 break;
945 }
946
947 ptr_fiber = (gate_callstack_fiber_t*)gate_mem_alloc(sizeof(gate_callstack_fiber_t));
948 if (!ptr_fiber)
949 {
950 ret = GATE_RESULT_OUTOFMEMORY;
951 break;
952 }
953 gate_mem_clear(ptr_fiber, sizeof(gate_callstack_fiber_t));
954
955 ptr_fiber->fiber = fibers->Win32CreateFiber(stacksize, &gate_callstack_fiber_dispatcher, ptr_fiber);
956 if (ptr_fiber == NULL)
957 {
958 ret = gate_platform_print_last_error(NULL, 0);
959 break;
960 }
961
962 *ptr_callstack = (void*)ptr_fiber;
963 *ptr_stacksize = stacksize;
964 ptr_fiber = NULL;
965 ret = GATE_RESULT_OK;
966 } while (0);
967
968 if (ptr_fiber)
969 {
970 gate_mem_dealloc(ptr_fiber);
971 }
972 return ret;
973 }
974 gate_result_t gate_callstack_destroy(void* ptr_callstack, gate_size_t stacksize)
975 {
976 gate_result_t ret = GATE_RESULT_FAILED;
977 gate_callstack_fiber_t* ptr_fiber = (gate_callstack_fiber_t*)ptr_callstack;
978
979 GATE_UNUSED_ARG(stacksize);
980
981 if (ptr_fiber)
982 {
983 gate_win32_fibers_t* fibers = gate_win32_fibers();
984 if (fibers)
985 {
986 fibers->Win32DeleteFiber(ptr_fiber->fiber);
987 }
988 gate_mem_dealloc(ptr_fiber);
989 ret = GATE_RESULT_OK;
990 }
991 return ret;
992 }
993
994
995 gate_result_t gate_callstack_run(void* ptr_callstack, gate_size_t stacksize,
996 gate_callstack_context_t* ptr_caller_ctx_to_save,
997 gate_entrypoint_t entry_func, void* param,
998 gate_result_t* ptr_returnvalue)
999 {
1000 gate_result_t ret = GATE_RESULT_FAILED;
1001 gate_callstack_fiber_t* ptr_fiber = (gate_callstack_fiber_t*)ptr_callstack;
1002 gate_callstack_context_t local_context = GATE_INIT_EMPTY;
1003 LPVOID current_fiber = NULL;
1004 gate_bool_t was_fiber = false;
1005
1006 GATE_UNUSED_ARG(stacksize);
1007
1008 do
1009 {
1010 gate_win32_fibers_t* fibers = gate_win32_fibers();
1011 if (!fibers)
1012 {
1013 break;
1014 }
1015
1016 if (NULL == ptr_caller_ctx_to_save)
1017 {
1018 ptr_caller_ctx_to_save = &local_context;
1019 }
1020
1021 ptr_fiber->entrypoint = entry_func;
1022 ptr_fiber->param = param;
1023
1024 current_fiber = fibers->Win32ConvertThreadToFiber(NULL);
1025 if (current_fiber != NULL)
1026 {
1027 /* previously a thread, now a fiber */
1028 was_fiber = false;
1029 }
1030 else
1031 {
1032 /* was already a fiber, retrieve its handle*/
1033 was_fiber = true;
1034 current_fiber = fibers->Win32GetCurrentFiber();
1035 if (NULL == current_fiber)
1036 {
1037 ret = GATE_RESULT_OUTOFRESOURCES;
1038 break;
1039 }
1040 }
1041 ptr_fiber->parent = current_fiber;
1042 ptr_caller_ctx_to_save->store[0] = current_fiber;
1043
1044 fibers->Win32SwitchToFiber(ptr_fiber->fiber);
1045
1046 ptr_fiber->parent = NULL;
1047
1048 if (ptr_returnvalue)
1049 {
1050 *ptr_returnvalue = ptr_fiber->result;
1051 }
1052
1053 if (!was_fiber)
1054 {
1055 if (fibers->Win32ConvertFiberToThread)
1056 {
1057 fibers->Win32ConvertFiberToThread();
1058 }
1059 }
1060 ret = GATE_RESULT_OK;
1061 } while (0);
1062
1063 return ret;
1064 }
1065
1066 gate_result_t gate_callstack_switch(gate_callstack_context_t* current_to_save,
1067 gate_callstack_context_t* next_to_load)
1068 {
1069 gate_result_t ret = GATE_RESULT_NOTIMPLEMENTED;
1070 #if !defined(GATE_SYS_WINCE)
1071 gate_win32_fibers_t* fibers = gate_win32_fibers();
1072 if (fibers->Win32SwitchToFiber)
1073 {
1074 PVOID current_fiber = fibers->Win32GetCurrentFiber();
1075 PVOID next_fiber = next_to_load->store[0];
1076 current_to_save->store[0] = current_fiber;
1077 fibers->Win32SwitchToFiber(next_fiber);
1078 ret = GATE_RESULT_OK;
1079 }
1080 #endif
1081 return ret;
1082 }
1083
1084
1085 #endif /* GATE_COMPILE_ASM_SUPPORT */
1086
1087
1088 #endif /* GATE_CALLSTACKS_WIN32_CALLSTACK_IMPL */
1089
1090
1091
1092
1093
1094
1095 #if defined(GATE_CALLSTACKS_POSIX_IMPL)
1096
1097 #include <stdlib.h>
1098 #include <dlfcn.h>
1099 #include <stdio.h>
1100 #include <setjmp.h>
1101 #include <signal.h>
1102 #include <sys/mman.h>
1103 #include "gate/platform/posix/posixcallstack.h"
1104 #include "gate/platform/posix/posixucontext.h"
1105 #include "gate/debugging.h"
1106
1107 #if defined(HAVE_BACKTRACE)
1108 #include <execinfo.h>
1109 #endif
1110
1111 #define DEFAULT_STACK_SIZE (1024 * 1024)
1112 #define MINMUM_STACK_SIZE (4 * 1024)
1113
1114 #if defined(GATE_SYS_FREEBSD)
1115 typedef size_t backtrace_length_t;
1116 #else
1117 typedef int backtrace_length_t;
1118 #endif
1119
1120 typedef backtrace_length_t(*backtrace_func_t)(void** buffer, backtrace_length_t size);
1121 typedef char** (*backtrace_symbols_func_t)(void* const* buffer, backtrace_length_t size);
1122
1123 static backtrace_func_t volatile backtrace_func = NULL;
1124 static backtrace_symbols_func_t volatile backtrace_symbols_func = NULL;
1125
1126
1127 #if defined(HAVE_BACKTRACE)
1128
1129 2 static gate_bool_t load_backtrace_functions()
1130 {
1131 2 backtrace_func = &backtrace;
1132 2 backtrace_symbols_func = &backtrace_symbols;
1133 2 return true;
1134 }
1135
1136 #else
1137
1138 static void* volatile execinfo_symbols = NULL;
1139
1140 static void close_execinfo_atexit()
1141 {
1142 if (NULL != execinfo_symbols)
1143 {
1144 void* const lib = execinfo_symbols;
1145 execinfo_symbols = NULL;
1146 dlclose(lib);
1147 }
1148 }
1149
1150 static void load_backtrace_from_libexecinfo()
1151 {
1152 if (execinfo_symbols == NULL)
1153 {
1154 execinfo_symbols = gate_posix_dlopen("libexecinfo.so", RTLD_GLOBAL | RTLD_NOW);
1155 if (NULL != execinfo_symbols)
1156 {
1157 gate_platform_atexit(&close_execinfo_atexit);
1158 }
1159 }
1160
1161 if (execinfo_symbols != NULL)
1162 {
1163 backtrace_func = dlsym(execinfo_symbols, "backtrace");
1164 backtrace_symbols_func = dlsym(execinfo_symbols, "backtrace_symbols");
1165 }
1166 }
1167
1168 static gate_bool_t load_backtrace_functions()
1169 {
1170 if (!backtrace_func || backtrace_symbols_func)
1171 {
1172 /* lookup in global function table: */
1173 backtrace_func = gate_posix_global_sym("backtrace");
1174 backtrace_symbols_func = gate_posix_global_sym("backtrace_symbols");
1175
1176 if ((NULL == backtrace_func) || (NULL == backtrace_symbols_func))
1177 {
1178 load_backtrace_from_libexecinfo();
1179 }
1180 }
1181 return backtrace_func && backtrace_symbols_func;
1182 }
1183
1184 #endif
1185
1186
1187 1 gate_result_t gate_callstack_trace(gate_callstack_address_t* addresses, gate_size_t addresses_count, gate_size_t* addresses_used)
1188 {
1189 1 gate_result_t ret = GATE_RESULT_OK;
1190 void* stackaddress[1024];
1191 backtrace_length_t index;
1192 backtrace_length_t stackaddress_count;
1193
1194 1 load_backtrace_functions();
1195
1196
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (!backtrace_func)
1197 {
1198 GATE_DEBUG_TRACE("backtrace() not supported");
1199 return GATE_RESULT_NOTSUPPORTED;
1200 }
1201
1202 1 stackaddress_count = backtrace_func(stackaddress, sizeof(stackaddress) / sizeof(stackaddress[0]));
1203
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (stackaddress_count > 0)
1204 {
1205
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (stackaddress_count > (backtrace_length_t)addresses_count)
1206 {
1207 stackaddress_count = (backtrace_length_t)addresses_count;
1208 }
1209
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 1 times.
13 for (index = 0; index != stackaddress_count; ++index)
1210 {
1211 12 addresses[index] = stackaddress[index];
1212 }
1213 1 *addresses_used = (gate_size_t)stackaddress_count;
1214 1 ret = GATE_RESULT_OK;
1215 }
1216 else
1217 {
1218 *addresses_used = 0;
1219 }
1220 1 return ret;
1221 }
1222
1223 1 gate_result_t gate_callstack_print(gate_callstack_address_t address, char* buffer, gate_size_t buffer_length, gate_size_t* buffer_used)
1224 {
1225 gate_size_t len;
1226 char** names;
1227
1228 1 load_backtrace_functions();
1229
1230
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (backtrace_symbols_func == NULL)
1231 {
1232 return GATE_RESULT_NOTSUPPORTED;
1233 }
1234
1235 1 names = backtrace_symbols_func((void**)&address, 1);
1236
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (names == NULL)
1237 {
1238 return GATE_RESULT_FAILED;
1239 }
1240
1241 1 len = gate_str_print_text(buffer, buffer_length, names[0], gate_str_length(names[0]));
1242 1 free(names);
1243 1 buffer[len] = 0;
1244 1 *buffer_used = len;
1245 1 return GATE_RESULT_OK;
1246 }
1247
1248 #define GATE_CALLSTACK_MALLOC 1
1249
1250 6 static void check_stack_size(gate_size_t* ptr_stacksize)
1251 {
1252
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 5 times.
6 if (*ptr_stacksize == 0)
1253 {
1254 1 *ptr_stacksize = DEFAULT_STACK_SIZE;
1255 }
1256
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if (*ptr_stacksize < MINMUM_STACK_SIZE)
1257 {
1258 *ptr_stacksize = MINMUM_STACK_SIZE;
1259 }
1260 /* apply 32-bit maximum and 16-bit alignment */
1261 6 *ptr_stacksize &= 0xfffffff0;
1262 6 }
1263
1264 2 gate_result_t gate_callstack_create(gate_size_t* ptr_stacksize, void** ptr_callstack)
1265 {
1266 void* ptr;
1267 gate_size_t stacksize;
1268
1269 2 check_stack_size(ptr_stacksize);
1270 2 stacksize = *ptr_stacksize;
1271
1272 #if defined(GATE_CALLSTACK_MALLOC)
1273 2 ptr = gate_mem_alloc(stacksize);
1274 #else
1275 ptr = mmap(NULL, stacksize, PROT_READ | PROT_WRITE /*| PROT_EXEC*/,
1276 MAP_PRIVATE | MAP_ANONYMOUS /*| MAP_GROWSDOWN*/ | MAP_STACK, -1, 0);
1277 if (ptr == MAP_FAILED)
1278 {
1279 return GATE_RESULT_FAILED;
1280 }
1281 #endif
1282
1283
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (ptr_callstack)
1284 {
1285 2 *ptr_callstack = ptr;
1286 }
1287 else
1288 {
1289 gate_callstack_destroy(ptr, stacksize);
1290 }
1291 2 return GATE_RESULT_OK;
1292 }
1293
1294 2 gate_result_t gate_callstack_destroy(void* ptr_callstack, gate_size_t stacksize)
1295 {
1296 2 check_stack_size(&stacksize);
1297
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (!ptr_callstack)
1298 {
1299 return GATE_RESULT_INVALIDARG;
1300 }
1301 #if defined(GATE_CALLSTACK_MALLOC)
1302 2 gate_mem_dealloc(ptr_callstack);
1303 #else
1304 if (0 == munmap(ptr_callstack, stacksize))
1305 {
1306 return GATE_RESULT_OK;
1307 }
1308 else
1309 {
1310 return GATE_RESULT_FAILED;
1311 }
1312 #endif
1313 2 return GATE_RESULT_OK;
1314 }
1315
1316 typedef struct gate_callstack_session_class
1317 {
1318 gate_entrypoint_t entry_func;
1319 void* param;
1320 gate_result_t* ptr_returnvalue;
1321 gate_callstack_context_t* parent_context;
1322 } gate_callstack_session_t;
1323
1324 union gate_callstack_entry_dispatcher_args
1325 {
1326 gate_callstack_session_t* session;
1327 int args[4];
1328 };
1329
1330 2 static void gate_callstack_entry_dispatcher(int a0, int a1, int a2, int a3)
1331 {
1332 union gate_callstack_entry_dispatcher_args args;
1333 gate_result_t result;
1334 gate_callstack_context_t dummy_context;
1335
1336 2 args.args[0] = a0;
1337 2 args.args[1] = a1;
1338 2 args.args[2] = a2;
1339 2 args.args[3] = a3;
1340 2 result = args.session->entry_func(args.session->param);
1341
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (args.session->ptr_returnvalue)
1342 {
1343 1 *args.session->ptr_returnvalue = result;
1344 }
1345 1 gate_posix_uctx_set(args.session->parent_context);
1346 }
1347
1348 2 gate_result_t gate_callstack_run(void* ptr_callstack, gate_size_t stacksize,
1349 gate_callstack_context_t* ptr_caller_ctx_to_save,
1350 gate_entrypoint_t entry_func, void* param,
1351 gate_result_t* ptr_returnvalue)
1352 {
1353 2 gate_result_t ret = GATE_RESULT_NOTIMPLEMENTED;
1354 2 gate_size_t stack_addr = (gate_size_t)ptr_callstack;
1355 void* stack_top;
1356 2 gate_callstack_context_t context = GATE_INIT_EMPTY;
1357 2 gate_callstack_context_t new_stack_context = GATE_INIT_EMPTY;
1358 void* entry_ret;
1359 gate_callstack_session_t session;
1360 union gate_callstack_entry_dispatcher_args disp_args;
1361
1362 2 check_stack_size(&stacksize);
1363
1364
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (NULL == ptr_caller_ctx_to_save)
1365 {
1366 1 ptr_caller_ctx_to_save = &context;
1367 }
1368
1369
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if (gate_posix_callstack_supported())
1370 {
1371 stack_addr += (stacksize - 4);
1372 stack_addr -= (stack_addr & 0x0f);
1373 stack_top = (void*)stack_addr;
1374
1375 entry_ret = gate_posix_callstack_create(stack_top, ptr_caller_ctx_to_save, entry_func, param);
1376 ret = GATE_RESULT_OK;
1377 if (ptr_returnvalue)
1378 {
1379 *ptr_returnvalue = (gate_result_t)(gate_intptr_t)entry_ret;
1380 }
1381 }
1382 else
1383 {
1384 do
1385 {
1386 2 ret = gate_posix_uctx_init();
1387
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 GATE_BREAK_IF_FAILED(ret);
1388
1389
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if (0 != gate_posix_uctx_get(&new_stack_context))
1390 {
1391 ret = GATE_RESULT_FAILED;
1392 break;
1393 }
1394 2 gate_posix_uctx_setup(&new_stack_context, ptr_callstack, stacksize);
1395 2 session.entry_func = entry_func;
1396 2 session.param = param;
1397 2 session.ptr_returnvalue = ptr_returnvalue;
1398 2 session.parent_context = ptr_caller_ctx_to_save;
1399 2 disp_args.session = &session;
1400 2 gate_posix_uctx_make(&new_stack_context, &gate_callstack_entry_dispatcher,
1401 disp_args.args[0], disp_args.args[1], disp_args.args[2], disp_args.args[3]);
1402 2 gate_posix_uctx_swap(ptr_caller_ctx_to_save, &new_stack_context);
1403
1404 2 ret = GATE_RESULT_OK;
1405 } while (0);
1406 }
1407 2 return ret;
1408 }
1409
1410 1 gate_result_t gate_callstack_switch(gate_callstack_context_t* current_to_save, gate_callstack_context_t* next_to_load)
1411 {
1412 1 gate_result_t ret = GATE_RESULT_NOTIMPLEMENTED;
1413
1414
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (gate_posix_callstack_supported())
1415 {
1416 if (NULL == gate_posix_callstack_switch(current_to_save, next_to_load))
1417 {
1418 ret = GATE_RESULT_FAILED;
1419 }
1420 else
1421 {
1422 ret = GATE_RESULT_OK;
1423 }
1424 }
1425 else
1426 {
1427 1 gate_posix_uctx_swap(current_to_save, next_to_load);
1428 ret = GATE_RESULT_OK;
1429 }
1430 return ret;
1431 }
1432
1433
1434 typedef struct gate_native_jmp_buf_class
1435 {
1436 sigjmp_buf buf;
1437 } gate_native_jmp_buf_t;
1438
1439 6 gate_result_t gate_callstack_fork(gate_callstack_jmpbuf_t* jmpbuffer,
1440 gate_entrypoint_t entry, void* param,
1441 gate_entrypoint_t branch, void* branch_param)
1442 {
1443 6 gate_result_t volatile ret = GATE_RESULT_OK;
1444 6 gate_native_jmp_buf_t* volatile ptr_buf = (gate_native_jmp_buf_t*)jmpbuffer;
1445
1446 if (sizeof(gate_callstack_jmpbuf_t) < sizeof(gate_native_jmp_buf_t))
1447 {
1448 /* generic jump buffer is to small */
1449 return GATE_RESULT_OUTOFMEMORY;
1450 }
1451
1452
2/2
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 2 times.
6 if (0 == sigsetjmp(ptr_buf->buf, 1))
1453 {
1454
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 if (entry)
1455 {
1456 6 ret = entry(param);
1457 }
1458 }
1459 else
1460 {
1461
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (branch)
1462 {
1463 2 ret = branch(branch_param);
1464 }
1465 }
1466 6 gate_mem_clear(jmpbuffer, sizeof(gate_callstack_jmpbuf_t));
1467 6 return ret;
1468 }
1469
1470 2 void gate_callstack_jump(gate_callstack_jmpbuf_t* jmpbuffer)
1471 {
1472 2 gate_native_jmp_buf_t* ptr_buf = (gate_native_jmp_buf_t*)jmpbuffer;
1473 2 siglongjmp(ptr_buf->buf, 1);
1474 }
1475
1476
1477 #endif /* GATE_CALLSTACKS_POSIX_IMPL */
1478
1479
1480
1481 #if defined(GATE_CALLSTACKS_EFI_IMPL)
1482
1483 #include "gate/platform/efi/efi_gate.h"
1484
1485 #if defined(GATE_COMPILER_MSVC)
1486 # include "gate/platform/windows/win32callstack.h"
1487 # define gate_callstack_create_impl gate_win32_callstack_create
1488 # define gate_callstack_switch_impl gate_win32_callstack_switch
1489 #else
1490 # include "gate/platform/posix/posixcallstack.h"
1491 # define gate_callstack_create_impl gate_posix_callstack_create
1492 # define gate_callstack_switch_impl gate_posix_callstack_switch
1493 #endif
1494
1495
1496
1497 gate_result_t gate_callstack_trace(gate_callstack_address_t* addresses, gate_size_t addresses_count, gate_size_t* addresses_used)
1498 {
1499 /*TODO*/
1500 return GATE_RESULT_NOTIMPLEMENTED;
1501 }
1502 gate_result_t gate_callstack_print(gate_callstack_address_t address, char* buffer, gate_size_t buffer_length, gate_size_t* buffer_used)
1503 {
1504 /*TODO*/
1505 return GATE_RESULT_NOTIMPLEMENTED;
1506 }
1507
1508
1509
1510 gate_result_t gate_callstack_create(gate_size_t* ptr_stacksize, void** ptr_callstack)
1511 {
1512 gate_result_t ret = GATE_RESULT_FAILED;
1513 void* ptr;
1514 gate_size_t stacksize = 0;
1515
1516 if (!ptr_stacksize || !ptr_callstack)
1517 {
1518 ret = GATE_RESULT_INVALIDARG;
1519 }
1520 else
1521 {
1522 stacksize = *ptr_stacksize;
1523 if (stacksize < 4096)
1524 {
1525 stacksize = 4096;
1526 }
1527 stacksize &= ~((gate_size_t)0xff);
1528 if (stacksize == 0)
1529 {
1530 stacksize = 256 * 1024;
1531 }
1532 ptr = gate_mem_alloc(stacksize);
1533 if (ptr)
1534 {
1535 ret = GATE_RESULT_OK;
1536 if (ptr_callstack)
1537 {
1538 *ptr_callstack = ptr;
1539 *ptr_stacksize = stacksize;
1540 }
1541 else
1542 {
1543 gate_mem_dealloc(ptr);
1544 }
1545 }
1546 }
1547
1548 return ret;
1549 }
1550
1551 gate_result_t gate_callstack_destroy(void* ptr_callstack, gate_size_t stacksize)
1552 {
1553 if (!ptr_callstack)
1554 {
1555 return GATE_RESULT_INVALIDARG;
1556 }
1557
1558 gate_mem_dealloc(ptr_callstack);
1559 return GATE_RESULT_OK;
1560 }
1561
1562
1563 gate_result_t gate_callstack_run(void* ptr_callstack, gate_size_t stacksize,
1564 gate_callstack_context_t* ptr_caller_ctx_to_save,
1565 gate_entrypoint_t entry_func, void* param,
1566 gate_result_t* ptr_returnvalue)
1567 {
1568 gate_result_t ret = GATE_RESULT_NOTIMPLEMENTED;
1569 gate_size_t stack_addr = (gate_size_t)ptr_callstack;
1570 void* stack_top;
1571 gate_callstack_context_t context = GATE_INIT_EMPTY;
1572 void* entry_ret;
1573
1574 if (NULL == ptr_caller_ctx_to_save)
1575 {
1576 ptr_caller_ctx_to_save = &context;
1577 }
1578
1579 stack_addr += (stacksize - 4);
1580 stack_addr -= (stack_addr & 0x0f);
1581 stack_top = (void*)stack_addr;
1582
1583 entry_ret = gate_callstack_create_impl(stack_top, ptr_caller_ctx_to_save, entry_func, param);
1584 ret = GATE_RESULT_OK;
1585 if (ptr_returnvalue)
1586 {
1587 *ptr_returnvalue = (gate_result_t)(gate_intptr_t)entry_ret;
1588 }
1589 return ret;
1590 }
1591
1592 gate_result_t gate_callstack_switch(gate_callstack_context_t* current_to_save,
1593 gate_callstack_context_t* next_to_load)
1594 {
1595 gate_result_t ret = GATE_RESULT_NOTIMPLEMENTED;
1596 if (NULL == gate_callstack_switch_impl(current_to_save, next_to_load))
1597 {
1598 ret = GATE_RESULT_FAILED;
1599 }
1600 else
1601 {
1602 ret = GATE_RESULT_OK;
1603 }
1604 return ret;
1605 }
1606
1607 #endif /* GATE_CALLSTACKS_EFI_IMPL */
1608
1609
1610 #if defined(GATE_CALLSTACKS_DOS_IMPL)
1611
1612 #include "gate/platforms.h"
1613 #include "gate/platform/dos/doscallstack.h"
1614
1615
1616 gate_result_t gate_callstack_trace(gate_callstack_address_t* addresses, gate_size_t addresses_count, gate_size_t* addresses_used)
1617 {
1618 GATE_UNUSED_ARG(addresses);
1619 GATE_UNUSED_ARG(addresses_count);
1620 GATE_UNUSED_ARG(addresses_used);
1621 return GATE_RESULT_FAILED;
1622 }
1623
1624 gate_result_t gate_callstack_print(gate_callstack_address_t address, char* buffer, gate_size_t buffer_length, gate_size_t* buffer_used)
1625 {
1626 GATE_UNUSED_ARG(address);
1627 GATE_UNUSED_ARG(buffer);
1628 GATE_UNUSED_ARG(buffer_length);
1629 GATE_UNUSED_ARG(buffer_used);
1630 return GATE_RESULT_FAILED;
1631 }
1632
1633 #define DEFAULT_STACK_SIZE 16384
1634
1635 gate_result_t gate_callstack_create(gate_size_t* ptr_stacksize, void** ptr_callstack)
1636 {
1637 gate_result_t result = GATE_RESULT_OUTOFMEMORY;
1638 gate_size_t para_count;
1639 unsigned segment = 0;
1640 gate_size_t stacksize;
1641
1642 if (!ptr_stacksize || !ptr_callstack)
1643 {
1644 return GATE_RESULT_INVALIDARG;
1645 }
1646 stacksize = *ptr_stacksize;
1647
1648 if (stacksize == 0)
1649 {
1650 stacksize = DEFAULT_STACK_SIZE;
1651 }
1652 if (stacksize > 32768)
1653 {
1654 stacksize = 32768;
1655 }
1656 if (stacksize < 256)
1657 {
1658 stacksize = 256;
1659 }
1660 stacksize = (stacksize + 0x0f) & ~((gate_size_t)0x0f);
1661
1662 para_count = (stacksize + 15) / 16;
1663
1664 result = gate_dos_alloc(para_count, &segment);
1665 if (GATE_SUCCEEDED(result))
1666 {
1667 *ptr_callstack = (void*)gate_dos_farptr(segment, 0);
1668 *ptr_stacksize = stacksize;
1669 }
1670 return result;
1671 }
1672
1673 gate_result_t gate_callstack_destroy(void* ptr_callstack, gate_size_t stacksize)
1674 {
1675 unsigned segment = gate_dos_farptr_seg(ptr_callstack);
1676 GATE_UNUSED_ARG(stacksize);
1677 return gate_dos_dealloc(segment);
1678 }
1679
1680
1681 gate_result_t gate_callstack_run(void* ptr_callstack, gate_size_t stacksize, gate_callstack_context_t* ptr_caller_ctx_to_save,
1682 gate_entrypoint_t entry_func, void* param, gate_result_t* ptr_returnvalue)
1683 {
1684 gate_result_t result;
1685 char __far* ptr_stack_begin = (char __far*)ptr_callstack;
1686 char __far* ptr_stack_end;
1687
1688 if (stacksize == 0)
1689 {
1690 stacksize = DEFAULT_STACK_SIZE;
1691 }
1692 gate_mem_clear(ptr_callstack, stacksize);
1693 ptr_stack_end = ptr_stack_begin + stacksize - 2;
1694
1695
1696 result = gate_dos_callstack_create(ptr_stack_end, ptr_caller_ctx_to_save, entry_func, param);
1697 if (GATE_SUCCEEDED(result))
1698 {
1699 if (ptr_returnvalue)
1700 {
1701 *ptr_returnvalue = result;
1702 }
1703 }
1704 return result;
1705 }
1706
1707 gate_result_t gate_callstack_switch(gate_callstack_context_t* current_to_save,
1708 gate_callstack_context_t* next_to_load)
1709 {
1710 return gate_dos_callstack_switch(current_to_save, next_to_load);
1711 }
1712
1713 #endif /* GATE_CALLSTACKS_DOS_IMPL */
1714
1715
1716
1717 #if defined(GATE_CALLSTACKS_NO_IMPL)
1718
1719 gate_result_t gate_callstack_trace(gate_callstack_address_t* addresses, gate_size_t addresses_count, gate_size_t* addresses_used)
1720 {
1721 (void)addresses;
1722 (void)addresses_count;
1723 (void)addresses_used;
1724 return GATE_RESULT_NOTIMPLEMENTED;
1725 }
1726 gate_result_t gate_callstack_print(gate_callstack_address_t address, char* buffer, gate_size_t buffer_length, gate_size_t* buffer_used)
1727 {
1728 (void)address;
1729 (void)buffer;
1730 (void)buffer_length;
1731 (void)buffer_used;
1732 return GATE_RESULT_NOTIMPLEMENTED;
1733 }
1734
1735 gate_result_t gate_callstack_create(gate_size_t* ptr_stacksize, void** ptr_callstack)
1736 {
1737 (void)ptr_stacksize;
1738 (void)ptr_callstack;
1739 return GATE_RESULT_NOTIMPLEMENTED;
1740 }
1741 gate_result_t gate_callstack_destroy(void* ptr_callstack, gate_size_t stacksize)
1742 {
1743 (void)ptr_callstack;
1744 (void)stacksize;
1745 return GATE_RESULT_NOTIMPLEMENTED;
1746 }
1747 gate_result_t gate_callstack_run(void* ptr_callstack, gate_size_t stacksize, gate_callstack_context_t* ptr_caller_ctx_to_save,
1748 gate_entrypoint_t entry_func, void* param, gate_result_t* ptr_returnvalue)
1749 {
1750 (void)ptr_callstack;
1751 (void)stacksize;
1752 (void)ptr_caller_ctx_to_save;
1753 (void)entry_func;
1754 (void)param;
1755 (void)ptr_returnvalue;
1756 return GATE_RESULT_NOTIMPLEMENTED;
1757 }
1758
1759 gate_result_t gate_callstack_switch(gate_callstack_context_t* current_to_save, gate_callstack_context_t* next_to_load)
1760 {
1761 (void)current_to_save;
1762 (void)next_to_load;
1763 return GATE_RESULT_NOTIMPLEMENTED;
1764 }
1765
1766
1767 #endif /* GATE_CALLSTACKS_NO_IMPL */
1768
1769