Skip to content

Add arch support for LoongArch64#50

Merged
nbdd0121 merged 13 commits intonbdd0121:trunkfrom
enkerewpo:trunk
Jan 20, 2026
Merged

Add arch support for LoongArch64#50
nbdd0121 merged 13 commits intonbdd0121:trunkfrom
enkerewpo:trunk

Conversation

@enkerewpo
Copy link
Contributor

  1. add loongarch64 assembly codes for unwinding support (https://loongson.github.io/LoongArch-Documentation/LoongArch-ELF-ABI-EN.html)
  2. update the Github CI to include tests for the loongarch64 target

@enkerewpo enkerewpo marked this pull request as draft June 29, 2025 07:42
…ter handling and streamline assembly code generation
@enkerewpo enkerewpo marked this pull request as ready for review June 29, 2025 07:57
@enkerewpo enkerewpo marked this pull request as draft June 29, 2025 10:44
@enkerewpo
Copy link
Contributor Author

enkerewpo commented Jun 29, 2025

related PR discussions for using unwinding in no_std loongarch os kernels : asterinas/asterinas#1915

@enkerewpo enkerewpo marked this pull request as ready for review August 28, 2025 13:38
@enkerewpo
Copy link
Contributor Author

enkerewpo commented Aug 28, 2025

Hi there,

I have tested the loongarch64 port with both std and no_std test cases in test_crates, and updated the CI configuration to automatically run these tests on loongarch64.

For the bare-metal application tests on loongarch64, I created a minimal bare-metal example app (repository link) to validate the unwinding library. The app was built with the following features:

[dependencies.unwinding]
path = "../unwinding" # my porting fork for loongarch64
default-features = false
features = ["fde-gnu-eh-frame-hdr", "panic", "personality", "unwinder"]

The bare-metal app successfully triggers a panic using unwinding’s logic, some important code sections:

src/test.rs

// ... ...
pub fn test_panic() {
    println!("testing loongarch64 baremetal unwinding...");
    panic!("test panic for baremetal application using unwinding library");
}
// ... ...

src/panic.rs

use core::ffi::c_void;
use gimli::Register;
use unwinding::abi::{
    _Unwind_Backtrace, _Unwind_FindEnclosingFunction, _Unwind_GetGR, _Unwind_GetIP, UnwindContext,
    UnwindReasonCode,
};

#[panic_handler]
fn panic(info: &core::panic::PanicInfo) -> ! {
    unsafe extern "Rust" {
        pub fn __panic_handler(info: &core::panic::PanicInfo) -> !;
    }
    unsafe { __panic_handler(info) }
}

#[unsafe(no_mangle)]
pub fn __panic_handler(info: &core::panic::PanicInfo) -> ! {
    println!(
        "(__panic_handler) panic at {:?}, reason: {:?}",
        info.location().unwrap(),
        info.message().as_str().unwrap_or("")
    );
    print_stack_trace();
    println!("halting, now we reach the end of the program");
    loop {}
}

pub fn print_stack_trace() {
    println!("printing stack trace...");

    struct CallbackData {
        counter: usize,
    }
    extern "C" fn callback(unwind_ctx: &UnwindContext<'_>, arg: *mut c_void) -> UnwindReasonCode {
        println!("callback...");
        let data = unsafe { &mut *(arg as *mut CallbackData) };
        data.counter += 1;
        let pc = _Unwind_GetIP(unwind_ctx);
        if pc > 0 {
            let fde_initial_address = _Unwind_FindEnclosingFunction(pc as *mut c_void) as usize;
            println!(
                "{:4}: fn {:#18x} - pc {:#18x} / registers:",
                data.counter, fde_initial_address, pc,
            );
        }
        // Print the first 8 general registers for any architecture. The register number follows
        // the DWARF standard.
        for i in 0..8u16 {
            let reg_i = _Unwind_GetGR(unwind_ctx, i as i32);
            // we are only testing on loongarch64 for now :D
            let reg_name = gimli::LoongArch::register_name(Register(i)).unwrap_or("unknown");
            if i % 4 == 0 {
                print!("\n    ");
            }
            print!(" {} {:#18x};", reg_name, reg_i);
        }
        print!("\n\n");
        UnwindReasonCode::NO_REASON
    }

    let mut data = CallbackData { counter: 0 };
    _Unwind_Backtrace(callback, &mut data as *mut _ as _);
}

Test results:
The test application was executed on a loongarch64 soft-float QEMU environment. The panic handling and stack unwinding completed successfully, producing the expected output.

image

To reproduce this result:

# install qemu-system-loongarch64 through package manager or just compile it from official QEMU git repo
# note: ./configure --target-list="loongarch64-softmmu"
git clone https://github.com/enkerewpo/unwinding
git clone https://github.com/enkerewpo/baremetal-loongarch64-unwinding-test
cd baremetal-loongarch64-unwinding-test
./build.sh
./run_qemu.sh

"
st.d $r1, $r3, 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)

@enkerewpo
Copy link
Contributor Author

enkerewpo commented Aug 28, 2025

