1/*
2 * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
3 * Copyright (C) 2007 Eric Seidel <eric@webkit.org>
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 <mutex>
23#include <wtf/NoTailCalls.h>
24#include <wtf/StackBounds.h>
25
26#if OS(DARWIN)
27
28#include <mach/task.h>
29#include <mach/thread_act.h>
30#include <pthread.h>
31
32#elif OS(WINDOWS)
33
34#include <windows.h>
35
36#elif OS(UNIX)
37
38#include <pthread.h>
39#if HAVE(PTHREAD_NP_H)
40#include <pthread_np.h>
41#endif
42
43#endif
44
45namespace WTF {
46
47#if CPU(X86) || CPU(X86_64) || CPU(ARM) || CPU(ARM64) || CPU(MIPS)
48ALWAYS_INLINE StackBounds::StackDirection StackBounds::stackDirection()
49{
50 return StackDirection::Downward;
51}
52#else
53static NEVER_INLINE NOT_TAIL_CALLED StackBounds::StackDirection testStackDirection2(volatile const uint8_t* pointer)
54{
55 volatile uint8_t* stackValue = bitwise_cast<uint8_t*>(currentStackPointer());
56 return (pointer < stackValue) ? StackBounds::StackDirection::Upward : StackBounds::StackDirection::Downward;
57}
58
59static NEVER_INLINE NOT_TAIL_CALLED StackBounds::StackDirection testStackDirection()
60{
61 NO_TAIL_CALLS();
62 volatile uint8_t* stackValue = bitwise_cast<uint8_t*>(currentStackPointer());
63 return testStackDirection2(stackValue);
64}
65
66NEVER_INLINE StackBounds::StackDirection StackBounds::stackDirection()
67{
68 static StackBounds::StackDirection result = StackBounds::StackDirection::Downward;
69 static std::once_flag onceKey;
70 std::call_once(onceKey, [] {
71 NO_TAIL_CALLS();
72 result = testStackDirection();
73 });
74 return result;
75}
76#endif
77
78#if OS(DARWIN)
79
80StackBounds StackBounds::newThreadStackBounds(PlatformThreadHandle thread)
81{
82 ASSERT(stackDirection() == StackDirection::Downward);
83 void* origin = pthread_get_stackaddr_np(thread);
84 rlim_t size = pthread_get_stacksize_np(thread);
85 void* bound = static_cast<char*>(origin) - size;
86 return StackBounds { origin, bound };
87}
88
89StackBounds StackBounds::currentThreadStackBoundsInternal()
90{
91 ASSERT(stackDirection() == StackDirection::Downward);
92 if (pthread_main_np()) {
93 // FIXME: <rdar://problem/13741204>
94 // pthread_get_size lies to us when we're the main thread, use get_rlimit instead
95 void* origin = pthread_get_stackaddr_np(pthread_self());
96 rlimit limit;
97 getrlimit(RLIMIT_STACK, &limit);
98 rlim_t size = limit.rlim_cur;
99 void* bound = static_cast<char*>(origin) - size;
100 return StackBounds { origin, bound };
101 }
102 return newThreadStackBounds(pthread_self());
103}
104
105#elif OS(UNIX)
106
107#if OS(OPENBSD)
108
109StackBounds StackBounds::newThreadStackBounds(PlatformThreadHandle thread)
110{
111 stack_t stack;
112 pthread_stackseg_np(thread, &stack);
113 void* origin = stack.ss_sp;
114 void* bound = nullptr;
115 if (stackDirection() == StackDirection::Upward)
116 bound = static_cast<char*>(origin) + stack.ss_size;
117 else
118 bound = static_cast<char*>(origin) - stack.ss_size;
119 return StackBounds { origin, bound };
120}
121
122#else // !OS(OPENBSD)
123
124StackBounds StackBounds::newThreadStackBounds(PlatformThreadHandle thread)
125{
126 void* bound = nullptr;
127 size_t stackSize = 0;
128
129 pthread_attr_t sattr;
130 pthread_attr_init(&sattr);
131#if HAVE(PTHREAD_NP_H) || OS(NETBSD)
132 // e.g. on FreeBSD 5.4, neundorf@kde.org
133 pthread_attr_get_np(thread, &sattr);
134#else
135 // FIXME: this function is non-portable; other POSIX systems may have different np alternatives
136 pthread_getattr_np(thread, &sattr);
137#endif
138 int rc = pthread_attr_getstack(&sattr, &bound, &stackSize);
139 UNUSED_PARAM(rc);
140 ASSERT(bound);
141 pthread_attr_destroy(&sattr);
142 void* origin = static_cast<char*>(bound) + stackSize;
143 // pthread_attr_getstack's bound is the lowest accessible pointer of the stack.
144 // If stack grows up, origin and bound in this code should be swapped.
145 if (stackDirection() == StackDirection::Upward)
146 std::swap(origin, bound);
147
148 return StackBounds { origin, bound };
149}
150
151#endif // OS(OPENBSD)
152
153StackBounds StackBounds::currentThreadStackBoundsInternal()
154{
155 return newThreadStackBounds(pthread_self());
156}
157
158#elif OS(WINDOWS)
159
160StackBounds StackBounds::currentThreadStackBoundsInternal()
161{
162 ASSERT(stackDirection() == StackDirection::Downward);
163 MEMORY_BASIC_INFORMATION stackOrigin { };
164 VirtualQuery(&stackOrigin, &stackOrigin, sizeof(stackOrigin));
165 // stackOrigin.AllocationBase points to the reserved stack memory base address.
166
167 void* origin = static_cast<char*>(stackOrigin.BaseAddress) + stackOrigin.RegionSize;
168 // The stack on Windows consists out of three parts (uncommitted memory, a guard page and present
169 // committed memory). The 3 regions have different BaseAddresses but all have the same AllocationBase
170 // since they are all from the same VirtualAlloc. The 3 regions are laid out in memory (from high to
171 // low) as follows:
172 //
173 // High |-------------------| -----
174 // | committedMemory | ^
175 // |-------------------| |
176 // | guardPage | reserved memory for the stack
177 // |-------------------| |
178 // | uncommittedMemory | v
179 // Low |-------------------| ----- <--- stackOrigin.AllocationBase
180 //
181 // See http://msdn.microsoft.com/en-us/library/ms686774%28VS.85%29.aspx for more information.
182
183 MEMORY_BASIC_INFORMATION uncommittedMemory;
184 VirtualQuery(stackOrigin.AllocationBase, &uncommittedMemory, sizeof(uncommittedMemory));
185 ASSERT(uncommittedMemory.State == MEM_RESERVE);
186
187 MEMORY_BASIC_INFORMATION guardPage;
188 VirtualQuery(static_cast<char*>(uncommittedMemory.BaseAddress) + uncommittedMemory.RegionSize, &guardPage, sizeof(guardPage));
189 ASSERT(guardPage.Protect & PAGE_GUARD);
190
191 void* endOfStack = stackOrigin.AllocationBase;
192
193#ifndef NDEBUG
194 MEMORY_BASIC_INFORMATION committedMemory;
195 VirtualQuery(static_cast<char*>(guardPage.BaseAddress) + guardPage.RegionSize, &committedMemory, sizeof(committedMemory));
196 ASSERT(committedMemory.State == MEM_COMMIT);
197
198 void* computedEnd = static_cast<char*>(origin) - (uncommittedMemory.RegionSize + guardPage.RegionSize + committedMemory.RegionSize);
199
200 ASSERT(stackOrigin.AllocationBase == uncommittedMemory.AllocationBase);
201 ASSERT(stackOrigin.AllocationBase == guardPage.AllocationBase);
202 ASSERT(stackOrigin.AllocationBase == committedMemory.AllocationBase);
203 ASSERT(stackOrigin.AllocationBase == uncommittedMemory.BaseAddress);
204 ASSERT(endOfStack == computedEnd);
205#endif // NDEBUG
206 void* bound = static_cast<char*>(endOfStack) + guardPage.RegionSize;
207 return StackBounds { origin, bound };
208}
209
210#else
211#error Need a way to get the stack bounds on this platform
212#endif
213
214} // namespace WTF
215