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
74// We may not be able to take the negative of the type
75// used to represent stack size, but we can always add
76// or subtract it to/from a quint8 pointer.
77
78template<typename Size>
79static void *incrementStackPointer(void *base, Size amount)
80{
81#if Q_STACK_GROWTH_DIRECTION > 0
82 return static_cast<quint8 *>(base) + amount;
83#else
84 return static_cast<quint8 *>(base) - amount;
85#endif
86}
87
88template<typename Size>
89static void *decrementStackPointer(void *base, Size amount)
90{
91#if Q_STACK_GROWTH_DIRECTION > 0
92 return static_cast<quint8 *>(base) - amount;
93#else
94 return static_cast<quint8 *>(base) + amount;
95#endif
96}
97
98static StackProperties createStackProperties(void *base, qsizetype size = PlatformStackSize)
99{
100 return StackProperties {
101 base,
102 incrementStackPointer(base, size - PlatformSafetyMargin),
103 incrementStackPointer(base, size),
104 };
105}
106
107#if defined(Q_OS_DARWIN) || defined(Q_OS_LINUX)
108
109// On linux and darwin, on the main thread, the pthread functions
110// may not return the true stack size since the main thread stack
111// may grow. Use rlimit instead. rlimit does not work for secondary
112// threads, though. If getrlimit fails, we assume the platform
113// stack size.
115{
120}
121#endif
122
123#if defined(Q_OS_INTEGRITY)
124
126{
129# if Q_STACK_GROWTH_DIRECTION < 0
130 return createStackProperties(reinterpret_cast<void *>(stackHigh), stackHigh - stackLow);
131# else
132 return createStackProperties(reinterpret_cast<void *>(stackLow), stackHigh - stackLow);
133# endif
134}
135
136#elif defined(Q_OS_DARWIN)
137
139{
146}
147
148#elif defined(Q_OS_WIN)
149
150static_assert(Q_STACK_GROWTH_DIRECTION < 0);
152{
153 // MinGW complains about out of bounds array access in compiler headers
155 QT_WARNING_DISABLE_GCC("-Warray-bounds")
156
157 // Get the stack base.
158# ifdef _WIN64
159 PNT_TIB64 pTib = reinterpret_cast<PNT_TIB64>(NtCurrentTeb());
160# else
161 PNT_TIB pTib = reinterpret_cast<PNT_TIB>(NtCurrentTeb());
162# endif
163
165
166 quint8 *stackBase = reinterpret_cast<quint8 *>(pTib->StackBase);
167
168 // Get the stack limit. tib->StackLimit is the size of the
169 // currently mapped stack. The address space is larger.
171 if (!VirtualQuery(&mbi, &mbi, sizeof(mbi)))
172 qFatal("Could not retrieve memory information for stack.");
173
174 quint8 *stackLimit = reinterpret_cast<quint8 *>(mbi.AllocationBase);
176}
177
178#elif defined(Q_OS_OPENBSD)
179
181{
182 // From the OpenBSD docs:
183 //
184 // The pthread_stackseg_np() function returns information about the given thread's stack.
185 // A stack_t is the same as a struct sigaltstack (see sigaltstack(2)) except the ss_sp
186 // variable points to the top of the stack instead of the base.
187 //
188 // Since the example in the sigaltstack(2) documentation shows ss_sp being assigned the result
189 // of a malloc() call, we can assume that "top of the stack" means "the highest address", not
190 // the logical top of the stack.
191
192 stack_t ss;
194#if Q_STACK_GROWTH_DIRECTION < 0
196#else
198#endif
199}
200
201#elif defined(Q_OS_QNX)
202
204{
205 const auto tid = pthread_self();
207 status.tid = tid;
208
209 const int fd = open("/proc/self/ctl", O_RDONLY);
210 if (fd == -1)
211 qFatal("Could not open /proc/self/ctl");
212 const auto guard = qScopeGuard([fd]() { close(fd); });
213
214 if (devctl(fd, DCMD_PROC_TIDSTATUS, &status, sizeof(status), 0) != EOK)
215 qFatal("Could not query thread status for current thread");
216
217 if (status.tid != tid)
218 qFatal("Thread status query returned garbage");
219
220#if Q_STACK_GROWTH_DIRECTION < 0
222 decrementStackPointer(reinterpret_cast<void *>(status.stkbase), status.stksize),
224#else
225 return createStackProperties(reinterpret_cast<void *>(status.stkbase), status.stksize);
226#endif
227}
228
229#elif defined(Q_OS_WASM)
230
232{
235 const size_t size = base - end;
236 return createStackProperties(reinterpret_cast<void *>(base), size);
237}
238
239#elif defined(Q_OS_VXWORKS)
240
242{
246}
247
248#else
249
251{
252 // If stackSize is given, do not trust the stack size returned by pthread_attr_getstack
253
254 pthread_t thread = pthread_self();
255 pthread_attr_t sattr;
256# if defined(PTHREAD_NP_H) || defined(_PTHREAD_NP_H_) || defined(Q_OS_NETBSD)
257 pthread_attr_init(&sattr);
258 pthread_attr_get_np(thread, &sattr);
259# else
260 pthread_getattr_np(thread, &sattr);
261# endif
262
263 // pthread_attr_getstack returns the address of the memory region, which is the physical
264 // base of the stack, not the logical one.
265 void *stackBase;
266 size_t regionSize;
267 int rc = pthread_attr_getstack(&sattr, &stackBase, &regionSize);
268 pthread_attr_destroy(&sattr);
269
270 if (rc)
271 qFatal("Cannot find stack base");
272
273# if Q_STACK_GROWTH_DIRECTION < 0
274 stackBase = decrementStackPointer(stackBase, regionSize);
275# endif
276
277 return createStackProperties(stackBase, stackSize ? stackSize : regionSize);
278}
279
280#if defined(Q_OS_LINUX)
281
282static void *stackBaseFromLibc()
283{
284#if defined(__GLIBC__) && QT_CONFIG(dlopen)
285 void **libcStackEnd = static_cast<void **>(dlsym(RTLD_DEFAULT, "__libc_stack_end"));
286 if (!libcStackEnd)
287 return nullptr;
288 if (void *stackBase = *libcStackEnd)
289 return stackBase;
290#endif
291 return nullptr;
292}
293
294struct StackSegment {
297};
298
300{
301 QFile maps(QStringLiteral("/proc/self/maps"));
303 return {0, 0};
304
305 const quintptr stackAddr = reinterpret_cast<quintptr>(&maps);
306
307 char buffer[1024];
308 while (true) {
309 const qint64 length = maps.readLine(buffer, 1024);
310 if (length <= 0)
311 break;
312
314 bool ok = false;
315
316 const qsizetype boundary = line.indexOf('-');
317 if (boundary < 0)
318 continue;
319
320 const quintptr base = line.sliced(0, boundary).toULongLong(&ok, 16);
321 if (!ok || base > stackAddr)
322 continue;
323
324 const qsizetype end = line.indexOf(' ', boundary);
325 if (end < 0)
326 continue;
327
328 const quintptr limit = line.sliced(boundary + 1, end - boundary - 1).toULongLong(&ok, 16);
329 if (!ok || limit <= stackAddr)
330 continue;
331
332 return {base, limit};
333 }
334
335 return {0, 0};
336}
337
339{
340 if (getpid() != static_cast<pid_t>(syscall(SYS_gettid)))
341 return stackPropertiesGeneric();
342
343 // On linux (including android), the pthread functions are expensive
344 // and unreliable on the main thread.
345
346 // First get the stack size from rlimit
348
349 // If we have glibc and libdl, we can query a special symbol in glibc to find the base.
350 // That is extremely cheap, compared to all other options.
351 if (stackSize) {
352 if (void *base = stackBaseFromLibc())
354 }
355
356 // Try to read the stack segment from /proc/self/maps if possible.
358 if (segment.base) {
359# if Q_STACK_GROWTH_DIRECTION > 0
360 void *stackBase = reinterpret_cast<void *>(segment.base);
361# else
362 void *stackBase = reinterpret_cast<void *>(segment.limit);
363# endif
366 }
367
368 // If we can't read /proc/self/maps, use the pthread functions after all, but
369 // override the stackSize. The main thread can grow its stack, and the pthread
370 // functions typically return the currently allocated stack size.
372}
373
374#else // Q_OS_LINUX
375
376StackProperties stackProperties() { return stackPropertiesGeneric(); }
377
378#endif // Q_OS_LINUX
379#endif
380
381} // namespace QV4
382
383QT_END_NAMESPACE
Definition qjsvalue.h:23
static void * incrementStackPointer(void *base, Size amount)
static void * decrementStackPointer(void *base, Size amount)
@ DefaultSafetyMargin
@ PlatformSafetyMargin
@ PlatformStackSize
static StackProperties createStackProperties(void *base, qsizetype size=PlatformStackSize)
StackProperties stackProperties()
StackProperties stackPropertiesGeneric(qsizetype stackSize=0)