GCC Code Coverage Report


Directory: src/gate/
File: src/gate/callstacks.c
Date: 2025-09-14 13:10:38
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 void* stack_top;
854 gate_callstack_context_t context = GATE_INIT_EMPTY;
855 void* entry_ret = NULL;
856
857 if (NULL == ptr_caller_ctx_to_save)
858 {
859 ptr_caller_ctx_to_save = &context;
860 }
861
862 stack_addr += (stacksize - 4);
863 stack_addr -= (stack_addr & 0x0f);
864 stack_top = (void*)stack_addr;
865
866 #if !defined(GATE_SYS_WINCE)
867 entry_ret = gate_win32_callstack_create(stack_top, ptr_caller_ctx_to_save, entry_func, param);
868 ret = GATE_RESULT_OK;
869 #endif
870 if (ptr_returnvalue)
871 {
872 *ptr_returnvalue = (gate_result_t)(gate_intptr_t)entry_ret;
873 }
874 return ret;
875 }
876
877 gate_result_t gate_callstack_switch(gate_callstack_context_t* current_to_save,
878 gate_callstack_context_t* next_to_load)
879 {
880 gate_result_t ret = GATE_RESULT_NOTIMPLEMENTED;
881 #if !defined(GATE_SYS_WINCE)
882 if (NULL == gate_win32_callstack_switch(current_to_save, next_to_load))
883 {
884 ret = GATE_RESULT_FAILED;
885 }
886 else
887 {
888 ret = GATE_RESULT_OK;
889 }
890 #endif
891 return ret;
892 }
893
894 #else /* GATE_COMPILE_ASM_SUPPORT */
895
896 /* use FIBERs if no native ASM support is present */
897
898 typedef struct gate_callstack_fiber_class
899 {
900 LPVOID fiber;
901 LPVOID parent;
902 gate_entrypoint_t entrypoint;
903 void* param;
904 gate_result_t result;
905
906 } gate_callstack_fiber_t;
907
908
909 static VOID WINAPI gate_callstack_fiber_dispatcher(LPVOID lpFiberParameter)
910 {
911 gate_callstack_fiber_t* ptr_fiber = (gate_callstack_fiber_t*)lpFiberParameter;
912 if (ptr_fiber && ptr_fiber->entrypoint)
913 {
914 ptr_fiber->result = ptr_fiber->entrypoint(ptr_fiber->param);
915 gate_win32_fibers()->Win32SwitchToFiber(ptr_fiber->parent);
916 }
917 }
918
919
920 gate_result_t gate_callstack_create(gate_size_t* ptr_stacksize, void** ptr_callstack)
921 {
922 gate_result_t ret = GATE_RESULT_FAILED;
923 gate_callstack_fiber_t* ptr_fiber = NULL;
924 gate_size_t stacksize = 0;
925 gate_win32_fibers_t* fibers = NULL;
926
927 do
928 {
929 if (!ptr_callstack || !ptr_stacksize)
930 {
931 ret = GATE_RESULT_INVALIDARG;
932 break;
933 }
934 stacksize = *ptr_stacksize;
935 stacksize &= ~((gate_size_t)0xff);
936 if (stacksize == 0)
937 {
938 stacksize = 256 * 1024;
939 }
940 if (stacksize < 4096)
941 {
942 stacksize = 4096;
943 }
944
945 fibers = gate_win32_fibers();
946
947 if (!fibers)
948 {
949 ret = GATE_RESULT_NOTSUPPORTED;
950 break;
951 }
952
953 ptr_fiber = (gate_callstack_fiber_t*)gate_mem_alloc(sizeof(gate_callstack_fiber_t));
954 if (!ptr_fiber)
955 {
956 ret = GATE_RESULT_OUTOFMEMORY;
957 break;
958 }
959 gate_mem_clear(ptr_fiber, sizeof(gate_callstack_fiber_t));
960
961 ptr_fiber->fiber = fibers->Win32CreateFiber(stacksize, &gate_callstack_fiber_dispatcher, ptr_fiber);
962 if (ptr_fiber == NULL)
963 {
964 ret = gate_platform_print_last_error(NULL, 0);
965 break;
966 }
967
968 *ptr_callstack = (void*)ptr_fiber;
969 *ptr_stacksize = stacksize;
970 ptr_fiber = NULL;
971 ret = GATE_RESULT_OK;
972 } while (0);
973
974 if (ptr_fiber)
975 {
976 gate_mem_dealloc(ptr_fiber);
977 }
978 return ret;
979 }
980 gate_result_t gate_callstack_destroy(void* ptr_callstack, gate_size_t stacksize)
981 {
982 gate_result_t ret = GATE_RESULT_FAILED;
983 gate_callstack_fiber_t* ptr_fiber = (gate_callstack_fiber_t*)ptr_callstack;
984
985 GATE_UNUSED_ARG(stacksize);
986
987 if (ptr_fiber)
988 {
989 gate_win32_fibers_t* fibers = gate_win32_fibers();
990 if (fibers)
991 {
992 fibers->Win32DeleteFiber(ptr_fiber->fiber);
993 }
994 gate_mem_dealloc(ptr_fiber);
995 ret = GATE_RESULT_OK;
996 }
997 return ret;
998 }
999
1000
1001 gate_result_t gate_callstack_run(void* ptr_callstack, gate_size_t stacksize,
1002 gate_callstack_context_t* ptr_caller_ctx_to_save,
1003 gate_entrypoint_t entry_func, void* param,
1004 gate_result_t* ptr_returnvalue)
1005 {
1006 gate_result_t ret = GATE_RESULT_FAILED;
1007 gate_callstack_fiber_t* ptr_fiber = (gate_callstack_fiber_t*)ptr_callstack;
1008 gate_callstack_context_t local_context = GATE_INIT_EMPTY;
1009 LPVOID current_fiber = NULL;
1010 gate_bool_t was_fiber = false;
1011
1012 GATE_UNUSED_ARG(stacksize);
1013
1014 do
1015 {
1016 gate_win32_fibers_t* fibers = gate_win32_fibers();
1017 if (!fibers)
1018 {
1019 break;
1020 }
1021
1022 if (NULL == ptr_caller_ctx_to_save)
1023 {
1024 ptr_caller_ctx_to_save = &local_context;
1025 }
1026
1027 ptr_fiber->entrypoint = entry_func;
1028 ptr_fiber->param = param;
1029
1030 current_fiber = fibers->Win32ConvertThreadToFiber(NULL);
1031 if (current_fiber != NULL)
1032 {
1033 /* previously a thread, now a fiber */
1034 was_fiber = false;
1035 }
1036 else
1037 {
1038 /* was already a fiber, retrieve its handle*/
1039 was_fiber = true;
1040 current_fiber = fibers->Win32GetCurrentFiber();
1041 if (NULL == current_fiber)
1042 {
1043 ret = GATE_RESULT_OUTOFRESOURCES;
1044 break;
1045 }
1046 }
1047 ptr_fiber->parent = current_fiber;
1048 ptr_caller_ctx_to_save->store[0] = current_fiber;
1049
1050 fibers->Win32SwitchToFiber(ptr_fiber->fiber);
1051
1052 ptr_fiber->parent = NULL;
1053
1054 if (ptr_returnvalue)
1055 {
1056 *ptr_returnvalue = ptr_fiber->result;
1057 }
1058
1059 if (!was_fiber)
1060 {
1061 if (fibers->Win32ConvertFiberToThread)
1062 {
1063 fibers->Win32ConvertFiberToThread();
1064 }
1065 }
1066 ret = GATE_RESULT_OK;
1067 } while (0);
1068
1069 return ret;
1070 }
1071
1072 gate_result_t gate_callstack_switch(gate_callstack_context_t* current_to_save,
1073 gate_callstack_context_t* next_to_load)
1074 {
1075 gate_result_t ret = GATE_RESULT_NOTIMPLEMENTED;
1076 #if !defined(GATE_SYS_WINCE)
1077 gate_win32_fibers_t* fibers = gate_win32_fibers();
1078 if (fibers->Win32SwitchToFiber)
1079 {
1080 PVOID current_fiber = fibers->Win32GetCurrentFiber();
1081 PVOID next_fiber = next_to_load->store[0];
1082 current_to_save->store[0] = current_fiber;
1083 fibers->Win32SwitchToFiber(next_fiber);
1084 ret = GATE_RESULT_OK;
1085 }
1086 #endif
1087 return ret;
1088 }
1089
1090
1091 #endif /* GATE_COMPILE_ASM_SUPPORT */
1092
1093
1094 #endif /* GATE_CALLSTACKS_WIN32_CALLSTACK_IMPL */
1095
1096
1097
1098
1099
1100
1101 #if defined(GATE_CALLSTACKS_POSIX_IMPL)
1102
1103 #include <stdlib.h>
1104 #include <dlfcn.h>
1105 #include <stdio.h>
1106 #include <setjmp.h>
1107 #include <signal.h>
1108 #include <sys/mman.h>
1109 #include "gate/platform/posix/posixcallstack.h"
1110 #include "gate/platform/posix/posixucontext.h"
1111 #include "gate/debugging.h"
1112
1113 #if defined(HAVE_BACKTRACE)
1114 #include <execinfo.h>
1115 #endif
1116
1117 #define DEFAULT_STACK_SIZE (1024 * 1024)
1118 #define MINMUM_STACK_SIZE (4 * 1024)
1119
1120 #if defined(GATE_SYS_FREEBSD)
1121 typedef size_t backtrace_length_t;
1122 #else
1123 typedef int backtrace_length_t;
1124 #endif
1125
1126 typedef backtrace_length_t(*backtrace_func_t)(void** buffer, backtrace_length_t size);
1127 typedef char** (*backtrace_symbols_func_t)(void* const* buffer, backtrace_length_t size);
1128
1129 static backtrace_func_t volatile backtrace_func = NULL;
1130 static backtrace_symbols_func_t volatile backtrace_symbols_func = NULL;
1131
1132
1133 #if defined(HAVE_BACKTRACE)
1134
1135 2 static gate_bool_t load_backtrace_functions()
1136 {
1137 2 backtrace_func = &backtrace;
1138 2 backtrace_symbols_func = &backtrace_symbols;
1139 2 return true;
1140 }
1141
1142 #else
1143
1144 static void* volatile execinfo_symbols = NULL;
1145
1146 static void close_execinfo_atexit()
1147 {
1148 if (NULL != execinfo_symbols)
1149 {
1150 void* const lib = execinfo_symbols;
1151 execinfo_symbols = NULL;
1152 dlclose(lib);
1153 }
1154 }
1155
1156 static void load_backtrace_from_libexecinfo()
1157 {
1158 if (execinfo_symbols == NULL)
1159 {
1160 execinfo_symbols = gate_posix_dlopen("libexecinfo.so", RTLD_GLOBAL);
1161 if (NULL != execinfo_symbols)
1162 {
1163 gate_platform_atexit(&close_execinfo_atexit);
1164 }
1165 }
1166
1167 if (execinfo_symbols != NULL)
1168 {
1169 backtrace_func = dlsym(execinfo_symbols, "backtrace");
1170 backtrace_symbols_func = dlsym(execinfo_symbols, "backtrace_symbols");
1171 }
1172 }
1173
1174 static gate_bool_t load_backtrace_functions()
1175 {
1176 if (!backtrace_func || backtrace_symbols_func)
1177 {
1178 /* lookup in global function table: */
1179 backtrace_func = gate_posix_global_sym("backtrace");
1180 backtrace_symbols_func = gate_posix_global_sym("backtrace_symbols");
1181
1182 if ((NULL == backtrace_func) || (NULL == backtrace_symbols_func))
1183 {
1184 load_backtrace_from_libexecinfo();
1185 }
1186 }
1187 return backtrace_func && backtrace_symbols_func;
1188 }
1189
1190 #endif
1191
1192
1193 1 gate_result_t gate_callstack_trace(gate_callstack_address_t* addresses, gate_size_t addresses_count, gate_size_t* addresses_used)
1194 {
1195 1 gate_result_t ret = GATE_RESULT_OK;
1196 void* stackaddress[1024];
1197 backtrace_length_t index;
1198 backtrace_length_t stackaddress_count;
1199
1200 1 load_backtrace_functions();
1201
1202
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (!backtrace_func)
1203 {
1204 GATE_DEBUG_TRACE("backtrace() not supported");
1205 return GATE_RESULT_NOTSUPPORTED;
1206 }
1207
1208 1 stackaddress_count = backtrace_func(stackaddress, sizeof(stackaddress) / sizeof(stackaddress[0]));
1209
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (stackaddress_count > 0)
1210 {
1211
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (stackaddress_count > (backtrace_length_t)addresses_count)
1212 {
1213 stackaddress_count = (backtrace_length_t)addresses_count;
1214 }
1215
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 1 times.
13 for (index = 0; index != stackaddress_count; ++index)
1216 {
1217 12 addresses[index] = stackaddress[index];
1218 }
1219 1 *addresses_used = (gate_size_t)stackaddress_count;
1220 1 ret = GATE_RESULT_OK;
1221 }
1222 else
1223 {
1224 *addresses_used = 0;
1225 }
1226 1 return ret;
1227 }
1228
1229 1 gate_result_t gate_callstack_print(gate_callstack_address_t address, char* buffer, gate_size_t buffer_length, gate_size_t* buffer_used)
1230 {
1231 gate_size_t len;
1232 char** names;
1233
1234 1 load_backtrace_functions();
1235
1236
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (backtrace_symbols_func == NULL)
1237 {
1238 return GATE_RESULT_NOTSUPPORTED;
1239 }
1240
1241 1 names = backtrace_symbols_func((void**)&address, 1);
1242
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (names == NULL)
1243 {
1244 return GATE_RESULT_FAILED;
1245 }
1246
1247 1 len = gate_str_print_text(buffer, buffer_length, names[0], gate_str_length(names[0]));
1248 1 free(names);
1249 1 buffer[len] = 0;
1250 1 *buffer_used = len;
1251 1 return GATE_RESULT_OK;
1252 }
1253
1254 #define GATE_CALLSTACK_MALLOC 1
1255
1256 6 static void check_stack_size(gate_size_t* ptr_stacksize)
1257 {
1258
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 5 times.
6 if (*ptr_stacksize == 0)
1259 {
1260 1 *ptr_stacksize = DEFAULT_STACK_SIZE;
1261 }
1262
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if (*ptr_stacksize < MINMUM_STACK_SIZE)
1263 {
1264 *ptr_stacksize = MINMUM_STACK_SIZE;
1265 }
1266 /* apply 32-bit maximum and 16-bit alignment */
1267 6 *ptr_stacksize &= 0xfffffff0;
1268 6 }
1269
1270 2 gate_result_t gate_callstack_create(gate_size_t* ptr_stacksize, void** ptr_callstack)
1271 {
1272 void* ptr;
1273 gate_size_t stacksize;
1274
1275 2 check_stack_size(ptr_stacksize);
1276 2 stacksize = *ptr_stacksize;
1277
1278 #if defined(GATE_CALLSTACK_MALLOC)
1279 2 ptr = gate_mem_alloc(stacksize);
1280 #else
1281 ptr = mmap(NULL, stacksize, PROT_READ | PROT_WRITE /*| PROT_EXEC*/,
1282 MAP_PRIVATE | MAP_ANONYMOUS /*| MAP_GROWSDOWN*/ | MAP_STACK, -1, 0);
1283 if (ptr == MAP_FAILED)
1284 {
1285 return GATE_RESULT_FAILED;
1286 }
1287 #endif
1288
1289
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (ptr_callstack)
1290 {
1291 2 *ptr_callstack = ptr;
1292 }
1293 else
1294 {
1295 gate_callstack_destroy(ptr, stacksize);
1296 }
1297 2 return GATE_RESULT_OK;
1298 }
1299
1300 2 gate_result_t gate_callstack_destroy(void* ptr_callstack, gate_size_t stacksize)
1301 {
1302 2 check_stack_size(&stacksize);
1303
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (!ptr_callstack)
1304 {
1305 return GATE_RESULT_INVALIDARG;
1306 }
1307 #if defined(GATE_CALLSTACK_MALLOC)
1308 2 gate_mem_dealloc(ptr_callstack);
1309 #else
1310 if (0 == munmap(ptr_callstack, stacksize))
1311 {
1312 return GATE_RESULT_OK;
1313 }
1314 else
1315 {
1316 return GATE_RESULT_FAILED;
1317 }
1318 #endif
1319 2 return GATE_RESULT_OK;
1320 }
1321
1322 typedef struct gate_callstack_session_class
1323 {
1324 gate_entrypoint_t entry_func;
1325 void* param;
1326 gate_result_t* ptr_returnvalue;
1327 gate_callstack_context_t* parent_context;
1328 } gate_callstack_session_t;
1329
1330 union gate_callstack_entry_dispatcher_args
1331 {
1332 gate_callstack_session_t* session;
1333 int args[4];
1334 };
1335
1336 2 static void gate_callstack_entry_dispatcher(int a0, int a1, int a2, int a3)
1337 {
1338 union gate_callstack_entry_dispatcher_args args;
1339 gate_result_t result;
1340 gate_callstack_context_t dummy_context;
1341
1342 2 args.args[0] = a0;
1343 2 args.args[1] = a1;
1344 2 args.args[2] = a2;
1345 2 args.args[3] = a3;
1346 2 result = args.session->entry_func(args.session->param);
1347
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (args.session->ptr_returnvalue)
1348 {
1349 1 *args.session->ptr_returnvalue = result;
1350 }
1351 1 gate_posix_uctx_set(args.session->parent_context);
1352 }
1353
1354 2 gate_result_t gate_callstack_run(void* ptr_callstack, gate_size_t stacksize,
1355 gate_callstack_context_t* ptr_caller_ctx_to_save,
1356 gate_entrypoint_t entry_func, void* param,
1357 gate_result_t* ptr_returnvalue)
1358 {
1359 2 gate_result_t ret = GATE_RESULT_NOTIMPLEMENTED;
1360 2 gate_size_t stack_addr = (gate_size_t)ptr_callstack;
1361 void* stack_top;
1362 2 gate_callstack_context_t context = GATE_INIT_EMPTY;
1363 2 gate_callstack_context_t new_stack_context = GATE_INIT_EMPTY;
1364 void* entry_ret;
1365 gate_callstack_session_t session;
1366 union gate_callstack_entry_dispatcher_args disp_args;
1367
1368 2 check_stack_size(&stacksize);
1369
1370
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (NULL == ptr_caller_ctx_to_save)
1371 {
1372 1 ptr_caller_ctx_to_save = &context;
1373 }
1374
1375
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if (gate_posix_callstack_supported())
1376 {
1377 stack_addr += (stacksize - 4);
1378 stack_addr -= (stack_addr & 0x0f);
1379 stack_top = (void*)stack_addr;
1380
1381 entry_ret = gate_posix_callstack_create(stack_top, ptr_caller_ctx_to_save, entry_func, param);
1382 ret = GATE_RESULT_OK;
1383 if (ptr_returnvalue)
1384 {
1385 *ptr_returnvalue = (gate_result_t)(gate_intptr_t)entry_ret;
1386 }
1387 }
1388 else
1389 {
1390 do
1391 {
1392 2 ret = gate_posix_uctx_init();
1393
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 GATE_BREAK_IF_FAILED(ret);
1394
1395
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if (0 != gate_posix_uctx_get(&new_stack_context))
1396 {
1397 ret = GATE_RESULT_FAILED;
1398 break;
1399 }
1400 2 gate_posix_uctx_setup(&new_stack_context, ptr_callstack, stacksize);
1401 2 session.entry_func = entry_func;
1402 2 session.param = param;
1403 2 session.ptr_returnvalue = ptr_returnvalue;
1404 2 session.parent_context = ptr_caller_ctx_to_save;
1405 2 disp_args.session = &session;
1406 2 gate_posix_uctx_make(&new_stack_context, &gate_callstack_entry_dispatcher,
1407 disp_args.args[0], disp_args.args[1], disp_args.args[2], disp_args.args[3]);
1408 2 gate_posix_uctx_swap(ptr_caller_ctx_to_save, &new_stack_context);
1409
1410 2 ret = GATE_RESULT_OK;
1411 } while (0);
1412 }
1413 2 return ret;
1414 }
1415
1416 1 gate_result_t gate_callstack_switch(gate_callstack_context_t* current_to_save, gate_callstack_context_t* next_to_load)
1417 {
1418 1 gate_result_t ret = GATE_RESULT_NOTIMPLEMENTED;
1419
1420
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (gate_posix_callstack_supported())
1421 {
1422 if (NULL == gate_posix_callstack_switch(current_to_save, next_to_load))
1423 {
1424 ret = GATE_RESULT_FAILED;
1425 }
1426 else
1427 {
1428 ret = GATE_RESULT_OK;
1429 }
1430 }
1431 else
1432 {
1433 1 gate_posix_uctx_swap(current_to_save, next_to_load);
1434 ret = GATE_RESULT_OK;
1435 }
1436 return ret;
1437 }
1438
1439
1440 typedef struct gate_native_jmp_buf_class
1441 {
1442 sigjmp_buf buf;
1443 } gate_native_jmp_buf_t;
1444
1445 6 gate_result_t gate_callstack_fork(gate_callstack_jmpbuf_t* jmpbuffer,
1446 gate_entrypoint_t entry, void* param,
1447 gate_entrypoint_t branch, void* branch_param)
1448 {
1449 6 gate_result_t volatile ret = GATE_RESULT_OK;
1450 6 gate_native_jmp_buf_t* volatile ptr_buf = (gate_native_jmp_buf_t*)jmpbuffer;
1451
1452 if (sizeof(gate_callstack_jmpbuf_t) < sizeof(gate_native_jmp_buf_t))
1453 {
1454 /* generic jump buffer is to small */
1455 return GATE_RESULT_OUTOFMEMORY;
1456 }
1457
1458
2/2
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 2 times.
6 if (0 == sigsetjmp(ptr_buf->buf, 1))
1459 {
1460
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 if (entry)
1461 {
1462 6 ret = entry(param);
1463 }
1464 }
1465 else
1466 {
1467
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (branch)
1468 {
1469 2 ret = branch(branch_param);
1470 }
1471 }
1472 6 gate_mem_clear(jmpbuffer, sizeof(gate_callstack_jmpbuf_t));
1473 6 return ret;
1474 }
1475
1476 2 void gate_callstack_jump(gate_callstack_jmpbuf_t* jmpbuffer)
1477 {
1478 2 gate_native_jmp_buf_t* ptr_buf = (gate_native_jmp_buf_t*)jmpbuffer;
1479 2 siglongjmp(ptr_buf->buf, 1);
1480 }
1481
1482
1483 #endif /* GATE_CALLSTACKS_POSIX_IMPL */
1484
1485
1486
1487 #if defined(GATE_CALLSTACKS_EFI_IMPL)
1488
1489 #include "gate/platform/efi/efi_gate.h"
1490
1491 #if defined(GATE_COMPILER_MSVC)
1492 # include "gate/platform/windows/win32callstack.h"
1493 # define gate_callstack_create_impl gate_win32_callstack_create
1494 # define gate_callstack_switch_impl gate_win32_callstack_switch
1495 #else
1496 # include "gate/platform/posix/posixcallstack.h"
1497 # define gate_callstack_create_impl gate_posix_callstack_create
1498 # define gate_callstack_switch_impl gate_posix_callstack_switch
1499 #endif
1500
1501
1502
1503 gate_result_t gate_callstack_trace(gate_callstack_address_t* addresses, gate_size_t addresses_count, gate_size_t* addresses_used)
1504 {
1505 /*TODO*/
1506 return GATE_RESULT_NOTIMPLEMENTED;
1507 }
1508 gate_result_t gate_callstack_print(gate_callstack_address_t address, char* buffer, gate_size_t buffer_length, gate_size_t* buffer_used)
1509 {
1510 /*TODO*/
1511 return GATE_RESULT_NOTIMPLEMENTED;
1512 }
1513
1514
1515
1516 gate_result_t gate_callstack_create(gate_size_t* ptr_stacksize, void** ptr_callstack)
1517 {
1518 gate_result_t ret = GATE_RESULT_FAILED;
1519 void* ptr;
1520 gate_size_t stacksize = 0;
1521
1522 if (!ptr_stacksize || !ptr_callstack)
1523 {
1524 ret = GATE_RESULT_INVALIDARG;
1525 }
1526 else
1527 {
1528 stacksize = *ptr_stacksize;
1529 if (stacksize < 4096)
1530 {
1531 stacksize = 4096;
1532 }
1533 stacksize &= ~((gate_size_t)0xff);
1534 if (stacksize == 0)
1535 {
1536 stacksize = 256 * 1024;
1537 }
1538 ptr = gate_mem_alloc(stacksize);
1539 if (ptr)
1540 {
1541 ret = GATE_RESULT_OK;
1542 if (ptr_callstack)
1543 {
1544 *ptr_callstack = ptr;
1545 *ptr_stacksize = stacksize;
1546 }
1547 else
1548 {
1549 gate_mem_dealloc(ptr);
1550 }
1551 }
1552 }
1553
1554 return ret;
1555 }
1556
1557 gate_result_t gate_callstack_destroy(void* ptr_callstack, gate_size_t stacksize)
1558 {
1559 if (!ptr_callstack)
1560 {
1561 return GATE_RESULT_INVALIDARG;
1562 }
1563
1564 gate_mem_dealloc(ptr_callstack);
1565 return GATE_RESULT_OK;
1566 }
1567
1568
1569 gate_result_t gate_callstack_run(void* ptr_callstack, gate_size_t stacksize,
1570 gate_callstack_context_t* ptr_caller_ctx_to_save,
1571 gate_entrypoint_t entry_func, void* param,
1572 gate_result_t* ptr_returnvalue)
1573 {
1574 gate_result_t ret = GATE_RESULT_NOTIMPLEMENTED;
1575 gate_size_t stack_addr = (gate_size_t)ptr_callstack;
1576 void* stack_top;
1577 gate_callstack_context_t context = GATE_INIT_EMPTY;
1578 void* entry_ret;
1579
1580 if (NULL == ptr_caller_ctx_to_save)
1581 {
1582 ptr_caller_ctx_to_save = &context;
1583 }
1584
1585 stack_addr += (stacksize - 4);
1586 stack_addr -= (stack_addr & 0x0f);
1587 stack_top = (void*)stack_addr;
1588
1589 entry_ret = gate_callstack_create_impl(stack_top, ptr_caller_ctx_to_save, entry_func, param);
1590 ret = GATE_RESULT_OK;
1591 if (ptr_returnvalue)
1592 {
1593 *ptr_returnvalue = (gate_result_t)(gate_intptr_t)entry_ret;
1594 }
1595 return ret;
1596 }
1597
1598 gate_result_t gate_callstack_switch(gate_callstack_context_t* current_to_save,
1599 gate_callstack_context_t* next_to_load)
1600 {
1601 gate_result_t ret = GATE_RESULT_NOTIMPLEMENTED;
1602 if (NULL == gate_callstack_switch_impl(current_to_save, next_to_load))
1603 {
1604 ret = GATE_RESULT_FAILED;
1605 }
1606 else
1607 {
1608 ret = GATE_RESULT_OK;
1609 }
1610 return ret;
1611 }
1612
1613 #endif /* GATE_CALLSTACKS_EFI_IMPL */
1614
1615
1616 #if defined(GATE_CALLSTACKS_DOS_IMPL)
1617
1618 #include "gate/platforms.h"
1619 #include "gate/platform/dos/doscallstack.h"
1620
1621
1622 gate_result_t gate_callstack_trace(gate_callstack_address_t* addresses, gate_size_t addresses_count, gate_size_t* addresses_used)
1623 {
1624 GATE_UNUSED_ARG(addresses);
1625 GATE_UNUSED_ARG(addresses_count);
1626 GATE_UNUSED_ARG(addresses_used);
1627 return GATE_RESULT_FAILED;
1628 }
1629
1630 gate_result_t gate_callstack_print(gate_callstack_address_t address, char* buffer, gate_size_t buffer_length, gate_size_t* buffer_used)
1631 {
1632 GATE_UNUSED_ARG(address);
1633 GATE_UNUSED_ARG(buffer);
1634 GATE_UNUSED_ARG(buffer_length);
1635 GATE_UNUSED_ARG(buffer_used);
1636 return GATE_RESULT_FAILED;
1637 }
1638
1639 #define DEFAULT_STACK_SIZE 16384
1640
1641 gate_result_t gate_callstack_create(gate_size_t* ptr_stacksize, void** ptr_callstack)
1642 {
1643 gate_result_t result = GATE_RESULT_OUTOFMEMORY;
1644 gate_size_t para_count;
1645 unsigned segment = 0;
1646 gate_size_t stacksize;
1647
1648 if (!ptr_stacksize || !ptr_callstack)
1649 {
1650 return GATE_RESULT_INVALIDARG;
1651 }
1652 stacksize = *ptr_stacksize;
1653
1654 if (stacksize == 0)
1655 {
1656 stacksize = DEFAULT_STACK_SIZE;
1657 }
1658 if (stacksize > 32768)
1659 {
1660 stacksize = 32768;
1661 }
1662 if (stacksize < 256)
1663 {
1664 stacksize = 256;
1665 }
1666 stacksize = (stacksize + 0x0f) & ~((gate_size_t)0x0f);
1667
1668 para_count = (stacksize + 15) / 16;
1669
1670 result = gate_dos_alloc(para_count, &segment);
1671 if (GATE_SUCCEEDED(result))
1672 {
1673 *ptr_callstack = (void*)gate_dos_farptr(segment, 0);
1674 *ptr_stacksize = stacksize;
1675 }
1676 return result;
1677 }
1678
1679 gate_result_t gate_callstack_destroy(void* ptr_callstack, gate_size_t stacksize)
1680 {
1681 unsigned segment = gate_dos_farptr_seg(ptr_callstack);
1682 GATE_UNUSED_ARG(stacksize);
1683 return gate_dos_dealloc(segment);
1684 }
1685
1686
1687 gate_result_t gate_callstack_run(void* ptr_callstack, gate_size_t stacksize, gate_callstack_context_t* ptr_caller_ctx_to_save,
1688 gate_entrypoint_t entry_func, void* param, gate_result_t* ptr_returnvalue)
1689 {
1690 gate_result_t result;
1691 char __far* ptr_stack_begin = (char __far*)ptr_callstack;
1692 char __far* ptr_stack_end;
1693
1694 if (stacksize == 0)
1695 {
1696 stacksize = DEFAULT_STACK_SIZE;
1697 }
1698 gate_mem_clear(ptr_callstack, stacksize);
1699 ptr_stack_end = ptr_stack_begin + stacksize - 2;
1700
1701
1702 result = gate_dos_callstack_create(ptr_stack_end, ptr_caller_ctx_to_save, entry_func, param);
1703 if (GATE_SUCCEEDED(result))
1704 {
1705 if (ptr_returnvalue)
1706 {
1707 *ptr_returnvalue = result;
1708 }
1709 }
1710 return result;
1711 }
1712
1713 gate_result_t gate_callstack_switch(gate_callstack_context_t* current_to_save,
1714 gate_callstack_context_t* next_to_load)
1715 {
1716 return gate_dos_callstack_switch(current_to_save, next_to_load);
1717 }
1718
1719 #endif /* GATE_CALLSTACKS_DOS_IMPL */
1720
1721
1722
1723 #if defined(GATE_CALLSTACKS_NO_IMPL)
1724
1725 gate_result_t gate_callstack_trace(gate_callstack_address_t* addresses, gate_size_t addresses_count, gate_size_t* addresses_used)
1726 {
1727 (void)addresses;
1728 (void)addresses_count;
1729 (void)addresses_used;
1730 return GATE_RESULT_NOTIMPLEMENTED;
1731 }
1732 gate_result_t gate_callstack_print(gate_callstack_address_t address, char* buffer, gate_size_t buffer_length, gate_size_t* buffer_used)
1733 {
1734 (void)address;
1735 (void)buffer;
1736 (void)buffer_length;
1737 (void)buffer_used;
1738 return GATE_RESULT_NOTIMPLEMENTED;
1739 }
1740
1741 gate_result_t gate_callstack_create(gate_size_t* ptr_stacksize, void** ptr_callstack)
1742 {
1743 (void)ptr_stacksize;
1744 (void)ptr_callstack;
1745 return GATE_RESULT_NOTIMPLEMENTED;
1746 }
1747 gate_result_t gate_callstack_destroy(void* ptr_callstack, gate_size_t stacksize)
1748 {
1749 (void)ptr_callstack;
1750 (void)stacksize;
1751 return GATE_RESULT_NOTIMPLEMENTED;
1752 }
1753 gate_result_t gate_callstack_run(void* ptr_callstack, gate_size_t stacksize, gate_callstack_context_t* ptr_caller_ctx_to_save,
1754 gate_entrypoint_t entry_func, void* param, gate_result_t* ptr_returnvalue)
1755 {
1756 (void)ptr_callstack;
1757 (void)stacksize;
1758 (void)ptr_caller_ctx_to_save;
1759 (void)entry_func;
1760 (void)param;
1761 (void)ptr_returnvalue;
1762 return GATE_RESULT_NOTIMPLEMENTED;
1763 }
1764
1765 gate_result_t gate_callstack_switch(gate_callstack_context_t* current_to_save, gate_callstack_context_t* next_to_load)
1766 {
1767 (void)current_to_save;
1768 (void)next_to_load;
1769 return GATE_RESULT_NOTIMPLEMENTED;
1770 }
1771
1772
1773 #endif /* GATE_CALLSTACKS_NO_IMPL */
1774
1775