1/*
2 * Copyright (C) 2003-2019 Apple Inc. All rights reserved.
3 * Copyright (C) 2007 Eric Seidel <[email protected]>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 *
19 */
20
21#include "config.h"
22#include <wtf/StackBounds.h>
23
24#include <mutex>
25#include <wtf/NoTailCalls.h>
26
27#if OS(DARWIN)
28
29#include <mach/task.h>
30#include <mach/thread_act.h>
31#include <pthread.h>
32
33#elif OS(WINDOWS)
34
35#include <windows.h>
36
37#elif OS(UNIX)
38
39#include <pthread.h>
40#if HAVE(PTHREAD_NP_H)
41#include <pthread_np.h>
42#endif
43
44#endif
45
46namespace WTF {
47
48#if OS(DARWIN)
49
50StackBounds StackBounds::newThreadStackBounds(PlatformThreadHandle thread)
51{
52 void* origin = pthread_get_stackaddr_np(thread);
53 rlim_t size = pthread_get_stacksize_np(thread);
54 void* bound = static_cast<char*>(origin) - size;
55 return StackBounds { origin, bound };
56}
57
58StackBounds StackBounds::currentThreadStackBoundsInternal()
59{
60 if (pthread_main_np()) {
61 // FIXME: <rdar://problem/13741204>
62 // pthread_get_size lies to us when we're the main thread, use get_rlimit instead
63 void* origin = pthread_get_stackaddr_np(pthread_self());
64 rlimit limit;
65 getrlimit(RLIMIT_STACK, &limit);
66 rlim_t size = limit.rlim_cur;
67 void* bound = static_cast<char*>(origin) - size;
68 return StackBounds { origin, bound };
69 }
70 return newThreadStackBounds(pthread_self());
71}
72
73#elif OS(UNIX)
74
75#if OS(OPENBSD)
76
77StackBounds StackBounds::newThreadStackBounds(PlatformThreadHandle thread)
78{
79 stack_t stack;
80 pthread_stackseg_np(thread, &stack);
81 void* origin = stack.ss_sp;
82 void* bound = static_cast<char*>(origin) - stack.ss_size;
83 return StackBounds { origin, bound };
84}
85
86#else // !OS(OPENBSD)
87
88StackBounds StackBounds::newThreadStackBounds(PlatformThreadHandle thread)
89{
90 void* bound = nullptr;
91 size_t stackSize = 0;
92
93 pthread_attr_t sattr;
94 pthread_attr_init(&sattr);
95#if HAVE(PTHREAD_NP_H) || OS(NETBSD)
96 // e.g. on FreeBSD 5.4, [email protected]
97 pthread_attr_get_np(thread, &sattr);
98#else
99 // FIXME: this function is non-portable; other POSIX systems may have different np alternatives
100 pthread_getattr_np(thread, &sattr);
101#endif
102 int rc = pthread_attr_getstack(&sattr, &bound, &stackSize);
103 UNUSED_PARAM(rc);
104 ASSERT(bound);
105 pthread_attr_destroy(&sattr);
106 void* origin = static_cast<char*>(bound) + stackSize;
107 // pthread_attr_getstack's bound is the lowest accessible pointer of the stack.
108 return StackBounds { origin, bound };
109}
110
111#endif // OS(OPENBSD)
112
113StackBounds StackBounds::currentThreadStackBoundsInternal()
114{
115 return newThreadStackBounds(pthread_self());
116}
117
118#elif OS(WINDOWS)
119
120StackBounds StackBounds::currentThreadStackBoundsInternal()
121{
122 MEMORY_BASIC_INFORMATION stackOrigin { };
123 VirtualQuery(&stackOrigin, &stackOrigin, sizeof(stackOrigin));
124 // stackOrigin.AllocationBase points to the reserved stack memory base address.
125
126 void* origin = static_cast<char*>(stackOrigin.BaseAddress) + stackOrigin.RegionSize;
127 // The stack on Windows consists out of three parts (uncommitted memory, a guard page and present
128 // committed memory). The 3 regions have different BaseAddresses but all have the same AllocationBase
129 // since they are all from the same VirtualAlloc. The 3 regions are laid out in memory (from high to
130 // low) as follows:
131 //
132 // High |-------------------| -----
133 // | committedMemory | ^
134 // |-------------------| |
135 // | guardPage | reserved memory for the stack
136 // |-------------------| |
137 // | uncommittedMemory | v
138 // Low |-------------------| ----- <--- stackOrigin.AllocationBase
139 //
140 // See http://msdn.microsoft.com/en-us/library/ms686774%28VS.85%29.aspx for more information.
141
142 MEMORY_BASIC_INFORMATION uncommittedMemory;
143 VirtualQuery(stackOrigin.AllocationBase, &uncommittedMemory, sizeof(uncommittedMemory));
144 ASSERT(uncommittedMemory.State == MEM_RESERVE);
145
146 MEMORY_BASIC_INFORMATION guardPage;
147 VirtualQuery(static_cast<char*>(uncommittedMemory.BaseAddress) + uncommittedMemory.RegionSize, &guardPage, sizeof(guardPage));
148 ASSERT(guardPage.Protect & PAGE_GUARD);
149
150 void* endOfStack = stackOrigin.AllocationBase;
151
152#ifndef NDEBUG
153 MEMORY_BASIC_INFORMATION committedMemory;
154 VirtualQuery(static_cast<char*>(guardPage.BaseAddress) + guardPage.RegionSize, &committedMemory, sizeof(committedMemory));
155 ASSERT(committedMemory.State == MEM_COMMIT);
156
157 void* computedEnd = static_cast<char*>(origin) - (uncommittedMemory.RegionSize + guardPage.RegionSize + committedMemory.RegionSize);
158
159 ASSERT(stackOrigin.AllocationBase == uncommittedMemory.AllocationBase);
160 ASSERT(stackOrigin.AllocationBase == guardPage.AllocationBase);
161 ASSERT(stackOrigin.AllocationBase == committedMemory.AllocationBase);
162 ASSERT(stackOrigin.AllocationBase == uncommittedMemory.BaseAddress);
163 ASSERT(endOfStack == computedEnd);
164#endif // NDEBUG
165 void* bound = static_cast<char*>(endOfStack) + guardPage.RegionSize;
166 return StackBounds { origin, bound };
167}
168
169#else
170#error Need a way to get the stack bounds on this platform
171#endif
172
173} // namespace WTF
174