For the current number of DWARF_FRAME_REGISTERS, I just checked the latest GCC code(https://github.com/gcc-mirror/gcc) and its loongarch config. Since DWARF_FRAME_REGISTERS is not defined in gcc/config/loongarch, GCC will set it to FIRST_PSEUDO_REGISTER (according to gcc/defaults.h) which is defined as 74 in gcc/config/loongarch/loongarch.h. I'll update the MAX_REG_RULES to this number.

https://github.com/gcc-mirror/gcc/blob/48ef4af2274a0a1ad945ac14217806c1c1d8ccb2/gcc/config/loongarch/loongarch.h#L306

https://github.com/gcc-mirror/gcc/blob/48ef4af2274a0a1ad945ac14217806c1c1d8ccb2/gcc/defaults.h#L409

@enkerewpo
Copy link
Contributor Author

enkerewpo commented Aug 28, 2025

For the .cfi_offset, LLVM still does not support fancy names here for loongarch, a quick test on LLVM 19:
image

@enkerewpo enkerewpo requested a review from nbdd0121 August 29, 2025 13:40
@enkerewpo enkerewpo marked this pull request as draft August 31, 2025 10:38
@enkerewpo
Copy link
Contributor Author

enkerewpo commented Aug 31, 2025

I just found a problem, when using begin_panic instead of panic!, the panic process will trigger endless panics of "pointer mis-alignment":

image
...
(__panic_handler) panic at Location { file: "/home/wheatfox/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/mem/manually_drop.rs", line: 223, column: 18 }, reason: "unsafe precondition(s) violated: ptr::read requires that the pointer argument is aligned and non-null\n\nThis indicates a bug in the program. This Undefined Behavior check is optional, and cannot be relied on for safety."
printing stack trace...
(__panic_handler) panic at Location { file: "/home/wheatfox/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/mem/manually_drop.rs", line: 223, column: 18 }, reason: "unsafe precondition(s) violated: ptr::read requires that the pointer argument is aligned and non-null\n\nThis indicates a bug in the program. This Undefined Behavior check is optional, and cannot be relied on for safety."
printing stack trace...
(__panic_handler) panic at Location { file: "/home/wheatfox/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/mem/manually_drop.rs", line: 223, column: 18 }, reason: "unsafe precondition(s) violated: ptr::read requires that the pointer argument is aligned and non-null\n\nThis indicates a bug in the program. This Undefined Behavior check is optional, and cannot be relied on for safety."
printing stack trace...
...

I'll look into this problem.

@enkerewpo enkerewpo marked this pull request as ready for review August 31, 2025 12:31
@enkerewpo
Copy link
Contributor Author

enkerewpo commented Aug 31, 2025

Hi there,

I fixed the problem, in the linker script of my loongarch64 baremetal program, I wrongly set the inital sp to a non-8B aligned address : ( . After fixing the linker script, everything works fine using unwinding's catch_unwind and begin_panic in a loongarch64 baremetal environment (qemu-system-loongarch64):

// ...
    uart_init();
    heap_init();
    println!("hello world! {}", 42);

    catch_unwind(|| {
        println!("testing catch unwind...");
        test_panic();
        println!("testing catch unwind done");
    })
    .unwrap();

    // set global trap handler
    set_global_trap_handler(trap_handler);

    // test_vtimer();

    // panic!("test panic! (rust)"); // uncomment this when testing `panic!`

    println!("going to idle loop, the main function is done");
// ...
use unwinding::panic::begin_panic;

pub fn test_panic() {
    // panic!("test panic");
    begin_panic(Box::new("test panic"));
}
  1. Testing the unwinding process:
image
  1. Testing the panic! and stacktrace dump:
image

"
st.d $r1, $r3, 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.

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

@enkerewpo
Copy link
Contributor Author

enkerewpo commented Aug 31, 2025

Thanks for your comments! I updated the related code and tested the hard-float functionality in loongarch64 qemu with rust target loongarch64-unknown-none (the previous tests are using loongarch64-unknown-none-softfloat which does not have the d feature).

Below is a catch+unwind with panic!+stacktrace (with reg dumps for both GPRs, and FPs, which begin with f):

image

However, I found that the MAX_REG_RULES must be set to values <= 64. Setting it to above 64 will cause the unwinding to fail (for some reason):

image

UPDATE: I fixed the problem mentioned above (which actually stuck at creating the unwinding context). It turns out that the stack size I set for my baremetal program is not big enough. After doubling the stack size, eveything works fine.

@nbdd0121
Copy link
Owner

Starting in LLVM 20, CFI directive can use register names: llvm/llvm-project#117120

Copy link
Owner

@nbdd0121 nbdd0121 left a comment

Choose a reason for hiding this comment

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

Sorry for the late review.. This was completely lost from my radar.

@enkerewpo
Copy link
Contributor Author

enkerewpo commented Jan 18, 2026

OK :D

@enkerewpo enkerewpo requested a review from nbdd0121 January 20, 2026 03:22
@nbdd0121 nbdd0121 merged commit 5fd5fdc into nbdd0121:trunk Jan 20, 2026
6 checks passed
@nbdd0121
Copy link
Owner

Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants