Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Zend/Zend.m4
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ AC_CHECK_FUNCS(m4_normalize([
pthread_attr_getstack
pthread_get_stackaddr_np
pthread_getattr_np
pthread_getthrds_np
pthread_stackseg_np
strnlen
]))
Expand Down
65 changes: 64 additions & 1 deletion Zend/zend_call_stack.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@
#endif /* ZEND_WIN32 */
#if (defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK)) || \
defined(__FreeBSD__) || defined(__APPLE__) || defined(__OpenBSD__) || \
defined(__NetBSD__) || defined(__DragonFly__) || defined(__sun)
defined(__NetBSD__) || defined(__DragonFly__) || defined(__sun) || \
defined(_AIX)
# include <pthread.h>
#endif
#if defined(__FreeBSD__) || defined(__DragonFly__)
Expand Down Expand Up @@ -788,6 +789,64 @@ static bool zend_call_stack_get_solaris(zend_call_stack *stack)
}
#endif /* defined(__sun) */

#if defined(_AIX)
static bool zend_call_stack_get_aix_pthread(zend_call_stack *stack)
{
#ifdef HAVE_PTHREAD_GETTHRDS_NP
pthread_t pt = pthread_self();
struct __pthrdsinfo thread_info;
/* This buffer needs to exist sadly */
char reg[256];
int regsz = sizeof(reg);

if (pthread_getthrds_np(&pt, PTHRDSINFO_QUERY_ALL, &thread_info,
sizeof(thread_info), &reg, &regsz)) {
return false;
}

/*
* The top of the stack (stackend) is not page aligned, there's some
* internal stuff above it. Thankfully, we don't need page alignment.
*
* The size is a little weird. The stacksize field is smaller than
* subtracting the bottom (stackaddr) from the top; it's about 0x888
* to 0x1888 above stackaddr.
*
* A somewhat crude diagram is available here:
* https://www.ibm.com/docs/en/aix/7.2.0?topic=tuning-thread-environment-variables
*
* pthread->pt_stk.st_limit is __pi_stackend,
* above that is internal pthread junk close to the end of page
* pthread->pt_stk.st_base is __pi_stackaddr,
* below that is the red zone
*/
stack->base = thread_info.__pi_stackend;

@devnexen devnexen Jul 28, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for me the calculation is right but I wonder why you need a buffer of 256, for example the openjdk only sets to 1.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WRT registers/size param, I think it may just be unnecessary, the man page on the register size argument:

The pointer to the size of the regbuf buffer. On input, it identifies the maximum size of the buffer in bytes. On output, it identifies the number of bytes of register save data and pass the TLS information. If the thread is not in a system call, there is no register save data returned from the kernel, and regbufsize is 0. If the size of the register save data is larger than the input value of regbufsize, the number of bytes specified by the input value of regbufsize is copied to regbuf, pthread_getthrds_np() returns ERANGE, and the output value of regbufsize specifies the number of bytes required to hold all of the register save data.

Since we call this function only on our own thread, and not on a different one, we never need to save the registers. In a test program, I was able to set the register buffer to NULL and the size buffer to 0 (this can't be a null pointer sadly).

stack->max_size = thread_info.__pi_stackend - thread_info.__pi_stackaddr;
return true;
#else
/* pthread likely not linked in; default NTS build behaviour */
return false;
#endif
}

static bool zend_call_stack_get_aix(zend_call_stack *stack)
{
/*
* While we could use /proc on AIX (and the implementation basically
* like the Solaris one, as the procfs is similar), it doesn't work on
* PASE. The pthread API works even on the main thread, so we should
* use it when we have pthread linked (always with ZTS, maybe not with
* NTS builds).
*/
return zend_call_stack_get_aix_pthread(stack);
}
#else
static bool zend_call_stack_get_aix(zend_call_stack *stack)
{
return false;
}
#endif /* defined(_AIX) */

/** Get the stack information for the calling thread */
ZEND_API bool zend_call_stack_get(zend_call_stack *stack)
{
Expand Down Expand Up @@ -823,6 +882,10 @@ ZEND_API bool zend_call_stack_get(zend_call_stack *stack)
return true;
}

if (zend_call_stack_get_aix(stack)) {
return true;
}

return false;
}

Expand Down
Loading