redquark-amiberry-rb/src/od-pandora/sigsegv_handler.cpp

328 lines
8.6 KiB
C++

/*
* 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 <asm/sigcontext.h>
#include <signal.h>
#include <dlfcn.h>
#include <execinfo.h>
#include <SDL.h>
#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<size; ++i)
{
if (dladdr(array[i], &dlinfo)) {
const char *symname = dlinfo.dli_sname;
printf("%p <%s + 0x%08x> (%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);
}