Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ jobs:
- aarch64-unknown-linux-gnu
- riscv64gc-unknown-linux-gnu
- riscv32gc-unknown-linux-gnu
- loongarch64-unknown-linux-gnu
runs-on: ubuntu-latest

steps:
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ This library serves two purposes:
1. Provide a pure Rust alternative to libgcc_eh or libunwind.
2. Provide easier unwinding support for `#![no_std]` targets.

Currently supports x86_64, x86, RV64, RV32 and AArch64.
Currently supports x86_64, x86, RV64, RV32, AArch64, and LoongArch64.

## Unwinder

Expand Down
21 changes: 20 additions & 1 deletion src/arch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,30 @@ mod aarch64 {
#[cfg(target_arch = "aarch64")]
pub use aarch64::*;

#[cfg(target_arch = "loongarch64")]
mod loongarch64 {
use gimli::{LoongArch, Register};

pub struct Arch;

#[allow(unused)]
impl Arch {
pub const SP: Register = LoongArch::SP;
pub const RA: Register = LoongArch::RA;

pub const UNWIND_DATA_REG: (Register, Register) = (LoongArch::A0, LoongArch::A1);
pub const UNWIND_PRIVATE_DATA_SIZE: usize = 2;
}
}
#[cfg(target_arch = "loongarch64")]
pub use loongarch64::*;

#[cfg(not(any(
target_arch = "x86_64",
target_arch = "x86",
target_arch = "riscv64",
target_arch = "riscv32",
target_arch = "aarch64"
target_arch = "aarch64",
target_arch = "loongarch64"
)))]
compile_error!("Current architecture is not supported");
229 changes: 229 additions & 0 deletions src/unwinder/arch/loongarch64.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
use core::{fmt, ops};
use gimli::{LoongArch, Register};

use super::maybe_cfi;

// LoongArch64's DWARF_FRAME_REGISTERS in GCC is 74
pub const MAX_REG_RULES: usize = 74;

#[repr(C)]
#[derive(Clone, Default)]
pub struct Context {
pub gp: [usize; 32],
#[cfg(target_feature = "d")]
pub fp: [usize; 32],
}

impl fmt::Debug for Context {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut fmt = fmt.debug_struct("Context");
for i in 0..=31 {
fmt.field(
LoongArch::register_name(Register(i as _)).unwrap(),
&format_args!("{:#x}", self.gp[i]),
);
}
#[cfg(target_feature = "d")]
for i in 0..=31 {
fmt.field(
LoongArch::register_name(Register((i + 32) as _)).unwrap(),
&format_args!("{:#x}", self.fp[i]),
);
}
fmt.finish()
}
}

impl ops::Index<Register> for Context {
type Output = usize;

fn index(&self, reg: Register) -> &usize {
match reg {
Register(0..=31) => &self.gp[reg.0 as usize],
#[cfg(target_feature = "d")]
Register(32..=63) => &self.fp[(reg.0 - 32) as usize],
_ => unimplemented!(),
}
}
}

impl ops::IndexMut<gimli::Register> for Context {
fn index_mut(&mut self, reg: Register) -> &mut usize {
match reg {
Register(0..=31) => &mut self.gp[reg.0 as usize],
#[cfg(target_feature = "d")]
Register(32..=63) => &mut self.fp[(reg.0 - 32) as usize],
_ => unimplemented!(),
}
}
}

macro_rules! save_regs {
(gp$(, $fp:ident)?) => {
core::arch::naked_asm!(
maybe_cfi!(".cfi_startproc"),
"
move $t0, $sp
addi.d $sp, $sp, -0x210
",
maybe_cfi!(".cfi_def_cfa_offset 0x210"),
"
st.d $ra, $sp, 0x200
",
maybe_cfi!(".cfi_offset 1, -16"), // ra
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does loongarch assembler not support more friendly names in cfi_offset?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you tried .cfi_offset $ra, -16 (note the $ sign)

"
st.d $zero, $sp, 0x0
st.d $ra, $sp, 0x8
st.d $tp, $sp, 0x10
st.d $t0, $sp, 0x18
st.d $r21, $sp, 0xa8 // reserved
st.d $fp, $sp, 0xb0
st.d $s0, $sp, 0xb8
st.d $s1, $sp, 0xc0
st.d $s2, $sp, 0xc8
st.d $s3, $sp, 0xd0
st.d $s4, $sp, 0xd8
st.d $s5, $sp, 0xe0
st.d $s6, $sp, 0xe8
st.d $s7, $sp, 0xf0
st.d $s8, $sp, 0xf8
",
save_regs!(maybe_save_fp($($fp)?)),
"
move $t0, $a0
move $a0, $sp

jirl $ra, $t0, 0

ld.d $ra, $sp, 0x200
addi.d $sp, $sp, 0x210
",
maybe_cfi!(".cfi_def_cfa_offset 0"),
maybe_cfi!(".cfi_restore 1"), // ra
"
ret
",
maybe_cfi!(".cfi_endproc"),
)
};
(maybe_save_fp(fp)) => {
"
fst.d $fs0, $sp, 0x1c0
fst.d $fs1, $sp, 0x1c8
fst.d $fs2, $sp, 0x1d0
fst.d $fs3, $sp, 0x1d8
fst.d $fs4, $sp, 0x1e0
fst.d $fs5, $sp, 0x1e8
fst.d $fs6, $sp, 0x1f0
fst.d $fs7, $sp, 0x1f8
"
};
(maybe_save_fp()) => {
""
};
}

