/* * sigsegv_linux_arm.cpp - x86_64 Linux SIGSEGV handler * * Copyright (c) 2014 Jens Heitmann ARAnyM dev team (see AUTHORS) * * Inspired by Bernie Meyer's UAE-JIT and Gwenole Beauchesne's Basilisk II-JIT * * This file is part of the ARAnyM project which builds a new and powerful * TOS/FreeMiNT compatible virtual machine running on almost any hardware. * * ARAnyM is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * ARAnyM is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with ARAnyM; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include "sysconfig.h" #include "sysdeps.h" #include "options.h" #include "include/memory.h" #include "newcpu.h" #include "custom.h" #include "jit/comptbl.h" #include "jit/compemu.h" #include #include #include #include #include #define DEBUG 0 #include "debug.h" enum transfer_type_t { TYPE_UNKNOWN, TYPE_LOAD, TYPE_STORE }; enum type_size_t { SIZE_UNKNOWN, SIZE_BYTE, SIZE_WORD, SIZE_INT }; static int in_handler=0; enum { ARM_REG_PC = 15, ARM_REG_CPSR = 16 }; STATIC_INLINE void unknown_instruction(uae_u32 instr) { panicbug("Unknown instruction %08x!\n", instr); SDL_Quit(); abort(); } static bool handle_arm_instruction(unsigned long *pregs, uintptr addr) { unsigned int *pc = (unsigned int *)pregs[ARM_REG_PC]; panicbug("IP: %p [%08x] %p\n", pc, pc[0], addr); if (pc == 0) return false; if (in_handler > 0) { panicbug("Segmentation fault in handler :-(\n"); return false; } in_handler += 1; transfer_type_t transfer_type = TYPE_UNKNOWN; int transfer_size = SIZE_UNKNOWN; enum { SIGNED, UNSIGNED }; int style = UNSIGNED; // Handle load/store instructions only const unsigned int opcode = pc[0]; switch ((opcode >> 25) & 7) { case 0: // Halfword and Signed Data Transfer (LDRH, STRH, LDRSB, LDRSH) // Determine transfer size (S/H bits) switch ((opcode >> 5) & 3) { case 0: // SWP instruction panicbug("FIXME: SWP Instruction\n"); break; case 1: // Unsigned halfwords transfer_size = SIZE_WORD; break; case 3: // Signed halfwords style = SIGNED; transfer_size = SIZE_WORD; break; case 2: // Signed byte style = SIGNED; transfer_size = SIZE_BYTE; break; } break; case 2: case 3: // Single Data Transfer (LDR, STR) style = UNSIGNED; // Determine transfer size (B bit) if (((opcode >> 22) & 1) == 1) transfer_size = SIZE_BYTE; else transfer_size = SIZE_INT; break; default: panicbug("FIXME: support load/store mutliple?\n"); in_handler--; return false; } // Check for invalid transfer size (SWP instruction?) if (transfer_size == SIZE_UNKNOWN) { panicbug("Invalid transfer size\n"); in_handler--; return false; } // Determine transfer type (L bit) if (((opcode >> 20) & 1) == 1) transfer_type = TYPE_LOAD; else transfer_type = TYPE_STORE; int rd = (opcode >> 12) & 0xf; #if DEBUG static const char * reg_names[] = { "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "sl", "fp", "ip", "sp", "lr", "pc" }; panicbug("%s %s register %s\n", transfer_size == SIZE_BYTE ? "byte" : transfer_size == SIZE_WORD ? "word" : transfer_size == SIZE_INT ? "long" : "unknown", transfer_type == TYPE_LOAD ? "load to" : "store from", reg_names[rd]); panicbug("\n"); // for (int i = 0; i < 16; i++) { // panicbug("%s : %p", reg_names[i], pregs[i]); // } #endif // if ((addr < 0x00f00000) || (addr > 0x00ffffff)) // goto buserr; if (transfer_type == TYPE_LOAD) { switch(transfer_size) { case SIZE_BYTE: { pregs[rd] = style == SIGNED ? (uae_s8)get_byte(addr) : (uae_u8)get_byte(addr); break; } case SIZE_WORD: { pregs[rd] = do_byteswap_16(style == SIGNED ? (uae_s16)get_word(addr) : (uae_u16)get_word(addr)); break; } case SIZE_INT: { pregs[rd] = do_byteswap_32(get_long(addr)); break; } } } else { switch(transfer_size) { case SIZE_BYTE: { put_byte(addr, pregs[rd]); break; } case SIZE_WORD: { put_word(addr, do_byteswap_16(pregs[rd])); break; } case SIZE_INT: { put_long(addr, do_byteswap_32(pregs[rd])); break; } } } pregs[ARM_REG_PC] += 4; panicbug("processed: %p \n", pregs[ARM_REG_PC]); in_handler--; return true; buserr: panicbug("Amiga bus error\n"); in_handler--; // BUS_ERROR(addr); return false; } #define SIG_READ 1 #define SIG_WRITE 2 extern void dump_compiler(uae_u32 *sp); void signal_segv(int signum, siginfo_t* info, void*ptr) { int i, f = 0; ucontext_t *ucontext = (ucontext_t*)ptr; Dl_info dlinfo; #ifdef TRACER trace_end(); #endif void **bp = 0; void *ip = 0; mcontext_t *context = &(ucontext->uc_mcontext); unsigned long *regs = &context->arm_r0; uintptr addr = (uintptr)info->si_addr; addr = (uae_u32) addr - (uae_u32) natmem_offset; if (handle_arm_instruction(regs, addr)) return; if(signum == 4) printf("Illegal Instruction!\n"); else printf("Segmentation Fault!\n"); printf("info.si_signo = %d\n", signum); printf("info.si_errno = %d\n", info->si_errno); // printf("info.si_code = %d (%s)\n", info->si_code, si_codes[info->si_code]); printf("info.si_code = %d\n", info->si_code); printf("info.si_addr = %p\n", info->si_addr); if(signum == 4) printf(" value = 0x%08x\n", *((uae_u32*)(info->si_addr))); printf("reg[%02d] = 0x%08x\n",0 , ucontext->uc_mcontext.arm_r0); printf("reg[%02d] = 0x%08x\n",1 , ucontext->uc_mcontext.arm_r1); printf("reg[%02d] = 0x%08x\n",2 , ucontext->uc_mcontext.arm_r2); printf("reg[%02d] = 0x%08x\n",3 , ucontext->uc_mcontext.arm_r3); printf("reg[%02d] = 0x%08x\n",4 , ucontext->uc_mcontext.arm_r4); printf("reg[%02d] = 0x%08x\n",5 , ucontext->uc_mcontext.arm_r5); printf("reg[%02d] = 0x%08x\n",6 , ucontext->uc_mcontext.arm_r6); printf("reg[%02d] = 0x%08x\n",7 , ucontext->uc_mcontext.arm_r7); printf("reg[%02d] = 0x%08x\n",8 , ucontext->uc_mcontext.arm_r8); printf("reg[%02d] = 0x%08x\n",9 , ucontext->uc_mcontext.arm_r9); printf("reg[%02d] = 0x%08x\n",10 , ucontext->uc_mcontext.arm_r10); printf("FP = 0x%08x\n", ucontext->uc_mcontext.arm_fp); printf("IP = 0x%08x\n", ucontext->uc_mcontext.arm_ip); printf("SP = 0x%08x\n", ucontext->uc_mcontext.arm_sp); printf("LR = 0x%08x\n", ucontext->uc_mcontext.arm_lr); printf("PC = 0x%08x\n", ucontext->uc_mcontext.arm_pc); printf("CPSR = 0x%08x\n", ucontext->uc_mcontext.arm_cpsr); printf("Fault Address = 0x%08x\n", ucontext->uc_mcontext.fault_address); printf("Trap no = 0x%08x\n", ucontext->uc_mcontext.trap_no); printf("Err Code = 0x%08x\n", ucontext->uc_mcontext.error_code); printf("Old Mask = 0x%08x\n", ucontext->uc_mcontext.oldmask); // dump_compiler((uae_u32*)ucontext->uc_mcontext.arm_pc); void *getaddr = (void *)ucontext->uc_mcontext.arm_lr; if(dladdr(getaddr, &dlinfo)) printf("LR - 0x%08X: <%s> (%s)\n", getaddr, dlinfo.dli_sname, dlinfo.dli_fname); else printf("LR - 0x%08X: symbol not found\n", getaddr); // printf("Stack trace:\n"); /* #define MAX_BACKTRACE 10 void *array[MAX_BACKTRACE]; int size = backtrace(array, MAX_BACKTRACE); for(int i=0; i (%s)\n", array[i], symname, (unsigned long)array[i] - (unsigned long)dlinfo.dli_saddr, dlinfo.dli_fname); } } */ /* ip = (void*)ucontext->uc_mcontext.arm_r10; bp = (void**)ucontext->uc_mcontext.arm_r10; while(bp && ip) { if (!dladdr(ip, &dlinfo)) { printf("IP out of range\n"); break; } const char *symname = dlinfo.dli_sname; printf("% 2d: %p <%s + 0x%08x> (%s)\n", ++f, ip, symname, (unsigned long)ip - (unsigned long)dlinfo.dli_saddr, dlinfo.dli_fname); if(dlinfo.dli_sname && !strcmp(dlinfo.dli_sname, "main")) break; ip = bp[1]; bp = (void**)bp[0]; } printf("Stack trace (non-dedicated):\n"); char **strings; void *bt[20]; int sz = backtrace(bt, 20); strings = backtrace_symbols(bt, sz); for(i = 0; i < sz; ++i) printf("%s\n", strings[i]); printf("End of stack trace.\n"); */ SDL_Quit(); exit(1); }