Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qv4stacklimits.cpp
Go to the documentation of this file.
1// Copyright (C) 2022 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include <private/qv4stacklimits_p.h>
5#include <private/qobject_p.h>
6#include <private/qthread_p.h>
7
8#include <QtCore/qfile.h>
9
10#if defined(Q_OS_UNIX)
11# include <pthread.h>
12#endif
13
14#ifdef Q_OS_WIN
15# include <QtCore/qt_windows.h>
16#elif defined(Q_OS_FREEBSD_KERNEL) || defined(Q_OS_OPENBSD)
17# include <pthread_np.h>
18#elif defined(Q_OS_LINUX)
19# include <unistd.h>
20# include <sys/resource.h> // for getrlimit()
21# include <sys/syscall.h> // for SYS_gettid
22# if defined(__GLIBC__) && QT_CONFIG(dlopen)
23# include <dlfcn.h>
24# endif
25#elif defined(Q_OS_DARWIN)
26# include <sys/resource.h> // for getrlimit()
27#elif defined(Q_OS_QNX)
28# include <devctl.h>
29# include <sys/procfs.h>
30# include <sys/types.h>
31# include <unistd.h>
32# include <fcntl.h>
33#elif defined(Q_OS_INTEGRITY)
34# include <INTEGRITY.h>
35#elif defined(Q_OS_VXWORKS)
36# include <taskLib.h>
37#elif defined(Q_OS_WASM)
38# include <emscripten/stack.h>
39#endif
40
41QT_BEGIN_NAMESPACE
42
43namespace QV4 {
44
46 // Default safety margin at the end of the usable stack.
47 // Since we don't check the stack on every instruction, we might overrun our soft limit.
48 DefaultSafetyMargin = 128 * 1024,
49#if defined(Q_OS_IOS)
50 PlatformStackSize = 1024 * 1024,
52#elif defined(Q_OS_MACOS)
53 PlatformStackSize = 8 * 1024 * 1024,
55#elif defined(Q_OS_ANDROID)
56 // Android appears to have 1MB stacks.
57 PlatformStackSize = 1024 * 1024,
59#elif defined(Q_OS_LINUX)
60 // On linux, we assume 8MB stacks if rlimit doesn't work.
61 PlatformStackSize = 8 * 1024 * 1024,
63#elif defined(Q_OS_QNX)
64 // QNX's stack is only 512k by default
65 PlatformStackSize = 512 * 1024,
67#else
68 // We try to claim 512k if we don't know anything else.
69 PlatformStackSize = 512 * 1024,
71#endif
72};
73
74static StackProperties createStackProperties(void *base, qsizetype size = PlatformStackSize, qsizetype margin = PlatformSafetyMargin)
75{
76 return StackProperties {
77 base,
78 incrementStackPointer(base, size - margin),
79 incrementStackPointer(base, size),
80 };
81}
82
83#if defined(Q_OS_DARWIN) || defined(Q_OS_LINUX)
84
85// On linux and darwin, on the main thread, the pthread functions
86// may not return the true stack size since the main thread stack
87// may grow. Use rlimit instead. rlimit does not work for secondary
88// threads, though. If getrlimit fails, we assume the platform
89// stack size.
91{
96}
97#endif
98
99#if defined(Q_OS_INTEGRITY)
100
102{
105# if Q_STACK_GROWTH_DIRECTION < 0
106 return createStackProperties(reinterpret_cast<void *>(stackHigh), stackHigh - stackLow);
107# else
108 return createStackProperties(reinterpret_cast<void *>(stackLow), stackHigh - stackLow);
109# endif
110}
111
112#elif defined(Q_OS_DARWIN)
113
115{
122}
123
124#elif defined(Q_OS_WIN)
125
126static_assert(Q_STACK_GROWTH_DIRECTION < 0);
128{
129 // MinGW complains about out of bounds array access in compiler headers
131 QT_WARNING_DISABLE_GCC("-Warray-bounds")
132
133 // Get the stack base.
134# ifdef _WIN64
135 PNT_TIB64 pTib = reinterpret_cast<PNT_TIB64>(NtCurrentTeb());
136# else
137 PNT_TIB pTib = reinterpret_cast<PNT_TIB>(NtCurrentTeb());
138# endif
139
141
142 quint8 *stackBase = reinterpret_cast<quint8 *>(pTib->StackBase);
143
144 // Get the stack limit. tib->StackLimit is the size of the
145 // currently mapped stack. The address space is larger.
147 if (!VirtualQuery(&mbi, &mbi, sizeof(mbi)))
148 qFatal("Could not retrieve memory information for stack.");
149
150 quint8 *stackLimit = reinterpret_cast<quint8 *>(mbi.AllocationBase);
152}
153
154#elif defined(Q_OS_OPENBSD)
155
157{
158 // From the OpenBSD docs:
159 //
160 // The pthread_stackseg_np() function returns information about the given thread's stack.
161 // A stack_t is the same as a struct sigaltstack (see sigaltstack(2)) except the ss_sp
162 // variable points to the top of the stack instead of the base.
163 //
164 // Since the example in the sigaltstack(2) documentation shows ss_sp being assigned the result
165 // of a malloc() call, we can assume that "top of the stack" means "the highest address", not
166 // the logical top of the stack.
167
168 stack_t ss;
170#if Q_STACK_GROWTH_DIRECTION < 0
172#else
174#endif
175}
176
177#elif defined(Q_OS_QNX)
178
180{
181 const auto tid = pthread_self();
183 status.tid = tid;
184
185 const int fd = open("/proc/self/ctl", O_RDONLY);
186 if (fd == -1)
187 qFatal("Could not open /proc/self/ctl");
188 const auto guard = qScopeGuard([fd]() { close(fd); });
189
190 if (devctl(fd, DCMD_PROC_TIDSTATUS, &status, sizeof(status), 0) != EOK)
191 qFatal("Could not query thread status for current thread");
192
193 if (status.tid != tid)
194 qFatal("Thread status query returned garbage");
195
196#if Q_STACK_GROWTH_DIRECTION < 0
198 decrementStackPointer(reinterpret_cast<void *>(status.stkbase), status.stksize),
200#else
201 return createStackProperties(reinterpret_cast<void *>(status.stkbase), status.stksize);
202#endif
203}
204
205#elif defined(Q_OS_WASM)
206
208{
211 const size_t size = base - end;
212 return createStackProperties(reinterpret_cast<void *>(base), size);
213}
214
215#elif defined(Q_OS_VXWORKS)
216
218{
223}
224
225#else
226
228{
229 // If stackSize is given, do not trust the stack size returned by pthread_attr_getstack
230
231 pthread_t thread = pthread_self();
232 pthread_attr_t sattr;
233# if defined(PTHREAD_NP_H) || defined(_PTHREAD_NP_H_) || defined(Q_OS_NETBSD)
234 pthread_attr_init(&sattr);
235 pthread_attr_get_np(thread, &sattr);
236# else
237 pthread_getattr_np(thread, &sattr);
238# endif
239
240 // pthread_attr_getstack returns the address of the memory region, which is the physical
241 // base of the stack, not the logical one.
242 void *stackBase;
243 size_t regionSize;
244 int rc = pthread_attr_getstack(&sattr, &stackBase, &regionSize);
245 pthread_attr_destroy(&sattr);
246
247 if (rc)
248 qFatal("Cannot find stack base");
249
250# if Q_STACK_GROWTH_DIRECTION < 0
251 stackBase = decrementStackPointer(stackBase, regionSize);
252# endif
253
254 return createStackProperties(stackBase, stackSize ? stackSize : regionSize);
255}
256
257#if defined(Q_OS_LINUX)
258
259static void *stackBaseFromLibc()
260{
261#if defined(__GLIBC__) && QT_CONFIG(dlopen)
262 void **libcStackEnd = static_cast<void **>(dlsym(RTLD_DEFAULT, "__libc_stack_end"));
263 if (!libcStackEnd)
264 return nullptr;
265 if (void *stackBase = *libcStackEnd)
266 return stackBase;
267#endif
268 return nullptr;
269}
270
271struct StackSegment {
274};
275
277{
278 QFile maps(QStringLiteral("/proc/self/maps"));
280 return {0, 0};
281
282 const quintptr stackAddr = reinterpret_cast<quintptr>(&maps);
283
284 char buffer[1024];
285 while (true) {
286 const qint64 length = maps.readLine(buffer, 1024);
287 if (length <= 0)
288 break;
289
291 bool ok = false;
292
293 const qsizetype boundary = line.indexOf('-');
294 if (boundary < 0)
295 continue;
296
297 const quintptr base = line.sliced(0, boundary).toULongLong(&ok, 16);
298 if (!ok || base > stackAddr)
299 continue;
300
301 const qsizetype end = line.indexOf(' ', boundary);
302 if (end < 0)
303 continue;
304
305 const quintptr limit = line.sliced(boundary + 1, end - boundary - 1).toULongLong(&ok, 16);
306 if (!ok || limit <= stackAddr)
307 continue;
308
309 return {base, limit};
310 }
311
312 return {0, 0};
313}
314
316{
317 if (getpid() != static_cast<pid_t>(syscall(SYS_gettid)))
318 return stackPropertiesGeneric();
319
320 // On linux (including android), the pthread functions are expensive
321 // and unreliable on the main thread.
322
323 // First get the stack size from rlimit
325
326 // If we have glibc and libdl, we can query a special symbol in glibc to find the base.
327 // That is extremely cheap, compared to all other options.
328 if (stackSize) {
329 if (void *base = stackBaseFromLibc())
331 }
332
333 // Try to read the stack segment from /proc/self/maps if possible.
335 if (segment.base) {
336# if Q_STACK_GROWTH_DIRECTION > 0
337 void *stackBase = reinterpret_cast<void *>(segment.base);
338# else
339 void *stackBase = reinterpret_cast<void *>(segment.limit);
340# endif
343 }
344
345 // If we can't read /proc/self/maps, use the pthread functions after all, but
346 // override the stackSize. The main thread can grow its stack, and the pthread
347 // functions typically return the currently allocated stack size.
349}
350
351#else // Q_OS_LINUX
352
353StackProperties stackProperties() { return stackPropertiesGeneric(); }
354
355#endif // Q_OS_LINUX
356#endif
357
358} // namespace QV4
359
360QT_END_NAMESPACE
Definition qjsvalue.h:23
@ DefaultSafetyMargin
@ PlatformSafetyMargin
@ PlatformStackSize
static StackProperties createStackProperties(void *base, qsizetype size=PlatformStackSize, qsizetype margin=PlatformSafetyMargin)
StackProperties stackProperties()
StackProperties stackPropertiesGeneric(qsizetype stackSize=0)