mirror of
https://github.com/JakeHillion/drgn.git
synced 2024-12-23 09:43:06 +00:00
75c3679147
The current mixed Python/C implementation works well, but it has a couple of important limitations: - It's too slow for some common use cases, like iterating over large data structures. - It can't be reused in utilities written in other languages. This replaces the internals with a new library written in C, libdrgn. It includes Python bindings with mostly the same public interface as before, with some important improvements: - Types are now represented by a single Type class rather than the messy polymorphism in the Python implementation. - Qualifiers are a bitmask instead of a set of strings. - Bit fields are not considered a separate type. - The lvalue/rvalue terminology is replaced with reference/value. - Structure, union, and array values are better supported. - Function objects are supported. - Program distinguishes between lookups of variables, constants, and functions. The C rewrite is about 6x as fast as the original Python when using the Python bindings, and about 8x when using the C API directly. Currently, the exposed API in C is fairly conservative. In the future, the memory reader, type index, and object index APIs will probably be exposed for more flexibility.
97 lines
2.4 KiB
C
97 lines
2.4 KiB
C
// Copyright 2018-2019 - Omar Sandoval
|
|
// SPDX-License-Identifier: GPL-3.0+
|
|
|
|
#include <assert.h>
|
|
#include <byteswap.h>
|
|
#include <string.h>
|
|
|
|
#include "internal.h"
|
|
#include "serialize.h"
|
|
|
|
void serialize_bits(void *buf, uint64_t bit_offset, uint64_t uvalue,
|
|
uint8_t bit_size, bool little_endian)
|
|
{
|
|
uint8_t *p;
|
|
size_t bits, size;
|
|
unsigned char tmp[9];
|
|
uint8_t first_mask, last_mask;
|
|
|
|
assert(bit_size > 0);
|
|
assert(bit_size <= 64);
|
|
|
|
p = (uint8_t *)buf + bit_offset / 8;
|
|
bit_offset %= 8;
|
|
bits = bit_offset + bit_size;
|
|
size = (bits + 7) / 8;
|
|
if (little_endian) {
|
|
if (size > sizeof(uvalue))
|
|
tmp[8] = uvalue >> (64 - bit_offset);
|
|
uvalue = htole64(uvalue << bit_offset);
|
|
memcpy(tmp, &uvalue, sizeof(uvalue));
|
|
|
|
/* bit_offset least significant bits. */
|
|
first_mask = (1 << bit_offset) - 1;
|
|
/* 8 - (bit_offset + bit_size) % 8 most significant bits. */
|
|
last_mask = 0xff00 >> -bits % 8;
|
|
} else {
|
|
unsigned int shift;
|
|
|
|
shift = -bits % 8;
|
|
if (size > sizeof(uvalue)) {
|
|
tmp[0] = uvalue >> (64 - shift);
|
|
uvalue = htobe64(uvalue << shift);
|
|
memcpy(&tmp[1], &uvalue, sizeof(uvalue));
|
|
} else {
|
|
uvalue = htobe64(uvalue << (64 - bits));
|
|
memcpy(&tmp[0], &uvalue, sizeof(uvalue));
|
|
}
|
|
|
|
/* bit_offset most significant bits. */
|
|
first_mask = 0xff00 >> bit_offset;
|
|
/* 8 - (bit_offset + bit_size) % 8 least significant bits. */
|
|
last_mask = (1 << shift) - 1;
|
|
}
|
|
|
|
if (size == 1) {
|
|
p[0] = (p[0] & (first_mask | last_mask)) | tmp[0];
|
|
} else {
|
|
p[0] = (p[0] & first_mask) | tmp[0];
|
|
memcpy(p + 1, tmp + 1, size - 2);
|
|
p[size - 1] = (p[size - 1] & last_mask) | tmp[size - 1];
|
|
}
|
|
}
|
|
|
|
uint64_t deserialize_bits(const void *buf, uint64_t bit_offset,
|
|
uint8_t bit_size, bool little_endian)
|
|
{
|
|
const uint8_t *p;
|
|
size_t bits, size;
|
|
uint64_t ret = 0;
|
|
|
|
assert(bit_size > 0);
|
|
assert(bit_size <= 64);
|
|
|
|
p = (const uint8_t *)buf + bit_offset / 8;
|
|
bit_offset %= 8;
|
|
bits = bit_offset + bit_size;
|
|
size = (bits + 7) / 8;
|
|
if (little_endian) {
|
|
memcpy(&ret, p, min(size, sizeof(ret)));
|
|
ret = le64toh(ret) >> bit_offset;
|
|
if (size > sizeof(ret))
|
|
ret |= (uint64_t)p[8] << (64 - bit_offset);
|
|
} else {
|
|
unsigned int shift;
|
|
|
|
if (size > sizeof(ret))
|
|
memcpy(&ret, &p[1], sizeof(ret));
|
|
else
|
|
memcpy((char *)(&ret + 1) - size, p, size);
|
|
shift = -bits % 8;
|
|
ret = be64toh(ret) >> shift;
|
|
if (size > sizeof(ret))
|
|
ret |= (uint64_t)p[0] << (64 - shift);
|
|
}
|
|
return truncate_unsigned(ret, bit_size);
|
|
}
|