libdrgn: make memory reader pluggable with callbacks

I've been planning to make memory readers pluggable (in order to support
use cases like, e.g., reading a core file over the network), but the
C-style "inheritance" drgn uses internally is awkward as a library
interface; it's much easier to just register a callback. This change
effectively makes drgn_memory_reader a mapping from a memory range to an
arbitrary callback. As a bonus, this means that read callbacks can be
mixed and matched; a part of memory can be in a core file, another part
can be in the executable file, and another part could be filled from an
arbitrary buffer.
This commit is contained in:
Omar Sandoval 2019-04-26 11:56:47 -07:00
parent e78e6c9226
commit 417a6f0d76
13 changed files with 677 additions and 621 deletions

View File

@ -19,7 +19,7 @@ libdrgnimpl_la_SOURCES = cityhash.h \
language_c.c \
lexer.c \
lexer.h \
memory_file_reader.c \
memory_reader.c \
memory_reader.h \
mock.c \
object.c \
@ -60,6 +60,7 @@ CLEANFILES = python/constants.c python/docstrings.c
_drgn_la_SOURCES = python/docstrings.h \
python/drgnpy.h \
python/error.c \
python/memory_reader.c \
python/module.c \
python/object.c \
python/program.c \

View File

@ -1,167 +0,0 @@
// Copyright 2018-2019 - Omar Sandoval
// SPDX-License-Identifier: GPL-3.0+
#include <inttypes.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "internal.h"
#include "memory_reader.h"
static struct drgn_error *pread_all(int fd, void *buf, size_t count,
off_t offset)
{
char *p = buf;
while (count) {
ssize_t ret;
ret = pread(fd, p, count, offset);
if (ret == -1) {
if (errno == EINTR)
continue;
return drgn_error_create_os(errno, NULL, "pread");
} else if (ret == 0) {
return drgn_error_format(DRGN_ERROR_FAULT,
"short read from memory file");
}
p += ret;
count -= ret;
offset += ret;
}
return NULL;
}
static struct drgn_error *
drgn_memory_file_reader_read(struct drgn_memory_reader *reader, void *buf,
uint64_t address, size_t count, bool physical)
{
struct drgn_error *err;
struct drgn_memory_file_reader *freader;
char *p = buf;
freader = container_of(reader, struct drgn_memory_file_reader, reader);
while (count) {
struct drgn_memory_file_segment *segment;
uint64_t segment_address;
uint64_t segment_offset;
off_t read_offset;
size_t read_count, zero_count;
int i;
/*
* The most recently used segments are at the end of the list,
* so search backwards.
*/
for (i = freader->num_segments - 1; i >= 0; i--) {
segment = &freader->segments[i];
segment_address = (physical ? segment->phys_addr :
segment->virt_addr);
if (segment_address == UINT64_MAX)
continue;
if (segment_address <= address &&
address < segment_address + segment->mem_size)
break;
}
if (i < 0) {
return drgn_error_format(DRGN_ERROR_FAULT,
"could not find memory segment containing 0x%" PRIx64,
address);
}
/* Move the used segment to the end of the list. */
if ((size_t)i != freader->num_segments - 1) {
struct drgn_memory_file_segment tmp = *segment;
memmove(&freader->segments[i],
&freader->segments[i + 1],
(freader->num_segments - i - 1) *
sizeof(*freader->segments));
segment = &freader->segments[freader->num_segments - 1];
*segment = tmp;
}
segment_offset = address - segment_address;
if (segment_offset < segment->file_size)
read_count = min(segment->file_size - segment_offset,
(uint64_t)count);
else
read_count = 0;
if (segment_offset + read_count < segment->mem_size)
zero_count = min(segment->mem_size - segment_offset - read_count,
(uint64_t)(count - read_count));
else
zero_count = 0;
read_offset = segment->file_offset + segment_offset;
err = pread_all(freader->fd, p, read_count, read_offset);
if (err)
return err;
memset(p + read_count, 0, zero_count);
p += read_count + zero_count;
count -= read_count + zero_count;
address += read_count + zero_count;
}
return NULL;
}
static void drgn_memory_file_reader_destroy(struct drgn_memory_reader *reader)
{
struct drgn_memory_file_reader *freader;
freader = container_of(reader, struct drgn_memory_file_reader, reader);
free(freader->segments);
free(freader);
}
static const struct drgn_memory_reader_ops drgn_memory_file_reader_ops = {
.destroy = drgn_memory_file_reader_destroy,
.read = drgn_memory_file_reader_read,
};
struct drgn_error *
drgn_memory_file_reader_create(int fd, struct drgn_memory_file_reader **ret)
{
struct drgn_memory_file_reader *freader;
freader = malloc(sizeof(*freader));
if (!freader)
return &drgn_enomem;
freader->reader.ops = &drgn_memory_file_reader_ops;
freader->segments = NULL;
freader->num_segments = 0;
freader->capacity = 0;
freader->fd = fd;
*ret = freader;
return NULL;
}
struct drgn_error *
drgn_memory_file_reader_add_segment(struct drgn_memory_file_reader *freader,
const struct drgn_memory_file_segment *segment)
{
if (segment->file_offset > OFF_MAX) {
return drgn_error_create(DRGN_ERROR_INVALID_ARGUMENT,
"segment offset does not fit in off_t");
}
if (segment->file_size > OFF_MAX - segment->file_offset) {
return drgn_error_create(DRGN_ERROR_INVALID_ARGUMENT,
"segment end does not fit in off_t");
}
if (freader->num_segments >= freader->capacity) {
size_t new_capacity;
if (freader->capacity)
new_capacity = freader->capacity * 2;
else
new_capacity = 1;
if (!resize_array(&freader->segments, new_capacity))
return &drgn_enomem;
freader->capacity = new_capacity;
}
freader->segments[freader->num_segments++] = *segment;
return NULL;
}

150
libdrgn/memory_reader.c Normal file
View File

