| 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 | |