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 | |
45 | namespace WTF { |
46 | |
47 | #if CPU(X86) || CPU(X86_64) || CPU(ARM) || CPU(ARM64) || CPU(MIPS) |
48 | ALWAYS_INLINE StackBounds::StackDirection StackBounds::stackDirection() |
49 | { |
50 | return StackDirection::Downward; |
51 | } |
52 | #else |
53 | static 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 | |
59 | static 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 | |
66 | NEVER_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 | |
80 | StackBounds 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 | |
89 | StackBounds 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 | |
109 | StackBounds 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 | |
124 | StackBounds 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 | |
153 | StackBounds StackBounds::currentThreadStackBoundsInternal() |
154 | { |
155 | return newThreadStackBounds(pthread_self()); |
156 | } |
157 | |
158 | #elif OS(WINDOWS) |
159 | |
160 | StackBounds 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 | |