#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; }