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 | |
46 | namespace WTF { |
47 | |
48 | #if OS(DARWIN) |
49 | |
50 | StackBounds 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 | |
58 | StackBounds 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 | |
77 | StackBounds 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 | |
88 | StackBounds 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 | |
113 | StackBounds StackBounds::currentThreadStackBoundsInternal() |
114 | { |
115 | return newThreadStackBounds(pthread_self()); |
116 | } |
117 | |
118 | #elif OS(WINDOWS) |
119 | |
120 | StackBounds 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 | |