Implement new gdbstub target for arm7tdmi, add a small example & test as well

Former-commit-id: 16ba6dada28f8beb5971413acbbe153a26aa9ec3
Former-commit-id: f0661ad20245110797fd99fe16d3fb11382bb78a
This commit is contained in:
Michel Heily 2022-09-08 23:39:47 +03:00
parent 4c5d35c5d1
commit f3fac5e3b8
28 changed files with 964 additions and 117 deletions

130
Cargo.lock generated
View file

@ -77,12 +77,16 @@ dependencies = [
"bit", "bit",
"byteorder", "byteorder",
"cfg-if 1.0.0", "cfg-if 1.0.0",
"colored", "colored 1.9.3",
"enum-primitive-derive", "enum-primitive-derive",
"gdbstub 0.6.3",
"gdbstub_arch",
"log 0.4.11",
"num 0.2.1", "num 0.2.1",
"num-traits 0.2.12", "num-traits 0.2.12",
"rustboyadvance-utils", "rustboyadvance-utils",
"serde", "serde",
"simple_logger",
] ]
[[package]] [[package]]
@ -201,9 +205,9 @@ checksum = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d"
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "1.2.1" version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]] [[package]]
name = "blake2b_simd" name = "blake2b_simd"
@ -333,7 +337,7 @@ checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
dependencies = [ dependencies = [
"ansi_term 0.11.0", "ansi_term 0.11.0",
"atty", "atty",
"bitflags 1.2.1", "bitflags 1.3.2",
"strsim", "strsim",
"textwrap", "textwrap",
"unicode-width", "unicode-width",
@ -352,6 +356,17 @@ dependencies = [
"winapi 0.3.9", "winapi 0.3.9",
] ]
[[package]]
name = "colored"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3616f750b84d8f0de8a58bda93e08e2a81ad3f523089b05f1dffecab48c6cbd"
dependencies = [
"atty",
"lazy_static 1.4.0",
"winapi 0.3.9",
]
[[package]] [[package]]
name = "combine" name = "combine"
version = "4.3.2" version = "4.3.2"
@ -485,7 +500,7 @@ checksum = "00affe7f6ab566df61b4be3ce8cf16bc2576bca0963ceb0955e45d514bf9a279"
dependencies = [ dependencies = [
"bstr", "bstr",
"csv-core", "csv-core",
"itoa", "itoa 0.4.6",
"ryu", "ryu",
"serde", "serde",
] ]
@ -654,6 +669,30 @@ dependencies = [
"num-traits 0.2.12", "num-traits 0.2.12",
] ]
[[package]]
name = "gdbstub"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32c95766e0414f8bfc1d07055574c621b67739466d6ba516c4fef8e99d30d2e6"
dependencies = [
"bitflags 1.3.2",
"cfg-if 1.0.0",
"log 0.4.11",
"managed",
"num-traits 0.2.12",
"paste",
]
[[package]]
name = "gdbstub_arch"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eecb536c55c43593a00dde9074dbbdb0e81ce5f20dbca921400f8779c21dea9c"
dependencies = [
"gdbstub 0.6.3",
"num-traits 0.2.12",
]
[[package]] [[package]]
name = "gdi32-sys" name = "gdi32-sys"
version = "0.1.2" version = "0.1.2"
@ -793,6 +832,12 @@ version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6" checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6"
[[package]]
name = "itoa"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754"
[[package]] [[package]]
name = "jni" name = "jni"
version = "0.17.0" version = "0.17.0"
@ -851,7 +896,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db65c6da02e61f55dae90a0ae427b2a5f6b3e8db09f58d10efab23af92592616" checksum = "db65c6da02e61f55dae90a0ae427b2a5f6b3e8db09f58d10efab23af92592616"
dependencies = [ dependencies = [
"arrayvec 0.5.2", "arrayvec 0.5.2",
"bitflags 1.2.1", "bitflags 1.3.2",
"cfg-if 0.1.10", "cfg-if 0.1.10",
"ryu", "ryu",
"static_assertions", "static_assertions",
@ -859,9 +904,9 @@ dependencies = [
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.77" version = "0.2.132"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2f96b10ec2560088a8e76961b00d47107b3a625fecb76dedb29ee7ccbf98235" checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5"
[[package]] [[package]]
name = "libretro-backend" name = "libretro-backend"
@ -906,6 +951,12 @@ dependencies = [
"cfg-if 0.1.10", "cfg-if 0.1.10",
] ]
[[package]]
name = "managed"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ca88d725a0a943b096803bd34e73a4437208b6077654cc4ecb2947a5f91618d"
[[package]] [[package]]
name = "maybe-uninit" name = "maybe-uninit"
version = "2.0.0" version = "2.0.0"
@ -980,7 +1031,7 @@ version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83450fe6a6142ddd95fb064b746083fc4ef1705fe81f64a64e1d4b39f54a1055" checksum = "83450fe6a6142ddd95fb064b746083fc4ef1705fe81f64a64e1d4b39f54a1055"
dependencies = [ dependencies = [
"bitflags 1.2.1", "bitflags 1.3.2",
"cc", "cc",
"cfg-if 0.1.10", "cfg-if 0.1.10",
"libc", "libc",
@ -1110,6 +1161,15 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "num_threads"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "object" name = "object"
version = "0.20.0" version = "0.20.0"
@ -1144,6 +1204,12 @@ dependencies = [
"sdl2 0.31.0", "sdl2 0.31.0",
] ]
[[package]]
name = "paste"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1de2e551fb905ac83f73f7aedf2f0cb4a0da7e35efa24a202a936269f1f18e1"
[[package]] [[package]]
name = "pin-project-lite" name = "pin-project-lite"
version = "0.1.9" version = "0.1.9"
@ -1374,18 +1440,17 @@ dependencies = [
"bit-set", "bit-set",
"bit_reverse", "bit_reverse",
"bitfield", "bitfield",
"bitflags 1.2.1", "bitflags 1.3.2",
"byteorder", "byteorder",
"bytesize", "bytesize",
"cfg-if 1.0.0", "cfg-if 1.0.0",
"chrono", "chrono",
"colored", "colored 1.9.3",
"criterion", "criterion",
"debug_stub_derive", "debug_stub_derive",
"enum-primitive-derive", "enum-primitive-derive",
"fuzzy-matcher", "fuzzy-matcher",
"gdbstub", "gdbstub 0.1.2",
"goblin",
"hex-literal", "hex-literal",
"hexdump", "hexdump",
"lazy_static 1.4.0", "lazy_static 1.4.0",
@ -1461,7 +1526,9 @@ dependencies = [
name = "rustboyadvance-utils" name = "rustboyadvance-utils"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"goblin",
"instant", "instant",
"log 0.4.11",
"ringbuf", "ringbuf",
] ]
@ -1583,7 +1650,7 @@ version = "0.33.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f74124048ea86b5cd50236b2443f6f57cf4625a8e8818009b4e50dbb8729a43" checksum = "1f74124048ea86b5cd50236b2443f6f57cf4625a8e8818009b4e50dbb8729a43"
dependencies = [ dependencies = [
"bitflags 1.2.1", "bitflags 1.3.2",
"lazy_static 1.4.0", "lazy_static 1.4.0",
"libc", "libc",
"sdl2-sys 0.33.0", "sdl2-sys 0.33.0",
@ -1659,7 +1726,7 @@ version = "1.0.57"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "164eacbdb13512ec2745fb09d51fd5b22b0d65ed294a1dcf7285a360c80a675c" checksum = "164eacbdb13512ec2745fb09d51fd5b22b0d65ed294a1dcf7285a360c80a675c"
dependencies = [ dependencies = [
"itoa", "itoa 0.4.6",
"ryu", "ryu",
"serde", "serde",
] ]
@ -1682,6 +1749,19 @@ dependencies = [
"opaque-debug", "opaque-debug",
] ]
[[package]]
name = "simple_logger"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48047e77b528151aaf841a10a9025f9459da80ba820e425ff7eb005708a76dc7"
dependencies = [
"atty",
"colored 2.0.0",
"log 0.4.11",
"time 0.3.14",
"winapi 0.3.9",
]
[[package]] [[package]]
name = "smart-default" name = "smart-default"
version = "0.6.0" version = "0.6.0"
@ -1872,11 +1952,23 @@ dependencies = [
"libc", "libc",
"standback", "standback",
"stdweb", "stdweb",
"time-macros", "time-macros 0.1.1",
"version_check", "version_check",
"winapi 0.3.9", "winapi 0.3.9",
] ]
[[package]]
name = "time"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c3f9a28b618c3a6b9251b6908e9c99e04b9e5c02e6581ccbb67d59c34ef7f9b"
dependencies = [
"itoa 1.0.3",
"libc",
"num_threads",
"time-macros 0.2.4",
]
[[package]] [[package]]
name = "time-macros" name = "time-macros"
version = "0.1.1" version = "0.1.1"
@ -1887,6 +1979,12 @@ dependencies = [
"time-macros-impl", "time-macros-impl",
] ]
[[package]]
name = "time-macros"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42657b1a6f4d817cda8e7a0ace261fe0cc946cf3a80314390b22cc61ae080792"
[[package]] [[package]]
name = "time-macros-impl" name = "time-macros-impl"
version = "0.1.1" version = "0.1.1"

View file

@ -7,6 +7,7 @@ edition = "2021"
[dependencies] [dependencies]
rustboyadvance-utils = {"path" = "../utils" } rustboyadvance-utils = {"path" = "../utils" }
log = "0.4.8"
bit = "^0.1" bit = "^0.1"
cfg-if = "1.0.0" cfg-if = "1.0.0"
serde = { version = "1.0.104", features = ["derive", "rc"] } serde = { version = "1.0.104", features = ["derive", "rc"] }
@ -16,6 +17,11 @@ byteorder = "1"
num = "0.2.1" num = "0.2.1"
num-traits = "0.2" num-traits = "0.2"
enum-primitive-derive = "^0.1" enum-primitive-derive = "^0.1"
gdbstub = "0.6.3"
gdbstub_arch = "0.2.4"
[dev-dependencies]
simple_logger = "2.3.0" # For the examples
[build-dependencies] [build-dependencies]
bit = "^0.1" bit = "^0.1"

View file

@ -0,0 +1,129 @@
use log::info;
use simple_logger::SimpleLogger;
use gdbstub::common::Signal;
use gdbstub::conn::{Connection, ConnectionExt};
use gdbstub::stub::{run_blocking, GdbStub, SingleThreadStopReason};
use gdbstub::target::Target;
use arm7tdmi::gdb::wait_for_connection;
use arm7tdmi::{Arm7tdmiCore, SimpleMemory};
use rustboyadvance_utils::Shared;
struct SimpleEmulator {
cpu: Arm7tdmiCore<SimpleMemory>,
}
impl SimpleEmulator {
fn new(program: &[u8]) -> SimpleEmulator {
let mut memory = SimpleMemory::new(0x4000);
memory.load_program(program);
let bus = Shared::new(memory);
let mut cpu = Arm7tdmiCore::new(bus);
cpu.reset();
SimpleEmulator { cpu }
}
}
impl run_blocking::BlockingEventLoop for SimpleEmulator {
type Target = Arm7tdmiCore<SimpleMemory>;
type Connection = Box<dyn ConnectionExt<Error = std::io::Error>>;
type StopReason = SingleThreadStopReason<u32>;
fn wait_for_stop_reason(
target: &mut Self::Target,
conn: &mut Self::Connection,
) -> Result<
run_blocking::Event<SingleThreadStopReason<u32>>,
run_blocking::WaitForStopReasonError<
<Self::Target as Target>::Error,
<Self::Connection as Connection>::Error,
>,
> {
let mut steps = 0;
loop {
if conn.peek().map(|b| b.is_some()).unwrap_or(true) {
let byte = conn
.read()
.map_err(run_blocking::WaitForStopReasonError::Connection)?;
return Ok(run_blocking::Event::IncomingData(byte));
} else {
target.step();
if target.check_breakpoint().is_some() {
return Ok(run_blocking::Event::TargetStopped(
SingleThreadStopReason::SwBreak(()),
));
}
steps += 1;
if steps % 1024 == 0 {
return Ok(run_blocking::Event::TargetStopped(
SingleThreadStopReason::SwBreak(()),
));
}
}
}
}
fn on_interrupt(
_target: &mut Arm7tdmiCore<SimpleMemory>,
) -> Result<Option<SingleThreadStopReason<u32>>, <Arm7tdmiCore<SimpleMemory> as Target>::Error>
{
// Because this emulator runs as part of the GDB stub loop, there isn't any
// special action that needs to be taken to interrupt the underlying target. It
// is implicitly paused whenever the stub isn't within the
// `wait_for_stop_reason` callback.
Ok(Some(SingleThreadStopReason::Signal(Signal::SIGINT)))
}
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
SimpleLogger::new().env().init().unwrap();
let mut emulator = SimpleEmulator::new(include_bytes!("test_program/example.bin"));
let conn: Box<dyn ConnectionExt<Error = std::io::Error>> = Box::new(wait_for_connection(1337)?);
let gdb = GdbStub::new(conn);
let result = gdb.run_blocking::<SimpleEmulator>(&mut emulator.cpu);
info!("emulator stopped, gdb result {:?}", result);
Ok(())
}
#[cfg(test)]
mod test {
use super::*;
use arm7tdmi::memory::DebugRead;
use rustboyadvance_utils::elf::read_symbols;
#[test]
fn test_breakpoint() -> Result<(), Box<dyn std::error::Error>> {
let mut emulator = SimpleEmulator::new(include_bytes!("test_program/example.bin"));
let symbol_map = read_symbols(include_bytes!("test_program/example.elf"))?;
let breakpoint_addr = *symbol_map.get("breakpoint_on_me").unwrap();
println!("breakpoint_addr = {:08x}", breakpoint_addr);
let breakpoint_counter_addr = *symbol_map.get("breakpoint_count").unwrap();
emulator.cpu.breakpoints.push(breakpoint_addr);
for x in 0..10 {
println!("{}", x);
let timeout = std::time::Instant::now() + std::time::Duration::from_secs(1);
loop {
emulator.cpu.step();
if let Some(addr) = emulator.cpu.check_breakpoint() {
emulator.cpu.step();
assert_eq!(addr, breakpoint_addr);
assert_eq!(emulator.cpu.bus.debug_read_32(breakpoint_counter_addr), x);
break;
}
assert!(std::time::Instant::now() < timeout);
}
}
Ok(())
}
}

View file

@ -0,0 +1,2 @@
/*.o
/*.gdb_history

View file

@ -0,0 +1,37 @@
# Easiest to build this using `docker run -it --rm -v $(pwd):/code -w /code devkitpro/devkitarm:latest make
TOOLCHAIN := ${DEVKITARM}/bin/arm-none-eabi
CC := ${TOOLCHAIN}-gcc
OBJCOPY := ${TOOLCHAIN}-objcopy
OBJDUMP := ${TOOLCHAIN}-objdump
TARGET := test
CFLAGS := -O0 -g -std=c11 -march=armv4t -mthumb
LDFLAGS := -Wl,-static
SOURCES := $(wildcard *.[cs])
OBJECTS := $(filter %.o,$(SOURCES:%.c=%.o))
OBJECTS += $(filter %.o,$(SOURCES:%.s=%.o))
all: ${TARGET}.bin objdump
${OBJECTS}: ${SOURCES}
${CC} -c ${CFLAGS} $^
${TARGET}.elf ${TARGET}.map: ${OBJECTS} ${TARGET}.ld
${CC} ${LDFLAGS} -Wl,-Map=${TARGET}.map -Wl,-T${TARGET}.ld ${OBJECTS} -o $@
${TARGET}.bin: ${TARGET}.elf
$(OBJCOPY) -v -O binary $^ $@
objdump: ${TARGET}.elf
${OBJDUMP} -d $^
clean:
rm -f ${TARGET}.elf
rm -f ${TARGET}.bin
rm -f ${TARGET}.map
rm -f *.o
.PHONY: all clean

View file

@ -0,0 +1,40 @@
.section ".init"
.global _interrupt_vector
.align 4
.arm
.section ".init.vector"
_interrupt_vector:
b _reset
b _not_implemented
b _not_implemented
b _not_implemented
b _not_implemented
b _not_implemented
b _not_implemented
b _not_implemented
.section ".init.text"
.global _reset
_reset:
mrs r1, cpsr @ save the mode bits from CPSR
bic r0, r1, #0x1F
orr r0, r0, #0x13 @ supervisor
ldr sp, =_stack_top
mov r0, #0
mov r1, #0
mov r2, #0
mov r3, #0
mov r4, #0
mov r5, #0
mov r6, #0
mov r7, #0
mov r9, #0
mov r10, #0
mov r11, #0
mov r12, #0
b main
_not_implemented:
b _not_implemented

Binary file not shown.

View file

@ -0,0 +1,17 @@
int breakpoint_count = 0;
volatile int breakpoint_on_me() {
breakpoint_count++;
}
int main() {
int x = 1337;
for (;;) {
x += 1;
if (x == 7331) {
breakpoint_on_me();
x = 1337;
}
}
return 0;
}

Binary file not shown.

View file

@ -0,0 +1,32 @@
ENTRY(_reset)
MEMORY {
RAM : ORIGIN = 0x00000000, LENGTH = 16K
}
SECTIONS {
. = ORIGIN(RAM);
.init : {
*(.init.vector);
*(.init*);
} > RAM
.text : ALIGN(4) {
*(.text)
*(.text.*)
} > RAM
.data : ALIGN(4) {
*(.rodata*);
*(.data*);
} > RAM
.bss : ALIGN(4) {
*(.bss*)
} > RAM
.stack : AT (0x00003000) {
_stack_bottom = .;
. += 0xfe0;
_stack_top = .;
} > RAM
}

View file

@ -0,0 +1,162 @@
Memory Configuration
Name Origin Length Attributes
RAM 0x0000000000000000 0x0000000000004000
*default* 0x0000000000000000 0xffffffffffffffff
Linker script and memory map
LOAD /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crti.o
LOAD /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtbegin.o
0x0000000000000000 . = ORIGIN (RAM)
.init 0x0000000000000000 0x88
*(.init.vector)
.init.vector 0x0000000000000000 0x20 reset.o
0x0000000000000000 _interrupt_vector
*(.init*)
.init 0x0000000000000020 0xc /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crti.o
0x0000000000000020 _init
.init_array 0x000000000000002c 0x4 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtbegin.o
.init 0x0000000000000030 0x0 reset.o
.init.text 0x0000000000000030 0x4c reset.o
0x0000000000000030 _reset
.init 0x000000000000007c 0xc /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtn.o
.text 0x0000000000000088 0xbc
*(.text)
.text 0x0000000000000088 0x0 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crti.o
.text 0x0000000000000088 0x0 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtbegin.o
.text 0x0000000000000088 0x4c test.o
0x0000000000000088 breakpoint_on_me
0x00000000000000a8 main
.text 0x00000000000000d4 0x0 reset.o
.text 0x00000000000000d4 0x0 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtend.o
.text 0x00000000000000d4 0x0 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtn.o
*(.text.*)
.text.__do_global_dtors_aux
0x00000000000000d4 0x40 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtbegin.o
.text.frame_dummy
0x0000000000000114 0x30 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtbegin.o
.glue_7 0x0000000000000144 0x0
.glue_7 0x0000000000000144 0x0 linker stubs
.glue_7t 0x0000000000000144 0x0
.glue_7t 0x0000000000000144 0x0 linker stubs
.vfp11_veneer 0x0000000000000144 0x0
.vfp11_veneer 0x0000000000000144 0x0 linker stubs
.v4_bx 0x0000000000000144 0x0
.v4_bx 0x0000000000000144 0x0 linker stubs
.fini 0x0000000000000144 0x18
.fini 0x0000000000000144 0xc /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crti.o
0x0000000000000144 _fini
.fini 0x0000000000000150 0xc /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtn.o
.iplt 0x000000000000015c 0x0
.iplt 0x000000000000015c 0x0 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtbegin.o
.eh_frame 0x000000000000015c 0x4
.eh_frame 0x000000000000015c 0x0 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtbegin.o
.eh_frame 0x000000000000015c 0x4 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtend.o
.rel.dyn 0x0000000000000160 0x0
.rel.iplt 0x0000000000000160 0x0 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtbegin.o
.data 0x0000000000000160 0x4c
*(.rodata*)
.rodata.all_implied_fbits
0x0000000000000160 0x24 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtbegin.o
.rodata.all_implied_fbits
0x0000000000000184 0x24 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtend.o
*(.data*)
.data 0x00000000000001a8 0x0 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crti.o
.data 0x00000000000001a8 0x0 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtbegin.o
.data.__dso_handle
0x00000000000001a8 0x4 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtbegin.o
0x00000000000001a8 __dso_handle
.data 0x00000000000001ac 0x0 test.o
.data 0x00000000000001ac 0x0 reset.o
.data 0x00000000000001ac 0x0 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtend.o
.data 0x00000000000001ac 0x0 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtn.o
.fini_array 0x00000000000001ac 0x4
.fini_array 0x00000000000001ac 0x4 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtbegin.o
.igot.plt 0x00000000000001b0 0x0
.igot.plt 0x00000000000001b0 0x0 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtbegin.o
.bss 0x00000000000001b0 0x20
*(.bss*)
.bss 0x00000000000001b0 0x0 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crti.o
.bss 0x00000000000001b0 0x0 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtbegin.o
.bss.completed.1
0x00000000000001b0 0x1 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtbegin.o
*fill* 0x00000000000001b1 0x3
.bss.object.0 0x00000000000001b4 0x18 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtbegin.o
.bss 0x00000000000001cc 0x4 test.o
0x00000000000001cc breakpoint_count
.bss 0x00000000000001d0 0x0 reset.o
.bss 0x00000000000001d0 0x0 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtend.o
.bss 0x00000000000001d0 0x0 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtn.o
.stack 0x00000000000001d0 0xfe0 load address 0x0000000000003000
0x00000000000001d0 _stack_bottom = .
0x00000000000011b0 . = (. + 0xfe0)
*fill* 0x00000000000001d0 0xfe0
0x00000000000011b0 _stack_top = .
LOAD test.o
LOAD reset.o
START GROUP
LOAD /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/libgcc.a
LOAD /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/../../../../arm-none-eabi/lib/libc.a
LOAD /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/../../../../arm-none-eabi/lib/libsysbase.a
END GROUP
LOAD /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtend.o
LOAD /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtn.o
OUTPUT(test.elf elf32-littlearm)
LOAD linker stubs
.ARM.attributes
0x0000000000000000 0x26
.ARM.attributes
0x0000000000000000 0x1c /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crti.o
.ARM.attributes
0x000000000000001c 0x2a /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtbegin.o
.ARM.attributes
0x0000000000000046 0x2a test.o
.ARM.attributes
0x0000000000000070 0x1a reset.o
.ARM.attributes
0x000000000000008a 0x2a /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtend.o
.ARM.attributes
0x00000000000000b4 0x1c /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtn.o
.comment 0x0000000000000000 0x23
.comment 0x0000000000000000 0x23 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtbegin.o
0x24 (size before relaxing)
.comment 0x0000000000000023 0x24 test.o
.comment 0x0000000000000023 0x24 /opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/12.1.0/crtend.o
.debug_info 0x0000000000000000 0x7e
.debug_info 0x0000000000000000 0x7e test.o
.debug_abbrev 0x0000000000000000 0x77
.debug_abbrev 0x0000000000000000 0x77 test.o
.debug_aranges 0x0000000000000000 0x20
.debug_aranges
0x0000000000000000 0x20 test.o
.debug_line 0x0000000000000000 0x51
.debug_line 0x0000000000000000 0x51 test.o
.debug_str 0x0000000000000000 0x69
.debug_str 0x0000000000000000 0x69 test.o
.debug_frame 0x0000000000000000 0x4c
.debug_frame 0x0000000000000000 0x4c test.o

View file

@ -54,14 +54,14 @@ pub enum CpuAction {
} }
#[derive(Serialize, Deserialize, Clone, Debug, Default)] #[derive(Serialize, Deserialize, Clone, Debug, Default)]
pub(super) struct BankedRegisters { pub struct BankedRegisters {
// r13 and r14 are banked for all modes. System&User mode share them // r13 and r14 are banked for all modes. System&User mode share them
pub(super) gpr_banked_r13: [u32; 6], pub gpr_banked_r13: [u32; 6],
pub(super) gpr_banked_r14: [u32; 6], pub gpr_banked_r14: [u32; 6],
// r8-r12 are banked for fiq mode // r8-r12 are banked for fiq mode
pub(super) gpr_banked_old_r8_12: [u32; 5], pub gpr_banked_old_r8_12: [u32; 5],
pub(super) gpr_banked_fiq_r8_12: [u32; 5], pub gpr_banked_fiq_r8_12: [u32; 5],
pub(super) spsr_bank: [RegPSR; 6], pub spsr_bank: [RegPSR; 6],
} }
#[derive(Serialize, Deserialize, Clone, Debug)] #[derive(Serialize, Deserialize, Clone, Debug)]
@ -106,17 +106,21 @@ impl Default for DebuggerState {
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Arm7tdmiCore<I: MemoryInterface> { pub struct Arm7tdmiCore<I: MemoryInterface> {
pub pc: u32, pub pc: u32,
pub(super) bus: Shared<I>, pub bus: Shared<I>,
next_fetch_access: MemoryAccess, next_fetch_access: MemoryAccess,
pipeline: [u32; 2], pipeline: [u32; 2],
pub gpr: [u32; 15], pub gpr: [u32; 15],
pub cpsr: RegPSR, pub cpsr: RegPSR,
pub(super) spsr: RegPSR, pub spsr: RegPSR,
pub(super) banks: BankedRegisters, pub banks: BankedRegisters,
/// Hardware breakpoints for use by gdb
pub breakpoints: Vec<Addr>,
/// Deprecated in-house debugger state
#[cfg(feature = "debugger")] #[cfg(feature = "debugger")]
pub dbg: DebuggerState, pub dbg: DebuggerState,
} }
@ -134,6 +138,8 @@ impl<I: MemoryInterface> Arm7tdmiCore<I> {
spsr: Default::default(), spsr: Default::default(),
banks: BankedRegisters::default(), banks: BankedRegisters::default(),
breakpoints: Vec::new(),
#[cfg(feature = "debugger")] #[cfg(feature = "debugger")]
dbg: DebuggerState::default(), dbg: DebuggerState::default(),
} }
@ -156,6 +162,8 @@ impl<I: MemoryInterface> Arm7tdmiCore<I> {
pipeline: state.pipeline, pipeline: state.pipeline,
next_fetch_access: state.next_fetch_access, next_fetch_access: state.next_fetch_access,
breakpoints: Vec::new(), // TODO include breakpoints in saved state
// savestate does not keep debugger related information, so just reinitialize to default // savestate does not keep debugger related information, so just reinitialize to default
#[cfg(feature = "debugger")] #[cfg(feature = "debugger")]
dbg: DebuggerState::default(), dbg: DebuggerState::default(),
@ -473,20 +481,6 @@ impl<I: MemoryInterface> Arm7tdmiCore<I> {
pub fn get_cpu_state(&self) -> CpuState { pub fn get_cpu_state(&self) -> CpuState {
self.cpsr.state() self.cpsr.state()
} }
pub fn skip_bios(&mut self) {
self.banks.gpr_banked_r13[0] = 0x0300_7f00; // USR/SYS
self.banks.gpr_banked_r13[1] = 0x0300_7f00; // FIQ
self.banks.gpr_banked_r13[2] = 0x0300_7fa0; // IRQ
self.banks.gpr_banked_r13[3] = 0x0300_7fe0; // SVC
self.banks.gpr_banked_r13[4] = 0x0300_7f00; // ABT
self.banks.gpr_banked_r13[5] = 0x0300_7f00; // UND
self.gpr[13] = 0x0300_7f00;
self.pc = 0x0800_0000;
self.cpsr.set(0x5f);
}
} }
#[cfg(feature = "debugger")] #[cfg(feature = "debugger")]

View file

@ -0,0 +1,56 @@
use gdbstub::target;
use gdbstub::target::TargetResult;
use log::debug;
use crate::Arm7tdmiCore;
use super::target::MemoryGdbInterface;
impl<I: MemoryGdbInterface> target::ext::breakpoints::Breakpoints for Arm7tdmiCore<I> {
// there are several kinds of breakpoints - this target uses software breakpoints
#[inline(always)]
fn support_sw_breakpoint(
&mut self,
) -> Option<target::ext::breakpoints::SwBreakpointOps<'_, Self>> {
Some(self)
}
}
impl<I: MemoryGdbInterface> target::ext::breakpoints::SwBreakpoint for Arm7tdmiCore<I> {
fn add_sw_breakpoint(
&mut self,
addr: u32,
_kind: gdbstub_arch::arm::ArmBreakpointKind,
) -> TargetResult<bool, Self> {
debug!("adding breakpoint {:08x}", addr);
self.breakpoints.push(addr);
Ok(true)
}
fn remove_sw_breakpoint(
&mut self,
addr: u32,
_kind: gdbstub_arch::arm::ArmBreakpointKind,
) -> TargetResult<bool, Self> {
match self.breakpoints.iter().position(|x| *x == addr) {
None => Ok(false),
Some(pos) => {
debug!("deleting breakpoint {:08x}", addr);
self.breakpoints.remove(pos);
Ok(true)
}
}
}
}
impl<I: MemoryGdbInterface> Arm7tdmiCore<I> {
pub fn check_breakpoint(&self) -> Option<u32> {
let next_pc = self.get_next_pc();
for bp in &self.breakpoints {
if (*bp & !1) == next_pc {
return Some(*bp);
}
}
None
}
}

45
arm7tdmi/src/gdb/mod.rs Normal file
View file

@ -0,0 +1,45 @@
use std::io;
use std::net::{TcpListener, TcpStream};
use log::info;
mod breakpoints;
pub mod target;
/// Wait for tcp connection on port
pub fn wait_for_connection(port: u16) -> io::Result<TcpStream> {
let bind_addr = format!("localhost:{port}");
let sock = TcpListener::bind(bind_addr)?;
info!("waiting for connection");
// Blocks until a GDB client connects via TCP.
// i.e: Running `target remote localhost:<port>` from the GDB prompt.
let (stream, addr) = sock.accept()?;
info!("gdb connected from {:?}", addr);
Ok(stream)
}
/// Copy all bytes of `data` to `buf`.
/// Return the size of data copied.
pub fn copy_to_buf(data: &[u8], buf: &mut [u8]) -> usize {
let len = buf.len().min(data.len());
buf[..len].copy_from_slice(&data[..len]);
len
}
/// Copy a range of `data` (start at `offset` with a size of `length`) to `buf`.
/// Return the size of data copied. Returns 0 if `offset >= buf.len()`.
///
/// Mainly used by qXfer:_object_:read commands.
pub fn copy_range_to_buf(data: &[u8], offset: u64, length: usize, buf: &mut [u8]) -> usize {
let offset = offset as usize;
if offset > data.len() {
return 0;
}
let start = offset;
let end = (offset + length).min(data.len());
copy_to_buf(&data[start..end], buf)
}

113
arm7tdmi/src/gdb/target.rs Normal file
View file

@ -0,0 +1,113 @@
/// Implementing the Target trait for gdbstub
use gdbstub::common::Signal;
use gdbstub::target::ext::base::singlethread::{
SingleThreadBase, SingleThreadResume, SingleThreadSingleStep,
};
use gdbstub::target::ext::base::singlethread::{SingleThreadResumeOps, SingleThreadSingleStepOps};
use gdbstub::target::ext::base::BaseOps;
use gdbstub::target::ext::breakpoints::BreakpointsOps;
use gdbstub::target::{self, Target, TargetError, TargetResult};
use crate::memory::{DebugRead, MemoryInterface};
use crate::registers_consts::*;
use crate::Arm7tdmiCore;
pub trait MemoryGdbInterface: MemoryInterface + DebugRead {
fn memory_map_xml(&self, offset: u64, length: usize, buf: &mut [u8]) -> usize;
}
impl<I: MemoryGdbInterface> Target for Arm7tdmiCore<I> {
type Error = ();
type Arch = gdbstub_arch::arm::Armv4t; // as an example
#[inline(always)]
fn base_ops(&mut self) -> BaseOps<Self::Arch, Self::Error> {
BaseOps::SingleThread(self)
}
// opt-in to support for setting/removing breakpoints
#[inline(always)]
fn support_breakpoints(&mut self) -> Option<BreakpointsOps<Self>> {
Some(self)
}
fn support_memory_map(&mut self) -> Option<target::ext::memory_map::MemoryMapOps<Self>> {
Some(self)
}
}
impl<I: MemoryGdbInterface> SingleThreadBase for Arm7tdmiCore<I> {
fn read_registers(
&mut self,
regs: &mut gdbstub_arch::arm::reg::ArmCoreRegs,
) -> TargetResult<(), Self> {
regs.pc = self.get_reg(REG_PC);
regs.lr = self.get_reg(REG_LR);
regs.sp = self.get_reg(REG_SP);
regs.r[..].copy_from_slice(&self.gpr[..13]);
regs.cpsr = self.cpsr.get();
Ok(())
}
fn write_registers(
&mut self,
regs: &gdbstub_arch::arm::reg::ArmCoreRegs,
) -> TargetResult<(), Self> {
self.set_reg(REG_PC, regs.pc);
self.set_reg(REG_LR, regs.lr);
self.set_reg(REG_SP, regs.sp);
self.gpr.copy_from_slice(&regs.r);
self.cpsr.set(regs.cpsr);
Ok(())
}
fn read_addrs(&mut self, start_addr: u32, data: &mut [u8]) -> TargetResult<(), Self> {
self.bus.debug_get_into_bytes(start_addr, data);
Ok(())
}
fn write_addrs(&mut self, _start_addr: u32, _data: &[u8]) -> TargetResult<(), Self> {
// todo!("implement DebugWrite bus extention")
Err(TargetError::NonFatal)
}
// most targets will want to support at resumption as well...
#[inline(always)]
fn support_resume(&mut self) -> Option<SingleThreadResumeOps<Self>> {
Some(self)
}
}
impl<I: MemoryGdbInterface> SingleThreadResume for Arm7tdmiCore<I> {
fn resume(&mut self, _signal: Option<Signal>) -> Result<(), Self::Error> {
// do nothing
Ok(())
}
// ...and if the target supports resumption, it'll likely want to support
// single-step resume as well
#[inline(always)]
fn support_single_step(&mut self) -> Option<SingleThreadSingleStepOps<'_, Self>> {
Some(self)
}
}
impl<I: MemoryGdbInterface> SingleThreadSingleStep for Arm7tdmiCore<I> {
fn step(&mut self, _signal: Option<Signal>) -> Result<(), Self::Error> {
self.step();
Ok(())
}
}
impl<I: MemoryGdbInterface> target::ext::memory_map::MemoryMap for Arm7tdmiCore<I> {
fn memory_map_xml(
&self,
offset: u64,
length: usize,
buf: &mut [u8],
) -> TargetResult<usize, Self> {
Ok(self.bus.memory_map_xml(offset, length, buf))
}
}

View file

@ -23,7 +23,10 @@ pub use alu::*;
use memory::Addr; use memory::Addr;
pub mod disass; pub mod disass;
pub mod exception; pub mod exception;
pub mod gdb;
pub mod psr; pub mod psr;
mod simple_memory;
pub use simple_memory::SimpleMemory;
pub mod registers_consts { pub mod registers_consts {
pub const REG_PC: usize = 15; pub const REG_PC: usize = 15;

View file

@ -225,13 +225,20 @@ pub trait DebugRead: BusIO {
fn debug_read_8(&mut self, addr: Addr) -> u8; fn debug_read_8(&mut self, addr: Addr) -> u8;
fn debug_get_bytes(&mut self, range: std::ops::Range<u32>) -> Vec<u8> { fn debug_get_bytes(&mut self, range: std::ops::Range<Addr>) -> Vec<u8> {
let mut bytes = Vec::new(); let mut bytes = Vec::new();
for b in range { for b in range {
bytes.push(self.debug_read_8(b)); bytes.push(self.debug_read_8(b));
} }
bytes bytes
} }
fn debug_get_into_bytes(&mut self, start_addr: Addr, bytes: &mut [u8]) {
bytes
.iter_mut()
.enumerate()
.for_each(|(idx, byte)| *byte = self.debug_read_8(start_addr + (idx as Addr)));
}
} }
/// The caller is assumed to handle out of bound accesses, /// The caller is assumed to handle out of bound accesses,

View file

@ -0,0 +1,83 @@
use crate::gdb::{copy_range_to_buf, target::MemoryGdbInterface};
use crate::memory::{Addr, BusIO, DebugRead, MemoryAccess, MemoryInterface};
/// Simple wrapper around a bytearray for memory access
/// For use by tests and examples of this crate.
pub struct SimpleMemory {
data: Box<[u8]>,
}
impl SimpleMemory {
pub fn new(capacity: usize) -> SimpleMemory {
SimpleMemory {
data: vec![0; capacity].into_boxed_slice(),
}
}
pub fn load_program(&mut self, program: &[u8]) {
self.data[..program.len()].copy_from_slice(program);
}
}
impl MemoryInterface for SimpleMemory {
#[inline]
fn load_8(&mut self, addr: u32, _access: MemoryAccess) -> u8 {
self.read_8(addr)
}
#[inline]
fn load_16(&mut self, addr: u32, _access: MemoryAccess) -> u16 {
self.read_16(addr & !1)
}
#[inline]
fn load_32(&mut self, addr: u32, _access: MemoryAccess) -> u32 {
self.read_32(addr & !3)
}
fn store_8(&mut self, addr: u32, value: u8, _access: MemoryAccess) {
self.write_8(addr, value);
}
fn store_16(&mut self, addr: u32, value: u16, _access: MemoryAccess) {
self.write_16(addr & !1, value);
}
fn store_32(&mut self, addr: u32, value: u32, _access: MemoryAccess) {
self.write_32(addr & !3, value);
}
fn idle_cycle(&mut self) {}
}
impl BusIO for SimpleMemory {
fn read_8(&mut self, addr: Addr) -> u8 {
*self.data.get(addr as usize).unwrap_or(&0)
}
fn write_8(&mut self, addr: Addr, value: u8) {
if let 0..=0x3FFF = addr {
self.data[addr as usize] = value;
}
}
}
impl DebugRead for SimpleMemory {
fn debug_read_8(&mut self, addr: Addr) -> u8 {
*self.data.get(addr as usize).unwrap_or(&0)
}
}
impl MemoryGdbInterface for SimpleMemory {
fn memory_map_xml(&self, offset: u64, length: usize, buf: &mut [u8]) -> usize {
let memory_map = r#"<?xml version="1.0"?>
<!DOCTYPE memory-map
PUBLIC "+//IDN gnu.org//DTD GDB Memory Map V1.0//EN"
"http://sourceware.org/gdb/gdb-memory-map.dtd">
<memory-map>
<memory type="ram" start="0x0" length="16384"/>
</memory-map>"#
.trim()
.as_bytes();
copy_range_to_buf(memory_map, offset, length, buf)
}
}

View file

@ -37,7 +37,6 @@ hex-literal = "0.2.1"
rustyline = { version = "6.0.0", optional = true } rustyline = { version = "6.0.0", optional = true }
nom = { version = "5.0.0", optional = true } nom = { version = "5.0.0", optional = true }
gdbstub = { version = "0.1.2", optional = true, features = ["std"] } gdbstub = { version = "0.1.2", optional = true, features = ["std"] }
goblin = { version = "0.2", optional = true }
fuzzy-matcher = { version = "0.3.4", optional = true } fuzzy-matcher = { version = "0.3.4", optional = true }
bit_reverse = "0.1.8" bit_reverse = "0.1.8"
yaml-rust = "0.4" yaml-rust = "0.4"
@ -53,7 +52,7 @@ harness = false
[features] [features]
default = [] default = []
elf_support = ["goblin"] elf_support = []
debugger = ["nom", "rustyline", "fuzzy-matcher", "elf_support"] debugger = ["nom", "rustyline", "fuzzy-matcher", "elf_support"]
gdb = ["gdbstub"] gdb = ["gdbstub"]
# For use for ports where VideoInterface is not needed like wasm & jni # For use for ports where VideoInterface is not needed like wasm & jni

View file

@ -6,11 +6,11 @@ use std::io::prelude::*;
use std::io::Cursor; use std::io::Cursor;
use std::path::Path; use std::path::Path;
#[cfg(feature = "elf_support")]
use rustboyadvance_utils::elf::{load_elf, GoblinError};
use rustboyadvance_utils::read_bin_file; use rustboyadvance_utils::read_bin_file;
use zip::ZipArchive; use zip::ZipArchive;
#[cfg(feature = "elf_support")]
use goblin;
pub enum LoadRom { pub enum LoadRom {
#[cfg(feature = "elf_support")] #[cfg(feature = "elf_support")]
@ -23,12 +23,22 @@ pub enum LoadRom {
type LoadRomResult = GBAResult<LoadRom>; type LoadRomResult = GBAResult<LoadRom>;
#[cfg(feature = "elf_support")] #[cfg(feature = "elf_support")]
impl From<goblin::error::Error> for GBAError { impl From<GoblinError> for GBAError {
fn from(err: goblin::error::Error) -> GBAError { fn from(err: GoblinError) -> GBAError {
GBAError::CartridgeLoadError(format!("elf parsing error: {}", err)) GBAError::CartridgeLoadError(format!("elf parsing error: {}", err))
} }
} }
#[cfg(feature = "elf_support")]
pub(super) fn try_load_elf(elf_bytes: &[u8]) -> LoadRomResult {
const CART_BASE: usize = 0x0800_0000;
let elf = load_elf(elf_bytes, CART_BASE)?;
Ok(LoadRom::Elf {
data: elf.data,
symbols: elf.symbols,
})
}
fn try_load_zip(data: &[u8]) -> LoadRomResult { fn try_load_zip(data: &[u8]) -> LoadRomResult {
let reader = Cursor::new(data); let reader = Cursor::new(data);
let mut archive = ZipArchive::new(reader)?; let mut archive = ZipArchive::new(reader)?;
@ -45,62 +55,6 @@ fn try_load_zip(data: &[u8]) -> LoadRomResult {
)) ))
} }
#[cfg(feature = "elf_support")]
fn try_load_elf(elf_bytes: &[u8]) -> LoadRomResult {
const CART_BASE: usize = 0x0800_0000;
let elf = goblin::elf::Elf::parse(&elf_bytes)?;
let entry = elf.entry;
if entry != (CART_BASE as u64) {
return Err(GBAError::CartridgeLoadError(
"bad elf entry point, maybe multiboot rom ?".to_owned(),
));
}
let mut rom = vec![0; 0x200_0000];
for phdr in &elf.program_headers {
if phdr.p_type == goblin::elf::program_header::PT_LOAD {
let file_range = phdr.file_range();
let phys_range =
(phdr.p_paddr as usize)..(phdr.p_paddr as usize + phdr.p_memsz as usize);
let phys_range_adjusted = (phdr.p_paddr as usize - CART_BASE)
..(phdr.p_paddr as usize + phdr.p_memsz as usize - CART_BASE);
if phys_range_adjusted.start + (phdr.p_filesz as usize) >= rom.len() {
warn!("ELF: skipping program header {:?}", phdr);
continue;
}
info!(
"ELF: loading segment phdr: {:?} range {:#x?} vec range {:#x?}",
phdr, file_range, phys_range,
);
let src = &elf_bytes[file_range];
let dst = &mut rom[phys_range_adjusted];
dst.copy_from_slice(src);
}
}
let mut symbols = HashMap::new();
let strtab = elf.strtab;
for sym in elf.syms.iter() {
if let Some(Ok(name)) = strtab.get(sym.st_name) {
// TODO do I also want to save the symbol size ?
symbols.insert(name.to_owned(), sym.st_value as u32);
} else {
warn!("failed to parse symbol name sym {:?}", sym);
}
}
Ok(LoadRom::Elf {
data: rom,
symbols: symbols,
})
}
pub(super) fn load_from_file(path: &Path) -> LoadRomResult { pub(super) fn load_from_file(path: &Path) -> LoadRomResult {
let bytes = read_bin_file(path)?; let bytes = read_bin_file(path)?;

View file

@ -331,7 +331,15 @@ impl GameBoyAdvance {
} }
pub fn skip_bios(&mut self) { pub fn skip_bios(&mut self) {
self.cpu.skip_bios(); self.cpu.banks.gpr_banked_r13[0] = 0x0300_7f00; // USR/SYS
self.cpu.banks.gpr_banked_r13[1] = 0x0300_7f00; // FIQ
self.cpu.banks.gpr_banked_r13[2] = 0x0300_7fa0; // IRQ
self.cpu.banks.gpr_banked_r13[3] = 0x0300_7fe0; // SVC
self.cpu.banks.gpr_banked_r13[4] = 0x0300_7f00; // ABT
self.cpu.banks.gpr_banked_r13[5] = 0x0300_7f00; // UND
self.cpu.gpr[13] = 0x0300_7f00;
self.cpu.pc = 0x0800_0000;
self.cpu.cpsr.set(0x5f);
self.sysbus.io.gpu.skip_bios(); self.sysbus.io.gpu.skip_bios();
} }

View file

@ -1,5 +1,5 @@
use super::arm7tdmi::CpuState; use super::arm7tdmi::CpuState;
use super::core::GameBoyAdvance; use super::GameBoyAdvance;
use super::interrupt::*; use super::interrupt::*;
use super::iodev::IoDevices; use super::iodev::IoDevices;
use super::sysbus::SysBus; use super::sysbus::SysBus;

View file

@ -52,9 +52,6 @@ pub use bus::*;
mod mgba_debug; mod mgba_debug;
pub(crate) mod overrides; pub(crate) mod overrides;
#[cfg(feature = "gdb")]
pub mod gdb;
#[cfg(feature = "debugger")] #[cfg(feature = "debugger")]
pub mod debugger; pub mod debugger;

View file

@ -20,5 +20,6 @@ spin_sleep = "0.3.7"
winres = "0.1" winres = "0.1"
[features] [features]
default = ["gdb"]
debugger = ["rustboyadvance-core/debugger"] debugger = ["rustboyadvance-core/debugger"]
gdb = ["rustboyadvance-core/gdb"] gdb = ["rustboyadvance-core/gdb"]

View file

@ -41,14 +41,11 @@ use input::create_input;
use video::{create_video_interface, SCREEN_HEIGHT, SCREEN_WIDTH}; use video::{create_video_interface, SCREEN_HEIGHT, SCREEN_WIDTH};
use rustboyadvance_core::cartridge::BackupType; use rustboyadvance_core::cartridge::BackupType;
#[cfg(feature = "gdb")]
use rustboyadvance_core::gdb::spawn_and_run_gdb_server;
use rustboyadvance_core::prelude::*; use rustboyadvance_core::prelude::*;
use rustboyadvance_utils::FpsCounter; use rustboyadvance_utils::FpsCounter;
const LOG_DIR: &str = ".logs"; const LOG_DIR: &str = ".logs";
#[cfg(feature = "gdb")]
const DEFAULT_GDB_SERVER_ADDR: &'static str = "localhost:1337"; const DEFAULT_GDB_SERVER_ADDR: &'static str = "localhost:1337";
const CANVAS_WIDTH: u32 = SCREEN_WIDTH; const CANVAS_WIDTH: u32 = SCREEN_WIDTH;
@ -240,13 +237,8 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
} }
} }
#[cfg(feature = "gdb")]
if with_gdbserver { if with_gdbserver {
spawn_and_run_gdb_server(&mut gba, DEFAULT_GDB_SERVER_ADDR)?; todo!("gdb")
}
#[cfg(not(feature = "gdb"))]
if with_gdbserver {
panic!("Please compile me with 'gdb' feature")
} }
let mut fps_counter = FpsCounter::default(); let mut fps_counter = FpsCounter::default();
@ -277,7 +269,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
info!("ending debugger...") info!("ending debugger...")
} }
#[cfg(feature = "gdb")] #[cfg(feature = "gdb")]
Scancode::F2 => spawn_and_run_gdb_server(&mut gba, DEFAULT_GDB_SERVER_ADDR)?, Scancode::F2 => todo!("gdb"),
Scancode::F5 => { Scancode::F5 => {
info!("Saving state ..."); info!("Saving state ...");
let save = gba.save_state()?; let save = gba.save_state()?;

View file

@ -7,6 +7,9 @@ edition = "2021"
[dependencies] [dependencies]
ringbuf = "0.2.2" ringbuf = "0.2.2"
log = "0.4.8"
goblin = "0.2"
[target.'cfg(target_arch="wasm32")'.dependencies] [target.'cfg(target_arch="wasm32")'.dependencies]
instant = { version = "0.1.2", features = ["wasm-bindgen"] } instant = { version = "0.1.2", features = ["wasm-bindgen"] }

67
utils/src/elf.rs Normal file
View file

@ -0,0 +1,67 @@
use goblin::elf;
use log::{info, warn};
use std::collections::HashMap;
pub use goblin::error::Error as GoblinError;
pub type SymbolMap = HashMap<String, u32>;
pub struct LoadedElf {
pub data: Vec<u8>,
pub entry: u64,
pub symbols: SymbolMap,
}
fn read_symbols_from_elf(elf: &elf::Elf) -> SymbolMap {
let mut symbols = SymbolMap::new();
let strtab = &elf.strtab;
for sym in elf.syms.iter() {
if let Some(Ok(name)) = strtab.get(sym.st_name) {
// TODO do I also want to save the symbol size ?
symbols.insert(name.to_owned(), sym.st_value as u32);
} else {
warn!("failed to parse symbol name sym {:?}", sym);
}
}
symbols
}
pub fn read_symbols(elf_bytes: &[u8]) -> goblin::error::Result<SymbolMap> {
let elf = elf::Elf::parse(elf_bytes)?;
Ok(read_symbols_from_elf(&elf))
}
pub fn load_elf(elf_bytes: &[u8], base: usize) -> goblin::error::Result<LoadedElf> {
let elf = elf::Elf::parse(elf_bytes)?;
let mut rom = vec![0; 0x200_0000];
for phdr in &elf.program_headers {
if phdr.p_type == elf::program_header::PT_LOAD {
let file_range = phdr.file_range();
let phys_range =
(phdr.p_paddr as usize)..(phdr.p_paddr as usize + phdr.p_memsz as usize);
let phys_range_adjusted = (phdr.p_paddr as usize - base)
..(phdr.p_paddr as usize + phdr.p_memsz as usize - base);
if phys_range_adjusted.start + (phdr.p_filesz as usize) >= rom.len() {
warn!("ELF: skipping program header {:?}", phdr);
continue;
}
info!(
"ELF: loading segment phdr: {:?} range {:#x?} vec range {:#x?}",
phdr, file_range, phys_range,
);
let src = &elf_bytes[file_range];
let dst = &mut rom[phys_range_adjusted];
dst.copy_from_slice(src);
}
}
Ok(LoadedElf {
data: rom,
entry: elf.entry,
symbols: read_symbols_from_elf(&elf),
})
}

View file

@ -6,6 +6,8 @@ use std::path::Path;
use std::ptr; use std::ptr;
use std::time; use std::time;
pub mod elf;
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
type Instant = time::Instant; type Instant = time::Instant;
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]