librz/debug: native debugger plugin for RISC-V (#5966)

* added breakpoints and stepping

* add link register to allow single-stepping a ret instruction, fix stacktraces
* refactor to avoid passing the IO layer structs to the breakpoint function
* add tests, refine the stacktrace to not include non-function
* add register information for core file parsing
* core file generation for RISC-V
* make tests run under riscv-64
* make tests run under riscv-32
This commit is contained in:
مصطفي محمود كمال الدين 2026-04-26 11:28:07 +03:00 committed by GitHub
parent 241a9f1321
commit 29b04fa460
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
28 changed files with 1209 additions and 71 deletions

View file

@ -9,7 +9,7 @@ LIB_PATH=/usr/riscv64-linux-gnu/lib
LIB_C=libc.so.6 LIB_C=libc.so.6
LIB_M=libm.so.6 LIB_M=libm.so.6
DYN_LD=ld-linux-riscv64-lp64d.so.1 DYN_LD=ld-linux-riscv64-lp64d.so.1
TEST_BIN_URL=https://raw.githubusercontent.com/rizinorg/rizin-testbins/master/elf/riscv_bitmanip
# Fetch the kernel package # Fetch the kernel package
wget "$DEBIAN_SERVER/$LINUX_IMG" wget "$DEBIAN_SERVER/$LINUX_IMG"
# Extract # Extract
@ -27,15 +27,13 @@ cd ..
# Create a rootfs # Create a rootfs
mkdir -p rootfs mkdir -p rootfs
mkdir -p rootfs/lib rootfs/tmp rootfs/proc rootfs/etc mkdir -p rootfs/bin rootfs/lib rootfs/tmp rootfs/proc rootfs/etc rootfs/test/bins/elf rootfs/test/db/archos/linux-riscv64
# Copy all .so files from the build directory to rootfs/lib (only the real ones, not shorthand symlinks) # Simple copying of rizin binaries and .so libs doesn't work, as it skips sdb generaton and other important steps
find rizin/build/librz/ -name "*.so.[0-9].[0-9].[0-9]" -type f \ # Install to a custom prefix instead
-exec cp {} rootfs/lib/ \; \ cd rizin && DESTDIR=../../rootfs/ meson install -C build && cd ..
-exec sh -c 'echo "linking $(basename "${1%.*}") -> $(basename "$1")"; ln -sf "$(basename "$1")" "rootfs/lib/$(basename "${1%.*}")"' _ {} \; # symlinking all *.so.0.9 to *.so.0.9.0 wget ${TEST_BIN_URL} && cp riscv_bitmanip rootfs/test/bins/elf/riscv_bitmanip && chmod +x rootfs/test/bins/elf/riscv_bitmanip
cp rizin/test/db/archos/linux-riscv64/dbg_basic rootfs/test/db/archos/linux-riscv64/dbg_basic
# Copy rizin binary
cp rizin/build/binrz/rizin/rizin rootfs/rizin
# libc and libm, dynamic linker # libc and libm, dynamic linker
cp "$LIB_PATH/$LIB_C" "$LIB_PATH/$LIB_M" "$LIB_PATH/$DYN_LD" rootfs/lib/ cp "$LIB_PATH/$LIB_C" "$LIB_PATH/$LIB_M" "$LIB_PATH/$DYN_LD" rootfs/lib/
@ -43,22 +41,23 @@ cp "$LIB_PATH/$LIB_C" "$LIB_PATH/$LIB_M" "$LIB_PATH/$DYN_LD" rootfs/lib/
# Copy busybox to rootfs # Copy busybox to rootfs
cp "busybox/$BUSYBOX_PATH/busybox" rootfs/bb cp "busybox/$BUSYBOX_PATH/busybox" rootfs/bb
# Make busybox the init launcher, poweroff, and mount # Make busybox the init launcher, poweroff, the shell, and mount
ln -s bb rootfs/init ln -s bb rootfs/init
ln -s bb rootfs/poweroff ln -s bb rootfs/poweroff
ln -s bb rootfs/mount ln -s bb rootfs/mount
ln -s ../bb rootfs/bin/sh
# Make the inittab # Make the inittab
cat << EOF > rootfs/etc/inittab cat << EOF > rootfs/etc/inittab
::sysinit:/mount -t tmpfs -o size=128M,nodev,nosuid tmpfs /tmp
::sysinit:/mount -t proc proc /proc ::sysinit:/mount -t proc proc /proc
::wait:$@ ::wait:/bin/sh -c "export PATH=/usr/local/bin && cd /test && rz-test db/archos/linux-riscv64"
::once:/poweroff -f ::once:/poweroff -f
EOF EOF
# filesystem state # filesystem state
# rootfs # rootfs
# / # /
# ├── rizin
# ├── bb # ├── bb
# ├── init -> bb # ├── init -> bb
# ├── poweroff -> bb # ├── poweroff -> bb
@ -67,17 +66,36 @@ EOF
# │ ├── libc.so.6 # │ ├── libc.so.6
# │ ├── libm.so.6 # │ ├── libm.so.6
# │ ├── ld-linux-riscv64-lp64d.so.1 # │ ├── ld-linux-riscv64-lp64d.so.1
# │ ├── librz_arch.so.0.9.0
# │ ├── librz_arch.so.0.9 -> librz_arch.so.0.9.0
# │ ├── librz_bin.so.0.9.0
# │ ├── librz_bin.so.0.9 -> librz_bin.so.0.9.0
# │ └── ... (*.so.0.9.0 objs + *.so.0.9 symlinks)
# ├── tmp/ # ├── tmp/
# │ └── (empty) # │ └── (mounted by kernel)
# ├── proc/ # ├── proc/
# │ └── (mounted by kernel) # │ └── (mounted by kernel)
# └── etc/ # └── etc/
# └── inittab # | └── inittab
# ├── test
# │ ├── bins
# │ │ └── elf
# │ └── db
# │ └── archos
# │ └── linux-riscv64
# └── usr
# └── local
# ├── bin
# │ ├── rizin
# │ ├── rz-ar
# │ ├── rz-asm
# │ ├── rz-ax
# │ ├── rz-bin
# │ ├── rz-diff
# │ ├── rz-find
# │ ├── rz-gg
# │ ├── rz-hash
# │ ├── rz-run
# │ ├── rz-sign
# │ └── rz-test
# ├── include
# | ....
# | ....
echo "#########################################################################" echo "#########################################################################"
# for debugging, always check the output tree against the intended tree above # for debugging, always check the output tree against the intended tree above
tree rootfs tree rootfs
@ -86,4 +104,3 @@ echo "#########################################################################"
# Package the whole thing into an initrd archive # Package the whole thing into an initrd archive
cd rootfs cd rootfs
find . | cpio -o -H newc | gzip > ../initrd.cpio.gz find . | cpio -o -H newc | gzip > ../initrd.cpio.gz
cd ..

View file

@ -1,7 +1,7 @@
#!/bin/sh #!/bin/sh
set -eux set -eux
MINSTACK_VERSION=v12 MINSTACK_VERSION=v17
# Download the linux kernel image, opensbi firmware, and the busybox userspace from the minstack repo # Download the linux kernel image, opensbi firmware, and the busybox userspace from the minstack repo
wget -q "https://github.com/moste00/riscv-minstack/releases/download/${MINSTACK_VERSION}/rv32-Image" wget -q "https://github.com/moste00/riscv-minstack/releases/download/${MINSTACK_VERSION}/rv32-Image"
@ -22,8 +22,14 @@ patch -p1 < patches/fix_zydis_amalgamated_riscv32_build
# Build again # Build again
meson compile -C build meson compile -C build
file build/binrz/rizin/rizin # Install to a custom prefix
mkdir -p ../rootfs-rv32
DESTDIR=../../rootfs-rv32 meson install -C build
cd .. cd ..
# Download the test binary (TODO: Change to rizinorg/rizin-testbins when the test binary PR is merged to rizinorg's repo)
TEST_BIN_URL=https://raw.githubusercontent.com/moste00/rizin-testbins/riscv_bitmanip_32bit/elf/riscv_bitmanip_32
wget ${TEST_BIN_URL} -O riscv_bitmanip_32
#--------------------------------------------------------------------------------------------------------------------- #---------------------------------------------------------------------------------------------------------------------
# Create a init config for busybox # Create a init config for busybox
@ -32,11 +38,13 @@ cat << EOF > inittab
::sysinit:/mount -t sysfs sysfs /sys ::sysinit:/mount -t sysfs sysfs /sys
::sysinit:/mount -t devtmpfs devtmpfs /dev ::sysinit:/mount -t devtmpfs devtmpfs /dev
::sysinit:/mount -t tmpfs -o size=128M,nodev,nosuid tmpfs /tmp ::sysinit:/mount -t tmpfs -o size=128M,nodev,nosuid tmpfs /tmp
::wait:$@ ::sysinit:/mkdir -p /mnt
::sysinit:/mount -t ext4 /dev/vda /mnt
::wait:/bin/sh -c "export PATH=/mnt/usr/local/bin && cd /test && rz-test db/archos/linux-riscv32"
::once:/poweroff -f ::once:/poweroff -f
EOF EOF
# Describe the boot filesystem # Describe a skeletal in-memory filesystem for booting (without Rizin installation)
cat << EOF > initramfs cat << EOF > initramfs
dir /bin 0755 0 0 dir /bin 0755 0 0
dir /etc 0755 0 0 dir /etc 0755 0 0
@ -45,6 +53,12 @@ dir /tmp 1777 0 0
dir /proc 0755 0 0 dir /proc 0755 0 0
dir /sys 0755 0 0 dir /sys 0755 0 0
dir /dev 0755 0 0 dir /dev 0755 0 0
dir /test 0755 0 0
dir /test/bins 0755 0 0
dir /test/bins/elf 0755 0 0
dir /test/db 0755 0 0
dir /test/db/archos 0755 0 0
dir /test/db/archos/linux-riscv32 0755 0 0
nod /dev/console 0600 0 0 c 5 1 nod /dev/console 0600 0 0 c 5 1
@ -52,12 +66,21 @@ file /bin/busybox rv32-busybox 0755 0 0
slink /init /bin/busybox 0777 0 0 slink /init /bin/busybox 0777 0 0
slink /mount /bin/busybox 0777 0 0 slink /mount /bin/busybox 0777 0 0
slink /poweroff /bin/busybox 0777 0 0 slink /poweroff /bin/busybox 0777 0 0
slink /bin/sh /bin/busybox 0777 0 0
slink /mkdir /bin/busybox 0777 0 0
file /etc/inittab inittab 0644 0 0 file /etc/inittab inittab 0644 0 0
file /test/bins/elf/riscv_bitmanip_32 riscv_bitmanip_32 0755 0 0
file /test/db/archos/linux-riscv32/dbg_basic rizin/test/db/archos/linux-riscv32/dbg_basic 0644 0 0
file /lib/ld-musl-riscv32.so.1 ${BOOTLIN_INSTALLATION}/riscv32-buildroot-linux-musl/sysroot/lib/ld-musl-riscv32.so.1 0755 0 0 file /lib/ld-musl-riscv32.so.1 ${BOOTLIN_INSTALLATION}/riscv32-buildroot-linux-musl/sysroot/lib/ld-musl-riscv32.so.1 0755 0 0
file /bin/rizin rizin/build/binrz/rizin/rizin 0755 0 0
EOF EOF
# Generate the boot filesystem # Generate the boot filesystem
gcc rizin/sys/gen_cpio_script.c -O3 -o gen_cpio gcc rizin/sys/gen_cpio_script.c -O3 -o gen_cpio
./gen_cpio initramfs > initrd_rv32.cpio ./gen_cpio initramfs > initrd_rv32.cpio
# Pack the Rizin installation into an ext4 filesystem
# (ext4 is necessary because Rizin cross-built into rv32, using musl and statically linked, is too big to fit in an initramfs)
truncate -s 4G rootfs-rv32.ext4
mkfs.ext4 -d rootfs-rv32 rootfs-rv32.ext4

View file

@ -932,13 +932,13 @@ jobs:
- name: Setup RISC-V QEMU environment - name: Setup RISC-V QEMU environment
run: | run: |
cd .. cd ..
sh rizin/.github/riscv-qemu-setup-all.sh /rizin -hh sh rizin/.github/riscv-qemu-setup-all.sh
- name: Run inside RISC-V QEMU - name: Run inside RISC-V QEMU
run: | run: |
cd .. cd ..
qemu-system-riscv64 \ qemu-system-riscv64 \
-machine virt -cpu rv64 -m 512M \ -machine virt -cpu rv64 -m 2G \
-kernel linux-image/boot/vmlinux-6.12.73+deb13-riscv64 \ -kernel linux-image/boot/vmlinux-6.12.73+deb13-riscv64 \
-initrd initrd.cpio.gz \ -initrd initrd.cpio.gz \
-append "console=ttyS0,115200 earlycon" \ -append "console=ttyS0,115200 earlycon" \
@ -988,10 +988,12 @@ jobs:
- name: Run Rizin on RISCV32 inside QEMU - name: Run Rizin on RISCV32 inside QEMU
run: | run: |
cd .. cd ..
qemu-system-riscv32 -machine virt -m 512M \ qemu-system-riscv32 -machine virt -m 2G \
-bios rv32-fw_dynamic.bin \ -bios rv32-fw_dynamic.bin \
-kernel rv32-Image \ -kernel rv32-Image \
-initrd initrd_rv32.cpio \ -initrd initrd_rv32.cpio \
-drive file=rootfs-rv32.ext4,format=raw,id=hd0,if=none \
-device virtio-blk-device,drive=hd0 \
-append "earlycon console=hvc0" \ -append "earlycon console=hvc0" \
-nographic -nographic

View file

@ -946,17 +946,19 @@ RZ_API void rz_asm_list_directives(void) {
/** /**
* \brief Returns the software breakpoint instruction (binary encoded) of the current selected arch * \brief Returns the software breakpoint instruction (binary encoded) of the current selected arch
* *
* \param a The RzAsm structure to use * \param a The RzAsm structure to use.
* \param addr The address where the software breakpoint is to be inserted.
* \param original The original RzAsmOp at addr.
* \param op The RzAsmOp to fill. * \param op The RzAsmOp to fill.
* *
* \return On success true, otherwise false. * \return On success true, otherwise false.
*/ */
RZ_API bool rz_asm_software_breakpoint(RZ_NONNULL const RzAsm *a, RZ_NONNULL RzAsmOp *op) { RZ_API bool rz_asm_software_breakpoint(RZ_NONNULL const RzAsm *a, ut64 addr, const RZ_NONNULL RzAsmOp *original, RZ_NONNULL RzAsmOp *op) {
rz_return_val_if_fail(a && op, false); rz_return_val_if_fail(a && op, false);
memset(op, 0, sizeof(RzAsmOp)); memset(op, 0, sizeof(RzAsmOp));
if (a->cur && a->cur->sw_breakpoint) { if (a->cur && a->cur->sw_breakpoint) {
return a->cur->sw_breakpoint(a, op); return a->cur->sw_breakpoint(a, addr, original, op);
} }
return false; return false;

View file

@ -298,13 +298,13 @@ static char **arm_cpu_descriptions() {
return cpu_desc; return cpu_desc;
} }
static bool arm_sw_breakpoint(const RzAsm *a, RzAsmOp *op) { static bool arm_sw_breakpoint(const RzAsm *a, ut64 addr, const RzAsmOp *original, RzAsmOp *breakpoint) {
if (a->bits == 64) { if (a->bits == 64) {
// arm64/aarch64 // arm64/aarch64
// { 64, 4, 0, "\x00\x00\x20\xd4" }, // le - arm64 brk0 // { 64, 4, 0, "\x00\x00\x20\xd4" }, // le - arm64 brk0
// { 64, 4, 1, "\xd4\x20\x00\x00" }, // be - arm64 // { 64, 4, 1, "\xd4\x20\x00\x00" }, // be - arm64
// { 64, 1, 0, "\xfe\xde\xff\xe7" }, // le - arm64 - hacky fix // { 64, 1, 0, "\xfe\xde\xff\xe7" }, // le - arm64 - hacky fix
rz_asm_op_set_buf(op, a->big_endian ? (const ut8 *)"\xd4\x20\x00\x00" : (const ut8 *)"\x00\x00\x20\xd4", 4); rz_asm_op_set_buf(breakpoint, a->big_endian ? (const ut8 *)"\xd4\x20\x00\x00" : (const ut8 *)"\x00\x00\x20\xd4", 4);
return true; return true;
} else if (a->bits == 32) { } else if (a->bits == 32) {
// arm32 // arm32
@ -313,7 +313,7 @@ static bool arm_sw_breakpoint(const RzAsm *a, RzAsmOp *op) {
// { 4, 0, "\xf0\x01\xf0\xe7" }, // eabi-le - undefined instruction - for all kernels // { 4, 0, "\xf0\x01\xf0\xe7" }, // eabi-le - undefined instruction - for all kernels
// { 4, 1, "\xe7\xf0\x01\xf0" }, // eabi-be // { 4, 1, "\xe7\xf0\x01\xf0" }, // eabi-be
// eabi - undefined instruction - for all kernels // eabi - undefined instruction - for all kernels
rz_asm_op_set_buf(op, a->big_endian ? (const ut8 *)"\xe7\xf0\x01\xf0" : (const ut8 *)"\xf0\x01\xf0\xe7", 4); rz_asm_op_set_buf(breakpoint, a->big_endian ? (const ut8 *)"\xe7\xf0\x01\xf0" : (const ut8 *)"\xf0\x01\xf0\xe7", 4);
return true; return true;
} }
@ -324,7 +324,7 @@ static bool arm_sw_breakpoint(const RzAsm *a, RzAsmOp *op) {
// { 16, 2, 1, "\xdf\xfe" }, // arm-thumb-be // { 16, 2, 1, "\xdf\xfe" }, // arm-thumb-be
// { 16, 4, 0, "\xff\xff\xff\xff" }, // arm-thumb-le // { 16, 4, 0, "\xff\xff\xff\xff" }, // arm-thumb-le
// { 16, 4, 1, "\xff\xff\xff\xff" }, // arm-thumb-be // { 16, 4, 1, "\xff\xff\xff\xff" }, // arm-thumb-be
rz_asm_op_set_buf(op, a->big_endian ? (const ut8 *)"\xbe\x01" : (const ut8 *)"\x01\xbe", 2); rz_asm_op_set_buf(breakpoint, a->big_endian ? (const ut8 *)"\xbe\x01" : (const ut8 *)"\x01\xbe", 2);
return true; return true;
} }

View file

@ -188,10 +188,10 @@ static bool bf_fini(void *user) {
return true; return true;
} }
static bool bf_sw_breakpoint(const RzAsm *a, RzAsmOp *op) { static bool bf_sw_breakpoint(const RzAsm *a, ut64 addr, const RzAsmOp *original, RzAsmOp *breakpoint) {
// { 0, 1, 0, (const ut8 *)"\xff" }, // { 0, 1, 0, (const ut8 *)"\xff" },
// { 0, 1, 0, (const ut8 *)"\x00" }, // { 0, 1, 0, (const ut8 *)"\x00" },
rz_asm_op_set_buf(op, (const ut8 *)"\xff", 1); rz_asm_op_set_buf(breakpoint, (const ut8 *)"\xff", 1);
return true; return true;
} }

View file

@ -122,13 +122,13 @@ static char **mips_cpu_descriptions() {
return cpu_desc; return cpu_desc;
} }
static bool mips_sw_breakpoint(const RzAsm *a, RzAsmOp *op) { static bool mips_sw_breakpoint(const RzAsm *a, ut64 addr, const RzAsmOp *original, RzAsmOp *breakpoint) {
// mips32/64 // mips32/64
// { 32, 4, 0, "\x0d\x00\x00\x00" }, // { 32, 4, 0, "\x0d\x00\x00\x00" },
// { 32, 4, 1, "\x00\x00\x00\x0d" }, // { 32, 4, 1, "\x00\x00\x00\x0d" },
// { 64, 4, 0, "\x0d\x00\x00\x00" }, // { 64, 4, 0, "\x0d\x00\x00\x00" },
// { 64, 4, 1, "\x00\x00\x00\x0d" }, // { 64, 4, 1, "\x00\x00\x00\x0d" },
rz_asm_op_set_buf(op, a->big_endian ? (const ut8 *)"\x00\x00\x00\x0d" : (const ut8 *)"\x0d\x00\x00\x00", 4); rz_asm_op_set_buf(breakpoint, a->big_endian ? (const ut8 *)"\x00\x00\x00\x0d" : (const ut8 *)"\x0d\x00\x00\x00", 4);
return true; return true;
} }

View file

@ -128,11 +128,11 @@ static char **ppc_cpu_descriptions() {
return cpu_desc; return cpu_desc;
} }
static bool ppc_sw_breakpoint(const RzAsm *a, RzAsmOp *op) { static bool ppc_sw_breakpoint(const RzAsm *a, ut64 addr, const RzAsmOp *original, RzAsmOp *breakpoint) {
// ppc | tw 31, 0, 0 | trap // ppc | tw 31, 0, 0 | trap
// { 0x7f, 0xe0, 0x00, 0x08 } | big endian // { 0x7f, 0xe0, 0x00, 0x08 } | big endian
// { 0x08, 0x00, 0xe0, 0x7f } | little endian // { 0x08, 0x00, 0xe0, 0x7f } | little endian
rz_asm_op_set_buf(op, a->big_endian ? (const ut8 *)"\x7f\xe0\x00\x08" : (const ut8 *)"\x08\x00\xe0\x7f", 4); rz_asm_op_set_buf(breakpoint, a->big_endian ? (const ut8 *)"\x7f\xe0\x00\x08" : (const ut8 *)"\x08\x00\xe0\x7f", 4);
return true; return true;
} }

View file

@ -53,6 +53,29 @@ fin:
return op->size; return op->size;
} }
/*
* \brief Places a breakpoint instruction at addr depending on the size of the original instruction there
* The returned instruction bytes are either (in big endian hex notation):
* 0x00100073 ebreak, or
* 0x9002 c.ebreak
* \param a [in] The asm plugin.
* \param addr [in] The address to place the breakpoint.
* \param original [in] The original asm op at addr.
* \param breakpoint [out] The asm op to store the breakpoint instruction.
* \return true if the breakpoint was placed successfully, false otherwise.
*/
static bool riscv_sw_breakpoint(const RzAsm *a, ut64 addr, const RzAsmOp *original, RzAsmOp *breakpoint) {
if (original->size == 2) {
rz_asm_op_set_buf(breakpoint, a->big_endian ? (const ut8 *)"\x90\x02" : (const ut8 *)"\x02\x90", 2);
} else if (original->size == 4) {
rz_asm_op_set_buf(breakpoint, a->big_endian ? (const ut8 *)"\x00\x10\x00\x73" : (const ut8 *)"\x73\x00\x10\x00", 4);
} else {
RZ_LOG_ERROR("Can't set breakpoint at 0x%" PFMT64x " : bad size (%ld bytes) of the instruction there, RISC-V instructions are expected to either be 2 or 4 bytes\n", addr, original->buf.len);
return false;
}
return true;
}
RzAsmPlugin rz_asm_plugin_riscv_cs = { RzAsmPlugin rz_asm_plugin_riscv_cs = {
.name = "riscv", .name = "riscv",
.desc = "RISC-V Capstone-based disassembler", .desc = "RISC-V Capstone-based disassembler",
@ -66,6 +89,7 @@ RzAsmPlugin rz_asm_plugin_riscv_cs = {
.fini = riscv_asm_fini, .fini = riscv_asm_fini,
.disassemble = &riscv_disassemble, .disassemble = &riscv_disassemble,
.mnemonics = riscv_asm_mnemonics, .mnemonics = riscv_asm_mnemonics,
.sw_breakpoint = riscv_sw_breakpoint,
}; };
#ifndef RZ_PLUGIN_INCORE #ifndef RZ_PLUGIN_INCORE

View file

@ -36,10 +36,10 @@ static int assemble(const RzAsm *a, RzAsmOp *ao, const char *str) {
return 2; return 2;
} }
static bool sh_sw_breakpoint(const RzAsm *a, RzAsmOp *op) { static bool sh_sw_breakpoint(const RzAsm *a, ut64 addr, const RzAsmOp *original, RzAsmOp *breakpoint) {
// { 32, 2, 1, "\xc3\x20" }, // Big endian // { 32, 2, 1, "\xc3\x20" }, // Big endian
// { 32, 2, 0, "\x20\xc3" }, // Little endian // { 32, 2, 0, "\x20\xc3" }, // Little endian
rz_asm_op_set_buf(op, a->big_endian ? (const ut8 *)"\xc3\x20" : (const ut8 *)"\x20\xc3", 2); rz_asm_op_set_buf(breakpoint, a->big_endian ? (const ut8 *)"\xc3\x20" : (const ut8 *)"\x20\xc3", 2);
return true; return true;
} }

