From 11913091045ebc44f003138d9e69cdc91fe4982e Mon Sep 17 00:00:00 2001 From: notaz Date: Thu, 25 Feb 2010 16:43:55 +0200 Subject: [PATCH] segfault handler, op parser --- loader/Makefile | 24 ++++ loader/emu.c | 284 ++++++++++++++++++++++++++++++++++++++++ loader/header.h | 10 ++ loader/loader.c | 14 +- loader/patches.c | 96 ++++++++++++++ loader/sys_cacheflush.S | 26 ++++ loader/sys_cacheflush.h | 1 + loader/tools/mcut.c | 19 +++ loader/tools/static.c | 21 +++ 9 files changed, 492 insertions(+), 3 deletions(-) create mode 100644 loader/Makefile create mode 100644 loader/emu.c create mode 100644 loader/header.h create mode 100644 loader/patches.c create mode 100644 loader/sys_cacheflush.S create mode 100644 loader/sys_cacheflush.h create mode 100644 loader/tools/mcut.c create mode 100644 loader/tools/static.c diff --git a/loader/Makefile b/loader/Makefile new file mode 100644 index 0000000..67dd24c --- /dev/null +++ b/loader/Makefile @@ -0,0 +1,24 @@ +CC = $(CROSS_COMPILE)gcc +AS = $(CROSS_COMPILE)as +CFLAGS += -Wall -ggdb +LDFLAGS += -static -ggdb + +ifndef ARCH +ARCH = ia32 +CFLAGS += -m32 +LDFLAGS += -m32 +ASFLAGS += --32 +endif +ifeq "$(ARCH)" "arm" +ASFLAGS += -mfloat-abi=soft +OBJ += sys_cacheflush.o +endif + +OBJ += loader.o loader_$(ARCH).o patches.o emu.o + +loader: $(OBJ) + +loader: LDFLAGS += -Wl,-T script_$(ARCH).lds + +clean: + $(RM) loader $(OBJ) diff --git a/loader/emu.c b/loader/emu.c new file mode 100644 index 0000000..9dc3185 --- /dev/null +++ b/loader/emu.c @@ -0,0 +1,284 @@ +// vim:shiftwidth=2:expandtab +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "header.h" + +#define iolog printf +//#define iolog(...) + +typedef unsigned int u32; +typedef unsigned short u16; +typedef unsigned char u8; + +static int memdev; +static volatile u16 *memregs, *blitter; + + +static volatile void *translate_addr(u32 a, u32 *mask) +{ + if ((a & 0xfff00000) == 0x7f000000) { + *mask = 0xffff; + return memregs; + } + if ((a & 0xfff00000) == 0x7f100000) { + *mask = 0xff; + return blitter; + } + fprintf(stderr, "bad IO @ %08x\n", a); + abort(); +} + +static u32 xread8(u32 a) +{ + volatile u8 *mem; + u32 mask; + + iolog("r8 %08x\n", a); + mem = translate_addr(a, &mask); + return mem[a & mask]; +} + +static u32 xread16(u32 a) +{ + volatile u16 *mem; + u32 mask; + + iolog("r16 %08x\n", a); + mem = translate_addr(a, &mask); + return mem[(a & mask) / 2]; +} + +static u32 xread32(u32 a) +{ + volatile u32 *mem; + u32 mask; + + iolog("r32 %08x\n", a); + mem = translate_addr(a, &mask); + return mem[(a & mask) / 4]; +} + +static void xwrite8(u32 a, u32 d) +{ + volatile u8 *mem; + u32 mask; + + iolog("w8 %08x %08x\n", a, d); + mem = translate_addr(a, &mask); + mem[a & mask] = d; +} + +static void xwrite16(u32 a, u32 d) +{ + volatile u16 *mem; + u32 mask; + + iolog("w16 %08x %08x\n", a, d); + mem = translate_addr(a, &mask); + mem[(a & mask) / 2] = d; +} + +static void xwrite32(u32 a, u32 d) +{ + volatile u32 *mem; + u32 mask; + + iolog("w32 %08x %08x\n", a, d); + mem = translate_addr(a, &mask); + mem[(a & mask) / 4] = d; +} + +#define BIT_SET(v, b) (v & (1 << (b))) + +static void handle_op(u32 addr_pc, u32 op, u32 *regs, u32 addr_check) +{ + u32 t, shift, ret, addr; + int rn, rd; + + rd = (op & 0x0000f000) >> 12; + rn = (op & 0x000f0000) >> 16; + + if ((op & 0x0f200090) == 0x01000090) { // AM3: LDRH, STRH + if (BIT_SET(op, 6)) // S + goto unhandled; + + if (BIT_SET(op, 22)) // imm offset + t = ((op & 0xf00) >> 4) | (op & 0x0f); + else // reg offset + t = regs[op & 0x000f]; + + if (!BIT_SET(op, 23)) + t = -t; + addr = regs[rn] + t; + + if (BIT_SET(op, 20)) { // Load + ret = xread16(addr); + regs[rd] = ret; + } + else + xwrite16(addr, regs[rd]); + } + else if ((op & 0x0d200000) == 0x05000000) { // AM2: LDR[B], STR[B] + if (BIT_SET(op, 25)) { // reg offs + if (BIT_SET(op, 4)) + goto unhandled; + + t = regs[op & 0x000f]; + shift = (op & 0x0f80) >> 7; + switch ((op & 0x0060) >> 5) { + case 0: t = t << shift; break; + case 1: t = t >> (shift + 1); break; + case 2: t = (signed int)t >> (shift + 1); break; + case 3: goto unhandled; // I'm just lazy + } + } + else // imm offs + t = op & 0x0fff; + + if (!BIT_SET(op, 23)) + t = -t; + addr = regs[rn] + t; + + if (BIT_SET(op, 20)) { // Load + if (BIT_SET(op, 22)) // Byte + ret = xread8(addr); + else + ret = xread32(addr); + regs[rd] = ret; + } + else { + if (BIT_SET(op, 22)) // Byte + xwrite8(addr, regs[rd]); + else + xwrite32(addr, regs[rd]); + } + } + else + goto unhandled; + + if (addr != addr_check) { + fprintf(stderr, "bad calculated addr: %08x vs %08x\n", addr, addr_check); + abort(); + } + return; + +unhandled: + fprintf(stderr, "unhandled IO op %08x @ %08x\n", op, addr_pc); +} + +static void segv_sigaction(int num, siginfo_t *info, void *ctx) +{ + struct ucontext *context = ctx; + u32 *regs = (u32 *)&context->uc_mcontext.arm_r0; + u32 op = *(u32 *)regs[15]; + + //printf("segv %d %p @ %08x\n", info->si_code, info->si_addr, regs[15]); +/* + static int thissec, sfps; + struct timeval tv; + gettimeofday(&tv, NULL); + sfps++; + if (tv.tv_sec != thissec) { + printf("%d\n", sfps); + sfps = 0; + thissec = tv.tv_sec; + } +*/ + + handle_op(regs[15], op, regs, (u32)info->si_addr); + regs[15] += 4; + return; + + //signal(num, SIG_DFL); + //raise(num); +} + +#define LINKPAGE_SIZE 0x1000 + +struct linkpage { + u32 (*xread8)(u32 a); + u32 (*xread16)(u32 a); + u32 (*xread32)(u32 a); + void (*xwrite8)(u32 a, u32 d); + void (*xwrite16)(u32 a, u32 d); + void (*xwrite32)(u32 a, u32 d); + u32 retval; + u32 *reg_ptr; + u32 saved_regs[6]; // r0-r3,r12,lr + u32 code[0]; +}; + +static struct linkpage *g_linkpage; +static u32 *g_code_ptr; + +void emu_init(void *map_bottom) +{ + struct sigaction segv_action = { + .sa_sigaction = segv_sigaction, + .sa_flags = SA_SIGINFO, + }; + struct linkpage init_linkpage = { + .xread8 = xread8, + .xread16 = xread16, + .xread32 = xread32, + .xwrite8 = xwrite8, + .xwrite16 = xwrite16, + .xwrite32 = xwrite32, + }; + void *ret; + + sigemptyset(&segv_action.sa_mask); + sigaction(SIGSEGV, &segv_action, NULL); + + g_linkpage = (void *)(((u32)map_bottom - LINKPAGE_SIZE) & ~0xfff); + ret = mmap(g_linkpage, LINKPAGE_SIZE, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); + if (ret != g_linkpage) { + perror("mmap linkpage"); + exit(1); + } + printf("linkpage @ %p\n", g_linkpage); + memcpy(g_linkpage, &init_linkpage, sizeof(*g_linkpage)); + g_linkpage->reg_ptr = g_linkpage->saved_regs; + g_code_ptr = g_linkpage->code; + + memdev = open("/dev/mem", O_RDWR); + memregs = mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, memdev, 0xc0000000); + blitter = mmap(NULL, 0x100, PROT_READ|PROT_WRITE, MAP_SHARED, memdev, 0xe0020000); + printf("mapped %d %p %p\n", memdev, memregs, blitter); +} + +void *emu_mmap_dev(unsigned int length, int prot, int flags, unsigned int offset) +{ + char name[32]; + int fd; + + if ((offset & ~0xffff) == 0xc0000000) { + return mmap((void *)0x7f000000, length, PROT_NONE, + MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED|MAP_NORESERVE, -1, 0); + } + if ((offset & ~0xffff) == 0xe0020000) { + return mmap((void *)0x7f100000, length, PROT_NONE, + MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED|MAP_NORESERVE, -1, 0); + } + // pass through + if ((offset & 0xfe000000) == 0x02000000) + return mmap(NULL, length, prot, flags, memdev, offset); + + sprintf(name, "m%08x", offset); + fd = open(name, O_CREAT|O_RDWR, 0644); + lseek(fd, length - 1, SEEK_SET); + name[0] = 0; + write(fd, name, 1); + + return mmap(NULL, length, prot, MAP_SHARED, fd, 0); +} + diff --git a/loader/header.h b/loader/header.h new file mode 100644 index 0000000..ddb948d --- /dev/null +++ b/loader/header.h @@ -0,0 +1,10 @@ + +void do_entry(unsigned long entry, void *stack_frame, int stack_frame_cnt, void *exitf); + +void do_patches(void *ptr, unsigned int size); + +void emu_init(void *map_bottom); +void *emu_mmap_dev(unsigned int length, int prot, int flags, unsigned int offset); + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) + diff --git a/loader/loader.c b/loader/loader.c index 6a829ff..530eed3 100644 --- a/loader/loader.c +++ b/loader/loader.c @@ -5,6 +5,8 @@ #include #include +#include "header.h" + #define CHECK_(val, fail_operator, expect, err_msg) \ if (val fail_operator expect) { \ fprintf(stderr, err_msg ", exiting (%d)\n", (int)(long)val); \ @@ -28,8 +30,6 @@ return 1; \ } -#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) - typedef struct { unsigned long start; unsigned long end; @@ -51,10 +51,10 @@ static int is_range_used(maps_range *maps, int map_cnt, unsigned long start, uns } extern char **environ; -extern void do_entry(Elf32_Addr entry, void *stack_frame, int stack_frame_size, void *exitf); int main(int argc, char *argv[]) { + void *lowest_segment = (void *)-1; Elf32_Ehdr hdr; Elf32_Phdr *phdr; FILE *fi; @@ -139,9 +139,17 @@ int main(int argc, char *argv[]) FAIL_PERROR("fseek"); if (fread((char *)ptr + align, 1, phdr[i].p_filesz, fi) != phdr[i].p_filesz) FAIL_PERROR("too small or"); + + if (phdr[i].p_flags & PF_X) + do_patches((char *)ptr + align, phdr[i].p_filesz); } + + if (map_ptr < lowest_segment) + lowest_segment = map_ptr; } + emu_init(lowest_segment); + stack_frame[0] = 1; // argc stack_frame[1] = (long)argv[1]; stack_frame[2] = 0; diff --git a/loader/patches.c b/loader/patches.c new file mode 100644 index 0000000..9354d2f --- /dev/null +++ b/loader/patches.c @@ -0,0 +1,96 @@ +// vim:shiftwidth=2:expandtab +#include +#include +#include +#include +#include +#include + +#include "header.h" +#include "sys_cacheflush.h" + +static const unsigned int sig_open[] = { + 0xe59cc000, 0xe33c0000, 0x1a000003, 0xef900005 +}; + +static const unsigned int sig_mmap[] = { + 0xe92d000f, 0xe1a0000d, 0xef90005a, 0xe28dd010 +}; + +static const unsigned int sig_mmap_[] = { + 0xe52d5004, 0xe59d5008, 0xe52d4004, 0xe59d4008, + 0xe1b0ca05, 0x1a000006, 0xe1a05625, 0xef9000c0 +}; + +#define FAKE_DEVMEM_DEVICE 10001 + +static int w_open(const char *pathname, int flags, mode_t mode) +{ + int ret; + if (strcmp(pathname, "/dev/mem") != 0) + ret = open(pathname, flags, mode); + else + ret = FAKE_DEVMEM_DEVICE; + + printf("open(%s) = %d\n", pathname, ret); + return ret; +} + +static void *w_mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset) +{ + void *ret; + if (fd != FAKE_DEVMEM_DEVICE) + ret = mmap(addr, length, prot, flags, fd, offset); + else + ret = emu_mmap_dev(length, prot, flags, offset); + + printf("mmap(%p, %x, %x, %x, %d, %lx) = %p\n", addr, length, prot, flags, fd, (long)offset, ret); + return ret; +} +#define w_mmap_ w_mmap + +#define PATCH(f) { sig_##f, ARRAY_SIZE(sig_##f), w_##f } + +static const struct { + const unsigned int *sig; + size_t sig_cnt; + void *func; +} patches[] = { + PATCH(open), + PATCH(mmap), + PATCH(mmap_), // mmap using mmap2 syscall +}; + +void do_patches(void *ptr, unsigned int size) +{ + int i, s; + + for (i = 0; i < ARRAY_SIZE(patches); i++) { + const unsigned int *sig = patches[i].sig; + unsigned int *seg = (void *)(((long)ptr + 3) & ~3); + unsigned int *seg_end = seg + size / 4; + unsigned int sig0 = sig[0]; + + for (; seg < seg_end; seg++) { + if (*seg != sig0) + continue; + + for (s = 1; s < patches[i].sig_cnt; s++) + if (seg[s] != sig[s]) + break; + + if (s == patches[i].sig_cnt) + goto found; + } + continue; + +found: + printf(" patch #%i @ %08x\n", i, (int)seg); + seg[0] = 0xe59ff000; // ldr pc, [pc] + seg[1] = 0; + seg[2] = (unsigned int)patches[i].func; + } + + sys_cacheflush(ptr, (char *)ptr + size); +} + diff --git a/loader/sys_cacheflush.S b/loader/sys_cacheflush.S new file mode 100644 index 0000000..d6ecebe --- /dev/null +++ b/loader/sys_cacheflush.S @@ -0,0 +1,26 @@ +@ vim:filetype=armasm +#include + + +.global sys_cacheflush @ const void *start_addr, const void *end_addr + +sys_cacheflush: + mov r2, #0 +#ifdef __ARM_EABI__ + /* EABI version */ + str r7, [sp, #-4]! + mov r7, #(__ARM_NR_cacheflush & 0xff) +#if (__ARM_NR_cacheflush & 0x00ff00) + orr r7, r7, #(__ARM_NR_cacheflush & 0x00ff00) +#endif +#if (__ARM_NR_cacheflush & 0xff0000) + orr r7, r7, #(__ARM_NR_cacheflush & 0xff0000) +#endif + swi 0 + ldr r7, [sp], #4 +#else + /* OABI */ + swi __ARM_NR_cacheflush +#endif + bx lr + diff --git a/loader/sys_cacheflush.h b/loader/sys_cacheflush.h new file mode 100644 index 0000000..a35c00e --- /dev/null +++ b/loader/sys_cacheflush.h @@ -0,0 +1 @@ +void sys_cacheflush(const void *start_addr, const void *end_addr); diff --git a/loader/tools/mcut.c b/loader/tools/mcut.c new file mode 100644 index 0000000..98a2d0a --- /dev/null +++ b/loader/tools/mcut.c @@ -0,0 +1,19 @@ +#include + +int main(int argc, char *argv[]) +{ + FILE *fi; + int c, t; + + fi = fopen(argv[1], "rb"); + fseek(fi, strtoul(argv[2], NULL, 0), SEEK_SET); + c = atoi(argv[3]); + + while (c--) { + fread(&t, 1, 4, fi); + printf("0x%08x, ", t); + } + + return 0; +} + diff --git a/loader/tools/static.c b/loader/tools/static.c new file mode 100644 index 0000000..41f237b --- /dev/null +++ b/loader/tools/static.c @@ -0,0 +1,21 @@ +#include +#include +#include +#include +#include + +int main() +{ + volatile void *memregs; + int memdev; + + printf("hi\n"); + + memdev = open("/dev/mem", O_RDWR); + memregs = mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, memdev, 0xc0000000); + printf("%02x %04x %08x\n", ((char *)memregs)[0x2011], ((short *)memregs)[0x1198/2], ((int *)memregs)[0xbcdc/4]); + //sleep(1000); + + return 0; +} + -- 2.39.2