2

On Windows I can get the stack-boundaries from the Thread Information Block like this:

    void  *stackBottom,
          *stackTop;
#if defined _MSC_VER
    void **teb  = (void **)NtCurrentTeb();
    stackBottom = teb[2];
    stackTop    = teb[1];
#else
    #error "unsupported platform"
#endif

... or with GetCurrentThreadStackLimits(). But GetCurrentThreadStackLimits() doesn't return the boundaries of the currently allocated Stack (Windows does overcommit stacks) but the whole address-range of the stack to where it ultimately might extend.

Is something similar like the above possible with Linux ?

[EDIT] I've got it:

#include <iostream>
#include <pthread.h>

using namespace std;

int main()
{
    pthread_attr_t attrs;
    if( pthread_getattr_np( pthread_self(), &attrs ) != 0 )
        return -1;
    void   *stackAddr;
    size_t  stackSize;
    if( pthread_attr_getstack( &attrs, &stackAddr, &stackSize ) != 0 )
        return -1;
    cout << "stack-address: " << stackAddr          << endl;
    cout << "stack-size:    " << stackSize          << endl;
    cout << "var-addr:      " << (void *)&stackAddr << endl;
}

This determines the base-address of the stack and its size. As var_addr shows stackAddr is the lower bound, i.e. the stack begins at (char *)stackAddr + stackSize. The next thing I'm going to do is to determine the performance of that code.

Bonita Montero
  • 2,817
  • 9
  • 22
  • see this So answer: https://stackoverflow.com/a/23253635/5639126 – secret squirrel Jul 27 '20 at 09:00
  • 5 min network search resulted in `grep -A 1 stack /proc/$$/smaps` – KamilCuk Jul 27 '20 at 10:04
  • Does this answer your question? [How do I find the maximum stack size?](https://stackoverflow.com/questions/7535994/how-do-i-find-the-maximum-stack-size) – KamilCuk Jul 27 '20 at 10:05
  • 1: secretsquirrel: using a proc filesystem-access for this purpose is too slow. 2: KamilCuk: same as with secretsquirrel 3: KamilCuk: don't want to get the maximum stack size but the stack-boundaries of a certain stack or the current stack. – Bonita Montero Jul 27 '20 at 10:59
  • There is also https://linux.die.net/man/3/pthread_attr_getstacksize – KamilCuk Jul 27 '20 at 11:23
  • pthread_attr_getstacksize() is used to set the stack-size properties of a pthread attribute object which is used when creating a stack. It's not used to determine the stack-boundaries of a running thread. – Bonita Montero Jul 27 '20 at 11:30
  • reading the proc file system isn't really slow, if you do it from code. It's just a memcpy from kernel memory so not really any more overhead than any other system calls, unless you need it **really** fast. – secret squirrel Jul 27 '20 at 11:36
  • It _is_ slow as it needs a multiple kernel-calls. What I did above by reading the TEB-pointer under Windows is magnitudes faster as reading the TEB-pointer like above is just a single instruction. – Bonita Montero Jul 27 '20 at 11:37
  • ok, so you can do it by declaring a variable at the start of main like *auto int a;*...then &a will give the top address of the stack minus whatever is used for args and environment variables. The linux glibc libraries do make some process info available, but not stack start address AFAIK. – secret squirrel Jul 27 '20 at 12:56

1 Answers1

0

The pthread_getattr_np() appears to be glibc specific and judging by its strace, it also opens and parses /proc/self/maps (not super slow but a bunch of syscalls in there).

I figured out it can be done with just one cheap syscall: getrlimit to get the size of the stack. The head of the stack is then tail minus size (downward growth) and tail can be obtained from extern char **environ;.

This LWN article https://lwn.net/Articles/631631/ describes the initial linux stack layout. It starts with (or ends with considering the downward growth of stacks):

  • a null pointer (4-8 zero bytes) at the very tail of the stack
  • the executable's file name
  • environment variable strings

Working backwards from that you can get to the tail/bottom.

#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/resource.h>

void perrorAndExit(char const*X) { perror(X); _exit(1); }
int main(int C, char **V){
    //print maps data to check the later result:
    close(3); int mapsfd = open("/proc/self/maps",O_RDONLY);
    if(0>mapsfd) perrorAndExit("open");
    system("cat /dev/fd/3 | grep stack");
    //shows higher head (indicating a smaller stack) than what's accessible
    //I guess it shows only the faulted-in part of the stack


    struct rlimit rlim;
    if(0>getrlimit(RLIMIT_STACK,&rlim)) perrorAndExit("getrlimit");

    extern char **environ;
    //get the last envvar pointer, which points to the highest addr envvar string
    char **ee=environ; for (;*ee;ee++){}--ee;
    char *filename = *ee + strlen(*ee) + 1;  //move past it into the filename
    char *null = filename + strlen(filename)+1; //move filename into the null pointer
    char *tail = null + sizeof(void*); //get the tail

    char *head = tail - rlim.rlim_cur;
    printf("%zu-%lx\n", (unsigned long)rlim.rlim_cur, (unsigned long)tail);


    head[0] =  0; //still accessible
    //head[-1] =  0; //segfaults

    return 0;
}

This of course works just for the main stack (sufficient for my use case, might not be for yours).

Petr Skocik
  • 58,047
  • 6
  • 95
  • 142
  • Doesn't getrlimit return the default stack size of the process and not the stack size of the thread, which may be set to an indivudal value ? – Bonita Montero Jun 02 '22 at 16:07
  • @BonitaMontero Yeah, this is not for when you're in a `pthread`. If you're the one to invoke the `phtread_create` call, then that case it simple: give it your own stack for which you already know the bounds (at least that's what I'm doing). For the main stack the above works (seemingly, also on MacOS with some rounding) and is faster than a `SIGSEGV` protected stackwalk until stackoverflow. – Petr Skocik Jun 02 '22 at 16:22
  • I just want to get the limits from inside the thread and not with external help. And I even want to get the limits if the stack-size is defaulted. – Bonita Montero Jun 03 '22 at 17:54