You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
126 lines
2.7 KiB
C
126 lines
2.7 KiB
C
#include <fcntl.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <sys/mman.h>
|
|
#include "objc/runtime.h"
|
|
#include "objc/blocks_runtime.h"
|
|
#include "blocks_runtime.h"
|
|
#include "lock.h"
|
|
#include "visibility.h"
|
|
|
|
#define PAGE_SIZE 4096
|
|
|
|
static void *executeBuffer;
|
|
static void *writeBuffer;
|
|
static ptrdiff_t offset;
|
|
static mutex_t trampoline_lock;
|
|
static char *tmpPattern;
|
|
|
|
struct wx_buffer
|
|
{
|
|
void *w;
|
|
void *x;
|
|
};
|
|
|
|
PRIVATE void init_trampolines(void)
|
|
{
|
|
INIT_LOCK(trampoline_lock);
|
|
char *tmp = getenv("TMPDIR");
|
|
if (NULL == tmp)
|
|
{
|
|
tmp = "/tmp/";
|
|
}
|
|
if (0 > asprintf(&tmpPattern, "%s/objc_trampolinesXXXXXXXXXXX", tmp))
|
|
{
|
|
abort();
|
|
}
|
|
}
|
|
|
|
static struct wx_buffer alloc_buffer(size_t size)
|
|
{
|
|
LOCK_FOR_SCOPE(&trampoline_lock);
|
|
if ((0 == offset) || (offset + size >= PAGE_SIZE))
|
|
{
|
|
int fd = mkstemp(tmpPattern);
|
|
unlink(tmpPattern);
|
|
ftruncate(fd, PAGE_SIZE);
|
|
void *w = mmap(NULL, PAGE_SIZE, PROT_WRITE, MAP_SHARED, fd, 0);
|
|
executeBuffer = mmap(NULL, PAGE_SIZE, PROT_READ|PROT_EXEC, MAP_SHARED, fd, 0);
|
|
*((void**)w) = writeBuffer;
|
|
writeBuffer = w;
|
|
offset = sizeof(void*);
|
|
}
|
|
struct wx_buffer b = { writeBuffer + offset, executeBuffer + offset };
|
|
offset += size;
|
|
return b;
|
|
}
|
|
|
|
extern void __objc_block_trampoline;
|
|
extern void __objc_block_trampoline_end;
|
|
extern void __objc_block_trampoline_sret;
|
|
extern void __objc_block_trampoline_end_sret;
|
|
|
|
IMP imp_implementationWithBlock(void *block)
|
|
{
|
|
struct block_literal *b = block;
|
|
void *start;
|
|
void *end;
|
|
|
|
if ((b->flags & BLOCK_USE_SRET) == BLOCK_USE_SRET)
|
|
{
|
|
start = &__objc_block_trampoline_sret;
|
|
end = &__objc_block_trampoline_end_sret;
|
|
}
|
|
else
|
|
{
|
|
start = &__objc_block_trampoline;
|
|
end = &__objc_block_trampoline_end;
|
|
}
|
|
|
|
size_t trampolineSize = end - start;
|
|
// If we don't have a trampoline intrinsic for this architecture, return a
|
|
// null IMP.
|
|
if (0 >= trampolineSize) { return 0; }
|
|
|
|
struct wx_buffer buf = alloc_buffer(trampolineSize + 2*sizeof(void*));
|
|
void **out = buf.w;
|
|
out[0] = (void*)b->invoke;
|
|
out[1] = Block_copy(b);
|
|
memcpy(&out[2], start, trampolineSize);
|
|
out = buf.x;
|
|
return (IMP)&out[2];
|
|
}
|
|
|
|
static void* isBlockIMP(void *anIMP)
|
|
{
|
|
LOCK(&trampoline_lock);
|
|
void *e = executeBuffer;
|
|
void *w = writeBuffer;
|
|
UNLOCK(&trampoline_lock);
|
|
while (e)
|
|
{
|
|
if ((anIMP > e) && (anIMP < e + PAGE_SIZE))
|
|
{
|
|
return ((char*)w) + ((char*)anIMP - (char*)e);
|
|
}
|
|
e = *(void**)e;
|
|
w = *(void**)w;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void *imp_getBlock(IMP anImp)
|
|
{
|
|
if (0 == isBlockIMP((void*)anImp)) { return 0; }
|
|
return *(((void**)anImp) - 1);
|
|
}
|
|
BOOL imp_removeBlock(IMP anImp)
|
|
{
|
|
void *w = isBlockIMP((void*)anImp);
|
|
if (0 == w) { return NO; }
|
|
Block_release(((void**)anImp) - 1);
|
|
return YES;
|
|
}
|