@ -0,0 +1,150 @@
// Copyright 2018-2019 - Omar Sandoval
// SPDX-License-Identifier: GPL-3.0+
#include <inttypes.h>
#include <string.h>
#include <unistd.h>
#include "internal.h"
#include "memory_reader.h"
void drgn_memory_reader_init(struct drgn_memory_reader *reader)
{
reader->segments = NULL;
reader->num_segments = reader->capacity = 0;
}
void drgn_memory_reader_deinit(struct drgn_memory_reader *reader)
{
free(reader->segments);
}
struct drgn_error *drgn_memory_reader_create(struct drgn_memory_reader **ret)
{
struct drgn_memory_reader *reader;
reader = malloc(sizeof(*reader));
if (!reader)
return &drgn_enomem;
drgn_memory_reader_init(reader);
*ret = reader;
return NULL;
}
void drgn_memory_reader_destroy(struct drgn_memory_reader *reader)
{
drgn_memory_reader_deinit(reader);
free(reader);
}
struct drgn_error *
drgn_memory_reader_add_segment(struct drgn_memory_reader *reader,
uint64_t virt_addr, uint64_t phys_addr,
uint64_t size, drgn_memory_read_fn read_fn,
void *arg)
{
struct drgn_memory_segment *segment;
if ((virt_addr == UINT64_MAX && phys_addr == UINT64_MAX) || size == 0)
return NULL;
if (reader->num_segments >= reader->capacity) {
size_t new_capacity;
new_capacity = reader->capacity ? 2 * reader->capacity : 1;
if (!resize_array(&reader->segments, new_capacity))
return &drgn_enomem;
reader->capacity = new_capacity;
}
segment = &reader->segments[reader->num_segments++];
segment->virt_addr = virt_addr;
segment->phys_addr = phys_addr;
segment->size = size;
segment->read_fn = read_fn;
segment->arg = arg;
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 *err;
size_t read = 0;
while (read < count) {
struct drgn_memory_segment *segment;
uint64_t segment_address, segment_offset;
size_t i, n;
/*
* The most recently used segments are at the end of the list,
* so search backwards.
*/
i = reader->num_segments;
for (;;) {
if (i == 0) {
return drgn_error_format(DRGN_ERROR_FAULT,
"could not find memory segment containing 0x%" PRIx64,
address);
}
segment = &reader->segments[--i];
segment_address = (physical ? segment->phys_addr :
segment->virt_addr);
if (segment_address != UINT64_MAX &&
segment_address <= address &&
address < segment_address + segment->size)
break;
}
/* Move the used segment to the end of the list. */
if (i != reader->num_segments - 1) {
struct drgn_memory_segment tmp = *segment;
memmove(&reader->segments[i], &reader->segments[i + 1],
(reader->num_segments - i - 1) *
sizeof(*reader->segments));
segment = &reader->segments[reader->num_segments - 1];
*segment = tmp;
}
segment_offset = address - segment_address;
n = min(segment->size - segment_offset, (uint64_t)(count - read));
err = segment->read_fn((char *)buf + read, address, n, physical,
segment_offset, segment->arg);
if (err)
return err;
read += n;
address += n;
}
return NULL;
}
struct drgn_error *drgn_read_memory_file(void *buf, uint64_t address,
size_t count, bool physical,
uint64_t offset, void *arg)
{
struct drgn_memory_file_segment *file_segment = arg;
char *p = buf;
uint64_t file_offset = file_segment->file_offset + offset;
while (count) {
ssize_t ret;
ret = pread(file_segment->fd, p, count, file_offset);
if (ret == -1) {
if (errno == EINTR)
continue;
return drgn_error_create_os(errno, NULL, "pread");
} else if (ret == 0) {
return drgn_error_format(DRGN_ERROR_FAULT,
"short read from memory file");
}
p += ret;
count -= ret;
file_offset += ret;
}
return NULL;
}

View File

