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 "StackBounds.h" 23 24#if OS(DARWIN) 25 26#include <mach/task.h> 27#include <mach/thread_act.h> 28#include <pthread.h> 29 30#elif OS(WINDOWS) 31 32#include <windows.h> 33 34#elif OS(HAIKU) 35 36#include <OS.h> 37 38#elif OS(SOLARIS) 39 40#include <thread.h> 41 42#elif OS(QNX) 43 44#include <fcntl.h> 45#include <sys/procfs.h> 46#include <stdio.h> 47#include <errno.h> 48 49#elif OS(UNIX) 50 51#include <pthread.h> 52#if HAVE(PTHREAD_NP_H) 53#include <pthread_np.h> 54#endif 55 56#endif 57 58namespace WTF { 59 60// Bug 26276 - Need a mechanism to determine stack extent 61// 62// These platforms should now be working correctly: 63// DARWIN, QNX, UNIX, SYMBIAN 64// These platforms are not: 65// WINDOWS, SOLARIS, OPENBSD, HAIKU, WINCE 66// 67// FIXME: remove this! - this code unsafely guesses at stack sizes! 68#if OS(WINDOWS) || OS(SOLARIS) || OS(OPENBSD) || OS(HAIKU) 69// Based on the current limit used by the JSC parser, guess the stack size. 70static const ptrdiff_t estimatedStackSize = 128 * sizeof(void*) * 1024; 71// This method assumes the stack is growing downwards. 72static void* estimateStackBound(void* origin) 73{ 74 return static_cast<char*>(origin) - estimatedStackSize; 75} 76#endif 77 78#if OS(DARWIN) 79 80void StackBounds::initialize() 81{ 82 pthread_t thread = pthread_self(); 83 m_origin = pthread_get_stackaddr_np(thread); 84 m_bound = static_cast<char*>(m_origin) - pthread_get_stacksize_np(thread); 85} 86 87#elif OS(QNX) 88 89void StackBounds::initialize() 90{ 91 void* stackBase = 0; 92 size_t stackSize = 0; 93 pthread_t thread = pthread_self(); 94 95 struct _debug_thread_info threadInfo; 96 memset(&threadInfo, 0, sizeof(threadInfo)); 97 threadInfo.tid = pthread_self(); 98 int fd = open("/proc/self", O_RDONLY); 99 if (fd == -1) { 100 LOG_ERROR("Unable to open /proc/self (errno: %d)", errno); 101 CRASH(); 102 } 103 devctl(fd, DCMD_PROC_TIDSTATUS, &threadInfo, sizeof(threadInfo), 0); 104 close(fd); 105 stackBase = reinterpret_cast<void*>(threadInfo.stkbase); 106 stackSize = threadInfo.stksize; 107 ASSERT(stackBase); 108 109 m_bound = stackBase; 110 m_origin = static_cast<char*>(stackBase) + stackSize; 111} 112 113#elif OS(SOLARIS) 114 115void StackBounds::initialize() 116{ 117 stack_t s; 118 thr_stksegment(&s); 119 m_origin = s.ss_sp; 120 m_bound = estimateStackBound(m_origin); 121} 122 123#elif OS(OPENBSD) 124 125void StackBounds::initialize() 126{ 127 pthread_t thread = pthread_self(); 128 stack_t stack; 129 pthread_stackseg_np(thread, &stack); 130 m_origin = stack.ss_sp; 131 m_bound = estimateStackBound(m_origin); 132} 133 134#elif OS(SYMBIAN) 135 136void StackBounds::initialize() 137{ 138 TThreadStackInfo info; 139 RThread thread; 140 thread.StackInfo(info); 141 m_origin = (void*)info.iBase; 142 m_bound = (void*)info.iLimit; 143} 144 145#elif OS(HAIKU) 146 147void StackBounds::initialize() 148{ 149 thread_info threadInfo; 150 get_thread_info(find_thread(NULL), &threadInfo); 151 m_origin = threadInfo.stack_end; 152 m_bound = estimateStackBound(m_origin); 153} 154 155#elif OS(UNIX) 156 157void StackBounds::initialize() 158{ 159 void* stackBase = 0; 160 size_t stackSize = 0; 161 162 pthread_t thread = pthread_self(); 163 pthread_attr_t sattr; 164 pthread_attr_init(&sattr); 165#if HAVE(PTHREAD_NP_H) || OS(NETBSD) 166 // e.g. on FreeBSD 5.4, neundorf@kde.org 167 pthread_attr_get_np(thread, &sattr); 168#else 169 // FIXME: this function is non-portable; other POSIX systems may have different np alternatives 170 pthread_getattr_np(thread, &sattr); 171#endif 172 int rc = pthread_attr_getstack(&sattr, &stackBase, &stackSize); 173 (void)rc; // FIXME: Deal with error code somehow? Seems fatal. 174 ASSERT(stackBase); 175 pthread_attr_destroy(&sattr); 176 m_bound = stackBase; 177 m_origin = static_cast<char*>(stackBase) + stackSize; 178} 179 180#elif OS(WINCE) 181 182static bool detectGrowingDownward(void* previousFrame) 183{ 184 // Find the address of this stack frame by taking the address of a local variable. 185 int thisFrame; 186 return previousFrame > &thisFrame; 187} 188 189static inline bool isPageWritable(void* page) 190{ 191 MEMORY_BASIC_INFORMATION memoryInformation; 192 DWORD result = VirtualQuery(page, &memoryInformation, sizeof(memoryInformation)); 193 194 // return false on error, including ptr outside memory 195 if (result != sizeof(memoryInformation)) 196 return false; 197 198 DWORD protect = memoryInformation.Protect & ~(PAGE_GUARD | PAGE_NOCACHE); 199 return protect == PAGE_READWRITE 200 || protect == PAGE_WRITECOPY 201 || protect == PAGE_EXECUTE_READWRITE 202 || protect == PAGE_EXECUTE_WRITECOPY; 203} 204 205static inline void* getLowerStackBound(char* currentPage, DWORD pageSize) 206{ 207 while (currentPage > 0) { 208 // check for underflow 209 if (currentPage >= reinterpret_cast<char*>(pageSize)) 210 currentPage -= pageSize; 211 else 212 currentPage = 0; 213 214 if (!isPageWritable(currentPage)) 215 return currentPage + pageSize; 216 } 217 218 return 0; 219} 220 221static inline void* getUpperStackBound(char* currentPage, DWORD pageSize) 222{ 223 do { 224 // guaranteed to complete because isPageWritable returns false at end of memory 225 currentPage += pageSize; 226 } while (isPageWritable(currentPage)); 227 228 return currentPage - pageSize; 229} 230 231void StackBounds::initialize() 232{ 233 // find the address of this stack frame by taking the address of a local variable 234 void* thisFrame = &thisFrame; 235 bool isGrowingDownward = detectGrowingDownward(thisFrame); 236 237 SYSTEM_INFO systemInfo; 238 GetSystemInfo(&systemInfo); 239 DWORD pageSize = systemInfo.dwPageSize; 240 241 // scan all of memory starting from this frame, and return the last writeable page found 242 char* currentPage = reinterpret_cast<char*>(reinterpret_cast<DWORD>(thisFrame) & ~(pageSize - 1)); 243 void* lowerStackBound = getLowerStackBound(currentPage, pageSize); 244 void* upperStackBound = getUpperStackBound(currentPage, pageSize); 245 246 m_origin = isGrowingDownward ? upperStackBound : lowerStackBound; 247 m_bound = isGrowingDownward ? lowerStackBound : upperStackBound; 248} 249 250#elif OS(WINDOWS) 251 252void StackBounds::initialize() 253{ 254#if CPU(X86) && COMPILER(MSVC) 255 // offset 0x18 from the FS segment register gives a pointer to 256 // the thread information block for the current thread 257 NT_TIB* pTib; 258 __asm { 259 MOV EAX, FS:[18h] 260 MOV pTib, EAX 261 } 262 m_origin = static_cast<void*>(pTib->StackBase); 263#elif CPU(X86) && COMPILER(GCC) 264 // offset 0x18 from the FS segment register gives a pointer to 265 // the thread information block for the current thread 266 NT_TIB* pTib; 267 asm ( "movl %%fs:0x18, %0\n" 268 : "=r" (pTib) 269 ); 270 m_origin = static_cast<void*>(pTib->StackBase); 271#elif CPU(X86_64) 272 PNT_TIB64 pTib = reinterpret_cast<PNT_TIB64>(NtCurrentTeb()); 273 m_origin = reinterpret_cast<void*>(pTib->StackBase); 274#else 275#error Need a way to get the stack bounds on this platform (Windows) 276#endif 277 // Looks like we should be able to get pTib->StackLimit 278 m_bound = estimateStackBound(m_origin); 279} 280 281#else 282#error Need a way to get the stack bounds on this platform 283#endif 284 285} // namespace WTF 286