1 /*
2  * Copyright 2018 Zebediah Figura
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18 
19 #include "windef.h"
20 #include "verrsrc.h"
21 #include "dbghelp.h"
22 #include "wine/test.h"
23 
24 #if defined(__i386__) || defined(__x86_64__)
25 
26 static DWORD CALLBACK stack_walk_thread(void *arg)
27 {
28     DWORD count = SuspendThread(GetCurrentThread());
29     ok(!count, "got %d\n", count);
30     return 0;
31 }
32 
33 static void test_stack_walk(void)
34 {
35     char si_buf[sizeof(SYMBOL_INFO) + 200];
36     SYMBOL_INFO *si = (SYMBOL_INFO *)si_buf;
37     STACKFRAME64 frame = {{0}}, frame0;
38     BOOL found_our_frame = FALSE;
39     DWORD machine;
40     HANDLE thread;
41     DWORD64 disp;
42     CONTEXT ctx;
43     DWORD count;
44     BOOL ret;
45 
46     thread = CreateThread(NULL, 0, stack_walk_thread, NULL, 0, NULL);
47 
48     /* wait for the thread to suspend itself */
49     do
50     {
51         Sleep(50);
52         count = SuspendThread(thread);
53         ResumeThread(thread);
54     }
55     while (!count);
56 
57     ctx.ContextFlags = CONTEXT_CONTROL;
58     ret = GetThreadContext(thread, &ctx);
59     ok(ret, "got error %u\n", ret);
60 
61     frame.AddrPC.Mode    = AddrModeFlat;
62     frame.AddrFrame.Mode = AddrModeFlat;
63     frame.AddrStack.Mode = AddrModeFlat;
64 
65 #ifdef __i386__
66     machine = IMAGE_FILE_MACHINE_I386;
67 
68     frame.AddrPC.Segment = ctx.SegCs;
69     frame.AddrPC.Offset = ctx.Eip;
70     frame.AddrFrame.Segment = ctx.SegSs;
71     frame.AddrFrame.Offset = ctx.Ebp;
72     frame.AddrStack.Segment = ctx.SegSs;
73     frame.AddrStack.Offset = ctx.Esp;
74 #elif defined(__x86_64__)
75     machine = IMAGE_FILE_MACHINE_AMD64;
76 
77     frame.AddrPC.Segment = ctx.SegCs;
78     frame.AddrPC.Offset = ctx.Rip;
79     frame.AddrFrame.Segment = ctx.SegSs;
80     frame.AddrFrame.Offset = ctx.Rbp;
81     frame.AddrStack.Segment = ctx.SegSs;
82     frame.AddrStack.Offset = ctx.Rsp;
83 #endif
84     frame0 = frame;
85 
86     /* first invocation just calculates the return address */
87     ret = StackWalk64(machine, GetCurrentProcess(), thread, &frame, &ctx, NULL,
88         SymFunctionTableAccess64, SymGetModuleBase64, NULL);
89     ok(ret, "StackWalk64() failed: %u\n", GetLastError());
90     ok(frame.AddrPC.Offset == frame0.AddrPC.Offset, "expected %s, got %s\n",
91         wine_dbgstr_longlong(frame0.AddrPC.Offset),
92         wine_dbgstr_longlong(frame.AddrPC.Offset));
93     ok(frame.AddrStack.Offset == frame0.AddrStack.Offset, "expected %s, got %s\n",
94         wine_dbgstr_longlong(frame0.AddrStack.Offset),
95         wine_dbgstr_longlong(frame.AddrStack.Offset));
96     ok(frame.AddrReturn.Offset && frame.AddrReturn.Offset != frame.AddrPC.Offset,
97         "got bad return address %s\n", wine_dbgstr_longlong(frame.AddrReturn.Offset));
98 
99     while (frame.AddrReturn.Offset)
100     {
101         char *addr;
102 
103         ret = StackWalk64(machine, GetCurrentProcess(), thread, &frame, &ctx, NULL,
104             SymFunctionTableAccess64, SymGetModuleBase64, NULL);
105         ok(ret, "StackWalk64() failed: %u\n", GetLastError());
106 
107         addr = (void *)(DWORD_PTR)frame.AddrPC.Offset;
108 
109         if (addr > (char *)stack_walk_thread && addr < (char *)stack_walk_thread + 0x100)
110         {
111             found_our_frame = TRUE;
112 
113             si->SizeOfStruct = sizeof(SYMBOL_INFO);
114             si->MaxNameLen = 200;
115             if (SymFromAddr(GetCurrentProcess(), frame.AddrPC.Offset, &disp, si))
116                 ok(!strcmp(si->Name, "stack_walk_thread"), "got wrong name %s\n", si->Name);
117         }
118     }
119 
120     ret = StackWalk64(machine, GetCurrentProcess(), thread, &frame, &ctx, NULL,
121         SymFunctionTableAccess64, SymGetModuleBase64, NULL);
122     ok(!ret, "StackWalk64() should have failed\n");
123 
124     ok(found_our_frame, "didn't find stack_walk_thread frame\n");
125 }
126 
127 #else /* __i386__ || __x86_64__ */
128 
129 static void test_stack_walk(void)
130 {
131 }
132 
133 #endif /* __i386__ || __x86_64__ */
134 
135 START_TEST(dbghelp)
136 {
137     BOOL ret = SymInitialize(GetCurrentProcess(), NULL, TRUE);
138     ok(ret, "got error %u\n", GetLastError());
139 
140     test_stack_walk();
141 
142     ret = SymCleanup(GetCurrentProcess());
143     ok(ret, "got error %u\n", GetLastError());
144 }
145