From f78839c15730533fc433c50ea83a3afea835c245 Mon Sep 17 00:00:00 2001 From: "gordana.cmiljanovic@imgtec.com" Date: Wed, 11 Sep 2013 11:37:04 +0000 Subject: Adding support for mips. Support for mips cpu is added to all breakapad targets including unittests. BUG=none TEST=unittests Review URL: https://breakpad.appspot.com/614002 git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@1212 4c0a9323-5329-0410-9bdc-e9ce6186880e --- src/client/linux/handler/exception_handler.cc | 9 ++- src/client/linux/handler/exception_handler.h | 4 +- .../linux/handler/exception_handler_unittest.cc | 54 +++++++++----- .../linux/minidump_writer/linux_core_dumper.cc | 3 + src/client/linux/minidump_writer/linux_dumper.h | 8 +- .../linux_dumper_unittest_helper.cc | 2 + .../linux/minidump_writer/linux_ptrace_dumper.cc | 14 ++++ .../linux_ptrace_dumper_unittest.cc | 5 +- .../linux/minidump_writer/minidump_writer.cc | 85 +++++++++++++++++++++- .../minidump_writer/minidump_writer_unittest.cc | 6 ++ 10 files changed, 163 insertions(+), 27 deletions(-) (limited to 'src/client/linux') diff --git a/src/client/linux/handler/exception_handler.cc b/src/client/linux/handler/exception_handler.cc index 2808fc90..9df720dd 100644 --- a/src/client/linux/handler/exception_handler.cc +++ b/src/client/linux/handler/exception_handler.cc @@ -396,8 +396,10 @@ bool ExceptionHandler::HandleSignal(int sig, siginfo_t* info, void* uc) { CrashContext context; memcpy(&context.siginfo, info, sizeof(siginfo_t)); memcpy(&context.context, uc, sizeof(struct ucontext)); -#if !defined(__ARM_EABI__) +#if !defined(__ARM_EABI__) && !defined(__mips__) // FP state is not part of user ABI on ARM Linux. + // In case of MIPS Linux FP state is already part of struct ucontext + // and 'float_state' is not a member of CrashContext. struct ucontext *uc_ptr = (struct ucontext*)uc; if (uc_ptr->uc_mcontext.fpregs) { memcpy(&context.float_state, @@ -602,7 +604,7 @@ bool ExceptionHandler::WriteMinidump() { } #endif -#if !defined(__ARM_EABI__) +#if !defined(__ARM_EABI__) && !defined(__mips__) // FPU state is not part of ARM EABI ucontext_t. memcpy(&context.float_state, context.context.uc_mcontext.fpregs, sizeof(context.float_state)); @@ -621,6 +623,9 @@ bool ExceptionHandler::WriteMinidump() { #elif defined(__arm__) context.siginfo.si_addr = reinterpret_cast(context.context.uc_mcontext.arm_pc); +#elif defined(__mips__) + context.siginfo.si_addr = + reinterpret_cast(context.context.uc_mcontext.pc); #else #error "This code has not been ported to your platform yet." #endif diff --git a/src/client/linux/handler/exception_handler.h b/src/client/linux/handler/exception_handler.h index 71554196..bbd962cb 100644 --- a/src/client/linux/handler/exception_handler.h +++ b/src/client/linux/handler/exception_handler.h @@ -190,8 +190,10 @@ class ExceptionHandler { siginfo_t siginfo; pid_t tid; // the crashing thread. struct ucontext context; -#if !defined(__ARM_EABI__) +#if !defined(__ARM_EABI__) && !defined(__mips__) // #ifdef this out because FP state is not part of user ABI for Linux ARM. + // In case of MIPS Linux FP state is already part of struct ucontext + // so 'float_state' is not required. struct _libc_fpstate float_state; #endif }; diff --git a/src/client/linux/handler/exception_handler_unittest.cc b/src/client/linux/handler/exception_handler_unittest.cc index 0ba52163..702a3adb 100644 --- a/src/client/linux/handler/exception_handler_unittest.cc +++ b/src/client/linux/handler/exception_handler_unittest.cc @@ -35,6 +35,9 @@ #include #include #include +#if defined(__mips__) +#include +#endif #include @@ -55,7 +58,7 @@ using namespace google_breakpad; namespace { // Flush the instruction cache for a given memory range. -// Only required on ARM. +// Only required on ARM and mips. void FlushInstructionCache(const char* memory, uint32_t memory_size) { #if defined(__arm__) long begin = reinterpret_cast(memory); @@ -72,6 +75,13 @@ void FlushInstructionCache(const char* memory, uint32_t memory_size) { # else # error "Your operating system is not supported yet" # endif +#elif defined(__mips__) +# if defined(__linux__) + // See http://www.linux-mips.org/wiki/Cacheflush_Syscall. + cacheflush(const_cast(memory), memory_size, ICACHE); +# else +# error "Your operating system is not supported yet" +# endif #endif } @@ -435,6 +445,16 @@ TEST(ExceptionHandlerTest, StackedHandlersUnhandledToBottom) { ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGKILL)); } +const unsigned char kIllegalInstruction[] = { +#if defined(__mips__) + // mfc2 zero,Impl - usually illegal in userspace. + 0x48, 0x00, 0x00, 0x48 +#else + // This crashes with SIGILL on x86/x86-64/arm. + 0xff, 0xff, 0xff, 0xff +#endif +}; + // Test that memory around the instruction pointer is written // to the dump as a MinidumpMemoryRegion. TEST(ExceptionHandlerTest, InstructionPointerMemory) { @@ -446,8 +466,6 @@ TEST(ExceptionHandlerTest, InstructionPointerMemory) { // data from the minidump afterwards. const uint32_t kMemorySize = 256; // bytes const int kOffset = kMemorySize / 2; - // This crashes with SIGILL on x86/x86-64/arm. - const unsigned char instructions[] = { 0xff, 0xff, 0xff, 0xff }; const pid_t child = fork(); if (child == 0) { @@ -469,7 +487,7 @@ TEST(ExceptionHandlerTest, InstructionPointerMemory) { // Write some instructions that will crash. Put them in the middle // of the block of memory, because the minidump should contain 128 // bytes on either side of the instruction pointer. - memcpy(memory + kOffset, instructions, sizeof(instructions)); + memcpy(memory + kOffset, kIllegalInstruction, sizeof(kIllegalInstruction)); FlushInstructionCache(memory, kMemorySize); // Now execute the instructions, which should crash. @@ -517,12 +535,13 @@ TEST(ExceptionHandlerTest, InstructionPointerMemory) { ASSERT_TRUE(bytes); uint8_t prefix_bytes[kOffset]; - uint8_t suffix_bytes[kMemorySize - kOffset - sizeof(instructions)]; + uint8_t suffix_bytes[kMemorySize - kOffset - sizeof(kIllegalInstruction)]; memset(prefix_bytes, 0, sizeof(prefix_bytes)); memset(suffix_bytes, 0, sizeof(suffix_bytes)); EXPECT_TRUE(memcmp(bytes, prefix_bytes, sizeof(prefix_bytes)) == 0); - EXPECT_TRUE(memcmp(bytes + kOffset, instructions, sizeof(instructions)) == 0); - EXPECT_TRUE(memcmp(bytes + kOffset + sizeof(instructions), + EXPECT_TRUE(memcmp(bytes + kOffset, kIllegalInstruction, + sizeof(kIllegalInstruction)) == 0); + EXPECT_TRUE(memcmp(bytes + kOffset + sizeof(kIllegalInstruction), suffix_bytes, sizeof(suffix_bytes)) == 0); unlink(minidump_path.c_str()); @@ -539,8 +558,6 @@ TEST(ExceptionHandlerTest, InstructionPointerMemoryMinBound) { // data from the minidump afterwards. const uint32_t kMemorySize = 256; // bytes const int kOffset = 0; - // This crashes with SIGILL on x86/x86-64/arm. - const unsigned char instructions[] = { 0xff, 0xff, 0xff, 0xff }; const pid_t child = fork(); if (child == 0) { @@ -562,7 +579,7 @@ TEST(ExceptionHandlerTest, InstructionPointerMemoryMinBound) { // Write some instructions that will crash. Put them in the middle // of the block of memory, because the minidump should contain 128 // bytes on either side of the instruction pointer. - memcpy(memory + kOffset, instructions, sizeof(instructions)); + memcpy(memory + kOffset, kIllegalInstruction, sizeof(kIllegalInstruction)); FlushInstructionCache(memory, kMemorySize); // Now execute the instructions, which should crash. @@ -609,10 +626,11 @@ TEST(ExceptionHandlerTest, InstructionPointerMemoryMinBound) { const uint8_t* bytes = region->GetMemory(); ASSERT_TRUE(bytes); - uint8_t suffix_bytes[kMemorySize / 2 - sizeof(instructions)]; + uint8_t suffix_bytes[kMemorySize / 2 - sizeof(kIllegalInstruction)]; memset(suffix_bytes, 0, sizeof(suffix_bytes)); - EXPECT_TRUE(memcmp(bytes + kOffset, instructions, sizeof(instructions)) == 0); - EXPECT_TRUE(memcmp(bytes + kOffset + sizeof(instructions), + EXPECT_TRUE(memcmp(bytes + kOffset, kIllegalInstruction, + sizeof(kIllegalInstruction)) == 0); + EXPECT_TRUE(memcmp(bytes + kOffset + sizeof(kIllegalInstruction), suffix_bytes, sizeof(suffix_bytes)) == 0); unlink(minidump_path.c_str()); } @@ -630,9 +648,7 @@ TEST(ExceptionHandlerTest, InstructionPointerMemoryMaxBound) { // if a smaller size is requested, and this test wants to // test the upper bound of the memory range. const uint32_t kMemorySize = 4096; // bytes - // This crashes with SIGILL on x86/x86-64/arm. - const unsigned char instructions[] = { 0xff, 0xff, 0xff, 0xff }; - const int kOffset = kMemorySize - sizeof(instructions); + const int kOffset = kMemorySize - sizeof(kIllegalInstruction); const pid_t child = fork(); if (child == 0) { @@ -654,7 +670,7 @@ TEST(ExceptionHandlerTest, InstructionPointerMemoryMaxBound) { // Write some instructions that will crash. Put them in the middle // of the block of memory, because the minidump should contain 128 // bytes on either side of the instruction pointer. - memcpy(memory + kOffset, instructions, sizeof(instructions)); + memcpy(memory + kOffset, kIllegalInstruction, sizeof(kIllegalInstruction)); FlushInstructionCache(memory, kMemorySize); // Now execute the instructions, which should crash. @@ -697,7 +713,7 @@ TEST(ExceptionHandlerTest, InstructionPointerMemoryMaxBound) { ASSERT_TRUE(region); const size_t kPrefixSize = 128; // bytes - EXPECT_EQ(kPrefixSize + sizeof(instructions), region->GetSize()); + EXPECT_EQ(kPrefixSize + sizeof(kIllegalInstruction), region->GetSize()); const uint8_t* bytes = region->GetMemory(); ASSERT_TRUE(bytes); @@ -705,7 +721,7 @@ TEST(ExceptionHandlerTest, InstructionPointerMemoryMaxBound) { memset(prefix_bytes, 0, sizeof(prefix_bytes)); EXPECT_TRUE(memcmp(bytes, prefix_bytes, sizeof(prefix_bytes)) == 0); EXPECT_TRUE(memcmp(bytes + kPrefixSize, - instructions, sizeof(instructions)) == 0); + kIllegalInstruction, sizeof(kIllegalInstruction)) == 0); unlink(minidump_path.c_str()); } diff --git a/src/client/linux/minidump_writer/linux_core_dumper.cc b/src/client/linux/minidump_writer/linux_core_dumper.cc index 47cc26c0..f5b19d10 100644 --- a/src/client/linux/minidump_writer/linux_core_dumper.cc +++ b/src/client/linux/minidump_writer/linux_core_dumper.cc @@ -99,6 +99,9 @@ bool LinuxCoreDumper::GetThreadInfoByIndex(size_t index, ThreadInfo* info) { memcpy(&stack_pointer, &info->regs.rsp, sizeof(info->regs.rsp)); #elif defined(__ARM_EABI__) memcpy(&stack_pointer, &info->regs.ARM_sp, sizeof(info->regs.ARM_sp)); +#elif defined(__mips__) + stack_pointer = + reinterpret_cast(info->regs.regs[MD_CONTEXT_MIPS_REG_SP]); #else #error "This code hasn't been ported to your platform yet." #endif diff --git a/src/client/linux/minidump_writer/linux_dumper.h b/src/client/linux/minidump_writer/linux_dumper.h index a4a3ab5a..2bb4cac7 100644 --- a/src/client/linux/minidump_writer/linux_dumper.h +++ b/src/client/linux/minidump_writer/linux_dumper.h @@ -54,7 +54,7 @@ typedef typeof(((struct user*) 0)->u_debugreg[0]) debugreg_t; #endif // Typedef for our parsing of the auxv variables in /proc/pid/auxv. -#if defined(__i386) || defined(__ARM_EABI__) +#if defined(__i386) || defined(__ARM_EABI__) || defined(__mips__) typedef Elf32_auxv_t elf_aux_entry; #elif defined(__x86_64) typedef Elf64_auxv_t elf_aux_entry; @@ -88,6 +88,12 @@ struct ThreadInfo { // Mimicking how strace does this(see syscall.c, search for GETREGS) struct user_regs regs; struct user_fpregs fpregs; +#elif defined(__mips__) + user_regs_struct regs; + user_fpregs_struct fpregs; + uint32_t hi[3]; + uint32_t lo[3]; + uint32_t dsp_control; #endif }; diff --git a/src/client/linux/minidump_writer/linux_dumper_unittest_helper.cc b/src/client/linux/minidump_writer/linux_dumper_unittest_helper.cc index 68020694..461a6cfe 100644 --- a/src/client/linux/minidump_writer/linux_dumper_unittest_helper.cc +++ b/src/client/linux/minidump_writer/linux_dumper_unittest_helper.cc @@ -47,6 +47,8 @@ #define TID_PTR_REGISTER "ecx" #elif defined(__x86_64) #define TID_PTR_REGISTER "rcx" +#elif defined(__mips__) +#define TID_PTR_REGISTER "$1" #else #error This test has not been ported to this platform. #endif diff --git a/src/client/linux/minidump_writer/linux_ptrace_dumper.cc b/src/client/linux/minidump_writer/linux_ptrace_dumper.cc index 45d0f48f..3256f532 100644 --- a/src/client/linux/minidump_writer/linux_ptrace_dumper.cc +++ b/src/client/linux/minidump_writer/linux_ptrace_dumper.cc @@ -208,6 +208,17 @@ bool LinuxPtraceDumper::GetThreadInfoByIndex(size_t index, ThreadInfo* info) { } #endif +#if defined(__mips__) + for (int i = 0; i < 3; ++i) { + sys_ptrace(PTRACE_PEEKUSER, tid, + reinterpret_cast(DSP_BASE + (i * 2)), &info->hi[i]); + sys_ptrace(PTRACE_PEEKUSER, tid, + reinterpret_cast(DSP_BASE + (i * 2) + 1), &info->lo[i]); + } + sys_ptrace(PTRACE_PEEKUSER, tid, + reinterpret_cast(DSP_CONTROL), &info->dsp_control); +#endif + const uint8_t* stack_pointer; #if defined(__i386) my_memcpy(&stack_pointer, &info->regs.esp, sizeof(info->regs.esp)); @@ -215,6 +226,9 @@ bool LinuxPtraceDumper::GetThreadInfoByIndex(size_t index, ThreadInfo* info) { my_memcpy(&stack_pointer, &info->regs.rsp, sizeof(info->regs.rsp)); #elif defined(__ARM_EABI__) my_memcpy(&stack_pointer, &info->regs.ARM_sp, sizeof(info->regs.ARM_sp)); +#elif defined(__mips__) + stack_pointer = + reinterpret_cast(info->regs.regs[MD_CONTEXT_MIPS_REG_SP]); #else #error "This code hasn't been ported to your platform yet." #endif diff --git a/src/client/linux/minidump_writer/linux_ptrace_dumper_unittest.cc b/src/client/linux/minidump_writer/linux_ptrace_dumper_unittest.cc index bc7eae61..15a66ce2 100644 --- a/src/client/linux/minidump_writer/linux_ptrace_dumper_unittest.cc +++ b/src/client/linux/minidump_writer/linux_ptrace_dumper_unittest.cc @@ -266,7 +266,7 @@ TEST_F(LinuxPtraceDumperChildTest, BuildProcPath) { EXPECT_FALSE(dumper.BuildProcPath(maps_path, 123, long_node)); } -#if !defined(__ARM_EABI__) +#if !defined(__ARM_EABI__) && !defined(__mips__) // Ensure that the linux-gate VDSO is included in the mapping list. TEST_F(LinuxPtraceDumperChildTest, MappingsIncludeLinuxGate) { LinuxPtraceDumper dumper(getppid()); @@ -437,6 +437,9 @@ TEST(LinuxPtraceDumperTest, VerifyStackReadWithMultipleThreads) { pid_t* process_tid_location = (pid_t*)(one_thread.regs.ecx); #elif defined(__x86_64) pid_t* process_tid_location = (pid_t*)(one_thread.regs.rcx); +#elif defined(__mips__) + pid_t* process_tid_location = + reinterpret_cast(one_thread.regs.regs[1]); #else #error This test has not been ported to this platform. #endif diff --git a/src/client/linux/minidump_writer/minidump_writer.cc b/src/client/linux/minidump_writer/minidump_writer.cc index 2cbf4352..413e0807 100644 --- a/src/client/linux/minidump_writer/minidump_writer.cc +++ b/src/client/linux/minidump_writer/minidump_writer.cc @@ -374,6 +374,67 @@ void CPUFillFromUContext(MDRawContextARM* out, const ucontext* uc, my_memset(&out->float_save.extra, 0, sizeof(out->float_save.extra)); } +#elif defined(__mips__) +typedef MDRawContextMIPS RawContextCPU; + +static void CPUFillFromThreadInfo(MDRawContextMIPS* out, + const google_breakpad::ThreadInfo& info) { + out->context_flags = MD_CONTEXT_MIPS_FULL; + + for (int i = 0; i < MD_CONTEXT_MIPS_GPR_COUNT; ++i) + out->iregs[i] = info.regs.regs[i]; + + out->mdhi = info.regs.hi; + out->mdlo = info.regs.lo; + + for (int i = 0; i < MD_CONTEXT_MIPS_DSP_COUNT; ++i) { + out->hi[i] = info.hi[i]; + out->lo[i] = info.lo[i]; + } + out->dsp_control = info.dsp_control; + + out->epc = info.regs.epc; + out->badvaddr = info.regs.badvaddr; + out->status = info.regs.status; + out->cause = info.regs.cause; + + for (int i = 0; i < MD_FLOATINGSAVEAREA_MIPS_FPR_COUNT; ++i) + out->float_save.regs[i] = info.fpregs.regs[i]; + + out->float_save.fpcsr = info.fpregs.fpcsr; + out->float_save.fir = info.fpregs.fir; +} + +static void CPUFillFromUContext(MDRawContextMIPS* out, const ucontext* uc, + const struct _libc_fpstate* fpregs) { + out->context_flags = MD_CONTEXT_MIPS_FULL; + + for (int i = 0; i < MD_CONTEXT_MIPS_GPR_COUNT; ++i) + out->iregs[i] = uc->uc_mcontext.gregs[i]; + + out->mdhi = uc->uc_mcontext.mdhi; + out->mdlo = uc->uc_mcontext.mdlo; + + out->hi[0] = uc->uc_mcontext.hi1; + out->hi[1] = uc->uc_mcontext.hi2; + out->hi[2] = uc->uc_mcontext.hi3; + out->lo[0] = uc->uc_mcontext.lo1; + out->lo[1] = uc->uc_mcontext.lo2; + out->lo[2] = uc->uc_mcontext.lo3; + out->dsp_control = uc->uc_mcontext.dsp; + + out->epc = uc->uc_mcontext.pc; + out->badvaddr = 0; // Not reported in signal context. + out->status = 0; // Not reported in signal context. + out->cause = 0; // Not reported in signal context. + + for (int i = 0; i < MD_FLOATINGSAVEAREA_MIPS_FPR_COUNT; ++i) + out->float_save.regs[i] = uc->uc_mcontext.fpregs.fp_r.fp_dregs[i]; + + out->float_save.fpcsr = uc->uc_mcontext.fpc_csr; + out->float_save.fir = uc->uc_mcontext.fpc_eir; // Unused. +} + #else #error "This code has not been ported to your platform yet." #endif @@ -405,7 +466,7 @@ class MinidumpWriter { : fd_(minidump_fd), path_(minidump_path), ucontext_(context ? &context->context : NULL), -#if !defined(__ARM_EABI__) +#if !defined(__ARM_EABI__) && !defined(__mips__) float_state_(context ? &context->float_state : NULL), #else // TODO: fix this after fixing ExceptionHandler @@ -1206,6 +1267,18 @@ class MinidumpWriter { uintptr_t GetInstructionPointer(const ThreadInfo& info) { return info.regs.uregs[15]; } +#elif defined(__mips__) + uintptr_t GetStackPointer() { + return ucontext_->uc_mcontext.gregs[MD_CONTEXT_MIPS_REG_SP]; + } + + uintptr_t GetInstructionPointer() { + return ucontext_->uc_mcontext.pc; + } + + uintptr_t GetInstructionPointer(const ThreadInfo& info) { + return info.regs.epc; + } #else #error "This code has not been ported to your platform yet." #endif @@ -1216,7 +1289,7 @@ class MinidumpWriter { dirent->location.rva = 0; } -#if defined(__i386__) || defined(__x86_64__) +#if defined(__i386__) || defined(__x86_64__) || defined(__mips__) bool WriteCPUInformation(MDRawSystemInfo* sys_info) { char vendor_id[sizeof(sys_info->cpu.x86_cpu_info.vendor_id) + 1] = {0}; static const char vendor_id_name[] = "vendor_id"; @@ -1227,14 +1300,18 @@ class MinidumpWriter { bool found; } cpu_info_table[] = { { "processor", -1, false }, +#if !defined(__mips__) { "model", 0, false }, { "stepping", 0, false }, { "cpu family", 0, false }, +#endif }; // processor_architecture should always be set, do this first sys_info->processor_architecture = -#if defined(__i386__) +#if defined(__mips__) + MD_CPU_ARCHITECTURE_MIPS; +#elif defined(__i386__) MD_CPU_ARCHITECTURE_X86; #else MD_CPU_ARCHITECTURE_AMD64; @@ -1297,9 +1374,11 @@ class MinidumpWriter { cpu_info_table[0].value++; sys_info->number_of_processors = cpu_info_table[0].value; +#if !defined(__mips__) sys_info->processor_level = cpu_info_table[3].value; sys_info->processor_revision = cpu_info_table[1].value << 8 | cpu_info_table[2].value; +#endif if (vendor_id[0] != '\0') { my_memcpy(sys_info->cpu.x86_cpu_info.vendor_id, vendor_id, diff --git a/src/client/linux/minidump_writer/minidump_writer_unittest.cc b/src/client/linux/minidump_writer/minidump_writer_unittest.cc index 9b7d73c0..1ef064b1 100644 --- a/src/client/linux/minidump_writer/minidump_writer_unittest.cc +++ b/src/client/linux/minidump_writer/minidump_writer_unittest.cc @@ -540,6 +540,12 @@ TEST(MinidumpWriterTest, InvalidStackPointer) { uintptr_t invalid_stack_pointer = reinterpret_cast(&context) - 1024*1024; context.context.uc_mcontext.arm_sp = invalid_stack_pointer; +#elif defined(__mips__) + // Try 1MB below the current stack. + uintptr_t invalid_stack_pointer = + reinterpret_cast(&context) - 1024 * 1024; + context.context.uc_mcontext.gregs[MD_CONTEXT_MIPS_REG_SP] = + invalid_stack_pointer; #else # error "This code has not been ported to your platform yet." #endif -- cgit v1.2.1