328 lines
8.6 KiB
C++
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);
|
|
}
|