You may also find it useful to refer to the in-tree armv4t and armv4t_multicore examples when transitioning between versions.
0.4 -> 0.5While the overall structure of the API has remained the same, 0.5.0 does introduce a few breaking API changes that require some attention. That being said, it should not be a difficult migration, and updating to 0.5.0 from 0.4 shouldn't take more than 10 mins of refactoring.
Check out CHANGELOG.md for a full list of changes.
{Hw,Sw}Breakpoint/Watchpoint IDETs under the newly added Breakpoints IDETs.The various breakpoint IDETs that were previously directly implemented on the top-level Target trait have now been consolidated under a single Breakpoints IDET. This is purely an organizational change, and will not require rewriting any existing {add, remove}_{sw_break,hw_break,watch}point implementations.
Porting from 0.4 to 0.5 should be as simple as:
// ==== 0.4.x ==== // impl Target for Emu { fn sw_breakpoint(&mut self) -> Option<target::ext::breakpoints::SwBreakpointOps<Self>> { Some(self) } fn hw_watchpoint(&mut self) -> Option<target::ext::breakpoints::HwWatchpointOps<Self>> { Some(self) } } impl target::ext::breakpoints::SwBreakpoint for Emu { fn add_sw_breakpoint(&mut self, addr: u32) -> TargetResult<bool, Self> { ... } fn remove_sw_breakpoint(&mut self, addr: u32) -> TargetResult<bool, Self> { ... } } impl target::ext::breakpoints::HwWatchpoint for Emu { fn add_hw_watchpoint(&mut self, addr: u32, kind: WatchKind) -> TargetResult<bool, Self> { ... } fn remove_hw_watchpoint(&mut self, addr: u32, kind: WatchKind) -> TargetResult<bool, Self> { ... } } // ==== 0.5.0 ==== // impl Target for Emu { // (New Method) // fn breakpoints(&mut self) -> Option<target::ext::breakpoints::BreakpointsOps<Self>> { Some(self) } } impl target::ext::breakpoints::Breakpoints for Emu { fn sw_breakpoint(&mut self) -> Option<target::ext::breakpoints::SwBreakpointOps<Self>> { Some(self) } fn hw_watchpoint(&mut self) -> Option<target::ext::breakpoints::HwWatchpointOps<Self>> { Some(self) } } // (Almost Unchanged) // impl target::ext::breakpoints::SwBreakpoint for Emu { // /-- New `kind` parameter // \/ fn add_sw_breakpoint(&mut self, addr: u32, _kind: arch::arm::ArmBreakpointKind) -> TargetResult<bool, Self> { ... } fn remove_sw_breakpoint(&mut self, addr: u32, _kind: arch::arm::ArmBreakpointKind) -> TargetResult<bool, Self> { ... } } // (Unchanged) // impl target::ext::breakpoints::HwWatchpoint for Emu { fn add_hw_watchpoint(&mut self, addr: u32, kind: WatchKind) -> TargetResult<bool, Self> { ... } fn remove_hw_watchpoint(&mut self, addr: u32, kind: WatchKind) -> TargetResult<bool, Self> { ... } }
{read,write}_register) are now a separate SingleRegisterAccess traitSingle register access is not a required part of the GDB protocol, and as such, has been moved out into its own IDET. This is a purely organizational change, and will not require rewriting any existing {read,write}_register implementations.
Porting from 0.4 to 0.5 should be as simple as:
// ==== 0.4.x ==== // impl SingleThreadOps for Emu { fn read_register(&mut self, reg_id: arch::arm::reg::id::ArmCoreRegId, dst: &mut [u8]) -> TargetResult<(), Self> { ... } fn write_register(&mut self, reg_id: arch::arm::reg::id::ArmCoreRegId, val: &[u8]) -> TargetResult<(), Self> { ... } } // ==== 0.5.0 ==== // impl SingleThreadOps for Emu { // (New Method) // fn single_register_access(&mut self) -> Option<target::ext::base::SingleRegisterAccessOps<(), Self>> { Some(self) } } impl target::ext::base::SingleRegisterAccess<()> for Emu { // /-- New `tid` parameter (ignored on single-threaded systems) // \/ fn read_register(&mut self, _tid: (), reg_id: arch::arm::reg::id::ArmCoreRegId, dst: &mut [u8]) -> TargetResult<(), Self> { ... } fn write_register(&mut self, _tid: (), reg_id: arch::arm::reg::id::ArmCoreRegId, val: &[u8]) -> TargetResult<(), Self> { ... } }
MultiThreadOps::resume APIIn 0.4, resuming a multithreaded target was done using an Actions iterator passed to a single resume method. In hindsight, this approach had a couple issues:
Actions iterator was guaranteed to return at least one element, often forcing users to manually unwrapResumeAction (e.g: range stepping) required a breaking change, and forced users to change their resume method implementation regardless whether or not their target ended up using said action.In 0.5, the API has been refactored to address some of these issues, and the single resume method has now been split into multiple “lifecycle” methods:
resumeresume is called the target should resume execution.set_resume_actionresume, and notifies the target how a particular Tid should be resumed.set_resume_action_range_stepMultiThreadRangeStepping IDET which includes this method.clear_resume_actionsThreadStopReason from resume, this method will be called to reset the previously set per-tid resume actions.NOTE: This change does mean that targets are now responsible for maintaining some internal state that maps Tids to ResumeActions. Thankfully, this isn't difficult at all, and can as simple as maintaining a HashMap<Tid, ResumeAction>.
Please refer to the in-tree armv4t_multicore example for an example of how this new resume flow works.