Skip to content

Commit

Permalink
Add support for qXfer:libraries{,-svr4}:read (#142)
Browse files Browse the repository at this point in the history
* Add support for `qXfer:libraries{,-svr4}:read`

A toy project I've been working on involves loading a custom kernel and
a custom guest into a KVM instance, and these commands enable `gdb` to
be able to debug both the guest and the kernel at the same time by
reporting where both images are loaded in the address space.

Initially this commit only implemented `qXfer:libraries:read` but it
turned out that gdb never actually used that command. When implementing
`qXfer:libraries-svr4:read`, however, gdb invoked that automatically
(presumably it's target-specific). I ended up including both here for
completeness.

* Add a note to the README

* rustfmt

* Add armv4t example

* Remove support for `libraries` command

Unknown how to actually get gdb to use it yet.

* Fix a TODO

* Update `section_offsets` documentation

* Run cargo fmt
  • Loading branch information
alexcrichton committed Jan 6, 2024
1 parent 7cba7ca commit 9790714
Show file tree
Hide file tree
Showing 12 changed files with 145 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Expand Up @@ -88,6 +88,7 @@ Of course, most use-cases will want to support additional debugging features as
- Can be used to automatically read the remote executable on attach (using `ExecFile`)
- Read auxiliary vector (`info auxv`)
- Extra thread info (`info threads`)
- Extra library information (`info sharedlibraries`)

_Note:_ GDB features are implemented on an as-needed basis by `gdbstub`'s contributors. If there's a missing GDB feature that you'd like `gdbstub` to implement, please file an issue and/or open a PR!

Expand Down
28 changes: 28 additions & 0 deletions examples/armv4t/gdb/libraries.rs
@@ -0,0 +1,28 @@
use super::copy_range_to_buf;
use crate::emu::Emu;
use gdbstub::target;
use gdbstub::target::TargetResult;

impl target::ext::libraries::LibrariesSvr4 for Emu {
fn get_libraries_svr4(
&self,
offset: u64,
length: usize,
buf: &mut [u8],
) -> TargetResult<usize, Self> {
// `l_ld` is the address of the `PT_DYNAMIC` ELF segment, so fake an
// address here.
//
// The `main-lm`, `lm`, and `lmid` seem to refer to in-memory structures
// which gdb may read, but gdb also seems to work well enough if they're
// null-ish or otherwise pointing to non-present things.
let xml = r#"
<library-list-svr4 version="1.0" main-lm="0x4">
<library name="/test.elf" lm="0x8" l_addr="0" l_ld="0" lmid="0x14"/>
</library-list-svr4>
"#
.trim()
.as_bytes();
Ok(copy_range_to_buf(xml, offset, length, buf))
}
}
8 changes: 8 additions & 0 deletions examples/armv4t/gdb/mod.rs
Expand Up @@ -20,6 +20,7 @@ mod catch_syscalls;
mod exec_file;
mod extended_mode;
mod host_io;
mod libraries;
mod lldb_register_info_override;
mod memory_map;
mod monitor_cmd;
Expand Down Expand Up @@ -153,6 +154,13 @@ impl Target for Emu {
fn support_auxv(&mut self) -> Option<target::ext::auxv::AuxvOps<'_, Self>> {
Some(self)
}

#[inline(always)]
fn support_libraries_svr4(
&mut self,
) -> Option<target::ext::libraries::LibrariesSvr4Ops<'_, Self>> {
Some(self)
}
}

impl SingleThreadBase for Emu {
Expand Down
4 changes: 4 additions & 0 deletions src/protocol/commands.rs
Expand Up @@ -330,4 +330,8 @@ commands! {
lldb_register_info {
"qRegisterInfo" => _qRegisterInfo::qRegisterInfo,
}

libraries_svr4 use 'a {
"qXfer:libraries-svr4:read" => _qXfer_libraries_svr4_read::qXferLibrariesSvr4Read<'a>,
}
}
18 changes: 18 additions & 0 deletions src/protocol/commands/_qXfer_libraries_svr4_read.rs
@@ -0,0 +1,18 @@
use crate::protocol::common::qxfer::ParseAnnex;
use crate::protocol::common::qxfer::QXferReadBase;

pub type qXferLibrariesSvr4Read<'a> = QXferReadBase<'a, LibrariesSvr4Annex>;

#[derive(Debug)]
pub struct LibrariesSvr4Annex;

