Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add write support to Program #384

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions _drgn.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -402,12 +402,33 @@ class Program:
:raises FaultError: if the address is invalid; see :meth:`read()`
"""
...
def write(
self, address: IntegerLike, data: bytes, physical: bool = False
) -> bytes:
"""
Writes *data* starting at *address* in the program memory. The
address may be virtual (the default) or physical if the program
supports it.

>>> prog.write(0xffffffffbe012b40, b'swapper/0\x00\x00\x00\x00\x00\x00\x00')

:param address: The starting address.
:param data: The bytes to write.
:param physical: Whether *address* is a physical memory address. If
``False``, then it is a virtual memory address. Physical memory can
usually only be read when the program is an operating system
kernel.
:raises FaultError: if the address range is invalid or the type of
address (physical or virtual) is not supported by the program
"""
...
def add_memory_segment(
self,
address: IntegerLike,
size: IntegerLike,
read_fn: Callable[[int, int, int, bool], bytes],
physical: bool = False,
write_fn: Optional[Callable[[int, bytes, int, bool], None]] = None,
) -> None:
"""
Define a region of memory in the program.
Expand All @@ -425,6 +446,10 @@ class Program:
the address is physical: ``(address, count, offset, physical)``. It
should return the requested number of bytes as :class:`bytes` or
another :ref:`buffer <python:binaryseq>` type.
:param write_fn: Callable to call to write memory to the segment. It is
passed the address being written to, the bytes to write,
the offset in bytes from the beginning of the segment, and whether
the address is physical: ``(address, data, offset, physical)``.
"""
...
def add_type_finder(
Expand Down
4 changes: 2 additions & 2 deletions libdrgn/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,8 @@ libdrgnimpl_la_SOURCES = $(ARCH_DEFS_PYS:_defs.py=.c) \
linux_kernel_helpers.c \
log.c \
log.h \
memory_reader.c \
memory_reader.h \
memory_interface.c \
memory_interface.h \
minmax.h \
nstring.h \
object.c \
Expand Down
37 changes: 27 additions & 10 deletions libdrgn/drgn.h
Original file line number Diff line number Diff line change
Expand Up @@ -534,20 +534,22 @@ struct drgn_error *drgn_program_create(const struct drgn_platform *platform,
void drgn_program_destroy(struct drgn_program *prog);

/**
* Callback implementing a memory read.
* Callback implementing a memory read and write.
*
* @param[out] buf Buffer to read into.
* @param[in] address Address which we are reading from.
* @param[in] count Number of bytes to read.
* @param[in] is_wirte Whether the operation is a write.
* @param[inout] buf Buffer to read into or write to.
* @param[in] address Address which we are reading from or writing to.
* @param[in] count Number of bytes to read or write.
* @param[in] offset Offset in bytes of @p address from the beginning of the
* segment.
* @param[in] arg Argument passed to @ref drgn_program_add_memory_segment().
* @param[in] physical Whether @c address is physical.
* @return @c NULL on success, non-@c NULL on error.
*/
typedef struct drgn_error *(*drgn_memory_read_fn)(void *buf, uint64_t address,
size_t count, uint64_t offset,
void *arg, bool physical);
typedef struct drgn_error *(*drgn_memory_rw_fn)(bool is_write, void *buf,
uint64_t address, size_t count,
uint64_t offset, void *arg,
bool physical);

/**
* Register a segment of memory in a @ref drgn_program.
Expand All @@ -558,14 +560,14 @@ typedef struct drgn_error *(*drgn_memory_read_fn)(void *buf, uint64_t address,
*
* @param[in] address Address of the segment.
* @param[in] size Size of the segment in bytes.
* @param[in] read_fn Callback to read from segment.
* @param[in] arg Argument to pass to @p read_fn.
* @param[in] rw_fn Callback to read from/write to segment.
* @param[in] arg Argument to pass to @p rw_fn.
* @param[in] physical Whether to add a physical memory segment.
* @return @c NULL on success, non-@c NULL on error.
*/
struct drgn_error *
drgn_program_add_memory_segment(struct drgn_program *prog, uint64_t address,
uint64_t size, drgn_memory_read_fn read_fn,
uint64_t size, drgn_memory_rw_fn rw_fn,
void *arg, bool physical);

/**
Expand Down Expand Up @@ -791,6 +793,21 @@ struct drgn_error *drgn_program_read_memory(struct drgn_program *prog,
void *buf, uint64_t address,
size_t count, bool physical);

/**
* Write to a program's memory.
*
* @param[in] prog Program to write to.
* @param[in] buf Buffer to write.
* @param[in] address Starting address in memory to write.
* @param[in] count Number of bytes to write.
* @param[in] physical Whether @c address is physical. A program may support
* only virtual or physical addresses or both.
* @return @c NULL on success, non-@c NULL on error.
*/
struct drgn_error *drgn_program_write_memory(struct drgn_program *prog,
void *buf, uint64_t address,
size_t count, bool physical);

/**
* Read a C string from a program's memory.
*
Expand Down
14 changes: 9 additions & 5 deletions libdrgn/kdump.c
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,14 @@ static struct drgn_error *drgn_platform_from_kdump(kdump_ctx_t *ctx,
return NULL;
}

static struct drgn_error *drgn_read_kdump(void *buf, uint64_t address,
size_t count, uint64_t offset,
void *arg, bool physical)
static struct drgn_error *drgn_read_kdump(bool is_write, void *buf,
uint64_t address, size_t count,
uint64_t offset, void *arg,
bool physical)
{
if(is_write)
return drgn_error_create_fault("cannot write to kdump memory",
address);
kdump_ctx_t *ctx = arg;
kdump_status ks;

Expand Down Expand Up @@ -154,8 +158,8 @@ struct drgn_error *drgn_program_set_kdump(struct drgn_program *prog)
err = drgn_program_add_memory_segment(prog, 0, UINT64_MAX,
drgn_read_kdump, ctx, true);
if (err) {
drgn_memory_reader_deinit(&prog->reader);
drgn_memory_reader_init(&prog->reader);
drgn_memory_interface_deinit(&prog->memory);
drgn_memory_interface_init(&prog->memory);
goto err_platform;
}

Expand Down
10 changes: 7 additions & 3 deletions libdrgn/linux_kernel.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,14 @@

#include "drgn_program_parse_vmcoreinfo.inc"

struct drgn_error *read_memory_via_pgtable(void *buf, uint64_t address,
size_t count, uint64_t offset,
void *arg, bool physical)
struct drgn_error *read_memory_via_pgtable(bool is_write, void *buf,
uint64_t address, size_t count,
uint64_t offset, void *arg,
bool physical)
{
if(is_write)
return drgn_error_create_fault("cannot write to memory",
address);
struct drgn_program *prog = arg;
return linux_helper_read_vm(prog, prog->vmcoreinfo.swapper_pg_dir,
address, buf, count);
Expand Down
7 changes: 4 additions & 3 deletions libdrgn/linux_kernel.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@

struct drgn_debug_info_load_state;

struct drgn_error *read_memory_via_pgtable(void *buf, uint64_t address,
size_t count, uint64_t offset,
void *arg, bool physical);
struct drgn_error *read_memory_via_pgtable(bool is_write, void *buf,
uint64_t address, size_t count,
uint64_t offset, void *arg,
bool physical);

struct drgn_error *drgn_program_parse_vmcoreinfo(struct drgn_program *prog,
const char *desc,
Expand Down
72 changes: 39 additions & 33 deletions libdrgn/memory_reader.c → libdrgn/memory_interface.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@
#include <string.h>
#include <unistd.h>

#include "memory_reader.h"
#include "memory_interface.h"
#include "minmax.h"

/** Memory segment in a @ref drgn_memory_reader. */
/** Memory segment in a @ref drgn_memory_interface. */
struct drgn_memory_segment {
struct binary_tree_node node;
/** Address range of the segment in memory (inclusive). */
Expand All @@ -22,9 +22,9 @@ struct drgn_memory_segment {
* drgn_memory_segment::min_address.
*/
uint64_t orig_min_address;
/** Read callback. */
drgn_memory_read_fn read_fn;
/** Argument to pass to @ref drgn_memory_segment::read_fn. */
/** Read/write callback. */
drgn_memory_rw_fn rw_fn;
/** Argument to pass to @ref drgn_memory_segment::rw_fn. */
void *arg;
};

Expand All @@ -38,10 +38,10 @@ DEFINE_BINARY_SEARCH_TREE_FUNCTIONS(drgn_memory_segment_tree, node,
drgn_memory_segment_to_key,
binary_search_tree_scalar_cmp, splay);

void drgn_memory_reader_init(struct drgn_memory_reader *reader)
void drgn_memory_interface_init(struct drgn_memory_interface *memory)
{
drgn_memory_segment_tree_init(&reader->virtual_segments);
drgn_memory_segment_tree_init(&reader->physical_segments);
drgn_memory_segment_tree_init(&memory->virtual_segments);
drgn_memory_segment_tree_init(&memory->physical_segments);
}

static void free_memory_segment_tree(struct drgn_memory_segment_tree *tree)
Expand All @@ -57,29 +57,29 @@ static void free_memory_segment_tree(struct drgn_memory_segment_tree *tree)
}
}

void drgn_memory_reader_deinit(struct drgn_memory_reader *reader)
void drgn_memory_interface_deinit(struct drgn_memory_interface *memory)
{
free_memory_segment_tree(&reader->physical_segments);
free_memory_segment_tree(&reader->virtual_segments);
free_memory_segment_tree(&memory->physical_segments);
free_memory_segment_tree(&memory->virtual_segments);
}

bool drgn_memory_reader_empty(struct drgn_memory_reader *reader)
bool drgn_memory_interface_empty(struct drgn_memory_interface *memory)
{
return (drgn_memory_segment_tree_empty(&reader->virtual_segments) &&
drgn_memory_segment_tree_empty(&reader->physical_segments));
return (drgn_memory_segment_tree_empty(&memory->virtual_segments) &&
drgn_memory_segment_tree_empty(&memory->physical_segments));
}

struct drgn_error *
drgn_memory_reader_add_segment(struct drgn_memory_reader *reader,
uint64_t min_address, uint64_t max_address,
drgn_memory_read_fn read_fn, void *arg,
bool physical)
drgn_memory_interface_add_segment(struct drgn_memory_interface *memory,
uint64_t min_address, uint64_t max_address,
drgn_memory_rw_fn rw_fn, void *arg,
bool physical)
{
assert(min_address <= max_address);

struct drgn_memory_segment_tree *tree = (physical ?
&reader->physical_segments :
&reader->virtual_segments);
&memory->physical_segments :
&memory->virtual_segments);

/*
* This is split into two steps: the first step handles an overlapping
Expand Down Expand Up @@ -128,7 +128,7 @@ drgn_memory_reader_add_segment(struct drgn_memory_reader *reader,
tail->min_address = max_address + 1;
tail->max_address = it.entry->max_address;
tail->orig_min_address = it.entry->orig_min_address;
tail->read_fn = it.entry->read_fn;
tail->rw_fn = it.entry->rw_fn;
tail->arg = it.entry->arg;

drgn_memory_segment_tree_insert(tree, tail, NULL);
Expand Down Expand Up @@ -227,24 +227,25 @@ drgn_memory_reader_add_segment(struct drgn_memory_reader *reader,
truncate_tail->max_address = min_address - 1;
segment->min_address = segment->orig_min_address = min_address;
segment->max_address = max_address;
segment->read_fn = read_fn;
segment->rw_fn = rw_fn;
segment->arg = arg;
/* If the segment is stolen, then it's already in the tree. */
if (!stolen)
drgn_memory_segment_tree_insert(tree, segment, NULL);
return NULL;
}

struct drgn_error *drgn_memory_reader_read(struct drgn_memory_reader *reader,
void *buf, uint64_t address,
size_t count, bool physical)
struct drgn_error *drgn_memory_interface_rw(struct drgn_memory_interface *memory,
bool is_write, void *buf,
uint64_t address, size_t count,
bool physical)
{
assert(count == 0 || count - 1 <= UINT64_MAX - address);

struct drgn_error *err;
struct drgn_memory_segment_tree *tree = (physical ?
&reader->physical_segments :
&reader->virtual_segments);
&memory->physical_segments :
&memory->virtual_segments);
char *p = buf;
while (count > 0) {
struct drgn_memory_segment *segment =
Expand All @@ -257,9 +258,9 @@ struct drgn_error *drgn_memory_reader_read(struct drgn_memory_reader *reader,

size_t n = min((uint64_t)(count - 1),
segment->max_address - address) + 1;
err = segment->read_fn(p, address, n,
address - segment->orig_min_address,
segment->arg, physical);
err = segment->rw_fn(is_write, p, address, n,
address - segment->orig_min_address,
segment->arg, physical);
if (err)
return err;
p += n;
Expand All @@ -269,10 +270,15 @@ struct drgn_error *drgn_memory_reader_read(struct drgn_memory_reader *reader,
return NULL;
}

struct drgn_error *drgn_read_memory_file(void *buf, uint64_t address,
size_t count, uint64_t offset,
void *arg, bool physical)
struct drgn_error *drgn_read_memory_file(bool is_write, void *buf,
uint64_t address, size_t count,
uint64_t offset, void *arg,
bool physical)
{
if(is_write)
return drgn_error_create_fault("cannot write to memory",
address);

struct drgn_memory_file_segment *file_segment = arg;
size_t file_count;
if (offset < file_segment->file_size) {
Expand Down
Loading
Loading