@ -23,49 +23,100 @@
*
* Memory reading interface.
*
* @ref drgn_memory_reader provides a common interface for reading from the
* memory of a program.
* @ref drgn_memory_reader provides a common interface for registering regions
* of memory in a program and reading from memory.
*
* @{
*/
struct drgn_memory_reader;
struct drgn_memory_segment;
/** Memory reader operations. */
struct drgn_memory_reader_ops {
/** Implements @ref drgn_memory_reader_destroy(). */
void (*destroy)(struct drgn_memory_reader *reader);
/** Implements @ref drgn_memory_reader_read(). */
struct drgn_error *(*read)(struct drgn_memory_reader *reader, void *buf,
uint64_t address, size_t count,
bool physical);
/**
* Callback implementing a memory read.
*
* @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] physical Whether @c address is physical.
* @param[in] offset Offset in bytes of @p address from the beginning of the
* segment.
* @param[in] arg Argument passed to @ref drgn_memory_reader_add_segment().
* @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, bool physical,
uint64_t offset, void *arg);
/** Memory segment in a @ref drgn_memory_reader. */
struct drgn_memory_segment {
/**
* Virtual address of the segment in memory. If @c UINT64_MAX, the
* segment does not have a known virtual address.
*/
uint64_t virt_addr;
/**
* Physical address of the segment in memory. If @c UINT64_MAX, the
* segment does not have a known physical address.
*/
uint64_t phys_addr;
/** Size of the segment in bytes. */
uint64_t size;
/** Read callback. */
drgn_memory_read_fn read_fn;
/** Argument to pass to @ref drgn_memory_segment::read_fn. */
void *arg;
};
/**
* Abstract memory reader.
* Memory reader.
*
* A memory reader can be backed by a few things:
* - A file (@ref drgn_memory_file_reader).
* - Arbitrary memory (@ref drgn_mock_memory_reader).
*
* It is read from with @ref drgn_memory_reader_read() and freed with @ref
* drgn_memory_reader_destroy().
* A memory reader maps the segments of memory in an address space to callbacks
* which can be used to read memory from those segments.
*/
struct drgn_memory_reader {
/** Operation dispatch table. */
const struct drgn_memory_reader_ops *ops;
/** Memory segments. */
struct drgn_memory_segment *segments;
/** Number of segments. */
size_t num_segments;
/** Allocated number of segments. */
size_t capacity;
};
/**
* Free a @ref drgn_memory_reader.
* Initialize a @ref drgn_memory_reader.
*
* @param[in] reader Memory reader to destroy.
* The reader is initialized with no segments.
*/
static inline void drgn_memory_reader_destroy(struct drgn_memory_reader *reader)
{
if (reader)
reader->ops->destroy(reader);
}
void drgn_memory_reader_init(struct drgn_memory_reader *reader);
/** Deinitialize a @ref drgn_memory_reader. */
void drgn_memory_reader_deinit(struct drgn_memory_reader *reader);
struct drgn_error *drgn_memory_reader_create(struct drgn_memory_reader **ret);
void drgn_memory_reader_destroy(struct drgn_memory_reader *reader);
/**
* Register a segment of memory in a @ref drgn_memory_reader.
*
* If the segment overlaps a previously registered segment, the new segment
* takes precedence.
*
* @param[in] reader Reader to add segment to.
* @param[in] virt_addr Virtual address of segment, or @c UINT64_MAX if the
* segment does not have a virtual address.
* @param[in] phys_addr Physical address of segment, or @c UINT64_MAX if the
* segment does not have a physical address.
* @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.
* @return @c NULL on success, non-@c NULL on error.
*/
struct drgn_error *
drgn_memory_reader_add_segment(struct drgn_memory_reader *reader,
uint64_t virt_addr, uint64_t phys_addr,
uint64_t size, drgn_memory_read_fn read_fn,
void *arg);
/**
* Read from a @ref drgn_memory_reader.
@ -74,18 +125,33 @@ static inline void drgn_memory_reader_destroy(struct drgn_memory_reader *reader)
* @param[out] buf Buffer to read into.
* @param[in] address Starting address in memory to read.
* @param[in] count Number of bytes to read.
* @param[in] physical Whether @c address is physical. A reader may support only
* virtual or physical addresses or both.
* @param[in] physical Whether @c address is physical.
* @return @c NULL on success, non-@c NULL on error.
*/
static inline struct drgn_error *
drgn_memory_reader_read(struct drgn_memory_reader *reader, void *buf,
uint64_t address, size_t count, bool physical)
{
return reader->ops->read(reader, buf, address, count, physical);
}
struct drgn_error *drgn_memory_reader_read(struct drgn_memory_reader *reader,
void *buf, uint64_t address,
size_t count, bool physical);
/** Memory segment in a @ref drgn_mock_memory_reader. */
/** Argument for @ref drgn_read_memory_file(). */
struct drgn_memory_file_segment {
/** Offset in the file where the segment starts. */
uint64_t file_offset;
/**
* Size of the segment in the file. This may be less than the size of
* the segment in memory, in which case the remaining bytes are treated
* as if they contained zeroes.
*/
uint64_t file_size;
/** File descriptor. */
int fd;
};
/** @ref drgn_memory_read_fn which reads from a file. */
struct drgn_error *drgn_read_memory_file(void *buf, uint64_t address,
size_t count, bool physical,
uint64_t offset, void *arg);
/** Memory segment for testing. */
struct drgn_mock_memory_segment {
/**
* Virtual address of the segment in memory. If @c UINT64_MAX, the
@ -103,108 +169,6 @@ struct drgn_mock_memory_segment {
const void *buf;
};
/**
* Memory reader backed by arbitrary memory.
*
* This is mostly useful for testing. It is created with @ref
* drgn_mock_memory_reader_create().
*/
struct drgn_mock_memory_reader {
/** Abstract memory reader. */
struct drgn_memory_reader reader;
/** Memory segments. */
struct drgn_mock_memory_segment *segments;
/** Number of segments. */
size_t num_segments;
};
/**
* Create a @ref drgn_mock_memory_reader.
*
* @param[in] segments Memory segments. This will not be freed when the reader
* is destroyed.
* @param[in] num_segments Number of segments.
* @param[out] ret Returned memory reader.
* @return @c NULL on success, non-@c NULL on error.
*/
struct drgn_error *
drgn_mock_memory_reader_create(struct drgn_mock_memory_segment *segments,
size_t num_segments,
struct drgn_mock_memory_reader **ret);
/** Memory segment in a @ref drgn_memory_file_reader. */
struct drgn_memory_file_segment {
/** Offset in the file where the segment starts. */
uint64_t file_offset;
/**
* Virtual address of the segment in memory. If @c UINT64_MAX, the
* segment does not have a known virtual address.
*/
uint64_t virt_addr;
/**
* Physical address of the segment in memory. If @c UINT64_MAX, the
* segment does not have a known physical address.
*/
uint64_t phys_addr;
/** Size of the segment in the file. */
uint64_t file_size;
/**
* Size of the segment in memory. If greater than @ref
* drgn_memory_file_segment::file_size, the remainder of the segment is
* treated as if it contained zeroes.
*/
uint64_t mem_size;
};
/**
* Memory file reader.
*
* This is a concrete @ref drgn_memory_reader which is backed by a file. This
* may be, for example, an ELF core dump file, or a flat file like
* <tt>/proc/$pid/mem</tt>. It is created with @ref
* drgn_memory_file_reader_create(). Segments are added with @ref
* drgn_memory_file_reader_add_segment().
*/
struct drgn_memory_file_reader {
/** Abstract memory reader. */
struct drgn_memory_reader reader;
/** Memory segments. */
struct drgn_memory_file_segment *segments;
/** Number of segments. */
size_t num_segments;
/** Capacity of @c segments. */
size_t capacity;
/** File descriptor. */
int fd;
};
/**
* Create a @ref drgn_memory_file_reader.
*
* This creates a memory file reader with no segments.
*
* @param[in] fd File descriptor referring to the file. It will not be closed
* when the reader is destroyed.
* @param[out] ret Returned memory reader.
* @return @c NULL on success, non-@c NULL on error.
*/
struct drgn_error *
drgn_memory_file_reader_create(int fd, struct drgn_memory_file_reader **ret);
/**
* Add a memory segment to a @ref drgn_memory_file_reader.
*
* If there are overlapping segments, then the most recently added segment is
* used.
*
* @param[in] freader Memory file reader.
* @param[in] segment Memory segment to add.
* @return @c NULL on success, non-@c NULL on error.
*/
struct drgn_error *
drgn_memory_file_reader_add_segment(struct drgn_memory_file_reader *freader,
const struct drgn_memory_file_segment *segment);
/** @} */
#endif /* DRGN_MEMORY_READER_H */

View File

@ -7,77 +7,11 @@
#include "internal.h"
#include "program.h"
static struct drgn_error *
drgn_mock_memory_reader_read(struct drgn_memory_reader *reader, void *buf,
uint64_t address, size_t count, bool physical)
static struct drgn_error *drgn_mock_memory_read(void *buf, uint64_t address,
size_t count, bool physical,
uint64_t offset, void *arg)
{
struct drgn_mock_memory_reader *mreader;
char *p = buf;
mreader = container_of(reader, struct drgn_mock_memory_reader, reader);
while (count) {
struct drgn_mock_memory_segment *segment;
uint64_t segment_address;
uint64_t segment_offset;
size_t copy_count;
size_t i;
for (i = 0; i < mreader->num_segments; i++) {
segment = &mreader->segments[i];
segment_address = (physical ? segment->phys_addr :
segment->virt_addr);
if (segment_address <= address &&
address < segment_address + segment->size)
break;
}
if (i >= mreader->num_segments) {
return drgn_error_format(DRGN_ERROR_FAULT,
"could not find memory segment containing 0x%" PRIx64,
address);
}
segment_offset = address - segment_address;
copy_count = min(segment->size - segment_offset,
(uint64_t)count);
memcpy(p, (const char *)segment->buf + segment_offset,
copy_count);
p += copy_count;
count -= copy_count;
address += copy_count;
}
return NULL;
}
static void drgn_mock_memory_reader_destroy(struct drgn_memory_reader *reader)
{
struct drgn_mock_memory_reader *mreader;
mreader = container_of(reader, struct drgn_mock_memory_reader, reader);
free(mreader);
}
static const struct drgn_memory_reader_ops drgn_mock_memory_reader_ops = {
.destroy = drgn_mock_memory_reader_destroy,
.read = drgn_mock_memory_reader_read,
};
struct drgn_error *
drgn_mock_memory_reader_create(struct drgn_mock_memory_segment *segments,
size_t num_segments,
struct drgn_mock_memory_reader **ret)
{
struct drgn_mock_memory_reader *mreader;
mreader = malloc(sizeof(*mreader));
if (!mreader)
return &drgn_enomem;
mreader->reader.ops = &drgn_mock_memory_reader_ops;
mreader->segments = segments;
mreader->num_segments = num_segments;
*ret = mreader;
memcpy(buf, (char *)arg + offset, count);
return NULL;
}
@ -265,14 +199,27 @@ drgn_program_init_mock(struct drgn_program *prog, uint8_t word_size,
size_t num_objects)
{
struct drgn_error *err;
struct drgn_mock_memory_reader *mreader;
struct drgn_memory_reader *reader;
struct drgn_mock_type_index *mtindex;
struct drgn_mock_object_index *moindex;
size_t i;
err = drgn_mock_memory_reader_create(segments, num_segments, &mreader);
err = drgn_memory_reader_create(&reader);
if (err)
return err;
for (i = 0; i < num_segments; i++) {
struct drgn_mock_memory_segment *segment = &segments[i];
err = drgn_memory_reader_add_segment(reader, segment->virt_addr,
segment->phys_addr,
segment->size,
drgn_mock_memory_read,
(void *)segment->buf);
if (err)
goto err_reader;
}
err = drgn_mock_type_index_create(word_size, little_endian, types,
num_types, &mtindex);
if (err)
@ -282,13 +229,12 @@ drgn_program_init_mock(struct drgn_program *prog, uint8_t word_size,
if (err)
goto err_tindex;
drgn_program_init(prog, &mreader->reader, &mtindex->tindex,
&moindex->oindex);
drgn_program_init(prog, reader, &mtindex->tindex, &moindex->oindex);
return NULL;
err_tindex:
drgn_type_index_destroy(&mtindex->tindex);
err_reader:
drgn_memory_reader_destroy(&mreader->reader);
drgn_memory_reader_destroy(reader);
return err;
}

View File

@ -1091,14 +1091,16 @@ struct drgn_error *drgn_program_init_core_dump(struct drgn_program *prog,
int fd;
Elf *elf;
GElf_Ehdr ehdr_mem, *ehdr;
size_t phnum, i;
size_t phnum, i, num_file_segments;
struct drgn_memory_file_segment *file_segments = NULL;
struct drgn_memory_file_segment *current_file_segment;
struct file_mapping *mappings = NULL;
size_t num_mappings = 0, mappings_capacity = 0;
struct vmcoreinfo vmcoreinfo;
bool is_64_bit, have_nt_file = false;
bool have_nt_taskstruct = false, have_vmcoreinfo = false;
bool have_non_zero_phys_addr = false, is_proc_kcore;
struct drgn_memory_file_reader *freader;
struct drgn_memory_reader *reader;
struct drgn_dwarf_index *dindex;
struct drgn_dwarf_type_index *dtindex;
struct drgn_dwarf_object_index *doindex;
@ -1134,10 +1136,37 @@ struct drgn_error *drgn_program_init_core_dump(struct drgn_program *prog,
goto out_elf;
}
err = drgn_memory_file_reader_create(fd, &freader);
/*
* First pass: count the number of loadable segments and check if p_addr
* is valid.
*/
num_file_segments = 0;
for (i = 0; i < phnum; i++) {
GElf_Phdr phdr_mem, *phdr;
phdr = gelf_getphdr(elf, i, &phdr_mem);
if (!phdr) {
err = drgn_error_libelf();
goto out_elf;
}
if (phdr->p_type == PT_LOAD) {
if (phdr->p_paddr)
have_non_zero_phys_addr = true;
num_file_segments++;
}
}
err = drgn_memory_reader_create(&reader);
if (err)
goto out_elf;
file_segments = malloc_array(num_file_segments, sizeof(*file_segments));
if (!file_segments)
goto out_reader;
current_file_segment = file_segments;
/* Second pass: add the segments and parse notes. */
for (i = 0; i < phnum; i++) {
GElf_Phdr phdr_mem, *phdr;
@ -1148,20 +1177,30 @@ struct drgn_error *drgn_program_init_core_dump(struct drgn_program *prog,
}
if (phdr->p_type == PT_LOAD) {
struct drgn_memory_file_segment segment = {
.file_offset = phdr->p_offset,
.virt_addr = phdr->p_vaddr,
.phys_addr = phdr->p_paddr,
.file_size = phdr->p_filesz,
.mem_size = phdr->p_memsz,
};
uint64_t phys_addr;
if (segment.phys_addr)
have_non_zero_phys_addr = true;
err = drgn_memory_file_reader_add_segment(freader,
&segment);
/*
* If this happens, then the number of segments changed
* since the first pass. That's probably impossible, but
* skip it just in case.
*/
if (current_file_segment ==
file_segments + num_file_segments)
continue;
current_file_segment->file_offset = phdr->p_offset;
current_file_segment->file_size = phdr->p_filesz;
current_file_segment->fd = fd;
phys_addr = (have_non_zero_phys_addr ? phdr->p_paddr :
UINT64_MAX);
err = drgn_memory_reader_add_segment(reader,
phdr->p_vaddr,
phys_addr,
phdr->p_memsz,
drgn_read_memory_file,
current_file_segment);
if (err)
goto out_mappings;
current_file_segment++;
} else if (phdr->p_type == PT_NOTE) {
Elf_Data *data;
size_t offset;
@ -1190,8 +1229,8 @@ struct drgn_error *drgn_program_init_core_dump(struct drgn_program *prog,
err = parse_nt_file(desc,
nhdr.n_descsz,
is_64_bit,
&mappings,
&num_mappings,
&prog->mappings,
&prog->num_mappings,
&mappings_capacity);
if (err)
goto out_mappings;
@ -1219,11 +1258,6 @@ struct drgn_error *drgn_program_init_core_dump(struct drgn_program *prog,
resize_array(&mappings, num_mappings);
}
if (!have_non_zero_phys_addr) {
for (i = 0; i < freader->num_segments; i++)
freader->segments[i].phys_addr = UINT64_MAX;
}
if (have_vmcoreinfo) {
is_proc_kcore = true;
} else if (have_nt_taskstruct) {
@ -1261,7 +1295,7 @@ struct drgn_error *drgn_program_init_core_dump(struct drgn_program *prog,
*/
if (!have_vmcoreinfo) {
if (have_non_zero_phys_addr) {
err = read_vmcoreinfo_from_sysfs(&freader->reader,
err = read_vmcoreinfo_from_sysfs(reader,
&vmcoreinfo);
} else {
err = get_fallback_vmcoreinfo(&vmcoreinfo);
@ -1298,14 +1332,16 @@ struct drgn_error *drgn_program_init_core_dump(struct drgn_program *prog,
if (err)
goto out_dtindex;
drgn_program_init(prog, &freader->reader, &dtindex->tindex,
&doindex->oindex);
drgn_program_init(prog, reader, &dtindex->tindex, &doindex->oindex);
err = drgn_program_add_cleanup(prog, cleanup_fd, (void *)(intptr_t)fd);
if (err)
goto out_program;
err = drgn_program_add_cleanup(prog, cleanup_dwarf_index, dindex);
err = drgn_program_add_cleanup(prog, free, file_segments);
if (err)
goto out_cleanup_fd;
err = drgn_program_add_cleanup(prog, cleanup_dwarf_index, dindex);
if (err)
goto out_cleanup_file_segments;
doindex->prog = prog;
if (have_vmcoreinfo) {
prog->flags |= DRGN_PROGRAM_IS_LINUX_KERNEL;
@ -1324,6 +1360,8 @@ struct drgn_error *drgn_program_init_core_dump(struct drgn_program *prog,
out_cleanup_dindex:
drgn_program_remove_cleanup(prog, cleanup_dwarf_index, dindex);
out_cleanup_file_segments:
drgn_program_remove_cleanup(prog, free, file_segments);
out_cleanup_fd:
drgn_program_remove_cleanup(prog, cleanup_fd, (void *)(intptr_t)fd);
out_program:
@ -1334,7 +1372,9 @@ out_dindex:
drgn_dwarf_index_destroy(dindex);
out_mappings:
free_file_mappings(mappings, num_mappings);
drgn_memory_reader_destroy(&freader->reader);
free(file_segments);
out_reader:
drgn_memory_reader_destroy(reader);
out_elf:
elf_end(elf);
out_fd:
@ -1406,16 +1446,10 @@ struct drgn_error *drgn_program_init_pid(struct drgn_program *prog, pid_t pid)
struct drgn_error *err;
char buf[64];
int fd;
struct drgn_memory_file_segment segment = {
.file_offset = 0,
.virt_addr = 0,
.phys_addr = UINT64_MAX,
.file_size = OFF_MAX,
.mem_size = OFF_MAX,
};
struct drgn_memory_file_segment *file_segment;
struct file_mapping *mappings = NULL;
size_t num_mappings = 0;
struct drgn_memory_file_reader *freader;
struct drgn_memory_reader *reader;
struct drgn_dwarf_index *dindex;
struct drgn_dwarf_type_index *dtindex;
struct drgn_dwarf_object_index *doindex;
@ -1425,11 +1459,22 @@ struct drgn_error *drgn_program_init_pid(struct drgn_program *prog, pid_t pid)
if (fd == -1)
return drgn_error_create_os(errno, buf, "open");
err = drgn_memory_file_reader_create(fd, &freader);
if (err)
file_segment = malloc(sizeof(*file_segment));
if (!file_segment) {
err = &drgn_enomem;
goto out_fd;
}
file_segment->file_offset = 0;
file_segment->file_size = UINT64_MAX;
file_segment->fd = fd;
err = drgn_memory_file_reader_add_segment(freader, &segment);
err = drgn_memory_reader_create(&reader);
if (err)
goto out_file_segment;
err = drgn_memory_reader_add_segment(reader, 0, UINT64_MAX, UINT64_MAX,
drgn_read_memory_file,
file_segment);
if (err)
goto out_reader;
@ -1456,8 +1501,7 @@ struct drgn_error *drgn_program_init_pid(struct drgn_program *prog, pid_t pid)
if (err)
goto out_dtindex;
drgn_program_init(prog, &freader->reader, &dtindex->tindex,
&doindex->oindex);
drgn_program_init(prog, reader, &dtindex->tindex, &doindex->oindex);
doindex->prog = prog;
prog->mappings = mappings;
prog->num_mappings = num_mappings;
@ -1465,9 +1509,12 @@ struct drgn_error *drgn_program_init_pid(struct drgn_program *prog, pid_t pid)
err = drgn_program_add_cleanup(prog, cleanup_fd, (void *)(intptr_t)fd);
if (err)
goto out_program;
err = drgn_program_add_cleanup(prog, cleanup_dwarf_index, dindex);
err = drgn_program_add_cleanup(prog, free, file_segment);
if (err)
goto out_cleanup_fd;
err = drgn_program_add_cleanup(prog, cleanup_dwarf_index, dindex);
if (err)
goto out_cleanup_file_segment;
err = drgn_program_add_cleanup(prog, cleanup_file_mappings, prog);
if (err)
goto out_cleanup_dindex;
@ -1475,6 +1522,8 @@ struct drgn_error *drgn_program_init_pid(struct drgn_program *prog, pid_t pid)
out_cleanup_dindex:
drgn_program_remove_cleanup(prog, cleanup_dwarf_index, dindex);
out_cleanup_file_segment:
drgn_program_remove_cleanup(prog, free, file_segment);
out_cleanup_fd:
drgn_program_remove_cleanup(prog, cleanup_fd, (void *)(intptr_t)fd);
out_program:
@ -1486,7 +1535,9 @@ out_dindex:
out_mappings:
free_file_mappings(mappings, num_mappings);
out_reader:
drgn_memory_reader_destroy(&freader->reader);
drgn_memory_reader_destroy(reader);
out_file_segment:
free(file_segment);
out_fd:
close(fd);
return err;

View File

@ -65,6 +65,12 @@ typedef struct {
};
} DrgnType;
typedef struct {
PyObject_HEAD
struct drgn_memory_reader reader;
PyObject *objects;
} MemoryReader;
typedef struct {
PyObject_HEAD
DrgnObject *obj;
@ -86,11 +92,32 @@ extern PyObject *Qualifiers_class;
extern PyObject *TypeKind_class;
extern PyTypeObject DrgnObject_type;
extern PyTypeObject DrgnType_type;
extern PyTypeObject MemoryReader_type;
extern PyTypeObject ObjectIterator_type;
extern PyTypeObject Program_type;
extern PyObject *FaultError;
extern PyObject *FileFormatError;
/* Keep a reference to @p obj in the dictionary @p objects. */
static inline int hold_object(PyObject *objects, PyObject *obj)
{
PyObject *key;
int ret;
if (!objects) {
PyErr_SetString(PyExc_ValueError, "object is not initialized");
return -1;
}
key = PyLong_FromVoidPtr(obj);
if (!key)
return -1;
ret = PyDict_SetItem(objects, key, obj);
Py_DECREF(key);
return ret;
}
int append_string(PyObject *parts, const char *s);
int append_format(PyObject *parts, const char *format, ...);
PyObject *byteorder_string(bool little_endian);

View File

@ -0,0 +1,220 @@
#include "drgnpy.h"
#include "../memory_reader.h"
static int MemoryReader_init(MemoryReader *self, PyObject *args, PyObject *kwds)
{
if (PyTuple_GET_SIZE(args) || (kwds && PyDict_Size(kwds))) {
PyErr_SetString(PyExc_ValueError,
"MemoryReader() takes no arguments");
return -1;
}
if (self->objects) {
drgn_memory_reader_deinit(&self->reader);
PyDict_Clear(self->objects);
} else {
self->objects = PyDict_New();
if (!self->objects)
return -1;
}
drgn_memory_reader_init(&self->reader);
return 0;
}
static void MemoryReader_dealloc(MemoryReader *self)
{
drgn_memory_reader_deinit(&self->reader);
Py_XDECREF(self->objects);
Py_TYPE(self)->tp_free((PyObject *)self);
}
static int MemoryReader_traverse(MemoryReader *self, visitproc visit, void *arg)
{
Py_VISIT(self->objects);
return 0;
}
static int MemoryReader_clear(MemoryReader *self)
{
Py_CLEAR(self->objects);
return 0;
}
static int addr_converter(PyObject *arg, void *result)
{
uint64_t *ret = result;
unsigned long long tmp;
if (arg == Py_None) {
*ret = UINT64_MAX;
return 1;
}
tmp = PyLong_AsUnsignedLongLong(arg);
if (tmp == (unsigned long long)-1 && PyErr_Occurred())
return 0;
if (tmp >= UINT64_MAX) {
PyErr_SetString(PyExc_OverflowError, "address is too large");
return 0;
}
*ret = tmp;
return 1;
}
static struct drgn_error *MemoryReader_read_fn(void *buf, uint64_t address,
size_t count, bool physical,
uint64_t offset, void *arg)
{
struct drgn_error *err;
PyGILState_STATE gstate;
PyObject *ret;
Py_buffer view;
gstate = PyGILState_Ensure();
ret = PyObject_CallFunction(arg, "KKOK", (unsigned long long)address,
(unsigned long long)count,
physical ? Py_True : Py_False,
(unsigned long long)offset);
if (!ret) {
err = drgn_error_from_python();
goto out;
}
if (PyObject_GetBuffer(ret, &view, PyBUF_SIMPLE) == -1) {
err = drgn_error_from_python();
goto out_ret;
}
if (view.len != count) {
PyErr_Format(PyExc_ValueError,
"memory read callback returned buffer of length %zd (expected %zu)",
view.len, count);
err = drgn_error_from_python();
goto out_view;
}
memcpy(buf, view.buf, count);
err = NULL;
out_view:
PyBuffer_Release(&view);
out_ret:
Py_DECREF(ret);
out:
PyGILState_Release(gstate);
return err;
}
static PyObject *MemoryReader_add_segment(MemoryReader *self, PyObject *args,
PyObject *kwds)
{
static char *keywords[] = {
"virt_addr", "phys_addr", "size", "read_fn", NULL,
};
struct drgn_error *err;
uint64_t virt_addr, phys_addr;
unsigned long long size;
PyObject *read_fn;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&O&KO:add_segment",
keywords, addr_converter, &virt_addr,
addr_converter, &phys_addr, &size,
&read_fn))
return NULL;
if (!PyCallable_Check(read_fn)) {
PyErr_SetString(PyExc_TypeError, "read_fn must be callable");
return NULL;
}
if (hold_object(self->objects, read_fn) == -1)
return NULL;
err = drgn_memory_reader_add_segment(&self->reader, virt_addr,
phys_addr, size,
MemoryReader_read_fn, read_fn);
if (err)
return set_drgn_error(err);
Py_RETURN_NONE;
}
static PyObject *MemoryReader_read(MemoryReader *self, PyObject *args,
PyObject *kwds)
{
static char *keywords[] = {"address", "size", "physical", NULL};
struct drgn_error *err;
unsigned long long address;
Py_ssize_t size;
int physical = 0;
PyObject *buf;
bool clear;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "Kn|p:read", keywords,
&address, &size, &physical))
return NULL;
if (size < 0) {
PyErr_SetString(PyExc_ValueError, "negative size");
return NULL;
}
buf = PyBytes_FromStringAndSize(NULL, size);
if (!buf)
return NULL;
clear = set_drgn_in_python();
err = drgn_memory_reader_read(&self->reader, PyBytes_AS_STRING(buf),
address, size, physical);
if (clear)
clear_drgn_in_python();
if (err) {
set_drgn_error(err);
Py_DECREF(buf);
return NULL;
}
return buf;
}
static PyMethodDef MemoryReader_methods[] = {
{"add_segment", (PyCFunction)MemoryReader_add_segment,
METH_VARARGS | METH_KEYWORDS,
"add_segment(virt_addr, phys_addr, size, read_fn)\n--\n\n"},
{"read", (PyCFunction)MemoryReader_read, METH_VARARGS | METH_KEYWORDS,
"read(address, size, physical=False)\n--\n\n"},
{},
};
PyTypeObject MemoryReader_type = {
PyVarObject_HEAD_INIT(NULL, 0)
"_drgn.MemoryReader", /* tp_name */
sizeof(MemoryReader), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)MemoryReader_dealloc, /* tp_dealloc */
NULL, /* tp_print */
NULL, /* tp_getattr */
NULL, /* tp_setattr */
NULL, /* tp_as_async */
NULL, /* tp_repr */
NULL, /* tp_as_number */
NULL, /* tp_as_sequence */
NULL, /* tp_as_mapping */
NULL, /* tp_hash */
NULL, /* tp_call */
NULL, /* tp_str */
NULL, /* tp_getattro */
NULL, /* tp_setattro */
NULL, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */
"drgn_memory_reader wrapper for testing",/* tp_doc */
(traverseproc)MemoryReader_traverse, /* tp_traverse */
(inquiry)MemoryReader_clear, /* tp_clear */
NULL, /* tp_richcompare */
0, /* tp_weaklistoffset */
NULL, /* tp_iter */
NULL, /* tp_iternext */
MemoryReader_methods, /* tp_methods */
NULL, /* tp_members */
NULL, /* tp_getset */
NULL, /* tp_base */
NULL, /* tp_dict */
NULL, /* tp_descr_get */
NULL, /* tp_descr_set */
0, /* tp_dictoffset */
(initproc)MemoryReader_init, /* tp_init */
NULL, /* tp_alloc */
PyType_GenericNew, /* tp_new */
};

View File

@ -106,6 +106,11 @@ DRGNPY_PUBLIC PyMODINIT_FUNC PyInit__drgn(void)
goto err;
PyModule_AddObject(m, "FileFormatError", FileFormatError);
if (PyType_Ready(&MemoryReader_type) < 0)
goto err;
Py_INCREF(&MemoryReader_type);
PyModule_AddObject(m, "MemoryReader", (PyObject *)&MemoryReader_type);
if (PyType_Ready(&DrgnObject_type) < 0)
goto err;
Py_INCREF(&DrgnObject_type);

View File

@ -21,27 +21,13 @@ static Program *Program_alloc(void)
return prog;
}
static int Program_hold_object(Program *prog, PyObject *obj)
{
PyObject *key;
int ret;
key = PyLong_FromVoidPtr(obj);
if (!key)
return -1;
ret = PyDict_SetItem(prog->objects, key, obj);
Py_DECREF(key);
return ret;
}
static int Program_hold_type(Program *prog, DrgnType *type)
{
PyObject *parent;
parent = DrgnType_parent(type);
if (parent && parent != (PyObject *)prog)
return Program_hold_object(prog, parent);
return hold_object(prog->objects, parent);
else
return 0;
}
@ -448,7 +434,7 @@ static int mock_get_filename(Program *prog, PyObject *mock_obj,
return -1;
}
Py_DECREF(filename_obj);
if (Program_hold_object(prog, encoded_obj) == -1) {
if (hold_object(prog->objects, encoded_obj) == -1) {
Py_DECREF(encoded_obj);
return -1;
}
@ -584,7 +570,7 @@ Program *mock_program(PyObject *self, PyObject *args, PyObject *kwds)
"mock type must be unqualified");
goto err;
}
if (Program_hold_object(prog, tmp) == -1) {
if (hold_object(prog->objects, tmp) == -1) {
Py_DECREF(tmp);
goto err;
}
@ -641,7 +627,7 @@ Program *mock_program(PyObject *self, PyObject *args, PyObject *kwds)
Py_DECREF(tmp);
goto err;
}
if (Program_hold_object(prog, tmp) == -1) {
if (hold_object(prog->objects, tmp) == -1) {
Py_DECREF(tmp);
goto err;
}

View File

@ -172,42 +172,6 @@ DRGNPY_PUBLIC struct drgn_error *drgn_test_lexer_c(struct drgn_lexer *lexer,
return drgn_lexer_c(lexer, token);
}
DRGNPY_PUBLIC void
drgn_test_memory_reader_destroy(struct drgn_memory_reader *reader)
{
drgn_memory_reader_destroy(reader);
}
DRGNPY_PUBLIC struct drgn_error *
drgn_test_memory_reader_read(struct drgn_memory_reader *reader, void *buf,
uint64_t address, size_t count, bool physical)
{
return drgn_memory_reader_read(reader, buf, address, count, physical);
}
DRGNPY_PUBLIC struct drgn_error *
drgn_test_memory_file_reader_create(int fd, struct drgn_memory_reader **ret)
{
struct drgn_error *err;
struct drgn_memory_file_reader *freader;
err = drgn_memory_file_reader_create(fd, &freader);
if (err)
return err;
*ret = &freader->reader;
return NULL;
}
DRGNPY_PUBLIC struct drgn_error *
drgn_test_memory_file_reader_add_segment(struct drgn_memory_reader *reader,
const struct drgn_memory_file_segment *segment)
{
struct drgn_memory_file_reader *freader;
freader = container_of(reader, struct drgn_memory_file_reader, reader);
return drgn_memory_file_reader_add_segment(freader, segment);
}
DRGNPY_PUBLIC bool drgn_test_path_iterator_next(struct path_iterator *it,
const char **component,
size_t *component_len)

View File

@ -102,95 +102,6 @@ _drgn_pydll.DrgnType_wrap.restype = ctypes.py_object
_drgn_pydll.DrgnType_wrap.argtypes = [_drgn_qualified_type, ctypes.py_object]
class _drgn_memory_reader(ctypes.Structure):
pass
class _drgn_memory_file_segment(ctypes.Structure):
_fields_ = [
('file_offset', ctypes.c_uint64),
('virt_addr', ctypes.c_uint64),
('phys_addr', ctypes.c_uint64),
('file_size', ctypes.c_uint64),
('mem_size', ctypes.c_uint64),
]
_drgn_cdll.drgn_test_memory_reader_read.restype = ctypes.POINTER(_drgn_error)
_drgn_cdll.drgn_test_memory_reader_read.argtypes = [
ctypes.POINTER(_drgn_memory_reader), ctypes.c_void_p, ctypes.c_uint64,
ctypes.c_size_t, ctypes.c_bool,
]
_drgn_cdll.drgn_test_memory_reader_destroy.restype = None
_drgn_cdll.drgn_test_memory_reader_destroy.argtypes = [
ctypes.POINTER(_drgn_memory_reader),
]
_drgn_cdll.drgn_test_memory_file_reader_create.restype = ctypes.POINTER(_drgn_error)
_drgn_cdll.drgn_test_memory_file_reader_create.argtypes = [
ctypes.c_int, ctypes.POINTER(ctypes.POINTER(_drgn_memory_reader)),
]
_drgn_cdll.drgn_test_memory_file_reader_add_segment.restype = ctypes.POINTER(_drgn_error)
_drgn_cdll.drgn_test_memory_file_reader_add_segment.argtypes = [
ctypes.POINTER(_drgn_memory_reader),
ctypes.POINTER(_drgn_memory_file_segment),
]
class MemoryReader:
def __init__(self, reader):
self._reader = reader
def __del__(self):
if hasattr(self, '_reader'):
_drgn_cdll.drgn_test_memory_reader_destroy(self._reader)
def read(self, address: int, count: int, physical: bool = False):
buf = ctypes.create_string_buffer(count)
_check_err(_drgn_cdll.drgn_test_memory_reader_read(self._reader, buf,
address, count,
physical))
return bytes(buf)
class MemoryFileSegment(NamedTuple):
file_offset: int
file_size: int
mem_size: int
virt_addr: Optional[int] = None
phys_addr: Optional[int] = None
class MemoryFileReader(MemoryReader):
def __init__(self, segments: Sequence[MemoryFileSegment],
file: Union[BinaryIO, int]) -> None:
if isinstance(file, int):
fd = file
else:
fd = file.fileno()
reader = ctypes.POINTER(_drgn_memory_reader)()
_check_err(_drgn_cdll.drgn_test_memory_file_reader_create(
fd, ctypes.pointer(reader)))
super().__init__(reader)
for segment in segments:
self.add_segment(segment)
def add_segment(self, segment: MemoryFileSegment):
c_segment = _drgn_memory_file_segment()
c_segment.file_offset = segment.file_offset
c_segment.file_size = segment.file_size
c_segment.mem_size = segment.mem_size
if segment.virt_addr is None:
c_segment.virt_addr = 2**64 - 1
else:
c_segment.virt_addr = segment.virt_addr
if segment.phys_addr is None:
c_segment.phys_addr = 2**64 - 1
else:
c_segment.phys_addr = segment.phys_addr
_check_err(_drgn_cdll.drgn_test_memory_file_reader_add_segment(
self._reader, ctypes.pointer(c_segment)))
class _drgn_dwarf_index(ctypes.Structure):
pass

View File

@ -1,74 +1,72 @@
import contextlib
import tempfile
import functools
import unittest
from drgn import FaultError
from tests.libdrgn import MemoryFileReader, MemoryFileSegment
from _drgn import MemoryReader
@contextlib.contextmanager
def tmpfile(data):
file = tempfile.TemporaryFile()
try:
file.write(data)
file.flush()
yield file
finally:
file.close()
def mock_read(data, address, count, physical, offset):
return data[offset:offset + count]
class TestMemoryFileReader(unittest.TestCase):
class TestMemoryReader(unittest.TestCase):
def test_simple_read(self):
data = b'hello, world!'
segments = [
MemoryFileSegment(0, len(data), len(data), virt_addr=0xffff0000,
phys_addr=0xa0),
]
with tmpfile(data) as file:
reader = MemoryFileReader(segments, file)
self.assertEqual(reader.read(0xffff0000, len(data)), data)
self.assertEqual(reader.read(0xa0, len(data), True), data)
data = b'hello, world'
reader = MemoryReader()
reader.add_segment(0xffff0000, 0xa0, len(data),
functools.partial(mock_read, data))
self.assertEqual(reader.read(0xffff0000, len(data)), data)
self.assertEqual(reader.read(0xa0, len(data), True), data)
def test_bad_address(self):
data = b'hello, world!'
segments = [
MemoryFileSegment(0, len(data), len(data), virt_addr=0xffff0000),
]
with tmpfile(data) as file:
reader = MemoryFileReader(segments, file)
self.assertRaisesRegex(FaultError, 'could not find memory segment',
reader.read, 0xdeadbeef, 4)
self.assertRaisesRegex(FaultError, 'could not find memory segment',
reader.read, 0xffff0000, 4, True)
reader = MemoryReader()
reader.add_segment(0xffff0000, None, len(data),
functools.partial(mock_read, data))
self.assertRaisesRegex(FaultError, 'could not find memory segment',
reader.read, 0xdeadbeef, 4)
self.assertRaisesRegex(FaultError, 'could not find memory segment',
reader.read, 0xffff0000, 4, True)
def test_segment_overflow(self):
data = b'hello, world!'
segments = [
MemoryFileSegment(0, len(data), len(data), virt_addr=0xffff0000),
]
with tmpfile(data) as file:
reader = MemoryFileReader(segments, file)
self.assertRaisesRegex(FaultError, 'could not find memory segment',
reader.read, 0xffff0000, len(data) + 1)
reader = MemoryReader()
reader.add_segment(0xffff0000, None, len(data),
functools.partial(mock_read, data))
self.assertRaisesRegex(FaultError, 'could not find memory segment',
reader.read, 0xffff0000, len(data) + 1)
def test_adjacent_segments(self):
data = b'hello, world!\0foobar'
segments = [
MemoryFileSegment(0, 4, 4, virt_addr=0xffff0000),
MemoryFileSegment(14, 6, 6, virt_addr=0xfffff000),
MemoryFileSegment(4, 10, 10, virt_addr=0xffff0004),
]
with tmpfile(data) as file:
reader = MemoryFileReader(segments, file)
self.assertEqual(reader.read(0xffff0000, 14), data[:14])
reader = MemoryReader()
reader.add_segment(0xffff0000, None, len(data[:4]),
functools.partial(mock_read, data[:4]))
reader.add_segment(0xffff0004, None, len(data[4:14]),
functools.partial(mock_read, data[4:14]))
reader.add_segment(0xfffff000, None, len(data[14:]),
functools.partial(mock_read, data[14:]))
self.assertEqual(reader.read(0xffff0000, 14), data[:14])
def test_zero_filled_segment(self):
data = b'hello, world!'
segments = [
MemoryFileSegment(0, 13, 17, virt_addr=0xffff0000),
]
with tmpfile(data) as file:
reader = MemoryFileReader(segments, file)
self.assertEqual(reader.read(0xffff0000, len(data) + 4),
data + bytes(4))
self.assertEqual(reader.read(0xffff0000 + len(data), 4), bytes(4))
def test_invalid_read_fn(self):
reader = MemoryReader()
self.assertRaises(TypeError, reader.add_segment, 0xffff0000, None, 8,
b'foo')
reader.add_segment(0xffff0000, None, 8, lambda: None)
self.assertRaises(TypeError, reader.read, 0xffff0000, 8)
reader.add_segment(0xffff0000, None, 8,
lambda address, count, physical, offset: None)
self.assertRaises(TypeError, reader.read, 0xffff0000, 8)
reader.add_segment(0xffff0000, None, 8,
lambda address, count, physical, offset: 'asdf')
self.assertRaises(TypeError, reader.read, 0xffff0000, 8)
reader.add_segment(0xffff0000, None, 8,
lambda address, count, physical, offset: b'')
self.assertRaisesRegex(
ValueError,
'memory read callback returned buffer of length 0 \(expected 8\)',
reader.read, 0xffff0000, 8)