impl<'a> ParseAnnex<'a> for LibrariesSvr4Annex {
#[inline(always)]
fn from_buf(buf: &[u8]) -> Option<Self> {
if buf != b"" {
return None;
}

Some(LibrariesSvr4Annex)
}
}
2 changes: 2 additions & 0 deletions src/stub/core_impl.rs
Expand Up @@ -31,6 +31,7 @@ mod catch_syscalls;
mod exec_file;
mod extended_mode;
mod host_io;
mod libraries;
mod lldb_register_info;
mod memory_map;
mod monitor_cmd;
Expand Down Expand Up @@ -216,6 +217,7 @@ impl<T: Target, C: Connection> GdbStubImpl<T, C> {
Command::Auxv(cmd) => self.handle_auxv(res, target, cmd),
Command::ThreadExtraInfo(cmd) => self.handle_thread_extra_info(res, target, cmd),
Command::LldbRegisterInfo(cmd) => self.handle_lldb_register_info(res, target, cmd),
Command::LibrariesSvr4(cmd) => self.handle_libraries_svr4(res, target, cmd),
// in the worst case, the command could not be parsed...
Command::Unknown(cmd) => {
// HACK: if the user accidentally sends a resume command to a
Expand Down
4 changes: 4 additions & 0 deletions src/stub/core_impl/base.rs
Expand Up @@ -195,6 +195,10 @@ impl<T: Target, C: Connection> GdbStubImpl<T, C> {
res.write_str(";qXfer:auxv:read+")?;
}

if target.support_libraries_svr4().is_some() {
res.write_str(";qXfer:libraries-svr4:read+")?;
}

HandlerStatus::Handled
}

Expand Down
36 changes: 36 additions & 0 deletions src/stub/core_impl/libraries.rs
@@ -0,0 +1,36 @@
use super::prelude::*;
use crate::protocol::commands::ext::LibrariesSvr4;

impl<T: Target, C: Connection> GdbStubImpl<T, C> {
pub(crate) fn handle_libraries_svr4(
&mut self,
res: &mut ResponseWriter<'_, C>,
target: &mut T,
command: LibrariesSvr4<'_>,
) -> Result<HandlerStatus, Error<T::Error, C::Error>> {
let ops = match target.support_libraries_svr4() {
Some(ops) => ops,
None => return Ok(HandlerStatus::Handled),
};

crate::__dead_code_marker!("libraries-svr4", "impl");

let handler_status = match command {
LibrariesSvr4::qXferLibrariesSvr4Read(cmd) => {
let ret = ops
.get_libraries_svr4(cmd.offset, cmd.length, cmd.buf)
.handle_error()?;
if ret == 0 {
res.write_str("l")?;
} else {
res.write_str("m")?;
// TODO: add more specific error variant?
res.write_binary(cmd.buf.get(..ret).ok_or(Error::PacketBufferOverflow)?)?;
}
HandlerStatus::Handled
}
};

Ok(handler_status)
}
}
29 changes: 29 additions & 0 deletions src/target/ext/libraries.rs
@@ -0,0 +1,29 @@
//! Report information about the loaded shared libraries for targets where there
//! are possibly multiple files to be debugged mapped into the same address
//! space.

use crate::target::Target;
use crate::target::TargetResult;

/// Target Extension - List an SVR4 (System-V/Unix) target's libraries.
pub trait LibrariesSvr4: Target {
/// Get library list XML for this target.
///
/// See the [GDB Documentation] for a description of the format.
///
/// [GDB Documentation]: https://sourceware.org/gdb/current/onlinedocs/gdb.html/Library-List-Format-for-SVR4-Targets.html
///
/// Return the number of bytes written into `buf` (which may be less than
/// `length`).
///
/// If `offset` is greater than the length of the underlying data, return
/// `Ok(0)`.
fn get_libraries_svr4(
&self,
offset: u64,
length: usize,
buf: &mut [u8],
) -> TargetResult<usize, Self>;
}

define_ext!(LibrariesSvr4Ops, LibrariesSvr4);
1 change: 1 addition & 0 deletions src/target/ext/mod.rs
Expand Up @@ -265,6 +265,7 @@ pub mod catch_syscalls;
pub mod exec_file;
pub mod extended_mode;
pub mod host_io;
pub mod libraries;
pub mod lldb_register_info_override;
pub mod memory_map;
pub mod monitor_cmd;
Expand Down
7 changes: 7 additions & 0 deletions src/target/ext/section_offsets.rs
Expand Up @@ -8,6 +8,13 @@
//! limited to reporting the offsets for code, data and bss, and is
//! generally considered a legacy feature.
//!
//! For System-V architectures GDB may use the `qXfer:libraries-svr4:read`
//! command to try to learn about loaded libraries and this can be implemented
//! with the [`LibrariesSvr4`
//! trait](crate::target::ext::libraries::LibrariesSvr4). Note that not all
//! targets may query this and it may not be applicable in all situations
//! either.
//!
//! For targets where library offsets are maintained externally (e.g. Windows)
//! you should consider implementing the more flexible `qXfer:library:read`.
//! See issue [#20](https://github.com/daniel5151/gdbstub/issues/20) for more
Expand Down
7 changes: 7 additions & 0 deletions src/target/mod.rs
Expand Up @@ -676,6 +676,13 @@ pub trait Target {
fn support_auxv(&mut self) -> Option<ext::auxv::AuxvOps<'_, Self>> {
None
}

/// Support for reading a list of libraries for SVR4 (System-V/Unix)
/// platforms.
#[inline(always)]
fn support_libraries_svr4(&mut self) -> Option<ext::libraries::LibrariesSvr4Ops<'_, Self>> {
None
}
}

macro_rules! __delegate {
Expand Down

0 comments on commit 9790714

Please sign in to comment.