#include <sys/mman.h>
#include <pthread.h>
#include <unistd.h>
#include <assert.h>
#include <unistd.h>
#include <sys/syscall.h>
#include "../../config.h"
#define VG_STRINGIFZ(__str) #__str
#define VG_STRINGIFY(__str) VG_STRINGIFZ(__str)
extern void _exit_with_stack_teardown(void*, size_t);
/* Below code is modified version of android bionic
pthread_exit: when a detached thread exits: it munmaps
its stack and then exits. We cannot do that in C,
as we cannot touch the stack after the munmap
and before the exit. */
#if defined(VGP_x86_linux)
asm("\n"
".text\n"
"\t.globl _exit_with_stack_teardown\n"
"\t.type _exit_with_stack_teardown,@function\n"
"_exit_with_stack_teardown:\n"
// We can trash registers because this function never returns.
"\tmov 4(%esp), %ebx\n" // stackBase
"\tmov 8(%esp), %ecx\n" // stackSize
"\tmov $"VG_STRINGIFY(__NR_munmap)", %eax\n"
"\tint $0x80\n"
// If munmap failed, we ignore the failure and exit anyway.
"\tmov $0, %ebx\n" // status
"\tmovl $"VG_STRINGIFY(__NR_exit)", %eax\n"
"\tint $0x80\n");
// The exit syscall does not return.
#elif defined(VGP_amd64_linux)
asm("\n"
".text\n"
"\t.globl _exit_with_stack_teardown\n"
"\t.type _exit_with_stack_teardown,@function\n"
"_exit_with_stack_teardown:\n"
"\tmov $"VG_STRINGIFY(__NR_munmap)", %eax\n"
"\tsyscall\n"
// If munmap failed, we ignore the failure and exit anyway.
"\tmov $0, %rdi\n"
"\tmov $"VG_STRINGIFY(__NR_exit)", %eax\n"
"\tsyscall\n");
// The exit syscall does not return.
#elif defined(VGP_arm_linux)
asm("\n"
".text\n"
"\t.globl _exit_with_stack_teardown\n"
"\t.type _exit_with_stack_teardown,%function\n"
"_exit_with_stack_teardown:\n"
"\tldr r7, ="VG_STRINGIFY(__NR_munmap)"\n"
"\tswi #0\n"
// If munmap failed, we ignore the failure and exit anyway.
"\tmov r0, #0\n"
"\tldr r7, ="VG_STRINGIFY(__NR_exit)"\n"
"\tswi #0\n");
// The exit syscall does not return.
#else
void _exit_with_stack_teardown(void*stack, size_t sz)
{
// asm code not done for this platform.
// Do nothing, just return. The thread will exit spontaneously
}
#endif
static void *stack;
static size_t sz = 64 * 1024;
/* This one detaches, does its own thing. */
void* child_fn ( void* arg )
{
int r;
r= pthread_detach( pthread_self() ); assert(!r);
_exit_with_stack_teardown(stack, sz);
return NULL;
}
/* Parent creates 1 child, that will detach, and exit after destroying
its own stack. */
int main ( void )
{
int r;
pthread_attr_t attr;
pthread_t child;
r = pthread_attr_init(&attr); assert(!r);
# if !defined(VGO_darwin)
stack = mmap(NULL, sz, PROT_READ|PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS,
-1, 0);
# else
stack = mmap(NULL, sz, PROT_READ|PROT_WRITE, MAP_PRIVATE | MAP_ANON,
-1, 0);
# endif
assert(stack != (void *)-1);
r = pthread_attr_setstack(&attr, stack, sz);
r = pthread_create(&child, &attr, child_fn, NULL); assert(!r);
sleep(1);
return 0;
}