macro_rules! restore_regs {
($ctx:expr, gp$(, $fp:ident)?) => {
core::arch::asm!(
restore_regs!(maybe_restore_fp($($fp)?)),
"
ld.d $ra, $a0, 0x8
ld.d $tp, $a0, 0x10
ld.d $sp, $a0, 0x18
ld.d $a1, $a0, 0x28
ld.d $a2, $a0, 0x30
ld.d $a3, $a0, 0x38
ld.d $a4, $a0, 0x40
ld.d $a5, $a0, 0x48
ld.d $a6, $a0, 0x50
ld.d $a7, $a0, 0x58
ld.d $t0, $a0, 0x60
ld.d $t1, $a0, 0x68
ld.d $t2, $a0, 0x70
ld.d $t3, $a0, 0x78
ld.d $t4, $a0, 0x80
ld.d $t5, $a0, 0x88
ld.d $t6, $a0, 0x90
ld.d $t7, $a0, 0x98
ld.d $t8, $a0, 0xa0
ld.d $r21, $a0, 0xa8 // reserved
ld.d $fp, $a0, 0xb0
ld.d $s0, $a0, 0xb8
ld.d $s1, $a0, 0xc0
ld.d $s2, $a0, 0xc8
ld.d $s3, $a0, 0xd0
ld.d $s4, $a0, 0xd8
ld.d $s5, $a0, 0xe0
ld.d $s6, $a0, 0xe8
ld.d $s7, $a0, 0xf0
ld.d $s8, $a0, 0xf8
",
"
ld.d $a0, $a0, 0x20
ret
",
in("$a0") $ctx,
options(noreturn)
)
};
(maybe_restore_fp(fp)) => {
"
fld.d $fa0, $a0, 0x100
fld.d $fa1, $a0, 0x108
fld.d $fa2, $a0, 0x110
fld.d $fa3, $a0, 0x118
fld.d $fa4, $a0, 0x120
fld.d $fa5, $a0, 0x128
fld.d $fa6, $a0, 0x130
fld.d $fa7, $a0, 0x138
fld.d $ft0, $a0, 0x140
fld.d $ft1, $a0, 0x148
fld.d $ft2, $a0, 0x150
fld.d $ft3, $a0, 0x158
fld.d $ft4, $a0, 0x160
fld.d $ft5, $a0, 0x168
fld.d $ft6, $a0, 0x170
fld.d $ft7, $a0, 0x178
fld.d $ft8, $a0, 0x180
fld.d $ft9, $a0, 0x188
fld.d $ft10, $a0, 0x190
fld.d $ft11, $a0, 0x198
fld.d $ft12, $a0, 0x1a0
fld.d $ft13, $a0, 0x1a8
fld.d $ft14, $a0, 0x1b0
fld.d $ft15, $a0, 0x1b8
fld.d $fs0, $a0, 0x1c0
fld.d $fs1, $a0, 0x1c8
fld.d $fs2, $a0, 0x1d0
fld.d $fs3, $a0, 0x1d8
fld.d $fs4, $a0, 0x1e0
fld.d $fs5, $a0, 0x1e8
fld.d $fs6, $a0, 0x1f0
fld.d $fs7, $a0, 0x1f8
"
};
(maybe_restore_fp()) => {
""
};
}

#[unsafe(naked)]
pub extern "C-unwind" fn save_context(f: extern "C" fn(&mut Context, *mut ()), ptr: *mut ()) {
#[allow(unused_unsafe)]
unsafe {
#[cfg(target_feature = "d")]
save_regs!(gp, fp);
#[cfg(not(target_feature = "d"))]
save_regs!(gp);
}
}

pub unsafe fn restore_context(ctx: &Context) -> ! {
unsafe {
#[cfg(target_feature = "d")]
restore_regs!(ctx, gp, fp);
#[cfg(not(target_feature = "d"))]
restore_regs!(ctx, gp);
}
}
8 changes: 7 additions & 1 deletion src/unwinder/arch/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,18 @@ mod aarch64;
#[cfg(target_arch = "aarch64")]
pub use aarch64::*;

#[cfg(target_arch = "loongarch64")]
mod loongarch64;
#[cfg(target_arch = "loongarch64")]
pub use loongarch64::*;

#[cfg(not(any(
target_arch = "x86_64",
target_arch = "x86",
target_arch = "riscv64",
target_arch = "riscv32",
target_arch = "aarch64"
target_arch = "aarch64",
target_arch = "loongarch64"
)))]
compile_error!("Current architecture is not supported");

Expand Down