User defined stacks for threads using glibc-2.3.5

Hi,

I am currently working with threads using linux 2.6.11 and glibc 2.3.5 on x86. When i am trying to create thread with user defined stacks glibc gives segmentation fault. I have written a wrapper to pthread_create () and generated shared object for the same. When i am trying to create any thread (for user defined stack) using this shared object i am getting segmentation fault. If i don't create shared object and try to create thread (with user defined stack) using same code, it works fine. I am not able to understand this behavior. Why creating a thread using shared object shall result in segfault. And how to overcome this situation.

NOTE: I got same behavior when tried with linux 2.6.12 and glibc-2.3.6 on arm platform.

gdb output for the behavior is :

Loaded symbols for /lib/libgcc_s.so.1
#0 0x0018a777 in memset () from /lib/libc.so.6
(gdb) bt'
#0 0x0018a777 in memset () from /lib/libc.so.6
No symbol table info available.
#1 0x003e27ef in pthread_create@@GLIBC_2.1 () from /lib/libpthread.so.0
No symbol table info available.
#2 0x003e29fd in pthread_create@GLIBC_2.0 () from /lib/libpthread.so.0
No symbol table info available.
#3 0x00ed9880 in process_create (entry_point=0x8048f4c <ThreadEntryPoint>, pArg=0x1, pStack=0x82e08a8, ulStack_Size=16384, ulPrio=127)
at multithreading.c:1916
stThAttr = {size = "c\000\000\000\002\000\000\000j\000\000\000\000\02 0\000\000�H.\b\000@", '\0' <repeats 13 times>, align = 99}
stSchedParam = {__sched_priority = 99}
pstTd = (ThreadData_t *) 0xedd7d8
state = 0
chPriority = 99 'c'
ulRetVal = 0
pRetVal = 0x0
__FUNCTION
= "process_create"
#4 0x08049af8 in process_create_test (ulInParam=1) at multithreading_test.c:539
ulIndex = 0
lRetVal = -1
ulThreadIndex = 1
lTestPassFlag = 0
stack = (void *) 0x82d88a8
pStack = (void *) 0x82e08a8
ulPrio = 127
stackSize = 16384
__FUNCTION
= "process_create_test"
#5 0x0804b14b in main () at multithreading_test.c:1460
chChoice = "1"
lChoice = 1
stack = (void *) 0x0
stackSize = 0
ulPrio = 0
ulLoopIndex = 0
stTime = 134533384
stTimeInfo = {tm_sec = 4, tm_min = 53, tm_hour = 7, tm_mday = 7, tm_mon = 3, tm_year = 74, tm_wday = 0, tm_yday = 96, tm_isdst = 0,
tm_gmtoff = 19800, tm_zone = 0x82d81b8 "IST"}
achTimebuf = "04/07/1974 07:53:w"
stThAttr = {__size = '\0' <repeats 13 times>, "\020", '\0' <repeats 21 times>, align = 0}
lFlag = 1
chFlag = 0
lTestPassFlag = 0
ulThreadIndex = 0
ulloopIndex = 1
ulThreadId = {0, 0, 0, 0}
__FUNCTION
= "main"
(gdb) f 0
#0 0x0018a777 in memset () from /lib/libc.so.6
(gdb) f 1
#1 0x003e27ef in pthread_create@@GLIBC_2.1 () from /lib/libpthread.so.0
(gdb)

Please let me know if someone can help me.

Thanks,
Ashish

Not nearly enough information. There are a tremendous number of
gotchas in using pthread_attr_setstack.

See suns docs (not entirely on subject, but generic enough to be helpful) and the man page itself.
Multithreaded Programming Guide

header

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>

typedef struct custom_thread {
        int cr_return;
	int thread_stack_size;
	pthread_t tid;
	pthread_attr_t attr;
	void *newstack;
} CTHREAD;


extern CTHREAD *init_custom_thread(int, int (*)(void *));
extern void destroy_custom_thread(CTHREAD *);

