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