45 |
|
// Type of the system signal handler |
46 |
|
typedef RETSIGTYPE (*signal_handler)(int); |
47 |
|
|
48 |
– |
// Ignore range chain |
49 |
– |
struct ignore_range_t { |
50 |
– |
sigsegv_address_t start; |
51 |
– |
unsigned long length; |
52 |
– |
int transfer_type; |
53 |
– |
}; |
54 |
– |
|
55 |
– |
typedef list<ignore_range_t> ignore_range_list_t; |
56 |
– |
ignore_range_list_t sigsegv_ignore_ranges; |
57 |
– |
|
48 |
|
// User's SIGSEGV handler |
49 |
|
static sigsegv_fault_handler_t sigsegv_fault_handler = 0; |
50 |
|
|
54 |
|
// Actual SIGSEGV handler installer |
55 |
|
static bool sigsegv_do_install_handler(int sig); |
56 |
|
|
67 |
– |
// Find ignore range matching address |
68 |
– |
static inline ignore_range_list_t::iterator sigsegv_find_ignore_range(sigsegv_address_t address) |
69 |
– |
{ |
70 |
– |
ignore_range_list_t::iterator it; |
71 |
– |
for (it = sigsegv_ignore_ranges.begin(); it != sigsegv_ignore_ranges.end(); it++) |
72 |
– |
if (address >= it->start && address < it->start + it->length) |
73 |
– |
break; |
74 |
– |
return it; |
75 |
– |
} |
76 |
– |
|
57 |
|
|
58 |
|
/* |
59 |
|
* Instruction decoding aids |
590 |
|
return false; |
591 |
|
} |
592 |
|
|
613 |
– |
ignore_range_list_t::iterator it = sigsegv_find_ignore_range((sigsegv_address_t)instr.addr); |
614 |
– |
if (it == sigsegv_ignore_ranges.end() || ((it->transfer_type & instr.transfer_type) != instr.transfer_type)) { |
615 |
– |
// Address doesn't fall into ignore ranges list, let it crash. |
616 |
– |
return false; |
617 |
– |
} |
618 |
– |
|
593 |
|
#if DEBUG |
594 |
|
printf("%08x: %s %s access", *nip_p, |
595 |
|
instr.transfer_size == SIZE_BYTE ? "byte" : instr.transfer_size == SIZE_WORD ? "word" : "long", |
635 |
|
bool fault_recovered = false; |
636 |
|
|
637 |
|
// Call user's handler and reinstall the global handler, if required |
638 |
< |
if (sigsegv_fault_handler(fault_address, fault_instruction)) { |
638 |
> |
switch (sigsegv_fault_handler(fault_address, fault_instruction)) { |
639 |
> |
case SIGSEGV_RETURN_SUCCESS: |
640 |
|
#if (defined(HAVE_SIGACTION) ? defined(SIGACTION_NEED_REINSTALL) : defined(SIGNAL_NEED_REINSTALL)) |
641 |
|
sigsegv_do_install_handler(sig); |
642 |
|
#endif |
643 |
|
fault_recovered = true; |
644 |
< |
} |
644 |
> |
break; |
645 |
|
#if HAVE_SIGSEGV_SKIP_INSTRUCTION |
646 |
< |
else if (sigsegv_ignore_ranges.size() > 0) { |
646 |
> |
case SIGSEGV_RETURN_SKIP_INSTRUCTION: |
647 |
|
// Call the instruction skipper with the register file available |
648 |
|
if (SIGSEGV_SKIP_INSTRUCTION(SIGSEGV_REGISTER_FILE)) |
649 |
|
fault_recovered = true; |
650 |
< |
} |
650 |
> |
break; |
651 |
|
#endif |
652 |
+ |
} |
653 |
|
|
654 |
|
if (!fault_recovered) { |
655 |
< |
// FAIL: reinstall default handler for "safe" crash |
655 |
> |
// Failure: reinstall default handler for "safe" crash |
656 |
|
#define FAULT_HANDLER(sig) signal(sig, SIG_DFL); |
657 |
|
SIGSEGV_ALL_SIGNALS |
658 |
|
#undef FAULT_HANDLER |
737 |
|
|
738 |
|
|
739 |
|
/* |
764 |
– |
* Add SIGSEGV ignore range |
765 |
– |
*/ |
766 |
– |
|
767 |
– |
void sigsegv_add_ignore_range(sigsegv_address_t address, unsigned long length, int transfer_type) |
768 |
– |
{ |
769 |
– |
ignore_range_t ignore_range; |
770 |
– |
ignore_range.start = address; |
771 |
– |
ignore_range.length = length; |
772 |
– |
ignore_range.transfer_type = transfer_type; |
773 |
– |
sigsegv_ignore_ranges.push_front(ignore_range); |
774 |
– |
} |
775 |
– |
|
776 |
– |
|
777 |
– |
/* |
778 |
– |
* Remove SIGSEGV ignore range. Range must match installed one, otherwise FALSE is returned. |
779 |
– |
*/ |
780 |
– |
|
781 |
– |
bool sigsegv_remove_ignore_range(sigsegv_address_t address, unsigned long length, int transfer_type) |
782 |
– |
{ |
783 |
– |
ignore_range_list_t::iterator it; |
784 |
– |
for (it = sigsegv_ignore_ranges.begin(); it != sigsegv_ignore_ranges.end(); it++) |
785 |
– |
if (it->start == address && it->length == length && ((it->transfer_type & transfer_type) == transfer_type)) |
786 |
– |
break; |
787 |
– |
|
788 |
– |
if (it != sigsegv_ignore_ranges.end()) { |
789 |
– |
if (it->transfer_type != transfer_type) |
790 |
– |
it->transfer_type &= ~transfer_type; |
791 |
– |
else |
792 |
– |
sigsegv_ignore_ranges.erase(it); |
793 |
– |
return true; |
794 |
– |
} |
795 |
– |
|
796 |
– |
return false; |
797 |
– |
} |
798 |
– |
|
799 |
– |
|
800 |
– |
/* |
740 |
|
* Set callback function when we cannot handle the fault |
741 |
|
*/ |
742 |
|
|
761 |
|
static volatile char * page = 0; |
762 |
|
static volatile int handler_called = 0; |
763 |
|
|
764 |
< |
static bool sigsegv_test_handler(sigsegv_address_t fault_address, sigsegv_address_t instruction_address) |
764 |
> |
static sigsegv_return_t sigsegv_test_handler(sigsegv_address_t fault_address, sigsegv_address_t instruction_address) |
765 |
|
{ |
766 |
|
handler_called++; |
767 |
|
if ((fault_address - 123) != page) |
768 |
|
exit(1); |
769 |
|
if (vm_protect((char *)((unsigned long)fault_address & -page_size), page_size, VM_PAGE_READ | VM_PAGE_WRITE) != 0) |
770 |
|
exit(1); |
771 |
< |
return true; |
771 |
> |
return SIGSEGV_RETURN_SUCCESS; |
772 |
|
} |
773 |
|
|
774 |
|
#ifdef HAVE_SIGSEGV_SKIP_INSTRUCTION |
775 |
< |
static bool sigsegv_insn_handler(sigsegv_address_t fault_address, sigsegv_address_t instruction_address) |
775 |
> |
static sigsegv_return_t sigsegv_insn_handler(sigsegv_address_t fault_address, sigsegv_address_t instruction_address) |
776 |
|
{ |
777 |
< |
return false; |
777 |
> |
if (((unsigned long)fault_address - (unsigned long)page) < page_size) |
778 |
> |
return SIGSEGV_SRETURN_KIP_INSTRUCTION; |
779 |
> |
return SIGSEGV_RETURN_FAILURE; |
780 |
|
} |
781 |
|
#endif |
782 |
|
|
814 |
|
if (vm_protect((char *)page, page_size, VM_PAGE_NOACCESS) < 0) |
815 |
|
return 1; |
816 |
|
|
876 |
– |
sigsegv_add_ignore_range((char *)page, page_size, SIGSEGV_TRANSFER_LOAD | SIGSEGV_TRANSFER_STORE); |
877 |
– |
|
817 |
|
#define TEST_SKIP_INSTRUCTION(TYPE) do { \ |
818 |
|
const unsigned int TAG = 0x12345678; \ |
819 |
|
TYPE data = *((TYPE *)(page + sizeof(TYPE))); \ |