mirror of
https://github.com/JakeHillion/drgn.git
synced 2024-12-23 01:33:06 +00:00
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:
parent
e78e6c9226
commit
417a6f0d76
@ -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 \
|
||||
|
@ -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
150
libdrgn/memory_reader.c
Normal 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;
|
||||
}
|
@ -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 */
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
220
libdrgn/python/memory_reader.c
Normal file
220
libdrgn/python/memory_reader.c
Normal 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 */
|
||||
};
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user