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