View file

@ -132,10 +132,10 @@ static int x86_disassemble(const RzAsm *a, RzAsmOp *op, const ut8 *buf, int len)
return op->size; return op->size;
} }
static bool x86_sw_breakpoint(const RzAsm *a, RzAsmOp *op) { static bool x86_sw_breakpoint(const RzAsm *a, ut64 addr, const RzAsmOp *original, RzAsmOp *breakpoint) {
// { 0, 1, 0, "\xcc" }, // valid for 16, 32, 64 // { 0, 1, 0, "\xcc" }, // valid for 16, 32, 64
// { 0, 2, 0, "\xcd\x03" }, // { 0, 2, 0, "\xcd\x03" },
rz_asm_op_set_buf(op, (const ut8 *)"\xcc", 1); rz_asm_op_set_buf(breakpoint, (const ut8 *)"\xcc", 1);
return true; return true;
} }

View file

@ -142,10 +142,10 @@ static int x86_zydis_disassemble(const RzAsm *a, RzAsmOp *op, const ut8 *buf, in
return op->size; return op->size;
} }
static bool x86_sw_breakpoint(const RzAsm *a, RzAsmOp *op) { static bool x86_sw_breakpoint(const RzAsm *a, ut64 addr, const RzAsmOp *original, RzAsmOp *breakpoint) {
// { 0, 1, 0, "\xcc" }, // valid for 16, 32, 64 // { 0, 1, 0, "\xcc" }, // valid for 16, 32, 64
// { 0, 2, 0, "\xcd\x03" }, // { 0, 2, 0, "\xcd\x03" },
rz_asm_op_set_buf(op, (const ut8 *)"\xcc", 1); rz_asm_op_set_buf(breakpoint, (const ut8 *)"\xcc", 1);
return true; return true;
} }