library

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>



extern CTHREAD *init_custom_thread(int sz, int (*teststackalloc)(void *arg)) {
CTHREAD *ret;

               if ( (ret = malloc(sizeof(*ret))) == NULL) {return NULL;}
               ret->thread_stack_size = sz;
               if ( (ret->newstack = malloc(sz)) == NULL) {free(ret); return NULL;}
               if (teststackalloc!= NULL)
                    if (teststackalloc(ret->newstack) != 0) {free(ret->newstack); free(ret); return NULL;}
               }
               pthread_attr_init(&ret->attr);
               ret->cr_return = pthread_attr_setstack(&ret->attr,ret->newstack,ret->thread_stack_size);
               return ret;
}

extern void destroy_custom_thread(CTHREAD *arg) {

                                  if (arg->newstack != NULL) {free(arg->newstack); arg->newstack = NULL;}
                                  free(arg);
                                  arg = NULL;
} 

test

#include "custom_stack.h"
#include <limits.h>

void *hello_world(void *arg) {
                             
                  printf("Hello world from thread id %d.\n",pthread_self());
                  pthread_exit(NULL);
}

int main(void) { 
CTHREAD *new_id;

               new_id = init_custom_thread(PTHREAD_STACK_MIN * 2, NULL);
               if (new_id == NULL) {return 1;}
               pthread_create(&new_id->tid,&new_id->attr,hello_world,NULL);
               pthread_join(new_id->tid,NULL);
               destroy_custom_thread(new_id);
               return 0;
}

Hopefully the above will point out any obvious errors you are making.

Thanks to your reply.

I am calling pthread_create () inside library call itself And not in main, stack is allocated (using malloc) in main and passed with size argument to library function. may this be the cause for segfault? Is it not possible to pass stack pointer, size and entry point function to thread to library function and library inturn creates thread ?

/* main */

#include <stdio.h>
#include <limits.h>
void *func1 (void *p1)
{
   pthread_setcancelstate (PTHREAD_CANCEL_ENABLE,NULL);
   pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS,NULL);
   printf ("\n In Thread Function1 :) \n");
   sleep (3);
   pthread_exit (NULL);
}
int main (void)
{
  size_t size = PTHREAD_STACK_MIN + 0x444000;
  void *base = malloc (size );
  test_main (func1,base+PTHREAD_STACK_MIN,size-PTHREAD_STACK_MIN);
  sleep (10);
  sleep (10);
//  free (base);
  getchar();
  return 0;
}

/* Library Function */
#include <stdio.h>
//#define __USE_XOPEN2K
//#define _XOPEN_SOURCE 600
#include <pthread.h>
#include <unistd.h>
#include <limits.h>
#include <sched.h>
#include <stdlib.h>

void CleanupHandler (void* pArg)
{
   printf ("\nI am In clean up handler \n");
   usleep (100);
}
void * a(void *);
void * func(void *p(void *))
{
   printf ("\n In Thread Function :) \n");
   pthread_cleanup_push(CleanupHandler,NULL);
   p (NULL);
   pthread_cleanup_pop(1);
   usleep (1000);
   sleep (3);
   pthread_exit (NULL);
  return NULL;
}
int test_main (void*fn(void*),void *stackbase,size_t size)
{
   pthread_attr_t tattr;
   pthread_t tid;
   int ret;
   struct sched_param stparam;
   void *stackbase1,*stackbase2;
   size_t size1,size2;
   int policy;

   /* initialized with default attributes */
   ret = pthread_attr_init(&tattr);
   if (ret != -1)
   {
#if 1
     printf ("\n Stack:size <%p:%d>\n",stackbase,size);
     /* setting the base address and size of the stack */
     stackbase2 = memset (stackbase,0,size);
     ret = pthread_attr_setstack(&tattr, stackbase,size);
     if (ret != -1)
     {
#endif
       if (pthread_attr_setschedpolicy (&tattr,SCHED_RR))
       {
          printf ("\n setschedpolicy failed\n");
          pthread_attr_destroy (&tattr);
          exit (0);
       }
       if (pthread_attr_getschedpolicy (&tattr,&policy))
       {
          printf ("\n getschedpolicy failed\n");
          pthread_attr_destroy (&tattr);
          exit (1);
       }
       printf ("\n Policy = <%s>\n",(policy==0)?"SCHED_NORMAL":(policy == 1)?"SCHED_FIFO":(policy == 2)?"SCEHD_RR":(policy==3)?"SCHED_OTHERS":"NONE");
       sched_getparam (0,&stparam);
       stparam.sched_priority = 99;
       pthread_attr_setschedparam (&tattr,&stparam);
//     pthread_attr_setdetachstate (&tattr,PTHREAD_CREATE_JOINABLE);
//     pthread_attr_setinheritsched (&tattr,PTHREAD_EXPLICIT_SCHED);

        /* address and size specified */
        ret = pthread_create(&tid, &tattr, func, (void*)fn);
        if (ret == -1)
        {
           printf ("\nThread Creation Failed\n");
           pthread_attr_destroy (&tattr);
           perror (NULL);
        }
        else
        {
           printf ("\n Thread Creation Passed\n");
           pthread_attr_getstack (&tattr, &stackbase1,&size1);
           printf ("\n Stack:size <%p:%d>\n",stackbase1,size1);
           sleep (2);
           sleep (1);
           printf ("\n AF Join \n");
           pthread_attr_destroy (&tattr);
        }
#if 1
     }
     else
     {
        printf ("\n Setstack failed\n");
     }
#endif
  }
  else
  {
     printf ("\n attrInit failed\n");
  }

  return 0;
}

Is there anything wrong with the above code ? I am not able to figure it out .

edit by bakunin: please use code-tags when posting code. It makes your program text all the more readable.

stackbase2 is being zeroed with no memory allocated for it.
That's probably your problem.

<EDIT>
Nevermind. I don't understand the gymnastics you are doing with the stackbase and size parameters to test_main.
You are passing what looks like an invalid address with the the pointer arithmetic here:

base+PTHREAD_STACK_MIN

and then attempting to set the stack to this address. That's all I can see.

stackbase2 is not getting used in program so i didn't bother about it. Passing pointer argument in test_main () function also i have tried with giving proper values like base as stack address and size as stack size. Same code works fine if i compile all in one file. Problem comes only if i move library code to another file and compile it to generate shared object (.so library). And when i link this to compile and run with main() then i get segmentation fault.

I can't see any other problem offhand. The link in my first post does note that there are some common-sense issues that could be pertinent.

"...Runtime stack requirements vary for library calls and dynamic linking. 
You should be absolutely certain that the specified stack satisfies the runtime
requirements for library calls and dynamic linking.
Very few occasions exist when specifying a stack, its size, or both, is appropriate.
Even an expert has a difficult time knowing whether the right size was specified. 
Even a program that is compliant with ABI standards cannot determine its stack size 
statically. The stack size is dependent on the needs of the particular runtime 
environment in execution...."

I'll see if I can find time to try your code as a shared lib and try to duplicate your issue.

Here is the way i compiled and generated the behavior. May be i am doing something wrong while compiling and linking itself. I am giving steps to duplicate the issue:

Step 1: source code given below is kept in a file name test.c

/*file : test.c*/ 
/* main */

#include <stdio.h>
#include <limits.h>
#include <pthread.h>

void *func1 (void *p1)
{
   pthread_setcancelstate (PTHREAD_CANCEL_ENABLE,NULL);
   pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS,NULL);
   printf ("\n In Thread Function1 :) \n");
   sleep (3);
   pthread_exit (NULL);
}
int main (void)
{
  size_t size = PTHREAD_STACK_MIN + 0x444000;
  void *base = malloc (size );
  test_main (func1,base+PTHREAD_STACK_MIN,size-PTHREAD_STACK_MIN);
  sleep (10);
  //sleep (10);
//  free (base);
  getchar();
  return 0;
}

Step 2 : Code given below is kept in a file test_shared.c

/* file : test_shared.c */
/* Library Function */
#include <stdio.h>
//#define __USE_XOPEN2K
//#define _XOPEN_SOURCE 600
#include <pthread.h>
#include <unistd.h>
#include <limits.h>
#include <sched.h>
#include <stdlib.h>

void CleanupHandler (void* pArg)
{
   printf ("\nI am In clean up handler \n");
   usleep (100);
}
void * a(void *);
void * func(void *p(void *))
{
   printf ("\n In Thread Function :) \n");
   pthread_cleanup_push(CleanupHandler,NULL);
   p (NULL);
   pthread_cleanup_pop(1);
   usleep (1000);
   sleep (2);
   pthread_exit (NULL);
  return NULL;
}
int test_main (void*fn(void*),void *stackbase,size_t size)
{
   pthread_attr_t tattr;
   pthread_t tid;
   int ret;
   struct sched_param stparam;
   void *stackbase1,*stackbase2;
   size_t size1,size2;
   int policy;

   /* initialized with default attributes */
   ret = pthread_attr_init(&tattr);
   if (ret != -1)
   {
#if 1
     printf ("\n Stack:size <%p:%d>\n",stackbase,size);
     /* setting the base address and size of the stack */
     stackbase2 = memset (stackbase,0,size);
     ret = pthread_attr_setstack(&tattr, stackbase,size);
     if (ret != -1)
     {
#endif
       if (pthread_attr_setschedpolicy (&tattr,SCHED_RR))
       {
          printf ("\n setschedpolicy failed\n");
          pthread_attr_destroy (&tattr);
          exit (0);
       }
       if (pthread_attr_getschedpolicy (&tattr,&policy))
       {
          printf ("\n getschedpolicy failed\n");
          pthread_attr_destroy (&tattr);
          exit (1);
       }
       printf ("\n Policy = <%s>\n",(policy==0)?"SCHED_NORMAL":(policy == 1)?"SCHED_FIFO":(policy == 2)?"SCEHD_RR":(policy==3)?"SCHED_OTHERS":"NONE");
       sched_getparam (0,&stparam);
       stparam.sched_priority = 99;
       pthread_attr_setschedparam (&tattr,&stparam);
//     pthread_attr_setdetachstate (&tattr,PTHREAD_CREATE_JOINABLE);
//     pthread_attr_setinheritsched (&tattr,PTHREAD_EXPLICIT_SCHED);

        /* address and size specified */
        ret = pthread_create(&tid, &tattr, func, (void*)fn);
        if (ret == -1)
        {
           printf ("\nThread Creation Failed\n");
           pthread_attr_destroy (&tattr);
           perror (NULL);
        }
        else
        {
           printf ("\n Thread Creation Passed\n");
           pthread_attr_getstack (&tattr, &stackbase1,&size1);
           printf ("\n Stack:size <%p:%d>\n",stackbase1,size1);
           sleep (2);
           sleep (1);
           printf ("\n AF Join \n");
           pthread_attr_destroy (&tattr);
        }
#if 1
     }
     else
     {
        printf ("\n Setstack failed\n");
     }
#endif
  }
  else
  {
     printf ("\n attrInit failed\n");
  }

  return 0;
}

Step : 3 : Now i generated .so using command given below.

gcc -fPIC -g -c -Wall test_shared.c
gcc -shared -Wl -o libtest_shared.so test_shared.o -lc

Step : 4 : Now i compiled test.c with command as below.

gcc -Wall -lpthread -L$PWD -ltest_shared -o test test.c

Step : 5 : Now i run the application using given command:

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD ; ./test

These step will generate issue posted by me. If i compile these files like the following command i don't get any issue.

gcc -Wall -lpthread -o test1 test.c test_shared.c ; ./test1

I am struggling to find cause for this behavior. Please let me know the cause for same.