diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp index 4125b1204..21dd306e7 100644 --- a/libunwindstack/Android.bp +++ b/libunwindstack/Android.bp @@ -64,6 +64,8 @@ cc_library { "RegsArm64.cpp", "RegsX86.cpp", "RegsX86_64.cpp", + "RegsMips.cpp", + "RegsMips64.cpp", "Unwinder.cpp", "Symbols.cpp", ], @@ -86,6 +88,12 @@ cc_library { x86_64: { srcs: ["AsmGetRegsX86_64.S"], }, + mips: { + srcs: ["AsmGetRegsMips.S"], + }, + mips64: { + srcs: ["AsmGetRegsMips64.S"], + }, }, shared_libs: [ diff --git a/libunwindstack/AsmGetRegsMips.S b/libunwindstack/AsmGetRegsMips.S new file mode 100644 index 000000000..183d0a928 --- /dev/null +++ b/libunwindstack/AsmGetRegsMips.S @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + + .text + .type AsmGetRegs, %function + .globl AsmGetRegs + .ent AsmGetRegs + .balign 16 +AsmGetRegs: + .cfi_startproc + .cfi_def_cfa $sp, 0 + .set push + .set noreorder + .cpload $t9 + sw $zero, 0($a0) + .set noat + sw $at, 4($a0) + .set at + sw $v0, 8($a0) + sw $v1, 12($a0) + sw $a0, 16($a0) + sw $a1, 20($a0) + sw $a2, 24($a0) + sw $a3, 28($a0) + sw $t0, 32($a0) + sw $t1, 36($a0) + sw $t2, 40($a0) + sw $t3, 44($a0) + sw $t4, 48($a0) + sw $t5, 52($a0) + sw $t6, 56($a0) + sw $t7, 60($a0) + sw $s0, 64($a0) + sw $s1, 68($a0) + sw $s2, 72($a0) + sw $s3, 76($a0) + sw $s4, 80($a0) + sw $s5, 84($a0) + sw $s6, 88($a0) + sw $s7, 92($a0) + sw $t8, 96($a0) + sw $t9, 100($a0) + sw $k0, 104($a0) + sw $k1, 108($a0) + sw $gp, 112($a0) + sw $sp, 116($a0) + sw $s8, 120($a0) + sw $ra, 124($a0) + jalr $zero, $ra + sw $ra, 128($a0) // set PC to the calling function + + .set pop + .cfi_endproc + .size AsmGetRegs, .-AsmGetRegs + .end AsmGetRegs diff --git a/libunwindstack/AsmGetRegsMips64.S b/libunwindstack/AsmGetRegsMips64.S new file mode 100644 index 000000000..7a244f6f8 --- /dev/null +++ b/libunwindstack/AsmGetRegsMips64.S @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + + .text + .type AsmGetRegs, %function + .globl AsmGetRegs + .ent AsmGetRegs + .balign 16 +AsmGetRegs: + .cfi_startproc + .cfi_def_cfa $sp, 0 + .set push + .set noreorder + .cpload $t9 + sd $zero, 0($a0) + .set noat + sd $at, 8($a0) + .set at + sd $v0, 16($a0) + sd $v1, 24($a0) + sd $a0, 32($a0) + sd $a1, 40($a0) + sd $a2, 48($a0) + sd $a3, 56($a0) + sd $a4, 64($a0) + sd $a5, 72($a0) + sd $a6, 80($a0) + sd $a7, 88($a0) + sd $t0, 96($a0) + sd $t1, 104($a0) + sd $t2, 112($a0) + sd $t3, 120($a0) + sd $s0, 128($a0) + sd $s1, 136($a0) + sd $s2, 144($a0) + sd $s3, 152($a0) + sd $s4, 160($a0) + sd $s5, 168($a0) + sd $s6, 176($a0) + sd $s7, 184($a0) + sd $t8, 192($a0) + sd $t9, 200($a0) + sd $k0, 208($a0) + sd $k1, 216($a0) + sd $gp, 224($a0) + sd $sp, 232($a0) + sd $s8, 240($a0) + sd $ra, 248($a0) + jalr $zero, $ra + sd $ra, 256($a0) // set PC to the calling function + + .set pop + .cfi_endproc + .size AsmGetRegs, .-AsmGetRegs + .end AsmGetRegs diff --git a/libunwindstack/Elf.cpp b/libunwindstack/Elf.cpp index 025429f52..f486e2314 100644 --- a/libunwindstack/Elf.cpp +++ b/libunwindstack/Elf.cpp @@ -189,9 +189,12 @@ ElfInterface* Elf::CreateInterfaceFromMemory(Memory* memory) { } else if (e_machine == EM_386) { arch_ = ARCH_X86; interface.reset(new ElfInterface32(memory)); + } else if (e_machine == EM_MIPS) { + arch_ = ARCH_MIPS; + interface.reset(new ElfInterface32(memory)); } else { // Unsupported. - ALOGI("32 bit elf that is neither arm nor x86: e_machine = %d\n", e_machine); + ALOGI("32 bit elf that is neither arm nor x86 nor mips: e_machine = %d\n", e_machine); return nullptr; } } else if (class_type_ == ELFCLASS64) { @@ -205,9 +208,12 @@ ElfInterface* Elf::CreateInterfaceFromMemory(Memory* memory) { arch_ = ARCH_ARM64; } else if (e_machine == EM_X86_64) { arch_ = ARCH_X86_64; + } else if (e_machine == EM_MIPS) { + arch_ = ARCH_MIPS64; } else { // Unsupported. - ALOGI("64 bit elf that is neither aarch64 nor x86_64: e_machine = %d\n", e_machine); + ALOGI("64 bit elf that is neither aarch64 nor x86_64 nor mips64: e_machine = %d\n", + e_machine); return nullptr; } interface.reset(new ElfInterface64(memory)); diff --git a/libunwindstack/MachineMips.h b/libunwindstack/MachineMips.h new file mode 100644 index 000000000..2dfb1e940 --- /dev/null +++ b/libunwindstack/MachineMips.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _LIBUNWINDSTACK_MACHINE_MIPS_H +#define _LIBUNWINDSTACK_MACHINE_MIPS_H + +#include + +namespace unwindstack { + +enum MipsReg : uint16_t { + MIPS_REG_R0 = 0, + MIPS_REG_R1, + MIPS_REG_R2, + MIPS_REG_R3, + MIPS_REG_R4, + MIPS_REG_R5, + MIPS_REG_R6, + MIPS_REG_R7, + MIPS_REG_R8, + MIPS_REG_R9, + MIPS_REG_R10, + MIPS_REG_R11, + MIPS_REG_R12, + MIPS_REG_R13, + MIPS_REG_R14, + MIPS_REG_R15, + MIPS_REG_R16, + MIPS_REG_R17, + MIPS_REG_R18, + MIPS_REG_R19, + MIPS_REG_R20, + MIPS_REG_R21, + MIPS_REG_R22, + MIPS_REG_R23, + MIPS_REG_R24, + MIPS_REG_R25, + MIPS_REG_R26, + MIPS_REG_R27, + MIPS_REG_R28, + MIPS_REG_R29, + MIPS_REG_R30, + MIPS_REG_R31, + MIPS_REG_PC, + MIPS_REG_LAST, + + MIPS_REG_SP = MIPS_REG_R29, + MIPS_REG_RA = MIPS_REG_R31, +}; + +} // namespace unwindstack + +#endif // _LIBUNWINDSTACK_MACHINE_MIPS_H \ No newline at end of file diff --git a/libunwindstack/MachineMips64.h b/libunwindstack/MachineMips64.h new file mode 100644 index 000000000..34addf24d --- /dev/null +++ b/libunwindstack/MachineMips64.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _LIBUNWINDSTACK_MACHINE_MIPS64_H +#define _LIBUNWINDSTACK_MACHINE_MIPS64_H + +#include + +namespace unwindstack { + +enum Mips64Reg : uint16_t { + MIPS64_REG_R0 = 0, + MIPS64_REG_R1, + MIPS64_REG_R2, + MIPS64_REG_R3, + MIPS64_REG_R4, + MIPS64_REG_R5, + MIPS64_REG_R6, + MIPS64_REG_R7, + MIPS64_REG_R8, + MIPS64_REG_R9, + MIPS64_REG_R10, + MIPS64_REG_R11, + MIPS64_REG_R12, + MIPS64_REG_R13, + MIPS64_REG_R14, + MIPS64_REG_R15, + MIPS64_REG_R16, + MIPS64_REG_R17, + MIPS64_REG_R18, + MIPS64_REG_R19, + MIPS64_REG_R20, + MIPS64_REG_R21, + MIPS64_REG_R22, + MIPS64_REG_R23, + MIPS64_REG_R24, + MIPS64_REG_R25, + MIPS64_REG_R26, + MIPS64_REG_R27, + MIPS64_REG_R28, + MIPS64_REG_R29, + MIPS64_REG_R30, + MIPS64_REG_R31, + MIPS64_REG_PC, + MIPS64_REG_LAST, + + MIPS64_REG_SP = MIPS64_REG_R29, + MIPS64_REG_RA = MIPS64_REG_R31, +}; + +} // namespace unwindstack + +#endif // _LIBUNWINDSTACK_MACHINE_MIPS64_H \ No newline at end of file diff --git a/libunwindstack/Regs.cpp b/libunwindstack/Regs.cpp index 29dbf9d15..7feafadf2 100644 --- a/libunwindstack/Regs.cpp +++ b/libunwindstack/Regs.cpp @@ -27,16 +27,20 @@ #include #include #include +#include +#include #include "UserArm.h" #include "UserArm64.h" #include "UserX86.h" #include "UserX86_64.h" +#include "UserMips.h" +#include "UserMips64.h" namespace unwindstack { // The largest user structure. -constexpr size_t MAX_USER_REGS_SIZE = sizeof(arm64_user_regs) + 10; +constexpr size_t MAX_USER_REGS_SIZE = sizeof(mips64_user_regs) + 10; // This function assumes that reg_data is already aligned to a 64 bit value. // If not this could crash with an unaligned access. @@ -60,6 +64,10 @@ Regs* Regs::RemoteGet(pid_t pid) { return RegsArm::Read(buffer.data()); case sizeof(arm64_user_regs): return RegsArm64::Read(buffer.data()); + case sizeof(mips_user_regs): + return RegsMips::Read(buffer.data()); + case sizeof(mips64_user_regs): + return RegsMips64::Read(buffer.data()); } return nullptr; } @@ -74,6 +82,10 @@ Regs* Regs::CreateFromUcontext(ArchEnum arch, void* ucontext) { return RegsArm::CreateFromUcontext(ucontext); case ARCH_ARM64: return RegsArm64::CreateFromUcontext(ucontext); + case ARCH_MIPS: + return RegsMips::CreateFromUcontext(ucontext); + case ARCH_MIPS64: + return RegsMips64::CreateFromUcontext(ucontext); case ARCH_UNKNOWN: default: return nullptr; @@ -89,6 +101,10 @@ ArchEnum Regs::CurrentArch() { return ARCH_X86; #elif defined(__x86_64__) return ARCH_X86_64; +#elif defined(__mips__) && !defined(__LP64__) + return ARCH_MIPS; +#elif defined(__mips__) && defined(__LP64__) + return ARCH_MIPS64; #else abort(); #endif @@ -104,6 +120,10 @@ Regs* Regs::CreateFromLocal() { regs = new RegsX86(); #elif defined(__x86_64__) regs = new RegsX86_64(); +#elif defined(__mips__) && !defined(__LP64__) + regs = new RegsMips(); +#elif defined(__mips__) && defined(__LP64__) + regs = new RegsMips64(); #else abort(); #endif diff --git a/libunwindstack/RegsMips.cpp b/libunwindstack/RegsMips.cpp new file mode 100644 index 000000000..44cde052f --- /dev/null +++ b/libunwindstack/RegsMips.cpp @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +#include +#include +#include +#include + +#include "MachineMips.h" +#include "UcontextMips.h" +#include "UserMips.h" + +namespace unwindstack { + +RegsMips::RegsMips() + : RegsImpl(MIPS_REG_LAST, MIPS_REG_SP, Location(LOCATION_REGISTER, MIPS_REG_RA)) {} + +ArchEnum RegsMips::Arch() { + return ARCH_MIPS; +} + +uint64_t RegsMips::GetAdjustedPc(uint64_t rel_pc, Elf* elf) { + if (!elf->valid()) { + return rel_pc; + } + + // For now, just assuming no compact branches + if (rel_pc < 8) { + return rel_pc; + } + return rel_pc - 8; +} + +void RegsMips::SetFromRaw() { + set_pc(regs_[MIPS_REG_PC]); + set_sp(regs_[MIPS_REG_SP]); +} + +bool RegsMips::SetPcFromReturnAddress(Memory*) { + if (pc() == regs_[MIPS_REG_RA]) { + return false; + } + + set_pc(regs_[MIPS_REG_RA]); + return true; +} + +void RegsMips::IterateRegisters(std::function fn) { + fn("r0", regs_[MIPS_REG_R0]); + fn("r1", regs_[MIPS_REG_R1]); + fn("r2", regs_[MIPS_REG_R2]); + fn("r3", regs_[MIPS_REG_R3]); + fn("r4", regs_[MIPS_REG_R4]); + fn("r5", regs_[MIPS_REG_R5]); + fn("r6", regs_[MIPS_REG_R6]); + fn("r7", regs_[MIPS_REG_R7]); + fn("r8", regs_[MIPS_REG_R8]); + fn("r9", regs_[MIPS_REG_R9]); + fn("r10", regs_[MIPS_REG_R10]); + fn("r11", regs_[MIPS_REG_R11]); + fn("r12", regs_[MIPS_REG_R12]); + fn("r13", regs_[MIPS_REG_R13]); + fn("r14", regs_[MIPS_REG_R14]); + fn("r15", regs_[MIPS_REG_R15]); + fn("r16", regs_[MIPS_REG_R16]); + fn("r17", regs_[MIPS_REG_R17]); + fn("r18", regs_[MIPS_REG_R18]); + fn("r19", regs_[MIPS_REG_R19]); + fn("r20", regs_[MIPS_REG_R20]); + fn("r21", regs_[MIPS_REG_R21]); + fn("r22", regs_[MIPS_REG_R22]); + fn("r23", regs_[MIPS_REG_R23]); + fn("r24", regs_[MIPS_REG_R24]); + fn("r25", regs_[MIPS_REG_R25]); + fn("r26", regs_[MIPS_REG_R26]); + fn("r27", regs_[MIPS_REG_R27]); + fn("r28", regs_[MIPS_REG_R28]); + fn("sp", regs_[MIPS_REG_SP]); + fn("r30", regs_[MIPS_REG_R30]); + fn("ra", regs_[MIPS_REG_RA]); + fn("pc", regs_[MIPS_REG_PC]); +} + +Regs* RegsMips::Read(void* remote_data) { + mips_user_regs* user = reinterpret_cast(remote_data); + RegsMips* regs = new RegsMips(); + uint32_t* reg_data = reinterpret_cast(regs->RawData()); + + memcpy(regs->RawData(), &user->regs[MIPS32_EF_R0], (MIPS_REG_R31 + 1) * sizeof(uint32_t)); + + reg_data[MIPS_REG_PC] = user->regs[MIPS32_EF_CP0_EPC]; + regs->SetFromRaw(); + return regs; +} + +Regs* RegsMips::CreateFromUcontext(void* ucontext) { + mips_ucontext_t* mips_ucontext = reinterpret_cast(ucontext); + + RegsMips* regs = new RegsMips(); + // Copy 64 bit sc_regs over to 32 bit regs + for (int i = 0; i < 32; i++) { + (*regs)[MIPS_REG_R0 + i] = mips_ucontext->uc_mcontext.sc_regs[i]; + } + (*regs)[MIPS_REG_PC] = mips_ucontext->uc_mcontext.sc_pc; + regs->SetFromRaw(); + return regs; +} + +bool RegsMips::StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) { + uint64_t data; + uint64_t offset = 0; + Memory* elf_memory = elf->memory(); + // Read from elf memory since it is usually more expensive to read from + // process memory. + if (!elf_memory->Read(rel_pc, &data, sizeof(data))) { + return false; + } + + // Look for the kernel sigreturn functions. + // __vdso_rt_sigreturn: + // 0x24021061 li v0, 0x1061 + // 0x0000000c syscall + // __vdso_sigreturn: + // 0x24021017 li v0, 0x1017 + // 0x0000000c syscall + if (data == 0x0000000c24021061ULL) { + // vdso_rt_sigreturn => read rt_sigframe + // offset = siginfo offset + sizeof(siginfo) + uc_mcontext offset + sc_pc offset + offset = 24 + 128 + 24 + 8; + } else if (data == 0x0000000c24021017LL) { + // vdso_sigreturn => read sigframe + // offset = sigcontext offset + sc_pc offset + offset = 24 + 8; + } else { + return false; + } + + // read sc_pc and sc_regs[32] from stack + uint64_t values[MIPS_REG_LAST]; + if (!process_memory->Read(sp() + offset, values, sizeof(values))) { + return false; + } + + // Copy 64 bit sc_pc over to 32 bit regs_[MIPS_REG_PC] + regs_[MIPS_REG_PC] = values[0]; + + // Copy 64 bit sc_regs over to 32 bit regs + for (int i = 0; i < 32; i++) { + regs_[MIPS_REG_R0 + i] = values[1 + i]; + } + + SetFromRaw(); + return true; +} + +} // namespace unwindstack diff --git a/libunwindstack/RegsMips64.cpp b/libunwindstack/RegsMips64.cpp new file mode 100644 index 000000000..b4e524603 --- /dev/null +++ b/libunwindstack/RegsMips64.cpp @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +#include +#include +#include +#include + +#include "MachineMips64.h" +#include "UcontextMips64.h" +#include "UserMips64.h" + +namespace unwindstack { + +RegsMips64::RegsMips64() + : RegsImpl(MIPS64_REG_LAST, MIPS64_REG_SP, + Location(LOCATION_REGISTER, MIPS64_REG_RA)) {} + +ArchEnum RegsMips64::Arch() { + return ARCH_MIPS64; +} + +uint64_t RegsMips64::GetAdjustedPc(uint64_t rel_pc, Elf* elf) { + if (!elf->valid()) { + return rel_pc; + } + + // For now, just assuming no compact branches + if (rel_pc < 8) { + return rel_pc; + } + return rel_pc - 8; +} + +void RegsMips64::SetFromRaw() { + set_pc(regs_[MIPS64_REG_PC]); + set_sp(regs_[MIPS64_REG_SP]); +} + +bool RegsMips64::SetPcFromReturnAddress(Memory*) { + if (pc() == regs_[MIPS64_REG_RA]) { + return false; + } + + set_pc(regs_[MIPS64_REG_RA]); + return true; +} + +void RegsMips64::IterateRegisters(std::function fn) { + fn("r0", regs_[MIPS64_REG_R0]); + fn("r1", regs_[MIPS64_REG_R1]); + fn("r2", regs_[MIPS64_REG_R2]); + fn("r3", regs_[MIPS64_REG_R3]); + fn("r4", regs_[MIPS64_REG_R4]); + fn("r5", regs_[MIPS64_REG_R5]); + fn("r6", regs_[MIPS64_REG_R6]); + fn("r7", regs_[MIPS64_REG_R7]); + fn("r8", regs_[MIPS64_REG_R8]); + fn("r9", regs_[MIPS64_REG_R9]); + fn("r10", regs_[MIPS64_REG_R10]); + fn("r11", regs_[MIPS64_REG_R11]); + fn("r12", regs_[MIPS64_REG_R12]); + fn("r13", regs_[MIPS64_REG_R13]); + fn("r14", regs_[MIPS64_REG_R14]); + fn("r15", regs_[MIPS64_REG_R15]); + fn("r16", regs_[MIPS64_REG_R16]); + fn("r17", regs_[MIPS64_REG_R17]); + fn("r18", regs_[MIPS64_REG_R18]); + fn("r19", regs_[MIPS64_REG_R19]); + fn("r20", regs_[MIPS64_REG_R20]); + fn("r21", regs_[MIPS64_REG_R21]); + fn("r22", regs_[MIPS64_REG_R22]); + fn("r23", regs_[MIPS64_REG_R23]); + fn("r24", regs_[MIPS64_REG_R24]); + fn("r25", regs_[MIPS64_REG_R25]); + fn("r26", regs_[MIPS64_REG_R26]); + fn("r27", regs_[MIPS64_REG_R27]); + fn("r28", regs_[MIPS64_REG_R28]); + fn("sp", regs_[MIPS64_REG_SP]); + fn("r30", regs_[MIPS64_REG_R30]); + fn("ra", regs_[MIPS64_REG_RA]); + fn("pc", regs_[MIPS64_REG_PC]); +} + +Regs* RegsMips64::Read(void* remote_data) { + mips64_user_regs* user = reinterpret_cast(remote_data); + RegsMips64* regs = new RegsMips64(); + uint64_t* reg_data = reinterpret_cast(regs->RawData()); + + memcpy(regs->RawData(), &user->regs[MIPS64_EF_R0], (MIPS64_REG_R31 + 1) * sizeof(uint64_t)); + + reg_data[MIPS64_REG_PC] = user->regs[MIPS64_EF_CP0_EPC]; + regs->SetFromRaw(); + return regs; +} + +Regs* RegsMips64::CreateFromUcontext(void* ucontext) { + mips64_ucontext_t* mips64_ucontext = reinterpret_cast(ucontext); + + RegsMips64* regs = new RegsMips64(); + // Copy 64 bit sc_regs over to 64 bit regs + memcpy(regs->RawData(), &mips64_ucontext->uc_mcontext.sc_regs[0], 32 * sizeof(uint64_t)); + (*regs)[MIPS64_REG_PC] = mips64_ucontext->uc_mcontext.sc_pc; + regs->SetFromRaw(); + return regs; +} + +bool RegsMips64::StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) { + uint64_t data; + Memory* elf_memory = elf->memory(); + // Read from elf memory since it is usually more expensive to read from + // process memory. + if (!elf_memory->Read(rel_pc, &data, sizeof(data))) { + return false; + } + + // Look for the kernel sigreturn function. + // __vdso_rt_sigreturn: + // 0x2402145b li v0, 0x145b + // 0x0000000c syscall + if (data != 0x0000000c2402145bULL) { + return false; + } + + // vdso_rt_sigreturn => read rt_sigframe + // offset = siginfo offset + sizeof(siginfo) + uc_mcontext offset + // read 64 bit sc_regs[32] from stack into 64 bit regs_ + if (!process_memory->Read(sp() + 24 + 128 + 40, regs_.data(), + sizeof(uint64_t) * (MIPS64_REG_LAST - 1))) { + return false; + } + + // offset = siginfo offset + sizeof(siginfo) + uc_mcontext offset + sc_pc offset + // read 64 bit sc_pc from stack into 64 bit regs_[MIPS64_REG_PC] + if (!process_memory->Read(sp() + 24 + 128 + 40 + 576, ®s_[MIPS64_REG_PC], + sizeof(uint64_t))) { + return false; + } + + SetFromRaw(); + return true; +} + +} // namespace unwindstack diff --git a/libunwindstack/UcontextMips.h b/libunwindstack/UcontextMips.h new file mode 100644 index 000000000..27185e713 --- /dev/null +++ b/libunwindstack/UcontextMips.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _LIBUNWINDSTACK_UCONTEXT_MIPS_H +#define _LIBUNWINDSTACK_UCONTEXT_MIPS_H + +#include + +#include "MachineMips.h" + +namespace unwindstack { + +struct mips_stack_t { + uint32_t ss_sp; // void __user* + uint32_t ss_size; // size_t + int32_t ss_flags; // int +}; + +struct mips_mcontext_t { + uint32_t sc_regmask; + uint32_t sc_status; + uint64_t sc_pc; + uint64_t sc_regs[32]; + // Nothing else is used, so don't define it. +}; + +struct mips_ucontext_t { + uint32_t uc_flags; // unsigned long + uint32_t uc_link; // struct ucontext* + mips_stack_t uc_stack; + mips_mcontext_t uc_mcontext; + // Nothing else is used, so don't define it. +}; + +} // namespace unwindstack + +#endif // _LIBUNWINDSTACK_UCONTEXT_MIPS_H diff --git a/libunwindstack/UcontextMips64.h b/libunwindstack/UcontextMips64.h new file mode 100644 index 000000000..623bf3a0b --- /dev/null +++ b/libunwindstack/UcontextMips64.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _LIBUNWINDSTACK_UCONTEXT_MIPS64_H +#define _LIBUNWINDSTACK_UCONTEXT_MIPS64_H + +#include + +#include "MachineMips64.h" + +namespace unwindstack { + +struct mips64_stack_t { + uint64_t ss_sp; // void __user* + uint64_t ss_size; // size_t + int32_t ss_flags; // int +}; + +struct mips64_mcontext_t { + uint64_t sc_regs[32]; + uint64_t sc_fpregs[32]; + uint64_t sc_mdhi; + uint64_t sc_hi1; + uint64_t sc_hi2; + uint64_t sc_hi3; + uint64_t sc_mdlo; + uint64_t sc_lo1; + uint64_t sc_lo2; + uint64_t sc_lo3; + uint64_t sc_pc; + // Nothing else is used, so don't define it. +}; + +struct mips64_ucontext_t { + uint64_t uc_flags; // unsigned long + uint64_t uc_link; // struct ucontext* + mips64_stack_t uc_stack; + mips64_mcontext_t uc_mcontext; + // Nothing else is used, so don't define it. +}; + +} // namespace unwindstack + +#endif // _LIBUNWINDSTACK_UCONTEXT_MIPS6464_H diff --git a/libunwindstack/UserMips.h b/libunwindstack/UserMips.h new file mode 100644 index 000000000..184be4f39 --- /dev/null +++ b/libunwindstack/UserMips.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _LIBUNWINDSTACK_USER_MIPS_H +#define _LIBUNWINDSTACK_USER_MIPS_H + +namespace unwindstack { + +enum Mips32UserReg : uint16_t { + MIPS32_EF_R0 = 6, + MIPS32_EF_CP0_EPC = 40, +}; + +struct mips_user_regs { + uint32_t regs[45]; +}; + +} // namespace unwindstack + +#endif // _LIBUNWINDSTACK_USER_MIPS_H diff --git a/libunwindstack/UserMips64.h b/libunwindstack/UserMips64.h new file mode 100644 index 000000000..c46befd4f --- /dev/null +++ b/libunwindstack/UserMips64.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _LIBUNWINDSTACK_USER_MIPS64_H +#define _LIBUNWINDSTACK_USER_MIPS64_H + +namespace unwindstack { + +enum Mips64UserReg : uint16_t { + MIPS64_EF_R0 = 0, + MIPS64_EF_CP0_EPC = 34, +}; + +struct mips64_user_regs { + uint64_t regs[45]; +}; + +} // namespace unwindstack + +#endif // _LIBUNWINDSTACK_USER_MIPS64_H diff --git a/libunwindstack/include/unwindstack/Elf.h b/libunwindstack/include/unwindstack/Elf.h index d27727b89..a85e5f458 100644 --- a/libunwindstack/include/unwindstack/Elf.h +++ b/libunwindstack/include/unwindstack/Elf.h @@ -42,6 +42,8 @@ enum ArchEnum : uint8_t { ARCH_ARM64, ARCH_X86, ARCH_X86_64, + ARCH_MIPS, + ARCH_MIPS64, }; class Elf { diff --git a/libunwindstack/include/unwindstack/RegsGetLocal.h b/libunwindstack/include/unwindstack/RegsGetLocal.h index c59e081d0..557eace32 100644 --- a/libunwindstack/include/unwindstack/RegsGetLocal.h +++ b/libunwindstack/include/unwindstack/RegsGetLocal.h @@ -87,7 +87,7 @@ inline __always_inline void RegsGetLocal(Regs* regs) { regs->SetFromRaw(); } -#elif defined(__i386__) || defined(__x86_64__) +#elif defined(__i386__) || defined(__x86_64__) || defined(__mips__) extern "C" void AsmGetRegs(void* regs); @@ -97,11 +97,6 @@ inline void RegsGetLocal(Regs* regs) { regs->SetFromRaw(); } -#elif defined(__mips__) - -// Stub to allow mips to build. -void RegsGetLocal(Regs*) {} - #endif } // namespace unwindstack diff --git a/libunwindstack/include/unwindstack/RegsMips.h b/libunwindstack/include/unwindstack/RegsMips.h new file mode 100644 index 000000000..3fe6a9f7f --- /dev/null +++ b/libunwindstack/include/unwindstack/RegsMips.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _LIBUNWINDSTACK_REGS_MIPS_H +#define _LIBUNWINDSTACK_REGS_MIPS_H + +#include + +#include + +#include +#include + +namespace unwindstack { + +// Forward declarations. +class Memory; + +class RegsMips : public RegsImpl { + public: + RegsMips(); + virtual ~RegsMips() = default; + + virtual ArchEnum Arch() override final; + + uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) override; + + void SetFromRaw() override; + + bool SetPcFromReturnAddress(Memory* process_memory) override; + + bool StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) override; + + virtual void IterateRegisters(std::function) override final; + + static Regs* Read(void* data); + + static Regs* CreateFromUcontext(void* ucontext); +}; + +} // namespace unwindstack + +#endif // _LIBUNWINDSTACK_REGS_MIPS_H diff --git a/libunwindstack/include/unwindstack/RegsMips64.h b/libunwindstack/include/unwindstack/RegsMips64.h new file mode 100644 index 000000000..6b4bcdf92 --- /dev/null +++ b/libunwindstack/include/unwindstack/RegsMips64.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _LIBUNWINDSTACK_REGS_MIPS64_H +#define _LIBUNWINDSTACK_REGS_MIPS64_H + +#include + +#include + +#include +#include + +namespace unwindstack { + +// Forward declarations. +class Memory; + +class RegsMips64 : public RegsImpl { + public: + RegsMips64(); + virtual ~RegsMips64() = default; + + virtual ArchEnum Arch() override final; + + uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) override; + + void SetFromRaw() override; + + bool SetPcFromReturnAddress(Memory* process_memory) override; + + bool StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) override; + + virtual void IterateRegisters(std::function) override final; + + static Regs* Read(void* data); + + static Regs* CreateFromUcontext(void* ucontext); +}; + +} // namespace unwindstack + +#endif // _LIBUNWINDSTACK_REGS_MIPS64_H diff --git a/libunwindstack/tests/ElfTest.cpp b/libunwindstack/tests/ElfTest.cpp index 00192f1a4..7491d4004 100644 --- a/libunwindstack/tests/ElfTest.cpp +++ b/libunwindstack/tests/ElfTest.cpp @@ -145,7 +145,7 @@ TEST_F(ElfTest, elf32_invalid_machine) { ASSERT_FALSE(elf.Init(false)); ASSERT_EQ("", GetFakeLogBuf()); - ASSERT_EQ("4 unwind 32 bit elf that is neither arm nor x86: e_machine = 20\n\n", + ASSERT_EQ("4 unwind 32 bit elf that is neither arm nor x86 nor mips: e_machine = 20\n\n", GetFakeLogPrint()); } @@ -158,7 +158,7 @@ TEST_F(ElfTest, elf64_invalid_machine) { ASSERT_FALSE(elf.Init(false)); ASSERT_EQ("", GetFakeLogBuf()); - ASSERT_EQ("4 unwind 64 bit elf that is neither aarch64 nor x86_64: e_machine = 21\n\n", + ASSERT_EQ("4 unwind 64 bit elf that is neither aarch64 nor x86_64 nor mips64: e_machine = 21\n\n", GetFakeLogPrint()); } @@ -174,6 +174,18 @@ TEST_F(ElfTest, elf_arm) { ASSERT_TRUE(elf.interface() != nullptr); } +TEST_F(ElfTest, elf_mips) { + Elf elf(memory_); + + InitElf32(EM_MIPS); + + ASSERT_TRUE(elf.Init(false)); + ASSERT_TRUE(elf.valid()); + ASSERT_EQ(static_cast(EM_MIPS), elf.machine_type()); + ASSERT_EQ(ELFCLASS32, elf.class_type()); + ASSERT_TRUE(elf.interface() != nullptr); +} + TEST_F(ElfTest, elf_x86) { Elf elf(memory_); @@ -210,6 +222,18 @@ TEST_F(ElfTest, elf_x86_64) { ASSERT_TRUE(elf.interface() != nullptr); } +TEST_F(ElfTest, elf_mips64) { + Elf elf(memory_); + + InitElf64(EM_MIPS); + + ASSERT_TRUE(elf.Init(false)); + ASSERT_TRUE(elf.valid()); + ASSERT_EQ(static_cast(EM_MIPS), elf.machine_type()); + ASSERT_EQ(ELFCLASS64, elf.class_type()); + ASSERT_TRUE(elf.interface() != nullptr); +} + TEST_F(ElfTest, gnu_debugdata_init_fail32) { TestInitGnuDebugdata(ELFCLASS32, EM_ARM, false, [&](uint64_t offset, const void* ptr, size_t size) { diff --git a/libunwindstack/tests/RegsIterateTest.cpp b/libunwindstack/tests/RegsIterateTest.cpp index 0cb70ba27..8b5b31ff9 100644 --- a/libunwindstack/tests/RegsIterateTest.cpp +++ b/libunwindstack/tests/RegsIterateTest.cpp @@ -29,11 +29,15 @@ #include #include #include +#include +#include #include "MachineArm.h" #include "MachineArm64.h" #include "MachineX86.h" #include "MachineX86_64.h" +#include "MachineMips.h" +#include "MachineMips64.h" namespace unwindstack { @@ -152,7 +156,87 @@ std::vector ExpectedRegisters() { return result; } -using RegTypes = ::testing::Types; +template<> +std::vector ExpectedRegisters() { + std::vector result; + result.push_back({"r0", MIPS_REG_R0}); + result.push_back({"r1", MIPS_REG_R1}); + result.push_back({"r2", MIPS_REG_R2}); + result.push_back({"r3", MIPS_REG_R3}); + result.push_back({"r4", MIPS_REG_R4}); + result.push_back({"r5", MIPS_REG_R5}); + result.push_back({"r6", MIPS_REG_R6}); + result.push_back({"r7", MIPS_REG_R7}); + result.push_back({"r8", MIPS_REG_R8}); + result.push_back({"r9", MIPS_REG_R9}); + result.push_back({"r10", MIPS_REG_R10}); + result.push_back({"r11", MIPS_REG_R11}); + result.push_back({"r12", MIPS_REG_R12}); + result.push_back({"r13", MIPS_REG_R13}); + result.push_back({"r14", MIPS_REG_R14}); + result.push_back({"r15", MIPS_REG_R15}); + result.push_back({"r16", MIPS_REG_R16}); + result.push_back({"r17", MIPS_REG_R17}); + result.push_back({"r18", MIPS_REG_R18}); + result.push_back({"r19", MIPS_REG_R19}); + result.push_back({"r20", MIPS_REG_R20}); + result.push_back({"r21", MIPS_REG_R21}); + result.push_back({"r22", MIPS_REG_R22}); + result.push_back({"r23", MIPS_REG_R23}); + result.push_back({"r24", MIPS_REG_R24}); + result.push_back({"r25", MIPS_REG_R25}); + result.push_back({"r26", MIPS_REG_R26}); + result.push_back({"r27", MIPS_REG_R27}); + result.push_back({"r28", MIPS_REG_R28}); + result.push_back({"sp", MIPS_REG_SP}); + result.push_back({"r30", MIPS_REG_R30}); + result.push_back({"ra", MIPS_REG_RA}); + result.push_back({"pc", MIPS_REG_PC}); + + return result; +} + +template<> +std::vector ExpectedRegisters() { + std::vector result; + result.push_back({"r0", MIPS64_REG_R0}); + result.push_back({"r1", MIPS64_REG_R1}); + result.push_back({"r2", MIPS64_REG_R2}); + result.push_back({"r3", MIPS64_REG_R3}); + result.push_back({"r4", MIPS64_REG_R4}); + result.push_back({"r5", MIPS64_REG_R5}); + result.push_back({"r6", MIPS64_REG_R6}); + result.push_back({"r7", MIPS64_REG_R7}); + result.push_back({"r8", MIPS64_REG_R8}); + result.push_back({"r9", MIPS64_REG_R9}); + result.push_back({"r10", MIPS64_REG_R10}); + result.push_back({"r11", MIPS64_REG_R11}); + result.push_back({"r12", MIPS64_REG_R12}); + result.push_back({"r13", MIPS64_REG_R13}); + result.push_back({"r14", MIPS64_REG_R14}); + result.push_back({"r15", MIPS64_REG_R15}); + result.push_back({"r16", MIPS64_REG_R16}); + result.push_back({"r17", MIPS64_REG_R17}); + result.push_back({"r18", MIPS64_REG_R18}); + result.push_back({"r19", MIPS64_REG_R19}); + result.push_back({"r20", MIPS64_REG_R20}); + result.push_back({"r21", MIPS64_REG_R21}); + result.push_back({"r22", MIPS64_REG_R22}); + result.push_back({"r23", MIPS64_REG_R23}); + result.push_back({"r24", MIPS64_REG_R24}); + result.push_back({"r25", MIPS64_REG_R25}); + result.push_back({"r26", MIPS64_REG_R26}); + result.push_back({"r27", MIPS64_REG_R27}); + result.push_back({"r28", MIPS64_REG_R28}); + result.push_back({"sp", MIPS64_REG_SP}); + result.push_back({"r30", MIPS64_REG_R30}); + result.push_back({"ra", MIPS64_REG_RA}); + result.push_back({"pc", MIPS64_REG_PC}); + + return result; +} + +using RegTypes = ::testing::Types; TYPED_TEST_CASE(RegsIterateTest, RegTypes); TYPED_TEST(RegsIterateTest, iterate) { diff --git a/libunwindstack/tests/RegsStepIfSignalHandlerTest.cpp b/libunwindstack/tests/RegsStepIfSignalHandlerTest.cpp index ae57cafb1..ef9e61cfa 100644 --- a/libunwindstack/tests/RegsStepIfSignalHandlerTest.cpp +++ b/libunwindstack/tests/RegsStepIfSignalHandlerTest.cpp @@ -23,11 +23,15 @@ #include #include #include +#include +#include #include "MachineArm.h" #include "MachineArm64.h" #include "MachineX86.h" #include "MachineX86_64.h" +#include "MachineMips.h" +#include "MachineMips64.h" #include "MemoryFake.h" @@ -204,4 +208,64 @@ TEST_F(RegsStepIfSignalHandlerTest, x86_64_step_if_signal_handler) { EXPECT_EQ(0x150U, regs.pc()); } +TEST_F(RegsStepIfSignalHandlerTest, mips_step_if_signal_handler_non_rt) { + uint64_t addr = 0x1000; + RegsMips regs; + regs[MIPS_REG_PC] = 0x8000; + regs[MIPS_REG_SP] = addr; + regs.SetFromRaw(); + + elf_memory_->SetData64(0x8000, 0x0000000c24021017ULL); + + for (uint64_t index = 0; index <= 50; index++) { + process_memory_.SetData64(addr + index * 8, index * 0x10); + } + + ASSERT_TRUE(regs.StepIfSignalHandler(0x8000, elf_.get(), &process_memory_)); + EXPECT_EQ(0x220U, regs[MIPS_REG_SP]); + EXPECT_EQ(0x040U, regs[MIPS_REG_PC]); + EXPECT_EQ(0x220U, regs.sp()); + EXPECT_EQ(0x040U, regs.pc()); +} + +TEST_F(RegsStepIfSignalHandlerTest, mips_step_if_signal_handler_rt) { + uint64_t addr = 0x1000; + RegsMips regs; + regs[MIPS_REG_PC] = 0x8000; + regs[MIPS_REG_SP] = addr; + regs.SetFromRaw(); + + elf_memory_->SetData64(0x8000, 0x0000000c24021061ULL); + + for (uint64_t index = 0; index <= 100; index++) { + process_memory_.SetData64(addr + index * 8, index * 0x10); + } + + ASSERT_TRUE(regs.StepIfSignalHandler(0x8000, elf_.get(), &process_memory_)); + EXPECT_EQ(0x350U, regs[MIPS_REG_SP]); + EXPECT_EQ(0x170U, regs[MIPS_REG_PC]); + EXPECT_EQ(0x350U, regs.sp()); + EXPECT_EQ(0x170U, regs.pc()); +} + +TEST_F(RegsStepIfSignalHandlerTest, mips64_step_if_signal_handler) { + uint64_t addr = 0x1000; + RegsMips64 regs; + regs[MIPS64_REG_PC] = 0x8000; + regs[MIPS64_REG_SP] = addr; + regs.SetFromRaw(); + + elf_memory_->SetData64(0x8000, 0x0000000c2402145bULL); + + for (uint64_t index = 0; index <= 100; index++) { + process_memory_.SetData64(addr + index * 8, index * 0x10); + } + + ASSERT_TRUE(regs.StepIfSignalHandler(0x8000, elf_.get(), &process_memory_)); + EXPECT_EQ(0x350U, regs[MIPS64_REG_SP]); + EXPECT_EQ(0x600U, regs[MIPS64_REG_PC]); + EXPECT_EQ(0x350U, regs.sp()); + EXPECT_EQ(0x600U, regs.pc()); +} + } // namespace unwindstack diff --git a/libunwindstack/tests/RegsTest.cpp b/libunwindstack/tests/RegsTest.cpp index a93297344..3f848909b 100644 --- a/libunwindstack/tests/RegsTest.cpp +++ b/libunwindstack/tests/RegsTest.cpp @@ -25,6 +25,8 @@ #include #include #include +#include +#include #include "ElfFake.h" #include "MemoryFake.h" @@ -112,6 +114,30 @@ TEST_F(RegsTest, rel_pc) { ASSERT_EQ(0x1U, x86_64.GetAdjustedPc(0x2, elf_.get())); ASSERT_EQ(0x0U, x86_64.GetAdjustedPc(0x1, elf_.get())); ASSERT_EQ(0x0U, x86_64.GetAdjustedPc(0x0, elf_.get())); + + RegsMips mips; + ASSERT_EQ(0x8U, mips.GetAdjustedPc(0x10, elf_.get())); + ASSERT_EQ(0x0U, mips.GetAdjustedPc(0x8, elf_.get())); + ASSERT_EQ(0x7U, mips.GetAdjustedPc(0x7, elf_.get())); + ASSERT_EQ(0x6U, mips.GetAdjustedPc(0x6, elf_.get())); + ASSERT_EQ(0x5U, mips.GetAdjustedPc(0x5, elf_.get())); + ASSERT_EQ(0x4U, mips.GetAdjustedPc(0x4, elf_.get())); + ASSERT_EQ(0x3U, mips.GetAdjustedPc(0x3, elf_.get())); + ASSERT_EQ(0x2U, mips.GetAdjustedPc(0x2, elf_.get())); + ASSERT_EQ(0x1U, mips.GetAdjustedPc(0x1, elf_.get())); + ASSERT_EQ(0x0U, mips.GetAdjustedPc(0x0, elf_.get())); + + RegsMips64 mips64; + ASSERT_EQ(0x8U, mips64.GetAdjustedPc(0x10, elf_.get())); + ASSERT_EQ(0x0U, mips64.GetAdjustedPc(0x8, elf_.get())); + ASSERT_EQ(0x7U, mips64.GetAdjustedPc(0x7, elf_.get())); + ASSERT_EQ(0x6U, mips64.GetAdjustedPc(0x6, elf_.get())); + ASSERT_EQ(0x5U, mips64.GetAdjustedPc(0x5, elf_.get())); + ASSERT_EQ(0x4U, mips64.GetAdjustedPc(0x4, elf_.get())); + ASSERT_EQ(0x3U, mips64.GetAdjustedPc(0x3, elf_.get())); + ASSERT_EQ(0x2U, mips64.GetAdjustedPc(0x2, elf_.get())); + ASSERT_EQ(0x1U, mips64.GetAdjustedPc(0x1, elf_.get())); + ASSERT_EQ(0x0U, mips64.GetAdjustedPc(0x0, elf_.get())); } TEST_F(RegsTest, rel_pc_arm) { @@ -154,6 +180,8 @@ TEST_F(RegsTest, elf_invalid) { RegsArm64 regs_arm64; RegsX86 regs_x86; RegsX86_64 regs_x86_64; + RegsMips regs_mips; + RegsMips64 regs_mips64; MapInfo map_info(0x1000, 0x2000); Elf* invalid_elf = new Elf(new MemoryFake); map_info.elf = invalid_elf; @@ -173,6 +201,14 @@ TEST_F(RegsTest, elf_invalid) { regs_x86_64.set_pc(0x1800); EXPECT_EQ(0x800U, invalid_elf->GetRelPc(regs_x86_64.pc(), &map_info)); EXPECT_EQ(0x800U, regs_x86_64.GetAdjustedPc(0x800U, invalid_elf)); + + regs_mips.set_pc(0x1900); + EXPECT_EQ(0x900U, invalid_elf->GetRelPc(regs_mips.pc(), &map_info)); + EXPECT_EQ(0x900U, regs_mips.GetAdjustedPc(0x900U, invalid_elf)); + + regs_mips64.set_pc(0x1a00); + EXPECT_EQ(0xa00U, invalid_elf->GetRelPc(regs_mips64.pc(), &map_info)); + EXPECT_EQ(0xa00U, regs_mips64.GetAdjustedPc(0xa00U, invalid_elf)); } TEST_F(RegsTest, arm_set_from_raw) { @@ -215,6 +251,26 @@ TEST_F(RegsTest, x86_64_set_from_raw) { EXPECT_EQ(0x4900000000U, x86_64.pc()); } +TEST_F(RegsTest, mips_set_from_raw) { + RegsMips mips; + uint32_t* regs = reinterpret_cast(mips.RawData()); + regs[29] = 0x100; + regs[32] = 0x200; + mips.SetFromRaw(); + EXPECT_EQ(0x100U, mips.sp()); + EXPECT_EQ(0x200U, mips.pc()); +} + +TEST_F(RegsTest, mips64_set_from_raw) { + RegsMips64 mips64; + uint64_t* regs = reinterpret_cast(mips64.RawData()); + regs[29] = 0xb100000000ULL; + regs[32] = 0xc200000000ULL; + mips64.SetFromRaw(); + EXPECT_EQ(0xb100000000U, mips64.sp()); + EXPECT_EQ(0xc200000000U, mips64.pc()); +} + TEST_F(RegsTest, machine_type) { RegsArm arm_regs; EXPECT_EQ(ARCH_ARM, arm_regs.Arch()); @@ -227,6 +283,12 @@ TEST_F(RegsTest, machine_type) { RegsX86_64 x86_64_regs; EXPECT_EQ(ARCH_X86_64, x86_64_regs.Arch()); + + RegsMips mips_regs; + EXPECT_EQ(ARCH_MIPS, mips_regs.Arch()); + + RegsMips64 mips64_regs; + EXPECT_EQ(ARCH_MIPS64, mips64_regs.Arch()); } } // namespace unwindstack diff --git a/libunwindstack/tests/UnwinderTest.cpp b/libunwindstack/tests/UnwinderTest.cpp index 2034191e2..cd468074e 100644 --- a/libunwindstack/tests/UnwinderTest.cpp +++ b/libunwindstack/tests/UnwinderTest.cpp @@ -32,6 +32,8 @@ #include #include #include +#include +#include #include #include "ElfFake.h" @@ -733,6 +735,16 @@ TEST_F(UnwinderTest, format_frame) { x86_64->set_sp(0x10000); reg_list.push_back(x86_64); + RegsMips* mips = new RegsMips; + mips->set_pc(0x2300); + mips->set_sp(0x10000); + reg_list.push_back(mips); + + RegsMips64* mips64 = new RegsMips64; + mips64->set_pc(0x2300); + mips64->set_sp(0x10000); + reg_list.push_back(mips64); + for (auto regs : reg_list) { ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 10)); @@ -744,10 +756,12 @@ TEST_F(UnwinderTest, format_frame) { switch (regs->Arch()) { case ARCH_ARM: case ARCH_X86: + case ARCH_MIPS: expected = " #00 pc 00001300 /system/fake/libc.so (Frame0+10)"; break; case ARCH_ARM64: case ARCH_X86_64: + case ARCH_MIPS64: expected = " #00 pc 0000000000001300 /system/fake/libc.so (Frame0+10)"; break; default: diff --git a/libunwindstack/tools/unwind.cpp b/libunwindstack/tools/unwind.cpp index 789627984..81bedb7d5 100644 --- a/libunwindstack/tools/unwind.cpp +++ b/libunwindstack/tools/unwind.cpp @@ -76,6 +76,12 @@ void DoUnwind(pid_t pid) { case unwindstack::ARCH_X86_64: printf("x86_64"); break; + case unwindstack::ARCH_MIPS: + printf("mips"); + break; + case unwindstack::ARCH_MIPS64: + printf("mips64"); + break; default: printf("unknown\n"); return;