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