View file

@ -36,6 +36,11 @@
// Floating point register layout. // Floating point register layout.
#define ARCH_LEN (FP_LAYOUT | 0xf) #define ARCH_LEN (FP_LAYOUT | 0xf)
#define RISCV_32 13
#define RISCV_64 14
#define RISCV_32_FP (FP_LAYOUT | RISCV_32)
#define RISCV_64_FP (FP_LAYOUT | RISCV_64)
// See elf.c::elfcore_grok_solaris_note_impl() of binutil's bfd // See elf.c::elfcore_grok_solaris_note_impl() of binutil's bfd
// https://sourceware.org/git/?p=binutils-gdb.git;a=blob;f=bfd/elf.c;h=6ef603010918f14eda69f0d0dc1637b4d51e8157;hb=HEAD#l11777 // https://sourceware.org/git/?p=binutils-gdb.git;a=blob;f=bfd/elf.c;h=6ef603010918f14eda69f0d0dc1637b4d51e8157;hb=HEAD#l11777
// OR // OR
@ -136,6 +141,16 @@
#define MIPS_GPR32_STATUS_OFFSET (96) #define MIPS_GPR32_STATUS_OFFSET (96)
#define MIPS_GPR64_STATUS_OFFSET (112) #define MIPS_GPR64_STATUS_OFFSET (112)
// RISCV number of registers.
#define RISCV_32_REGS_SIZE (4 * 32)
#define RISCV_64_REGS_SIZE (8 * 32)
#define RISCV_32_REG_OFFSET (72)
#define RISCV_64_REG_OFFSET (112)
#define RISCV_FP32_REGS_SIZE (4 * 32) // F
#define RISCV_FP64_REGS_SIZE (8 * 32) // D
#define RISCV_32_REG_OFFSET_SP (8)
#define RISCV_64_REG_OFFSET_SP (16)
static RzBinElfPrStatusLayout prstatus_layouts[ARCH_LEN] = { static RzBinElfPrStatusLayout prstatus_layouts[ARCH_LEN] = {
[X86] = { 160, 0x48, 32, 0x3c }, [X86] = { 160, 0x48, 32, 0x3c },
[X86_64] = { 216, 0x70, 64, 0x98 }, [X86_64] = { 216, 0x70, 64, 0x98 },
@ -161,6 +176,9 @@ static RzBinElfPrStatusLayout prstatus_layouts[ARCH_LEN] = {
[MIPS_FP32] = { MIPS_FP32_REGS_SIZE, 4, 0, 0 }, [MIPS_FP32] = { MIPS_FP32_REGS_SIZE, 4, 0, 0 },
[MIPS_FP64] = { MIPS_FP64_REGS_SIZE, 8, 0, 0 }, [MIPS_FP64] = { MIPS_FP64_REGS_SIZE, 8, 0, 0 },
[RISCV_32] = { RISCV_32_REGS_SIZE, RISCV_32_REG_OFFSET, 32, RISCV_32_REG_OFFSET_SP },
[RISCV_64] = { RISCV_64_REGS_SIZE, RISCV_64_REG_OFFSET, 64, RISCV_64_REG_OFFSET_SP },
}; };
static bool parse_register_note(ELFOBJ *bin, RzVector /*<RzBinElfNote>*/ *notes, Elf_(Nhdr) * note_segment_header, ut64 offset, size_t n_type) { static bool parse_register_note(ELFOBJ *bin, RzVector /*<RzBinElfNote>*/ *notes, Elf_(Nhdr) * note_segment_header, ut64 offset, size_t n_type) {
@ -390,6 +408,8 @@ RZ_BORROW RzBinElfPrStatusLayout *Elf_(rz_bin_elf_get_prstatus_layout)(RZ_NONNUL
return prstatus_layouts + SPARC_V8PLUS; return prstatus_layouts + SPARC_V8PLUS;
case EM_SPARCV9: case EM_SPARCV9:
return prstatus_layouts + SPARC_V9; return prstatus_layouts + SPARC_V9;
case EM_RISCV:
return prstatus_layouts + ((bin->ehdr.e_ident[EI_CLASS] == ELFCLASS64) ? RISCV_64 : RISCV_32);
case EM_MIPS: case EM_MIPS:
/* fall-thru */ /* fall-thru */
case EM_MIPS_RS3_LE: case EM_MIPS_RS3_LE:

View file

@ -1523,17 +1523,24 @@ static void core_set_rz_asm_by_config(RzCore *core) {
static RzStrBuf *bp_get_sw_breakpoint_at(ut64 addr, void *user) { static RzStrBuf *bp_get_sw_breakpoint_at(ut64 addr, void *user) {
RzCore *core = (RzCore *)user; RzCore *core = (RzCore *)user;
ut8 bytes[16] = { 0 }; // worst-case is 15-byte instructions in x86
rz_io_read_at_mapped(core->io, addr, bytes, sizeof(bytes));
RzStrBuf *opcode = NULL; RzStrBuf *opcode = NULL;
RzAsmOp op = { 0 }; RzAsmOp op = { 0 };
RzAsmOp original = { 0 };
core_set_rz_asm_by_hint(core, addr); core_set_rz_asm_by_hint(core, addr);
rz_asm_op_init(&op); rz_asm_op_init(&op);
if (rz_asm_software_breakpoint(core->rasm, &op) && rz_asm_op_init(&original);
(void)rz_asm_disassemble(core->rasm, &original, bytes, sizeof(bytes));
if (rz_asm_software_breakpoint(core->rasm, addr, &original, &op) &&
(opcode = rz_strbuf_new(NULL))) { (opcode = rz_strbuf_new(NULL))) {
rz_strbuf_copy(opcode, &op.buf); rz_strbuf_copy(opcode, &op.buf);
} }
rz_asm_op_fini(&op); rz_asm_op_fini(&op);
rz_asm_op_fini(&original);
core_set_rz_asm_by_config(core); core_set_rz_asm_by_config(core);
return opcode; return opcode;
@ -1541,16 +1548,23 @@ static RzStrBuf *bp_get_sw_breakpoint_at(ut64 addr, void *user) {
static size_t bp_get_sw_breakpoint_size_at(ut64 addr, void *user) { static size_t bp_get_sw_breakpoint_size_at(ut64 addr, void *user) {
RzCore *core = (RzCore *)user; RzCore *core = (RzCore *)user;
ut8 bytes[16] = { 0 }; // worst-case is 15-byte instructions in x86
rz_io_read_at_mapped(core->io, addr, bytes, sizeof(bytes));
size_t length = 0; size_t length = 0;
RzAsmOp op = { 0 }; RzAsmOp op = { 0 };
RzAsmOp original = { 0 };
core_set_rz_asm_by_hint(core, addr); core_set_rz_asm_by_hint(core, addr);
rz_asm_op_init(&op); rz_asm_op_init(&op);
if (rz_asm_software_breakpoint(core->rasm, &op)) { rz_asm_op_init(&original);
(void)rz_asm_disassemble(core->rasm, &original, bytes, sizeof(bytes));
if (rz_asm_software_breakpoint(core->rasm, addr, &original, &op)) {
length = rz_strbuf_length(&op.buf); length = rz_strbuf_length(&op.buf);
} }
rz_asm_op_fini(&op); rz_asm_op_fini(&op);
rz_asm_op_fini(&original);
core_set_rz_asm_by_config(core); core_set_rz_asm_by_config(core);
return length; return length;

View file

@ -82,7 +82,7 @@ if has_debugger
rz_debug_sources += ['p/native/linux/linux_debug.c'] rz_debug_sources += ['p/native/linux/linux_debug.c']
endif endif
if host_machine.system() == 'linux' if host_machine.system() == 'linux'
if host_machine.cpu_family() == 'x86' or host_machine.cpu_family() == 'x86_64' or host_machine.cpu_family() == 'arm' or host_machine.cpu_family() == 'aarch64' if host_machine.cpu_family() == 'x86' or host_machine.cpu_family() == 'x86_64' or host_machine.cpu_family() == 'arm' or host_machine.cpu_family() == 'aarch64' or host_machine.cpu_family() == 'riscv64' or host_machine.cpu_family() == 'riscv32'
rz_debug_sources += ['p/native/linux/linux_coredump.c'] rz_debug_sources += ['p/native/linux/linux_coredump.c']
endif endif
endif endif
@ -101,6 +101,10 @@ if has_debugger
rz_debug_sources += ['p/native/linux_mips32.c'] rz_debug_sources += ['p/native/linux_mips32.c']
elif host_machine.cpu_family() == 'mips64' elif host_machine.cpu_family() == 'mips64'
rz_debug_sources += ['p/native/linux_mips64.c'] rz_debug_sources += ['p/native/linux_mips64.c']
elif host_machine.cpu_family() == 'riscv64'
rz_debug_sources += ['p/native/linux_riscv64.c']
elif host_machine.cpu_family() == 'riscv32'
rz_debug_sources += ['p/native/linux_riscv32.c']
else else
rz_debug_sources += ['p/native/linux_other.c'] rz_debug_sources += ['p/native/linux_other.c']
endif endif

View file

@ -12,6 +12,24 @@
typedef RzList *(*RzDebugFrameCallback)(RzDebug *dbg, ut64 at); typedef RzList *(*RzDebugFrameCallback)(RzDebug *dbg, ut64 at);
static void prepend_link_register(RzDebug *dbg, RzList /*<RzDebugFrame *>*/ *list) {
bool is_riscv = dbg->arch && !strcmp(dbg->arch, "riscv");
if (!is_riscv)
return;
RzDebugFrame *frame;
const char *pcname;
if (list) {
pcname = rz_reg_get_name(dbg->reg, RZ_REG_NAME_LR);
if (pcname) {
ut64 addr = rz_reg_getv(dbg->reg, pcname);
frame = RZ_NEW0(RzDebugFrame);
frame->addr = addr;
frame->size = 0;
rz_list_prepend(list, frame);
}
}
}
static void prepend_current_pc(RzDebug *dbg, RzList /*<RzDebugFrame *>*/ *list) { static void prepend_current_pc(RzDebug *dbg, RzList /*<RzDebugFrame *>*/ *list) {
RzDebugFrame *frame; RzDebugFrame *frame;
const char *pcname; const char *pcname;
@ -84,7 +102,7 @@ static RzList /*<RzDebugFrame *>*/ *rz_debug_native_frames(RzDebug *dbg, ut64 at
list = cb(dbg, at); list = cb(dbg, at);
#endif #endif
} }
prepend_link_register(dbg, list);
prepend_current_pc(dbg, list); prepend_current_pc(dbg, list);
return list; return list;
} }

View file

@ -27,6 +27,24 @@ static int iscallret(RzDebug *dbg, ut64 addr) {
return 1; return 1;
} }
// IMMAMISSINGANYOP // IMMAMISSINGANYOP
} else if (dbg->arch && !strcmp(dbg->arch, "riscv")) {
RzAnalysisOp op = { 0 };
(void)dbg->iob.read_at(dbg->iob.io, addr - 4, buf, 4);
rz_analysis_op_init(&op);
(void)rz_analysis_op(dbg->analysis, &op, addr - 4, buf, 4, RZ_ANALYSIS_OP_MASK_ALL);
// is the operation precedeing the address a call ?
if (op.type == RZ_ANALYSIS_OP_TYPE_CALL || op.type == RZ_ANALYSIS_OP_TYPE_UCALL) {
bool target_known = op.jump != 0 && op.jump != UT64_MAX;
// The address is assumed to be a true return address when one of the following is true
// 1. The target of the call is known and is a valid function
// 2. The target of the call is unknown (register-indirect call or similar)
if (!target_known || rz_analysis_get_function_at(dbg->analysis, op.jump)) {
rz_analysis_op_fini(&op);
return 1;
}
}
rz_analysis_op_fini(&op);
} else { } else {
RzAnalysisOp op = { 0 }; RzAnalysisOp op = { 0 };
(void)dbg->iob.read_at(dbg->iob.io, addr - 8, buf, 8); (void)dbg->iob.read_at(dbg->iob.io, addr - 8, buf, 8);

View file

@ -10,14 +10,14 @@
#include "linux_ptrace.h" #include "linux_ptrace.h"
/* For compatibility */ /* For compatibility */
#if __x86_64__ || __aarch64__ #if __x86_64__ || __aarch64__ || (__riscv && __riscv_xlen == 64)
typedef Elf64_auxv_t elf_auxv_t; typedef Elf64_auxv_t elf_auxv_t;
typedef Elf64_Ehdr elf_hdr_t; typedef Elf64_Ehdr elf_hdr_t;
typedef Elf64_Phdr elf_phdr_t; typedef Elf64_Phdr elf_phdr_t;
typedef Elf64_Shdr elf_shdr_t; typedef Elf64_Shdr elf_shdr_t;
typedef Elf64_Nhdr elf_nhdr_t; typedef Elf64_Nhdr elf_nhdr_t;
typedef ut32 elf_offset_t; typedef ut32 elf_offset_t;
#elif __i386__ || __arm__ #elif __i386__ || __arm__ || (__riscv && __riscv_xlen == 32)
typedef Elf32_auxv_t elf_auxv_t; typedef Elf32_auxv_t elf_auxv_t;
typedef Elf32_Ehdr elf_hdr_t; typedef Elf32_Ehdr elf_hdr_t;
typedef Elf32_Phdr elf_phdr_t; typedef Elf32_Phdr elf_phdr_t;
@ -225,6 +225,14 @@ static prstatus_t *linux_get_prstatus(RzDebug *dbg, int pid, int tid, proc_conte
#if __aarch64__ #if __aarch64__
if (rz_debug_ptrace(dbg, PTRACE_GETREGSET, tid, (void *)NT_PRSTATUS, &regs) < 0) { if (rz_debug_ptrace(dbg, PTRACE_GETREGSET, tid, (void *)NT_PRSTATUS, &regs) < 0) {
perror("PTRACE_GETREGSET & NT_PRSTATUS"); perror("PTRACE_GETREGSET & NT_PRSTATUS");
#elif __riscv
memset(&regs, 0, sizeof(regs));
struct iovec io = {
.iov_base = &regs,
.iov_len = sizeof(regs),
};
if (rz_debug_ptrace(dbg, PTRACE_GETREGSET, pid, (void *)(size_t)NT_PRSTATUS, &io) < 0) {
perror("PTRACE_GETREGSET & NT_PRSTATUS");
#else #else
if (rz_debug_ptrace(dbg, PTRACE_GETREGS, tid, NULL, &regs) < 0) { if (rz_debug_ptrace(dbg, PTRACE_GETREGS, tid, NULL, &regs) < 0) {
perror("PTRACE_GETREGS"); perror("PTRACE_GETREGS");
@ -243,7 +251,7 @@ static elf_fpregset_t *linux_get_fp_regset(RzDebug *dbg, int pid) {
if (!p) { if (!p) {
return NULL; return NULL;
} }
#if __aarch64__ #if __aarch64__ || __riscv
if (rz_debug_ptrace(dbg, PTRACE_GETREGSET, pid, (void *)NT_PRFPREG, p) < 0) { if (rz_debug_ptrace(dbg, PTRACE_GETREGSET, pid, (void *)NT_PRFPREG, p) < 0) {
perror("PTRACE_GETREGSET & NT_PRFREG"); perror("PTRACE_GETREGSET & NT_PRFREG");
#else #else
@ -611,9 +619,9 @@ static elf_hdr_t *build_elf_hdr(int n_segments) {
h->e_ident[EI_MAG1] = ELFMAG1; h->e_ident[EI_MAG1] = ELFMAG1;
h->e_ident[EI_MAG2] = ELFMAG2; h->e_ident[EI_MAG2] = ELFMAG2;
h->e_ident[EI_MAG3] = ELFMAG3; h->e_ident[EI_MAG3] = ELFMAG3;
#if __x86_64__ || __aarch64__ #if __x86_64__ || __aarch64__ || (__riscv && __riscv_xlen == 64)
h->e_ident[EI_CLASS] = ELFCLASS64; /*64bits */ h->e_ident[EI_CLASS] = ELFCLASS64; /*64bits */
#elif __i386__ || __arm__ #elif __i386__ || __arm__ || (__riscv && __riscv_xlen == 32)
h->e_ident[EI_CLASS] = ELFCLASS32; h->e_ident[EI_CLASS] = ELFCLASS32;
#endif #endif
h->e_ident[EI_DATA] = ELFDATA2LSB; h->e_ident[EI_DATA] = ELFDATA2LSB;
@ -633,6 +641,8 @@ static elf_hdr_t *build_elf_hdr(int n_segments) {
h->e_machine = EM_ARM; h->e_machine = EM_ARM;
#elif __aarch64__ #elif __aarch64__
h->e_machine = EM_AARCH64; h->e_machine = EM_AARCH64;
#elif __riscv
h->e_machine = EM_RISCV;
#endif #endif
h->e_version = EV_CURRENT; h->e_version = EV_CURRENT;
h->e_entry = 0x0; h->e_entry = 0x0;

View file

@ -203,7 +203,7 @@ static void linux_remove_fork_bps(RzDebug *dbg) {
*/ */
RzDebugReasonType linux_ptrace_event(RzDebug *dbg, int ptid, int status, bool dowait) { RzDebugReasonType linux_ptrace_event(RzDebug *dbg, int ptid, int status, bool dowait) {
ut32 pt_evt; ut32 pt_evt;
#if __powerpc64__ || __arm64__ || __aarch64__ || __x86_64__ #if __powerpc64__ || __arm64__ || __aarch64__ || __x86_64__ || (__riscv && __riscv_xlen == 64)
ut64 data; ut64 data;
#else #else
ut32 data; ut32 data;

View file

@ -135,9 +135,11 @@ struct powerpc_regs_t {
#include <sys/ucontext.h> #include <sys/ucontext.h>
#include <asm/ptrace.h> #include <asm/ptrace.h>
// typedef ut64 riscv64_regs_t [65]; #define RZ_DEBUG_REG_T struct user_regs_struct
// #define RZ_DEBUG_REG_T riscv64_regs_t #define RZ_DEBUG_FPREG32_T __riscv_f_ext_state
#define RZ_DEBUG_REG_T struct user_regs_struct #define RZ_DEBUG_FPREG64_T __riscv_d_ext_state
#define RZ_DEBUG_FPREG128_T __riscv_q_ext_state
#define RZ_DEBUG_VREG_T __riscv_v_regset_state
#elif __loongarch64 #elif __loongarch64
#include <sys/ucontext.h> #include <sys/ucontext.h>

View file

@ -0,0 +1,123 @@
// SPDX-FileCopyrightText: 2024-2026 mostafa <ubermenchun@gmail.com>
// SPDX-License-Identifier: LGPL-3.0-only
#include "rz_util/rz_log.h"
#include <sys/ptrace.h>
#include <sys/uio.h>
#include "linux/linux_debug.h"
#ifndef NT_PRSTATUS
#define NT_PRSTATUS 1
#endif
#include "linux_riscv_common.c"
#include "bt.c"
static char *rz_debug_native_reg_profile(RzDebug *dbg) {
#include "reg/linux-riscv32.h"
}
static int rz_debug_native_reg_read(RzDebug *dbg, int type, ut8 *buf, int size) {
if (size < 1) {
return false;
}
int pid = dbg->tid;
switch (type) {
case RZ_REG_TYPE_DRX:
case RZ_REG_TYPE_FPU:
case RZ_REG_TYPE_MMX:
case RZ_REG_TYPE_XMM:
case RZ_REG_TYPE_YMM:
RZ_LOG_ERROR("Unsupported register type on this platform, type: %d\n", type);
return false;
case RZ_REG_TYPE_SEG:
case RZ_REG_TYPE_FLG:
case RZ_REG_TYPE_GPR: {
RZ_DEBUG_REG_T regs;
memset(&regs, 0, sizeof(regs));
memset(buf, 0, size);
struct iovec io = {
.iov_base = &regs,
.iov_len = sizeof(regs),
};
int ret = rz_debug_ptrace(dbg, PTRACE_GETREGSET, pid, (void *)(size_t)NT_PRSTATUS, &io);
if (ret != 0) {
rz_sys_perror("PTRACE_GETREGSET");
return false;
}
size = RZ_MIN(sizeof(regs), size);
memcpy(buf, &regs, size);
return size;
} break;
}
return false;
}
static int rz_debug_native_reg_write(RzDebug *dbg, int type, const ut8 *buf, int size) {
int pid = dbg->tid;
switch (type) {
case RZ_REG_TYPE_DRX:
case RZ_REG_TYPE_FPU:
RZ_LOG_ERROR("Unsupported register type on this platform, type: %d\n", type);
return false;
case RZ_REG_TYPE_GPR: {
struct iovec io = {
.iov_base = (void *)buf,
.iov_len = sizeof(RZ_DEBUG_REG_T),
};
int ret = rz_debug_ptrace(dbg, PTRACE_SETREGSET, pid, (void *)(size_t)NT_PRSTATUS, (rz_ptrace_data_t)(size_t)&io);
if (ret == -1) {
rz_sys_perror("PTRACE_SETREGSET");
return false;
}
return true;
}
default:
RZ_LOG_DEBUG("TODO: reg_write_non-gpr (%d)\n", type);
return false;
}
return false;
}
static int rz_debug_native_bp(RzBreakpoint *bp, RzBreakpointItem *b, bool set) {
return false;
}
static bool rz_debug_gcore(RzDebug *dbg, char *path, RzBuffer *dest) {
RZ_LOG_ERROR("gcore: unsupported on this platform\n");
return false;
}
RzDebugPlugin rz_debug_plugin_native = {
.name = "native",
.license = "LGPL3",
.arch = "riscv",
.bits = RZ_SYS_BITS_32,
.canstep = 0,
.init = &rz_debug_native_init,
.fini = &rz_debug_native_fini,
.step = &rz_debug_native_step,
.cont = &rz_debug_native_continue,
.stop = &rz_debug_native_stop,
.contsc = &rz_debug_native_continue_syscall,
.attach = &rz_debug_native_attach,
.detach = &rz_debug_native_detach,
.select = &rz_debug_native_select,
.pids = &rz_debug_native_pids,
.threads = &rz_debug_native_threads,
.wait = &rz_debug_native_wait,
.kill = &rz_debug_native_kill,
.frames = &rz_debug_native_frames,
.reg_profile = rz_debug_native_reg_profile,
.reg_read = rz_debug_native_reg_read,
.info = rz_debug_native_info,
.reg_write = (void *)&rz_debug_native_reg_write,
.map_alloc = rz_debug_native_map_alloc,
.map_dealloc = rz_debug_native_map_dealloc,
.map_get = rz_debug_native_map_get,
.modules_get = rz_debug_native_modules_get,
.map_protect = rz_debug_native_map_protect,
.breakpoint = rz_debug_native_bp,
.drx = rz_debug_native_drx,
.gcore = rz_debug_gcore,
};

View file

@ -0,0 +1,125 @@
// SPDX-FileCopyrightText: 2024-2026 mostafa <ubermenchun@gmail.com>
// SPDX-License-Identifier: LGPL-3.0-only
#include "rz_util/rz_log.h"
#include <sys/ptrace.h>
#include <sys/uio.h>
#include "linux/linux_debug.h"
#include "linux/linux_coredump.h"
#ifndef NT_PRSTATUS
#define NT_PRSTATUS 1
#endif
#include "linux_riscv_common.c"
#include "bt.c"
static char *rz_debug_native_reg_profile(RzDebug *dbg) {
#include "reg/linux-riscv64.h"
}
static int rz_debug_native_reg_read(RzDebug *dbg, int type, ut8 *buf, int size) {
if (size < 1) {
return false;
}
int pid = dbg->tid;
switch (type) {
case RZ_REG_TYPE_DRX:
case RZ_REG_TYPE_FPU:
case RZ_REG_TYPE_MMX:
case RZ_REG_TYPE_XMM:
case RZ_REG_TYPE_YMM:
RZ_LOG_ERROR("Unsupported register type on this platform, type: %d\n", type);
return false;
case RZ_REG_TYPE_SEG:
case RZ_REG_TYPE_FLG:
case RZ_REG_TYPE_GPR: {
RZ_DEBUG_REG_T regs;
memset(&regs, 0, sizeof(regs));
memset(buf, 0, size);
struct iovec io = {
.iov_base = &regs,
.iov_len = sizeof(regs),
};
int ret = rz_debug_ptrace(dbg, PTRACE_GETREGSET, pid, (void *)(size_t)NT_PRSTATUS, &io);
if (ret != 0) {
rz_sys_perror("PTRACE_GETREGSET");
return false;
}
size = RZ_MIN(sizeof(regs), size);
memcpy(buf, &regs, size);
return size;
} break;
}
return false;
}
static int rz_debug_native_reg_write(RzDebug *dbg, int type, const ut8 *buf, int size) {
int pid = dbg->tid;
switch (type) {
case RZ_REG_TYPE_DRX:
case RZ_REG_TYPE_FPU:
RZ_LOG_ERROR("Unsupported register type on this platform, type: %d\n", type);
return false;
case RZ_REG_TYPE_GPR: {
struct iovec io = {
.iov_base = (void *)buf,
.iov_len = sizeof(RZ_DEBUG_REG_T),
};
int ret = rz_debug_ptrace(dbg, PTRACE_SETREGSET, pid, (void *)(size_t)NT_PRSTATUS, (rz_ptrace_data_t)(size_t)&io);
if (ret == -1) {
rz_sys_perror("PTRACE_SETREGSET");
return false;
}
return true;
}
default:
RZ_LOG_DEBUG("TODO: reg_write_non-gpr (%d)\n", type);
return false;
}
return false;
}
static int rz_debug_native_bp(RzBreakpoint *bp, RzBreakpointItem *b, bool set) {
return false;
}
static bool rz_debug_gcore(RzDebug *dbg, char *path, RzBuffer *dest) {
(void)path;
return linux_generate_corefile(dbg, dest);
}
RzDebugPlugin rz_debug_plugin_native = {
.name = "native",
.license = "LGPL3",
.arch = "riscv",
.bits = RZ_SYS_BITS_64,
.canstep = 0,
.init = &rz_debug_native_init,
.fini = &rz_debug_native_fini,
.step = &rz_debug_native_step,
.cont = &rz_debug_native_continue,
.stop = &rz_debug_native_stop,
.contsc = &rz_debug_native_continue_syscall,
.attach = &rz_debug_native_attach,
.detach = &rz_debug_native_detach,
.select = &rz_debug_native_select,
.pids = &rz_debug_native_pids,
.threads = &rz_debug_native_threads,
.wait = &rz_debug_native_wait,
.kill = &rz_debug_native_kill,
.frames = &rz_debug_native_frames,
.reg_profile = rz_debug_native_reg_profile,
.reg_read = rz_debug_native_reg_read,
.info = rz_debug_native_info,
.reg_write = (void *)&rz_debug_native_reg_write,
.map_alloc = rz_debug_native_map_alloc,
.map_dealloc = rz_debug_native_map_dealloc,
.map_get = rz_debug_native_map_get,
.modules_get = rz_debug_native_modules_get,
.map_protect = rz_debug_native_map_protect,
.breakpoint = rz_debug_native_bp,
.drx = rz_debug_native_drx,
.gcore = rz_debug_gcore,
};

View file

@ -0,0 +1,222 @@
// SPDX-FileCopyrightText: 2024-2026 moste00 <ubermenchun@gmail.com>
// SPDX-License-Identifier: BSD-3-Clause
#include "linux/linux_debug.h"
/*
* \file Contains common implementations of most functions in a native debugger plugin
* Those functions are mostly duplicates for even different archiectures like ARM and x86
* (For now however, this file only de-duplicates the 2 RISC-V implementations linux_riscv32.c and linux_riscv64.c)
*/
/* ========================= DO NOT EVER COMPILE THIS FILE =========================*/
/* ========================= ONLY #include IT IN ANOTHER FILE =========================*/
static bool rz_debug_native_step(RzDebug *dbg) {
return linux_step(dbg);
}
static int rz_debug_native_attach(RzDebug *dbg, int pid) {
return linux_attach(dbg, pid);
}
static int rz_debug_native_detach(RzDebug *dbg, int pid) {
return rz_debug_ptrace(dbg, PTRACE_DETACH, pid, NULL, (rz_ptrace_data_t)(size_t)0);
}
static int rz_debug_native_select(RzDebug *dbg, int pid, int tid) {
return linux_select(dbg, pid, tid);
}
static int rz_debug_native_continue_syscall(RzDebug *dbg, int pid, int num) {
linux_set_options(dbg, pid);
return rz_debug_ptrace(dbg, PTRACE_SYSCALL, pid, 0, 0);
}
static void interrupt_process(RzDebug *dbg) {
rz_debug_kill(dbg, dbg->pid, dbg->tid, SIGINT);
rz_cons_break_pop();
}
static int rz_debug_native_stop(RzDebug *dbg) {
return linux_stop_threads(dbg, dbg->reason.tid);
}
static int rz_debug_native_continue(RzDebug *dbg, int pid, int tid, int sig) {
int contsig = dbg->reason.signum;
int ret = -1;
if (sig != -1) {
contsig = sig;
}
/* SIGINT handler for attached processes: dbg.consbreak (disabled by default) */
if (dbg->consbreak) {
rz_cons_break_push((RzConsBreak)interrupt_process, dbg);
}
if (dbg->continue_all_threads && dbg->n_threads && dbg->threads) {
RzDebugPid *th;
RzListIter *it;
rz_list_foreach (dbg->threads, it, th) {
ret = rz_debug_ptrace(dbg, PTRACE_CONT, th->pid, 0, 0);
if (ret) {
RZ_LOG_ERROR("(%d) is running or dead.\n", th->pid);
}
}
} else {
ret = rz_debug_ptrace(dbg, PTRACE_CONT, tid, NULL, (rz_ptrace_data_t)(size_t)contsig);
if (ret) {
rz_sys_perror("PTRACE_CONT");
}
}
// return ret >= 0 ? tid : false;
return tid;
}
static RzDebugInfo *rz_debug_native_info(RzDebug *dbg, const char *arg) {
return linux_info(dbg, arg);
}
static RzDebugReasonType rz_debug_native_wait(RzDebug *dbg, int pid) {
RzDebugReasonType reason = RZ_DEBUG_REASON_UNKNOWN;
if (pid == -1) {
RZ_LOG_ERROR("rz_debug_native_wait called with pid -1\n");
return RZ_DEBUG_REASON_ERROR;
}
reason = linux_dbg_wait(dbg, dbg->tid);
dbg->reason.type = reason;
return reason;
}
static RzList /*<RzDebugPid *>*/ *rz_debug_native_pids(RzDebug *dbg, int pid) {
RzList *list = rz_list_new();
if (!list) {
return NULL;
}
return linux_pid_list(pid, list);
}
RZ_API RZ_OWN RzList /*<RzDebugPid *>*/ *rz_debug_native_threads(RzDebug *dbg, int pid) {
RzList *list = rz_list_new();
if (!list) {
RZ_LOG_ERROR("Cannot create list\n");
return NULL;
}
return linux_thread_list(dbg, pid, list);
}
RZ_API ut64 rz_debug_get_tls(RZ_NONNULL RzDebug *dbg, int tid) {
rz_return_val_if_fail(dbg, 0);
return get_linux_tls_val(dbg, tid);
}
static RzDebugMap *rz_debug_native_map_alloc(RzDebug *dbg, ut64 addr, int size, bool thp) {
return linux_map_alloc(dbg, addr, size, thp);
}
static int rz_debug_native_map_dealloc(RzDebug *dbg, ut64 addr, int size) {
return linux_map_dealloc(dbg, addr, size);
}
static RzList /*<RzDebugMap *>*/ *rz_debug_native_map_get(RzDebug *dbg) {
if (dbg->pid == -1) {
return NULL;
}
RzList *map_list = linux_map_get(dbg);
if (!map_list) {
RZ_LOG_ERROR("Cannot create process map list.\n");
return NULL;
}
return map_list;
}
static RzList /*<RzDebugMap *>*/ *rz_debug_native_modules_get(RzDebug *dbg) {
char *lastname = NULL;
RzDebugMap *map;
RzListIter *iter, *iter2;
RzList *list, *last;
bool must_delete;
if (!(list = rz_debug_native_map_get(dbg))) {
return NULL;
}
if (!(last = rz_list_newf((RzListFree)rz_debug_map_free))) {
rz_list_free(list);
return NULL;
}
rz_list_foreach_safe (list, iter, iter2, map) {
const char *file = map->file;
if (!map->file) {
file = map->file = rz_str_dup(map->name);
}
must_delete = true;
if (file && *file == '/') {
if (!lastname || strcmp(lastname, file)) {
must_delete = false;
}
}
if (must_delete) {
rz_list_delete(list, iter);
} else {
rz_list_append(last, map);
free(lastname);
lastname = rz_str_dup(file);
}
}
list->free = NULL;
free(lastname);
rz_list_free(list);
return last;
}
static bool rz_debug_native_kill(RzDebug *dbg, int pid, int tid, int sig) {
bool ret = false;
if (pid == 0) {
pid = dbg->pid;
}
if (sig == SIGKILL && dbg->threads) {
rz_list_free(dbg->threads);
dbg->threads = NULL;
}
if ((rz_sys_kill(pid, sig) != -1)) {
ret = true;
}
if (errno == 1) {
ret = -true; // EPERM
}
return ret;
}
static int rz_debug_native_drx(RzDebug *dbg, int n, ut64 addr, int sz, int rwx, int g, int api_type) {
RZ_LOG_ERROR("drx: Unsupported platform\n");
return -1;
}
static RzList /*<RzDebugDesc *>*/ *rz_debug_desc_native_list(int pid) {
return linux_desc_list(pid);
}
static int rz_debug_native_map_protect(RzDebug *dbg, ut64 addr, int size, int perms) {
return linux_map_protect(dbg, addr, size, perms);
}
static int rz_debug_desc_native_open(const char *path) {
return 0;
}
struct rz_debug_desc_plugin_t rz_debug_desc_plugin_native = {
.open = rz_debug_desc_native_open,
.list = rz_debug_desc_native_list,
};
static bool rz_debug_native_init(RzDebug *dbg, void **user) {
dbg->cur->desc = rz_debug_desc_plugin_native;
return true;
}
static void rz_debug_native_fini(RzDebug *dbg, void *user) {
if (!user) {
return;
}
free(user);
}

View file

@ -0,0 +1,51 @@
// SPDX-FileCopyrightText: 2024-2026 mostafa <ubermenchun@gmail.com>
// SPDX-License-Identifier: BSD-3-Clause
return rz_str_dup(
"=PC pc\n"
"=LR x1\n"
"=SP x2\n"
"=BP x8\n" // s0/fp (frame pointer)
"=R0 x10\n" // a0
"=R1 x11\n" // a1
"=A0 x10\n"
"=A1 x11\n"
"=A2 x12\n"
"=A3 x13\n"
"=A4 x14\n"
"=A5 x15\n"
"=A6 x16\n"
"=A7 x17\n"
"gpr pc .32 0 0\n"
"gpr x1 .32 4 0\n" // ra
"gpr x2 .32 8 0\n" // sp
"gpr x3 .32 12 0\n" // gp
"gpr x4 .32 16 0\n" // tp
"gpr x5 .32 20 0\n" // t0
"gpr x6 .32 24 0\n" // t1
"gpr x7 .32 28 0\n" // t2
"gpr x8 .32 32 0\n" // s0 / fp
"gpr x9 .32 36 0\n" // s1
"gpr x10 .32 40 0\n" // a0
"gpr x11 .32 44 0\n" // a1
"gpr x12 .32 48 0\n" // a2
"gpr x13 .32 52 0\n" // a3
"gpr x14 .32 56 0\n" // a4
"gpr x15 .32 60 0\n" // a5
"gpr x16 .32 64 0\n" // a6
"gpr x17 .32 68 0\n" // a7
"gpr x18 .32 72 0\n" // s2
"gpr x19 .32 76 0\n" // s3
"gpr x20 .32 80 0\n" // s4
"gpr x21 .32 84 0\n" // s5
"gpr x22 .32 88 0\n" // s6
"gpr x23 .32 92 0\n" // s7
"gpr x24 .32 96 0\n" // s8
"gpr x25 .32 100 0\n" // s9
"gpr x26 .32 104 0\n" // s10
"gpr x27 .32 108 0\n" // s11
"gpr x28 .32 112 0\n" // t3
"gpr x29 .32 116 0\n" // t4
"gpr x30 .32 120 0\n" // t5
"gpr x31 .32 124 0\n" // t6
);

View file

@ -1,18 +1,23 @@
// SPDX-FileCopyrightText: 2024-2026 mostafa <ubermenchun@gmail.com>
// SPDX-FileCopyrightText: 2019 pancake <pancake@nopcode.org> // SPDX-FileCopyrightText: 2019 pancake <pancake@nopcode.org>
// SPDX-License-Identifier: LGPL-3.0-only // SPDX-License-Identifier: LGPL-3.0-only
return rz_str_dup( return rz_str_dup(
"=PC pc\n" "=PC pc\n"
"=LR x1\n"
"=SP x2\n" "=SP x2\n"
"=BP x4\n" "=BP x4\n"
"=R0 a10\n" "=R0 x10\n"
"=R1 a11\n" "=R1 x11\n"
"=A0 a10\n" "=A0 x10\n"
"=A1 a11\n" "=A1 x11\n"
"=A2 a12\n" "=A2 x12\n"
"=A3 a13\n" "=A3 x13\n"
"=A4 a14\n" "=A4 x14\n"
"gpr x0 .64 ? 0\n" // always zero "=A5 x15\n"
"=A6 x16\n"
"=A7 x17\n"
"gpr pc .64 0 0\n"
"gpr x1 .64 8 0\n" // RA - return address "gpr x1 .64 8 0\n" // RA - return address
"gpr x2 .64 16 0\n" // SP stack pointer "gpr x2 .64 16 0\n" // SP stack pointer
"gpr x3 .64 24 0\n" // GP global pointer "gpr x3 .64 24 0\n" // GP global pointer

View file

@ -112,7 +112,7 @@ typedef struct rz_asm_plugin_t {
const char *features; const char *features;
const char *platforms; const char *platforms;
char **(*get_cpu_desc)(); char **(*get_cpu_desc)();
bool (*sw_breakpoint)(const RzAsm *a, RzAsmOp *op); bool (*sw_breakpoint)(const RzAsm *a, ut64 addr, const RzAsmOp *original, RzAsmOp *breakpoint);
} RzAsmPlugin; } RzAsmPlugin;
/** /**
@ -190,7 +190,7 @@ RZ_API void rz_asm_set_pc(RZ_NONNULL RzAsm *a, ut64 pc);
RZ_API ut64 rz_asm_get_pc(RZ_NONNULL const RzAsm *a); RZ_API ut64 rz_asm_get_pc(RZ_NONNULL const RzAsm *a);
RZ_API int rz_asm_disassemble(RzAsm *a, RzAsmOp *op, const ut8 *buf, int len); RZ_API int rz_asm_disassemble(RzAsm *a, RzAsmOp *op, const ut8 *buf, int len);
RZ_API int rz_asm_assemble(const RzAsm *a, RzAsmOp *op, const char *buf); RZ_API int rz_asm_assemble(const RzAsm *a, RzAsmOp *op, const char *buf);
RZ_API bool rz_asm_software_breakpoint(RZ_NONNULL const RzAsm *a, RZ_NONNULL RzAsmOp *op); RZ_API bool rz_asm_software_breakpoint(RZ_NONNULL const RzAsm *a, ut64 addr, const RZ_NONNULL RzAsmOp *original, RZ_NONNULL RzAsmOp *breakpoint);
RZ_API RzAsmCode *rz_asm_mdisassemble(RzAsm *a, const ut8 *buf, int len); RZ_API RzAsmCode *rz_asm_mdisassemble(RzAsm *a, const ut8 *buf, int len);
RZ_API RzAsmCode *rz_asm_mdisassemble_hexstr(RzAsm *a, RzParse *p, const char *hexstr); RZ_API RzAsmCode *rz_asm_mdisassemble_hexstr(RzAsm *a, RzParse *p, const char *hexstr);
RZ_API RzAsmCode *rz_asm_massemble(RzAsm *a, const char *buf); RZ_API RzAsmCode *rz_asm_massemble(RzAsm *a, const char *buf);

View file

@ -0,0 +1,205 @@
NAME=Setting breakpoints on symbols works
FILE=bins/elf/riscv_bitmanip_32
ARGS=-d
CMDS=<<EOF
aaa
db @ sym.imp.printf
dbl
dc
dr PC; pdf @ sym.imp.printf
EOF
EXPECT=<<EOF
start end size perm hwsw type state valid cmd cond name module
------------------------------------------------------------------------------------------------------------------
0x75546400 0x75546404 4 --x sw break enabled valid sym.imp.printf /test/bins/elf/riscv_bitmanip_32
pc = 0x75546400
; XREFS(21)
;-- pc:
/ sym.imp.printf();
| 0x75546400 b auipc t3, 3
| 0x75546404 lw t3, -0x42c(t3)
\ 0x75546408 jalr t1, t3
EOF
RUN
NAME=Disabling breakpoints works
FILE=bins/elf/riscv_bitmanip_32
ARGS=-d
CMDS=<<EOF
aaa
db @ sym.imp.printf
dbl
dc
dr PC; pdf @ sym.imp.printf
ds
dbd @ sym.imp.printf
dbl
dc
EOF
EXPECT=<<EOF
start end size perm hwsw type state valid cmd cond name module
------------------------------------------------------------------------------------------------------------------
0x85912400 0x85912404 4 --x sw break enabled valid sym.imp.printf /test/bins/elf/riscv_bitmanip_32
pc = 0x85912400
; XREFS(21)
;-- pc:
/ sym.imp.printf();
| 0x85912400 b auipc t3, 3
| 0x85912404 lw t3, -0x42c(t3)
\ 0x85912408 jalr t1, t3
start end size perm hwsw type state valid cmd cond name module
-------------------------------------------------------------------------------------------------------------------
0x85912400 0x85912404 4 --x sw break disabled valid sym.imp.printf /test/bins/elf/riscv_bitmanip_32
32-bit rotate left 8: 0x34567812
32-bit rotate right 12: 0x67812345
32-bit rotate left var: 0x1A2B3C09
32-bit rotate right var: 0xCF02468A
64-bit rotate left: 0x8ACF13579BDE0246
64-bit rotate right: 0xE6F78091A2B3C4D5
Set bit 5: 0x12345678
Set bit var: 0x12345678
Clear bit 7: 0x12345678
Clear bit var: 0x12344678
Toggle bit 3: 0x12345670
Toggle bit var: 0x1234D678
Extract bit 9: 1
Extract bit var: 1
Swap endian 32: 0x78563412
Swap endian 64: 0xF0DEBC9A78563412
Network to host: 0xDDCCBBAA
Hash rotate: 0x5462BCA9
Device flags after enable: 0x00001020
Is flag 5 set? 1
Is flag 7 set? 0
Device flags after disable: 0x00001000
EOF
RUN
NAME=Stacktraces work
FILE=bins/elf/riscv_bitmanip_32
ARGS=-d
CMDS=<<EOF
aaa
db @ sym.imp.printf
dc
dr PC; pdf @ sym.imp.printf
dbt~[0,1,4,5,6]
EOF
EXPECT=<<EOF
pc = 0x86ae5400
; XREFS(21)
;-- pc:
/ sym.imp.printf();
| 0x86ae5400 b auipc t3, 3
| 0x86ae5404 lw t3, -0x42c(t3)
\ 0x86ae5408 jalr t1, t3
0 0x86ae5400 0 [sym.imp.printf] sym.imp.printf
1 0x86ae596c 0 [sym.demonstrate_all_operations] sym.demonstrate_all_operations+28
2 0x86ae542c 12 [main] main+12
EOF
RUN
NAME=Single-stepping into function calls
FILE=bins/elf/riscv_bitmanip_32
ARGS=-d
CMDS=<<EOF
aaa
db @ sym.demonstrate_all_operations+24
dc
dr PC; pd 3 @ sym.demonstrate_all_operations+24
ds
dr PC; pdf
dc
EOF
EXPECT=<<EOF
\pc = 0x86414968
| ;-- pc:
| 0x86414968 b jal sym.imp.printf
| 0x8641496c lui a1, 0x67812
| 0x86414970 addi a1, a1, 0x345
pc = 0x86414400
; XREFS(21)
;-- pc:
/ sym.imp.printf();
| 0x86414400 auipc t3, 3
| 0x86414404 lw t3, -0x42c(t3)
\ 0x86414408 jalr t1, t3
32-bit rotate left 8: 0x34567812
32-bit rotate right 12: 0x67812345
32-bit rotate left var: 0x1A2B3C09
32-bit rotate right var: 0xCF02468A
64-bit rotate left: 0x8ACF13579BDE0246
64-bit rotate right: 0xE6F78091A2B3C4D5
Set bit 5: 0x12345678
Set bit var: 0x12345678
Clear bit 7: 0x12345678
Clear bit var: 0x12344678
Toggle bit 3: 0x12345670
Toggle bit var: 0x1234D678
Extract bit 9: 1
Extract bit var: 1
Swap endian 32: 0x78563412
Swap endian 64: 0xF0DEBC9A78563412
Network to host: 0xDDCCBBAA
Hash rotate: 0x5462BCA9
Device flags after enable: 0x00001020
Is flag 5 set? 1
Is flag 7 set? 0
Device flags after disable: 0x00001000
EOF
RUN
NAME=Stepping over function calls to the next instruction
FILE=bins/elf/riscv_bitmanip_32
ARGS=-d
CMDS=<<EOF
aaa
db @ main+8
dc
dr PC
dso
dr PC; pdf
dc
EOF
EXPECT=<<EOF
pc = 0x792ee428
32-bit rotate left 8: 0x34567812
pc = 0x792ee42c
;-- section..text:
;-- .text:
;-- $xrv32i2p1_m2p0_a2p1_f2p2_d2p2_zicsr2p0_zifencei2p0_zmmul1p0_zaamo1p0_zalrsc1p0:
;-- x19:
/ main();
| ; var unknown_t var_4h @ stack - 0x4
| 0x792ee420 addi sp, sp, -0x10 ; [08] -r-x section size 1788 named .text
| 0x792ee424 sw ra, 0xc(sp)
| 0x792ee428 b jal sym.demonstrate_all_operations
| ;-- pc:
| ;-- x1:
| 0x792ee42c lw ra, 0xc(sp)
| 0x792ee430 li a0, 0
| 0x792ee434 addi sp, sp, 0x10
\ 0x792ee438 ret
32-bit rotate right 12: 0x67812345
32-bit rotate left var: 0x1A2B3C09
32-bit rotate right var: 0xCF02468A
64-bit rotate left: 0x8ACF13579BDE0246
64-bit rotate right: 0xE6F78091A2B3C4D5
Set bit 5: 0x12345678
Set bit var: 0x12345678
Clear bit 7: 0x12345678
Clear bit var: 0x12344678
Toggle bit 3: 0x12345670
Toggle bit var: 0x1234D678
Extract bit 9: 1
Extract bit var: 1
Swap endian 32: 0x78563412
Swap endian 64: 0xF0DEBC9A78563412
Network to host: 0xDDCCBBAA
Hash rotate: 0x5462BCA9
Device flags after enable: 0x00001020
Is flag 5 set? 1
Is flag 7 set? 0
Device flags after disable: 0x00001000
EOF
RUN

View file

@ -0,0 +1,253 @@
NAME=Setting breakpoints on symbols works
FILE=bins/elf/riscv_bitmanip
ARGS=-d
CMDS=<<EOF
aaa
db @ sym.imp.printf
dbl
dc
dr PC; pdf @ sym.imp.printf
EOF
EXPECT=<<EOF
start end size perm hwsw type state valid cmd cond name module
---------------------------------------------------------------------------------------------------------------
0x00010440 0x00010444 4 --x sw break enabled valid sym.imp.printf /test/bins/elf/riscv_bitmanip
pc = 0x0000000000010440
; XREFS(22)
;-- pc:
/ int sym.imp.printf(const char *format);
| 0x00010440 b auipc t3, 2
| 0x00010444 ld t3, -0x438(t3)
\ 0x00010448 jalr t1, t3
EOF
RUN
NAME=Disabling breakpoints works
FILE=bins/elf/riscv_bitmanip
ARGS=-d
CMDS=<<EOF
aaa
db @ sym.imp.printf
dbl
dc
dr PC; pdf @ sym.imp.printf
ds
dbd @ sym.imp.printf
dbl
dc
EOF
EXPECT=<<EOF
start end size perm hwsw type state valid cmd cond name module
---------------------------------------------------------------------------------------------------------------
0x00010440 0x00010444 4 --x sw break enabled valid sym.imp.printf /test/bins/elf/riscv_bitmanip
pc = 0x0000000000010440
; XREFS(22)
;-- pc:
/ int sym.imp.printf(const char *format);
| 0x00010440 b auipc t3, 2
| 0x00010444 ld t3, -0x438(t3)
\ 0x00010448 jalr t1, t3
start end size perm hwsw type state valid cmd cond name module
----------------------------------------------------------------------------------------------------------------
0x00010440 0x00010444 4 --x sw break disabled valid sym.imp.printf /test/bins/elf/riscv_bitmanip
32-bit rotate left 8: 0x34567812
32-bit rotate right 12: 0x67812345
32-bit rotate left var: 0x1A2B3C09
32-bit rotate right var: 0xCF02468A
64-bit rotate left: 0x8ACF13579BDE0246
64-bit rotate right: 0xE6F78091A2B3C4D5
Set bit 5: 0x12345678
Set bit var: 0x12345678
Clear bit 7: 0x12345678
Clear bit var: 0x12344678
Toggle bit 3: 0x12345670
Toggle bit var: 0x1234D678
Extract bit 9: 1
Extract bit var: 1
Swap endian 32: 0x78563412
Swap endian 64: 0xF0DEBC9A78563412
Network to host: 0xDDCCBBAA
Hash rotate: 0x5462BCA9
Device flags after enable: 0x00001020
Is flag 5 set? 1
Is flag 7 set? 0
Device flags after disable: 0x00001000
EOF
RUN
NAME=Stacktraces work
FILE=bins/elf/riscv_bitmanip
ARGS=-d
CMDS=<<EOF
aaa
db @ sym.imp.printf
dc
dr PC; pdf @ sym.imp.printf
dbt~[0,1,4,5,6]
EOF
EXPECT=<<EOF
pc = 0x0000000000010440
; XREFS(22)
;-- pc:
/ int sym.imp.printf(const char *format);
| 0x00010440 b auipc t3, 2
| 0x00010444 ld t3, -0x438(t3)
\ 0x00010448 jalr t1, t3
0 0x10440 0 [sym.imp.printf] sym.imp.printf
1 0x105fa 0 [sym.demonstrate_all_operations] sym.demonstrate_all_operations+22
2 0x10458 8 [main] main+8
3 0x10480 416 [??] entry0+32
EOF
RUN
NAME=Single-stepping into function calls
FILE=bins/elf/riscv_bitmanip
ARGS=-d
CMDS=<<EOF
aaa
db @ 0x000105f6
dc
dr PC; pd 3 @ 0x000105f6
ds
dr PC; pdf
dc
EOF
EXPECT=<<EOF
pc = 0x00000000000105f6
| ;-- pc:
| 0x000105f6 b jal sym.imp.printf ; int printf(const char *format)
| 0x000105fa lui a0, 0x10
| 0x000105fc lui a1, 0x67812
pc = 0x0000000000010440
; XREFS(22)
;-- pc:
/ int sym.imp.printf(const char *format);
| 0x00010440 auipc t3, 2
| 0x00010444 ld t3, -0x438(t3)
\ 0x00010448 jalr t1, t3
32-bit rotate left 8: 0x34567812
32-bit rotate right 12: 0x67812345
32-bit rotate left var: 0x1A2B3C09
32-bit rotate right var: 0xCF02468A
64-bit rotate left: 0x8ACF13579BDE0246
64-bit rotate right: 0xE6F78091A2B3C4D5
Set bit 5: 0x12345678
Set bit var: 0x12345678
Clear bit 7: 0x12345678
Clear bit var: 0x12344678
Toggle bit 3: 0x12345670
Toggle bit var: 0x1234D678
Extract bit 9: 1
Extract bit var: 1
Swap endian 32: 0x78563412
Swap endian 64: 0xF0DEBC9A78563412
Network to host: 0xDDCCBBAA
Hash rotate: 0x5462BCA9
Device flags after enable: 0x00001020
Is flag 5 set? 1
Is flag 7 set? 0
Device flags after disable: 0x00001000
EOF
RUN
NAME=Stepping over function calls to the next instruction
FILE=bins/elf/riscv_bitmanip
ARGS=-d
CMDS=<<EOF
aaa
db @ 0x00010454
dc
dr PC
dso
dr PC; pdf
dc
EOF
EXPECT=<<EOF
pc = 0x0000000000010454
pc = 0x0000000000010458
;-- section..text:
;-- .text:
;-- $xrv64i2p1_m2p0_a2p1_f2p2_d2p2_c2p0_zicsr2p0_zifencei2p0_zmmul1p0_zaamo1p0_zalrsc1p0_zca1p0_zcd1p0_zbb1p0_zbs1p0:
/ int main(int argc, char **argv, char **envp);
| ; arg int argc @ a0
| ; arg char **argv @ a1
| ; var int64_t var_8h @ stack - 0x8
| 0x00010450 addi sp, sp, -0x10 ; [09] -r-x section size 814 named .text
| 0x00010452 sd ra, 8(sp)
| 0x00010454 b jal sym.demonstrate_all_operations
| ;-- pc:
| ;-- x1:
| 0x00010458 ld ra, 8(sp)
| 0x0001045a li a0, 0
| 0x0001045c addi sp, sp, 0x10
\ 0x0001045e ret
32-bit rotate left 8: 0x34567812
32-bit rotate right 12: 0x67812345
32-bit rotate left var: 0x1A2B3C09
32-bit rotate right var: 0xCF02468A
64-bit rotate left: 0x8ACF13579BDE0246
64-bit rotate right: 0xE6F78091A2B3C4D5
Set bit 5: 0x12345678
Set bit var: 0x12345678
Clear bit 7: 0x12345678
Clear bit var: 0x12344678
Toggle bit 3: 0x12345670
Toggle bit var: 0x1234D678
Extract bit 9: 1
Extract bit var: 1
Swap endian 32: 0x78563412
Swap endian 64: 0xF0DEBC9A78563412
Network to host: 0xDDCCBBAA
Hash rotate: 0x5462BCA9
Device flags after enable: 0x00001020
Is flag 5 set? 1
Is flag 7 set? 0
Device flags after disable: 0x00001000
EOF
RUN
NAME=Stepping into a function call through a 2-byte tailcall jump instruction
FILE=bins/elf/riscv_bitmanip
ARGS=-d
CMDS=<<EOF
aaa
db @ 0x0001077c
dc
dr PC
ds
dr PC; pdf
dc
EOF
EXPECT=<<EOF
pc = 0x000000000001077c
pc = 0x0000000000010440
; XREFS(22)
;-- pc:
/ int sym.imp.printf(const char *format);
| 0x00010440 auipc t3, 2
| 0x00010444 ld t3, -0x438(t3)
\ 0x00010448 jalr t1, t3
32-bit rotate left 8: 0x34567812
32-bit rotate right 12: 0x67812345
32-bit rotate left var: 0x1A2B3C09
32-bit rotate right var: 0xCF02468A
64-bit rotate left: 0x8ACF13579BDE0246
64-bit rotate right: 0xE6F78091A2B3C4D5
Set bit 5: 0x12345678
Set bit var: 0x12345678
Clear bit 7: 0x12345678
Clear bit var: 0x12344678
Toggle bit 3: 0x12345670
Toggle bit var: 0x1234D678
Extract bit 9: 1
Extract bit var: 1
Swap endian 32: 0x78563412
Swap endian 64: 0xF0DEBC9A78563412
Network to host: 0xDDCCBBAA
Hash rotate: 0x5462BCA9
Device flags after enable: 0x00001020
Is flag 5 set? 1
Is flag 7 set? 0
Device flags after disable: 0x00001000
EOF
RUN