Skip to content

Commit

Permalink
feat(common): Add MIPS and MIPS64 architectures (#141)
Browse files Browse the repository at this point in the history
  • Loading branch information
jan-auer committed May 9, 2019
1 parent 1b63d10 commit bf09887
Show file tree
Hide file tree
Showing 4 changed files with 127 additions and 5 deletions.
13 changes: 11 additions & 2 deletions common/src/heuristics.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Heuristics for correcting instruction pointers based on the CPU architecture.

use crate::types::Arch;
use crate::types::{Arch, CpuFamily};

const SIGILL: u32 = 4;
const SIGBUS: u32 = 10;
Expand Down Expand Up @@ -54,7 +54,16 @@ impl InstructionInfo {
/// the preceding instruction will be returned. For this reason, the return
/// value of this function should be considered an upper bound.
pub fn previous_address(&self) -> u64 {
self.aligned_address() - self.arch.instruction_alignment().unwrap_or(1)
let instruction_size = self.arch.instruction_alignment().unwrap_or(1);

// In MIPS, the return address apparently often points two instructions after the the
// previous program counter. On other architectures, just subtract one instruction.
let pc_offset = match self.arch.cpu_family() {
CpuFamily::Mips32 | CpuFamily::Mips64 => 2 * instruction_size,
_ => instruction_size,
};

self.aligned_address() - pc_offset
}

/// Returns whether the application attempted to jump to an invalid,
Expand Down
34 changes: 31 additions & 3 deletions common/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,16 @@ static ARM64: &[&'static str] = &[
"v20", "v21", "v22", "v23", "v24", "v25", "v26", "v27", "v28", "v29", "v30", "v31",
];

/// Names for MIPS CPU registers by register number.
static MIPS: &[&'static str] = &[
"$zero", "$at", "$v0", "$v1", "$a0", "$a1", "$a2", "$a3", "$t0", "$t1", "$t2", "$t3", "$t4",
"$t5", "$t6", "$t7", "$s0", "$s1", "$s2", "$s3", "$s4", "$s5", "$s6", "$s7", "$t8", "$t9",
"$k0", "$k1", "$gp", "$sp", "$fp", "$ra", "$lo", "$hi", "$pc", "$f0", "$f2", "$f3", "$f4",
"$f5", "$f6", "$f7", "$f8", "$f9", "$f10", "$f11", "$f12", "$f13", "$f14", "$f15", "$f16",
"$f17", "$f18", "$f19", "$f20", "$f21", "$f22", "$f23", "$f24", "$f25", "$f26", "$f27", "$f28",
"$f29", "$f30", "$f31", "$fcsr", "$fir",
];

/// Represents a family of CPUs.
///
/// This is strongly connected to the [`Arch`] type, but reduces the selection to a range of
Expand All @@ -71,6 +81,10 @@ pub enum CpuFamily {
Ppc32 = 5,
/// 64-bit big-endian PowerPC.
Ppc64 = 6,
/// 32-bit MIPS.
Mips32 = 7,
/// 64-bit MIPS.
Mips64 = 8,
}

impl Default for CpuFamily {
Expand Down Expand Up @@ -112,6 +126,8 @@ pub enum Arch {
Arm64Unknown = 499,
Ppc = 501,
Ppc64 = 601,
Mips = 701,
Mips64 = 801,
}

impl Arch {
Expand Down Expand Up @@ -141,6 +157,8 @@ impl Arch {
499 => Arch::Arm64Unknown,
17 | 501 => Arch::Ppc,
18 | 601 => Arch::Ppc64,
701 => Arch::Mips,
801 => Arch::Mips64,
_ => Arch::Unknown,
}
}
Expand All @@ -165,6 +183,8 @@ impl Arch {
| Arch::ArmUnknown => CpuFamily::Arm32,
Arch::Ppc => CpuFamily::Ppc32,
Arch::Ppc64 => CpuFamily::Ppc64,
Arch::Mips => CpuFamily::Mips32,
Arch::Mips64 => CpuFamily::Mips64,
}
}

Expand Down Expand Up @@ -194,15 +214,17 @@ impl Arch {
Arch::ArmUnknown => "arm_unknown",
Arch::Ppc => "ppc",
Arch::Ppc64 => "ppc64",
Arch::Mips => "mips",
Arch::Mips64 => "mips64",
}
}

/// Returns the native pointer size.
pub fn pointer_size(self) -> Option<usize> {
match self.cpu_family() {
CpuFamily::Unknown => None,
CpuFamily::Amd64 | CpuFamily::Arm64 | CpuFamily::Ppc64 => Some(8),
CpuFamily::Intel32 | CpuFamily::Arm32 | CpuFamily::Ppc32 => Some(4),
CpuFamily::Amd64 | CpuFamily::Arm64 | CpuFamily::Ppc64 | CpuFamily::Mips64 => Some(8),
CpuFamily::Intel32 | CpuFamily::Arm32 | CpuFamily::Ppc32 | CpuFamily::Mips32 => Some(4),
}
}

Expand All @@ -211,7 +233,7 @@ impl Arch {
match self.cpu_family() {
CpuFamily::Arm32 => Some(2),
CpuFamily::Arm64 => Some(4),
CpuFamily::Ppc32 => Some(4),
CpuFamily::Ppc32 | CpuFamily::Mips32 | CpuFamily::Mips64 => Some(4),
CpuFamily::Ppc64 => Some(8),
CpuFamily::Intel32 | CpuFamily::Amd64 => None,
CpuFamily::Unknown => None,
Expand All @@ -220,11 +242,14 @@ impl Arch {

/// The name of the IP register if known.
pub fn ip_register_name(self) -> Option<&'static str> {
// NOTE: These values do not correspond to the register names defined in this file, but to
// the names exposed by breakpad. This mapping is implemented in `data_structures.cpp`.
match self.cpu_family() {
CpuFamily::Intel32 => Some("eip"),
CpuFamily::Amd64 => Some("rip"),
CpuFamily::Arm32 | CpuFamily::Arm64 => Some("pc"),
CpuFamily::Ppc32 | CpuFamily::Ppc64 => Some("srr0"),
CpuFamily::Mips32 | CpuFamily::Mips64 => Some("pc"),
CpuFamily::Unknown => None,
}
}
Expand All @@ -250,6 +275,7 @@ impl Arch {
CpuFamily::Amd64 => X86_64.get(index),
CpuFamily::Arm64 => ARM64.get(index),
CpuFamily::Arm32 => ARM.get(index),
CpuFamily::Mips32 | CpuFamily::Mips64 => MIPS.get(index),
_ => None,
};

Expand Down Expand Up @@ -299,6 +325,8 @@ impl str::FromStr for Arch {
"arm_unknown" => Arch::ArmUnknown,
"ppc" => Arch::Ppc,
"ppc64" => Arch::Ppc64,
"mips" => Arch::Mips,
"mips64" => Arch::Mips64,
_ => return Err(UnknownArchError),
})
}
Expand Down
21 changes: 21 additions & 0 deletions debuginfo/src/elf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,20 @@ const PAGE_SIZE: usize = 4096;
const SHN_UNDEF: usize = elf::section_header::SHN_UNDEF as usize;
const SHF_COMPRESSED: u64 = elf::section_header::SHF_COMPRESSED as u64;

/// This file follows the first MIPS 32 bit ABI
#[allow(unused)]
const EF_MIPS_ABI_O32: u32 = 0x0000_1000;
/// O32 ABI extended for 64-bit architecture.
const EF_MIPS_ABI_O64: u32 = 0x0000_2000;
/// EABI in 32 bit mode.
#[allow(unused)]
const EF_MIPS_ABI_EABI32: u32 = 0x0000_3000;
/// EABI in 64 bit mode.
const EF_MIPS_ABI_EABI64: u32 = 0x0000_4000;

/// Any flag value that might indicate 64-bit MIPS.
const MIPS_64_FLAGS: u32 = EF_MIPS_ABI_O64 | EF_MIPS_ABI_EABI64;

/// An error when dealing with [`ElfObject`](struct.ElfObject.html).
#[derive(Debug, Fail)]
pub enum ElfError {
Expand Down Expand Up @@ -113,6 +127,13 @@ impl<'d> ElfObject<'d> {
goblin::elf::header::EM_ARM => Arch::Arm,
goblin::elf::header::EM_PPC => Arch::Ppc,
goblin::elf::header::EM_PPC64 => Arch::Ppc64,
goblin::elf::header::EM_MIPS | goblin::elf::header::EM_MIPS_RS3_LE => {
if self.elf.header.e_flags & MIPS_64_FLAGS != 0 {
Arch::Mips64
} else {
Arch::Mips
}
}
_ => Arch::Unknown,
}
}
Expand Down
64 changes: 64 additions & 0 deletions minidump/cpp/data_structures.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ using google_breakpad::StackFrame;
using google_breakpad::StackFrameAMD64;
using google_breakpad::StackFrameARM;
using google_breakpad::StackFrameARM64;
using google_breakpad::StackFrameMIPS;
using google_breakpad::StackFramePPC;
using google_breakpad::StackFramePPC64;
using google_breakpad::StackFrameX86;
Expand Down Expand Up @@ -481,6 +482,69 @@ regval_t *stack_frame_registers(const stack_frame_t *frame,
break;
}

case 7: // Mips
case 8: { // Mips64
const StackFrameMIPS *frame_mips =
reinterpret_cast<const StackFrameMIPS *>(frame);

uint8_t reg_size = (family == 7) ? 4 : 8;

if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_GP)
registers.push_back(
{"gp", frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_GP],
reg_size});
if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_SP)
registers.push_back(
{"sp", frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_SP],
reg_size});
if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_FP)
registers.push_back(
{"fp", frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_FP],
reg_size});
if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_RA)
registers.push_back(
{"ra", frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_RA],
reg_size});
if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_PC)
registers.push_back({"pc", frame_mips->context.epc, reg_size});

// Save registers s0-s7
if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S0)
registers.push_back(
{"s0", frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S0],
reg_size});
if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S1)
registers.push_back(
{"s1", frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S1],
reg_size});
if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S2)
registers.push_back(
{"s2", frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S2],
reg_size});
if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S3)
registers.push_back(
{"s3", frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S3],
reg_size});
if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S4)
registers.push_back(
{"s4", frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S4],
reg_size});
if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S5)
registers.push_back(
{"s5", frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S5],
reg_size});
if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S6)
registers.push_back(
{"s6", frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S6],
reg_size});
if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S7)
registers.push_back(
{"s7", frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S7],
reg_size});

break;
}

case 0: // Unknown
default:
break; // leave registers empty
Expand Down

0 comments on commit bf09887

Please sign in to comment.