libdrgn: support value objects with >64-bit integer types

The Linux kernel's struct task_struct on AArch64 contains an array of
__uint128_t:

  >>> task = find_task(prog, 1)
  >>> task.type_
  struct task_struct *
  >>> task.thread.type_
  struct thread_struct {
          struct cpu_context cpu_context;
          struct {
                  unsigned long tp_value;
                  unsigned long tp2_value;
                  struct user_fpsimd_state fpsimd_state;
          } uw;
          enum fp_type fp_type;
          unsigned int fpsimd_cpu;
          void *sve_state;
          void *sme_state;
          unsigned int vl[2];
          unsigned int vl_onexec[2];
          unsigned long fault_address;
          unsigned long fault_code;
          struct debug_info debug;
          struct ptrauth_keys_user keys_user;
          struct ptrauth_keys_kernel keys_kernel;
          u64 mte_ctrl;
          u64 sctlr_user;
          u64 svcr;
          u64 tpidr2_el0;
  }
  >>> task.thread.uw.fpsimd_state.type_
  struct user_fpsimd_state {
          __int128 unsigned vregs[32];
          __u32 fpsr;
          __u32 fpcr;
          __u32 __reserved[2];
  }

As a result, printing a task_struct fails:

  >>> task
  Traceback (most recent call last):
    File "<console>", line 1, in <module>
    File "/host/home/osandov/repos/drgn3/drgn/cli.py", line 140, in _displayhook
      text = value.format_(columns=shutil.get_terminal_size((0, 0)).columns)
  NotImplementedError: integer values larger than 64 bits are not yet supported

PR #311 suggested treating >64-bit integers as byte arrays for now; I
tried an alternate hack of handling >64-bit integers only in the
pretty-printing code. Both of these had issues, though.

Instead, let's push >64-bit integer support a little further and allow
storing "big integer" value objects. We still don't support any
operations on them, so this still doesn't complete #170. We store the
raw bytes of the value for now, but we'll probably change this if we add
support for operations (e.g., to store the value as an mp_limb_t array
for GMP). We also print >64-bit integer types in hexadecimal for
simplicity. This is inconsistent with the existing behavior of printing
in decimal, but more readable. In the future, we might want to add
heuristics to decide when to print in decimal vs hexadecimal for all
sizes.

Closes #311.

Signed-off-by: Omar Sandoval <osandov@osandov.com>
This commit is contained in:
Omar Sandoval 2023-08-02 13:12:19 -07:00
parent 91b26e2338
commit 243f6fb7d5
11 changed files with 928 additions and 365 deletions

View File

@ -1138,24 +1138,37 @@ enum drgn_object_encoding {
* Memory buffer.
*
* This is used for objects with a structure, union, class, or array
* type. The value is a buffer of the contents of that object's memory
* in the program.
* type.
*/
DRGN_OBJECT_ENCODING_BUFFER,
/**
* Signed integer.
*
* This is used for objects with a signed integer or signed enumerated
* type.
* type no larger than 64 bits.
*/
DRGN_OBJECT_ENCODING_SIGNED,
/**
* Unsigned integer.
*
* This is used for objects with a unsigned integer, boolean, or pointer
* type.
* type no larger than 64 bits.
*/
DRGN_OBJECT_ENCODING_UNSIGNED,
/**
* Big signed integer.
*
* This is used for objects with a signed integer or signed enumerated
* type larger than 64 bits.
*/
DRGN_OBJECT_ENCODING_SIGNED_BIG,
/**
* Big unsigned integer.
*
* This is used for objects with a unsigned integer, boolean, or pointer
* type larger than 64 bits.
*/
DRGN_OBJECT_ENCODING_UNSIGNED_BIG,
/**
* Floating-point value.
*
@ -1198,23 +1211,30 @@ drgn_object_encoding_is_complete(enum drgn_object_encoding encoding)
/** Value of a @ref drgn_object. */
union drgn_value {
/**
* Pointer to an external buffer for a @ref
* drgn_object_encoding::DRGN_OBJECT_ENCODING_BUFFER value.
* Pointer to an external buffer for a @ref DRGN_OBJECT_ENCODING_BUFFER,
* @ref DRGN_OBJECT_ENCODING_SIGNED_BIG, or @ref
* DRGN_OBJECT_ENCODING_UNSIGNED_BIG value.
*
* For @ref DRGN_OBJECT_ENCODING_BUFFER, this contains the object's
* representation in the memory of the program.
*
* For @ref DRGN_OBJECT_ENCODING_SIGNED_BIG and @ref
* DRGN_OBJECT_ENCODING_UNSIGNED_BIG, the representation of the value is
* an implementation detail which may change.
*/
char *bufp;
/**
* Inline buffer for a @ref
* drgn_object_encoding::DRGN_OBJECT_ENCODING_BUFFER value.
* Inline buffer for a @ref DRGN_OBJECT_ENCODING_BUFFER value.
*
* Tiny buffers (see @ref drgn_value_is_inline()) are stored inline here
* instead of in a separate allocation.
*/
char ibuf[8];
/** @ref drgn_object_encoding::DRGN_OBJECT_ENCODING_SIGNED value. */
/** @ref DRGN_OBJECT_ENCODING_SIGNED value. */
int64_t svalue;
/** @ref drgn_object_encoding::DRGN_OBJECT_ENCODING_UNSIGNED value. */
/** @ref DRGN_OBJECT_ENCODING_UNSIGNED value. */
uint64_t uvalue;
/** @ref drgn_object_encoding::DRGN_OBJECT_ENCODING_FLOAT value. */
/** @ref DRGN_OBJECT_ENCODING_FLOAT value. */
double fvalue;
};
@ -1277,11 +1297,11 @@ struct drgn_object {
/**
* Whether this object is little-endian.
*
* Valid only for scalars (i.e., @ref
* drgn_object_encoding::DRGN_OBJECT_ENCODING_SIGNED, @ref
* drgn_object_encoding::DRGN_OBJECT_ENCODING_UNSIGNED, @ref
* drgn_object_encoding::DRGN_OBJECT_ENCODING_FLOAT, or @ref
* drgn_object_encoding::DRGN_OBJECT_ENCODING_INCOMPLETE_INTEGER).
* Valid only for scalars (i.e., @ref DRGN_OBJECT_ENCODING_SIGNED, @ref
* DRGN_OBJECT_ENCODING_UNSIGNED, @ref DRGN_OBJECT_ENCODING_SIGNED_BIG,
* @ref DRGN_OBJECT_ENCODING_UNSIGNED_BIG, @ref
* DRGN_OBJECT_ENCODING_FLOAT, or @ref
* DRGN_OBJECT_ENCODING_INCOMPLETE_INTEGER).
*/
bool little_endian;
/**

View File

@ -690,30 +690,70 @@ c_format_int_object(const struct drgn_object *obj,
return NULL;
}
union drgn_value value_mem;
const union drgn_value *value;
err = drgn_object_read_value(obj, &value_mem, &value);
if (err)
return err;
switch (obj->encoding) {
case DRGN_OBJECT_ENCODING_SIGNED: {
int64_t svalue;
err = drgn_object_read_signed(obj, &svalue);
if (err)
return err;
if (!string_builder_appendf(sb, "%" PRId64, svalue))
return &drgn_enomem;
return NULL;
case DRGN_OBJECT_ENCODING_SIGNED:
if (!string_builder_appendf(sb, "%" PRId64, value->svalue)) {
err = &drgn_enomem;
goto out;
}
case DRGN_OBJECT_ENCODING_UNSIGNED: {
uint64_t uvalue;
err = drgn_object_read_unsigned(obj, &uvalue);
if (err)
return err;
if (!string_builder_appendf(sb, "%" PRIu64, uvalue))
return &drgn_enomem;
return NULL;
break;
case DRGN_OBJECT_ENCODING_UNSIGNED:
if (!string_builder_appendf(sb, "%" PRIu64, value->uvalue)) {
err = &drgn_enomem;
goto out;
}
break;
case DRGN_OBJECT_ENCODING_SIGNED_BIG:
case DRGN_OBJECT_ENCODING_UNSIGNED_BIG: {
if (!string_builder_append(sb, "0x")) {
err = &drgn_enomem;
goto out;
}
const uint8_t *buf = (uint8_t *)value->bufp;
size_t bytes = drgn_object_size(obj);
if (obj->little_endian) {
size_t i = bytes - 1;
while (i > 0 && buf[i] == 0)
i--;
if (!string_builder_appendf(sb, "%" PRIx8, buf[i])) {
err = &drgn_enomem;
goto out;
}
while (i-- > 0) {
if (!string_builder_appendf(sb, "%02" PRIx8, buf[i])) {
err = &drgn_enomem;
goto out;
}
}
} else {
size_t i = 0;
while (i < bytes - 1 && buf[i] == 0)
i++;
if (!string_builder_appendf(sb, "%" PRIx8, buf[i])) {
err = &drgn_enomem;
goto out;
}
while (++i < bytes) {
if (!string_builder_appendf(sb, "%02" PRIx8, buf[i])) {
err = &drgn_enomem;
goto out;
}
}
}
break;
}
default:
UNREACHABLE();
}
err = NULL;
out:
drgn_object_deinit_value(obj, value);
return err;
}
static struct drgn_error *

View File

@ -7,6 +7,7 @@
#include <stdlib.h>
#include <string.h>
#include "cleanup.h"
#include "drgn.h"
#include "error.h"
#include "language.h"
@ -32,8 +33,10 @@ LIBDRGN_PUBLIC void drgn_object_init(struct drgn_object *obj,
static void drgn_value_deinit(const struct drgn_object *obj,
const union drgn_value *value)
{
if (obj->encoding == DRGN_OBJECT_ENCODING_BUFFER &&
!drgn_object_is_inline(obj))
if ((obj->encoding == DRGN_OBJECT_ENCODING_BUFFER
&& !drgn_object_is_inline(obj))
|| obj->encoding == DRGN_OBJECT_ENCODING_SIGNED_BIG
|| obj->encoding == DRGN_OBJECT_ENCODING_UNSIGNED_BIG)
free(value->bufp);
}
@ -74,8 +77,8 @@ drgn_object_type_impl(struct drgn_type *type, struct drgn_type *underlying_type,
ret->underlying_type = underlying_type;
ret->qualifiers = qualifiers;
ret->encoding = drgn_type_object_encoding(type);
if (drgn_object_encoding_is_complete(ret->encoding)) {
if (drgn_type_is_complete(underlying_type)
&& drgn_type_kind(underlying_type) != DRGN_TYPE_FUNCTION) {
err = drgn_type_bit_size(type, &ret->bit_size);
if (err)
return err;
@ -83,9 +86,17 @@ drgn_object_type_impl(struct drgn_type *type, struct drgn_type *underlying_type,
ret->bit_size = 0;
}
ret->is_bit_field = bit_field_size != 0;
if (ret->encoding == DRGN_OBJECT_ENCODING_SIGNED ||
ret->encoding == DRGN_OBJECT_ENCODING_UNSIGNED) {
struct drgn_type *compatible_type = underlying_type;
SWITCH_ENUM(drgn_type_kind(compatible_type),
case DRGN_TYPE_ENUM:
if (!drgn_type_is_complete(compatible_type)) {
ret->encoding = DRGN_OBJECT_ENCODING_INCOMPLETE_INTEGER;
break;
}
compatible_type = drgn_type_type(compatible_type).type;
fallthrough;
case DRGN_TYPE_INT:
case DRGN_TYPE_BOOL: {
if (bit_field_size != 0) {
if (bit_field_size > ret->bit_size) {
return drgn_error_create(DRGN_ERROR_INVALID_ARGUMENT,
@ -104,28 +115,57 @@ drgn_object_type_impl(struct drgn_type *type, struct drgn_type *underlying_type,
"unsupported integer bit size (%" PRIu64 ")",
ret->bit_size);
}
} else {
if (bit_field_size != 0) {
return drgn_error_create(DRGN_ERROR_INVALID_ARGUMENT,
"bit field must be integer");
bool is_signed =
drgn_type_kind(compatible_type) == DRGN_TYPE_INT
&& drgn_type_is_signed(compatible_type);
if (ret->bit_size <= 64 && is_signed)
ret->encoding = DRGN_OBJECT_ENCODING_SIGNED;
else if (ret->bit_size <= 64)
ret->encoding = DRGN_OBJECT_ENCODING_UNSIGNED;
else if (is_signed)
ret->encoding = DRGN_OBJECT_ENCODING_SIGNED_BIG;
else
ret->encoding = DRGN_OBJECT_ENCODING_UNSIGNED_BIG;
break;
}
if (ret->encoding == DRGN_OBJECT_ENCODING_FLOAT &&
(ret->bit_size < 1 || ret->bit_size > 256)) {
case DRGN_TYPE_POINTER:
ret->encoding = DRGN_OBJECT_ENCODING_UNSIGNED;
break;
case DRGN_TYPE_FLOAT:
if (ret->bit_size < 1 || ret->bit_size > 256) {
return drgn_error_format(DRGN_ERROR_INVALID_ARGUMENT,
"unsupported floating-point bit size (%" PRIu64 ")",
ret->bit_size);
}
}
ret->encoding = DRGN_OBJECT_ENCODING_FLOAT;
break;
case DRGN_TYPE_STRUCT:
case DRGN_TYPE_UNION:
case DRGN_TYPE_CLASS:
case DRGN_TYPE_ARRAY:
if (drgn_type_is_complete(compatible_type))
ret->encoding = DRGN_OBJECT_ENCODING_BUFFER;
else
ret->encoding = DRGN_OBJECT_ENCODING_INCOMPLETE_BUFFER;
break;
case DRGN_TYPE_VOID:
case DRGN_TYPE_FUNCTION:
ret->encoding = DRGN_OBJECT_ENCODING_NONE;
break;
// This is already the underlying type, so it can't be a typedef.
case DRGN_TYPE_TYPEDEF:
)
if (drgn_type_has_little_endian(underlying_type)) {
ret->little_endian = drgn_type_little_endian(underlying_type);
} else if (drgn_type_kind(underlying_type) == DRGN_TYPE_ENUM &&
drgn_type_is_complete(underlying_type)) {
ret->little_endian =
drgn_type_little_endian(drgn_type_type(underlying_type).type);
} else {
ret->little_endian = false;
if (bit_field_size != 0
&& drgn_type_kind(compatible_type) != DRGN_TYPE_INT
&& drgn_type_kind(compatible_type) != DRGN_TYPE_BOOL) {
return drgn_error_create(DRGN_ERROR_INVALID_ARGUMENT,
"bit field must be integer");
}
ret->is_bit_field = bit_field_size != 0;
ret->little_endian = (drgn_type_has_little_endian(compatible_type)
&& drgn_type_little_endian(compatible_type));
return NULL;
}
@ -143,23 +183,37 @@ static struct drgn_error *
drgn_object_type_operand(const struct drgn_operand_type *op_type,
struct drgn_object_type *ret)
{
return drgn_object_type_impl(op_type->type, op_type->underlying_type,
struct drgn_error *err;
err = drgn_object_type_impl(op_type->type, op_type->underlying_type,
op_type->qualifiers,
op_type->bit_field_size, ret);
if (err)
return err;
if (ret->encoding == DRGN_OBJECT_ENCODING_SIGNED_BIG
|| ret->encoding == DRGN_OBJECT_ENCODING_UNSIGNED_BIG) {
return drgn_error_create(DRGN_ERROR_NOT_IMPLEMENTED,
"operations on integer values larger than 64 bits are not yet supported");
}
return NULL;
}
static struct drgn_error drgn_integer_too_big = {
.code = DRGN_ERROR_NOT_IMPLEMENTED,
.message = "integer values larger than 64 bits are not yet supported",
};
struct drgn_error *
drgn_object_set_signed_internal(struct drgn_object *res,
const struct drgn_object_type *type,
int64_t svalue)
{
if (type->bit_size > 64)
return &drgn_integer_too_big;
if (type->encoding == DRGN_OBJECT_ENCODING_SIGNED_BIG) {
uint64_t size = drgn_value_size(type->bit_size);
void *buf = malloc64(size);
if (!buf)
return &drgn_enomem;
copy_lsbytes_fill(buf, size, type->little_endian, &svalue,
sizeof(svalue), HOST_LITTLE_ENDIAN,
svalue < 0 ? -1 : 0);
drgn_object_reinit(res, type, DRGN_OBJECT_VALUE);
res->value.bufp = buf;
return NULL;
}
drgn_object_reinit(res, type, DRGN_OBJECT_VALUE);
res->value.svalue = truncate_signed(svalue, type->bit_size);
return NULL;
@ -175,7 +229,8 @@ drgn_object_set_signed(struct drgn_object *res,
err = drgn_object_type(qualified_type, bit_field_size, &type);
if (err)
return err;
if (type.encoding != DRGN_OBJECT_ENCODING_SIGNED) {
if (type.encoding != DRGN_OBJECT_ENCODING_SIGNED
&& type.encoding != DRGN_OBJECT_ENCODING_SIGNED_BIG) {
return drgn_error_create(DRGN_ERROR_TYPE,
"not a signed integer type");
}
@ -187,8 +242,17 @@ drgn_object_set_unsigned_internal(struct drgn_object *res,
const struct drgn_object_type *type,
uint64_t uvalue)
{
if (type->bit_size > 64)
return &drgn_integer_too_big;
if (type->encoding == DRGN_OBJECT_ENCODING_UNSIGNED_BIG) {
uint64_t size = drgn_value_size(type->bit_size);
void *buf = malloc64(size);
if (!buf)
return &drgn_enomem;
copy_lsbytes(buf, size, type->little_endian, &uvalue,
sizeof(uvalue), HOST_LITTLE_ENDIAN);
drgn_object_reinit(res, type, DRGN_OBJECT_VALUE);
res->value.bufp = buf;
return NULL;
}
drgn_object_reinit(res, type, DRGN_OBJECT_VALUE);
res->value.uvalue = truncate_unsigned(uvalue, type->bit_size);
return NULL;
@ -204,7 +268,8 @@ drgn_object_set_unsigned(struct drgn_object *res,
err = drgn_object_type(qualified_type, bit_field_size, &type);
if (err)
return err;
if (type.encoding != DRGN_OBJECT_ENCODING_UNSIGNED) {
if (type.encoding != DRGN_OBJECT_ENCODING_UNSIGNED
&& type.encoding != DRGN_OBJECT_ENCODING_UNSIGNED_BIG) {
return drgn_error_create(DRGN_ERROR_TYPE,
"not an unsigned integer type");
}
@ -216,7 +281,7 @@ static struct drgn_error drgn_float_size_unsupported = {
.message = "float values which are not 32 or 64 bits are not yet supported",
};
static struct drgn_error *
struct drgn_error *
drgn_object_set_float_internal(struct drgn_object *res,
const struct drgn_object_type *type,
double fvalue)
@ -292,13 +357,16 @@ drgn_object_set_from_buffer_internal(struct drgn_object *res,
* copy to a temporary value before freeing or modifying the old value.
*/
union drgn_value value;
if (type->encoding == DRGN_OBJECT_ENCODING_BUFFER) {
if (bit_offset != 0) {
if (type->encoding == DRGN_OBJECT_ENCODING_BUFFER
|| type->encoding == DRGN_OBJECT_ENCODING_SIGNED_BIG
|| type->encoding == DRGN_OBJECT_ENCODING_UNSIGNED_BIG) {
if (type->encoding == DRGN_OBJECT_ENCODING_BUFFER
&& bit_offset != 0) {
return drgn_error_create(DRGN_ERROR_INVALID_ARGUMENT,
"non-scalar must be byte-aligned");
}
uint64_t size = drgn_value_size(type->bit_size);
char *dst;
void *dst;
if (size <= sizeof(res->value.ibuf)) {
dst = value.ibuf;
} else {
@ -307,13 +375,27 @@ drgn_object_set_from_buffer_internal(struct drgn_object *res,
return &drgn_enomem;
value.bufp = dst;
}
memcpy(dst, p, size);
int dst_bit_offset = 0;
if (type->encoding != DRGN_OBJECT_ENCODING_BUFFER
&& !type->little_endian)
dst_bit_offset = -type->bit_size % 8;
((uint8_t *)dst)[0] = 0;
((uint8_t *)dst)[size - 1] = 0;
copy_bits(dst, dst_bit_offset, p, bit_offset, type->bit_size,
type->little_endian);
if (type->encoding == DRGN_OBJECT_ENCODING_SIGNED_BIG
&& type->bit_size % 8 != 0) {
int8_t *p;
if (type->little_endian)
p = (int8_t *)dst + size - 1;
else
p = (int8_t *)dst;
*p = truncate_signed8(*p, type->bit_size % 8);
}
} else if (drgn_object_encoding_is_complete(type->encoding)) {
if (type->encoding == DRGN_OBJECT_ENCODING_FLOAT) {
if (type->bit_size != 32 && type->bit_size != 64)
if (type->encoding == DRGN_OBJECT_ENCODING_FLOAT
&& type->bit_size != 32 && type->bit_size != 64)
return &drgn_float_size_unsupported;
} else if (type->bit_size > 64)
return &drgn_integer_too_big;
drgn_value_deserialize(&value, p, bit_offset, type->encoding,
type->bit_size, type->little_endian);
} else {
@ -360,13 +442,21 @@ drgn_object_set_reference_internal(struct drgn_object *res,
address += bit_offset / 8;
address &= address_mask;
bit_offset %= 8;
if (type->encoding != DRGN_OBJECT_ENCODING_SIGNED &&
type->encoding != DRGN_OBJECT_ENCODING_UNSIGNED &&
type->encoding != DRGN_OBJECT_ENCODING_FLOAT &&
type->encoding != DRGN_OBJECT_ENCODING_INCOMPLETE_INTEGER &&
bit_offset != 0) {
if (bit_offset != 0) {
SWITCH_ENUM(type->encoding,
case DRGN_OBJECT_ENCODING_SIGNED:
case DRGN_OBJECT_ENCODING_UNSIGNED:
case DRGN_OBJECT_ENCODING_SIGNED_BIG:
case DRGN_OBJECT_ENCODING_UNSIGNED_BIG:
case DRGN_OBJECT_ENCODING_FLOAT:
case DRGN_OBJECT_ENCODING_INCOMPLETE_INTEGER:
break;
case DRGN_OBJECT_ENCODING_NONE:
case DRGN_OBJECT_ENCODING_BUFFER:
case DRGN_OBJECT_ENCODING_INCOMPLETE_BUFFER:
return drgn_error_create(DRGN_ERROR_INVALID_ARGUMENT,
"non-scalar must be byte-aligned");
)
}
if (type->bit_size > UINT64_MAX - bit_offset) {
return drgn_error_format(DRGN_ERROR_OVERFLOW,
@ -421,7 +511,9 @@ drgn_object_copy(struct drgn_object *res, const struct drgn_object *obj)
SWITCH_ENUM(obj->kind,
case DRGN_OBJECT_VALUE:
if (obj->encoding == DRGN_OBJECT_ENCODING_BUFFER) {
if (obj->encoding == DRGN_OBJECT_ENCODING_BUFFER
|| obj->encoding == DRGN_OBJECT_ENCODING_SIGNED_BIG
|| obj->encoding == DRGN_OBJECT_ENCODING_UNSIGNED_BIG) {
size_t size = drgn_object_size(obj);
char *dst;
const char *src;
@ -552,10 +644,17 @@ drgn_object_read_reference(const struct drgn_object *obj,
obj->type);
}
if (obj->encoding == DRGN_OBJECT_ENCODING_BUFFER) {
assert(obj->bit_offset == 0);
if (obj->encoding == DRGN_OBJECT_ENCODING_BUFFER
|| obj->encoding == DRGN_OBJECT_ENCODING_SIGNED_BIG
|| obj->encoding == DRGN_OBJECT_ENCODING_UNSIGNED_BIG) {
int dst_bit_offset = 0;
if (obj->encoding != DRGN_OBJECT_ENCODING_BUFFER
&& !obj->little_endian)
dst_bit_offset = -obj->bit_size % 8;
uint64_t size = drgn_object_size(obj);
char *dst;
void *dst;
if (obj->bit_offset == 0 && dst_bit_offset == 0) {
if (size <= sizeof(value->ibuf)) {
dst = value->ibuf;
} else {
@ -563,23 +662,69 @@ drgn_object_read_reference(const struct drgn_object *obj,
if (!dst)
return &drgn_enomem;
}
err = drgn_program_read_memory(drgn_object_program(obj), dst,
obj->address, size, false);
err = drgn_program_read_memory(drgn_object_program(obj),
dst, obj->address, size,
false);
if (err) {
if (dst != value->ibuf)
free(dst);
return err;
}
if (obj->encoding == DRGN_OBJECT_ENCODING_UNSIGNED_BIG
&& obj->bit_size % 8 != 0) {
// dst_bit_offset == 0 && obj->bit_size % 8 != 0
// implies obj->little_endian.
uint8_t *p = (uint8_t *)dst + size - 1;
*p = truncate_unsigned8(*p, obj->bit_size % 8);
}
} else {
// bit_offset + bit_size is guaranteed not to overflow
// because bit_offset can only be non-zero for
// SIGNED_BIG and UNSIGNED_BIG, which we limit to a
// reasonable bit_size.
uint64_t read_size =
drgn_value_size(obj->bit_offset + obj->bit_size);
// We could probably read directly into dst and move the
// bits in place if we really wanted to, but this is
// easier.
_cleanup_free_ void *tmp = malloc64(read_size);
if (!tmp)
return &drgn_enomem;
err = drgn_program_read_memory(drgn_object_program(obj),
tmp, obj->address,
read_size, false);
if (err)
return err;
if (size <= sizeof(value->ibuf)) {
dst = value->ibuf;
} else {
dst = malloc64(size);
if (!dst)
return &drgn_enomem;
}
((uint8_t *)dst)[0] = 0;
((uint8_t *)dst)[size - 1] = 0;
copy_bits(dst, dst_bit_offset, tmp, obj->bit_offset,
obj->bit_size, obj->little_endian);
}
if (obj->encoding == DRGN_OBJECT_ENCODING_SIGNED_BIG
&& obj->bit_size % 8 != 0) {
int8_t *p;
if (obj->little_endian)
p = (int8_t *)dst + size - 1;
else
p = (int8_t *)dst;
*p = truncate_signed8(*p, obj->bit_size % 8);
}
if (size > sizeof(value->ibuf))
value->bufp = dst;
return NULL;
} else {
uint64_t bit_size = obj->bit_size;
if (obj->encoding == DRGN_OBJECT_ENCODING_FLOAT) {
if (bit_size != 32 && bit_size != 64)
if (obj->encoding == DRGN_OBJECT_ENCODING_FLOAT
&& bit_size != 32 && bit_size != 64)
return &drgn_float_size_unsupported;
} else if (bit_size > 64)
return &drgn_integer_too_big;
uint8_t bit_offset = obj->bit_offset;
uint64_t read_size = drgn_value_size(bit_offset + bit_size);
char buf[9];
@ -653,9 +798,22 @@ drgn_object_read_bytes(const struct drgn_object *obj, void *buf)
SWITCH_ENUM(obj->kind,
case DRGN_OBJECT_VALUE:
if (obj->encoding == DRGN_OBJECT_ENCODING_BUFFER) {
if (obj->encoding == DRGN_OBJECT_ENCODING_BUFFER
|| obj->encoding == DRGN_OBJECT_ENCODING_SIGNED_BIG
|| obj->encoding == DRGN_OBJECT_ENCODING_UNSIGNED_BIG) {
if (obj->bit_size % 8 == 0) {
memcpy(buf, drgn_object_buffer(obj),
drgn_object_size(obj));
} else {
int bit_offset = 0;
if (obj->encoding != DRGN_OBJECT_ENCODING_BUFFER
&& !obj->little_endian)
bit_offset = -obj->bit_size % 8;
((uint8_t *)buf)[drgn_object_size(obj) - 1] = 0;
copy_bits(buf, 0, drgn_object_buffer(obj),
bit_offset, obj->bit_size,
obj->little_endian);
}
} else {
union {
uint64_t uvalue;
@ -700,8 +858,18 @@ drgn_object_read_bytes(const struct drgn_object *obj, void *buf)
} else {
uint64_t read_size =
drgn_value_size(obj->bit_offset + obj->bit_size);
char tmp[9];
assert(read_size <= sizeof(tmp));
void *tmp;
char tmp_small[9];
_cleanup_free_ void *tmp_large = NULL;
if (read_size > sizeof(tmp_small)) {
tmp_large = malloc64(read_size);
if (!tmp_large)
return &drgn_enomem;
tmp = tmp_large;
} else {
tmp = tmp_small;
}
err = drgn_program_read_memory(drgn_object_program(obj),
tmp, obj->address,
read_size, false);
@ -769,10 +937,17 @@ drgn_object_value_float(const struct drgn_object *obj, double *ret)
return err;
}
static struct drgn_error drgn_integer_too_big = {
.code = DRGN_ERROR_OVERFLOW,
.message = "integer type is too big",
};
LIBDRGN_PUBLIC struct drgn_error *
drgn_object_read_signed(const struct drgn_object *obj, int64_t *ret)
{
if (obj->encoding != DRGN_OBJECT_ENCODING_SIGNED) {
if (obj->encoding == DRGN_OBJECT_ENCODING_SIGNED_BIG) {
return &drgn_integer_too_big;
} else if (obj->encoding != DRGN_OBJECT_ENCODING_SIGNED) {
return drgn_error_create(DRGN_ERROR_TYPE,
"not a signed integer");
}
@ -782,7 +957,9 @@ drgn_object_read_signed(const struct drgn_object *obj, int64_t *ret)
LIBDRGN_PUBLIC struct drgn_error *
drgn_object_read_unsigned(const struct drgn_object *obj, uint64_t *ret)
{
if (obj->encoding != DRGN_OBJECT_ENCODING_UNSIGNED) {
if (obj->encoding == DRGN_OBJECT_ENCODING_UNSIGNED_BIG) {
return &drgn_integer_too_big;
} else if (obj->encoding != DRGN_OBJECT_ENCODING_UNSIGNED) {
return drgn_error_create(DRGN_ERROR_TYPE,
"not an unsigned integer");
}
@ -796,11 +973,12 @@ drgn_object_read_integer(const struct drgn_object *obj, union drgn_value *ret)
union drgn_value value_mem;
const union drgn_value *value;
if (obj->encoding != DRGN_OBJECT_ENCODING_SIGNED &&
obj->encoding != DRGN_OBJECT_ENCODING_UNSIGNED) {
return drgn_error_create(DRGN_ERROR_TYPE,
"not an integer");
}
if (obj->encoding == DRGN_OBJECT_ENCODING_SIGNED_BIG
|| obj->encoding == DRGN_OBJECT_ENCODING_UNSIGNED_BIG)
return &drgn_integer_too_big;
else if (obj->encoding != DRGN_OBJECT_ENCODING_SIGNED
&& obj->encoding != DRGN_OBJECT_ENCODING_UNSIGNED)
return drgn_error_create(DRGN_ERROR_TYPE, "not an integer");
err = drgn_object_read_value(obj, &value_mem, &value);
if (err)
return err;
@ -915,6 +1093,9 @@ drgn_object_convert_signed(const struct drgn_object *obj, uint64_t bit_size,
case DRGN_OBJECT_ENCODING_FLOAT:
*ret = truncate_signed(value->fvalue, bit_size);
break;
case DRGN_OBJECT_ENCODING_SIGNED_BIG:
case DRGN_OBJECT_ENCODING_UNSIGNED_BIG:
return &drgn_integer_too_big;
default:
err = drgn_error_create(DRGN_ERROR_TYPE,
"object cannot be converted to integer");
@ -943,6 +1124,9 @@ drgn_object_convert_unsigned(const struct drgn_object *obj, uint64_t bit_size,
case DRGN_OBJECT_ENCODING_FLOAT:
*ret = truncate_unsigned(value->fvalue, bit_size);
break;
case DRGN_OBJECT_ENCODING_SIGNED_BIG:
case DRGN_OBJECT_ENCODING_UNSIGNED_BIG:
return &drgn_integer_too_big;
default:
err = drgn_error_create(DRGN_ERROR_TYPE,
"object cannot be converted to integer");
@ -972,6 +1156,9 @@ drgn_object_convert_float(const struct drgn_object *obj, double *fvalue)
case DRGN_OBJECT_ENCODING_FLOAT:
*fvalue = value->fvalue;
break;
case DRGN_OBJECT_ENCODING_SIGNED_BIG:
case DRGN_OBJECT_ENCODING_UNSIGNED_BIG:
return &drgn_integer_too_big;
default:
err = drgn_error_create(DRGN_ERROR_TYPE,
"object cannot be converted to floating-point");
@ -1080,6 +1267,23 @@ drgn_object_is_zero_impl(const struct drgn_object *obj, bool *ret)
*ret = false;
return NULL;
}
case DRGN_OBJECT_ENCODING_SIGNED_BIG:
case DRGN_OBJECT_ENCODING_UNSIGNED_BIG: {
union drgn_value value_mem;
const union drgn_value *value;
err = drgn_object_read_value(obj, &value_mem, &value);
if (err)
return err;
size_t size = drgn_object_size(obj);
for (size_t i = 0; i < size; i++) {
if (value->bufp[i] != 0) {
*ret = false;
break;
}
}
drgn_object_deinit_value(obj, value);
return NULL;
}
case DRGN_OBJECT_ENCODING_FLOAT: {
double fvalue;
@ -1552,6 +1756,9 @@ static struct drgn_error *pointer_operand(const struct drgn_object *ptr,
switch (ptr->encoding) {
case DRGN_OBJECT_ENCODING_UNSIGNED:
return drgn_object_value_unsigned(ptr, ret);
case DRGN_OBJECT_ENCODING_SIGNED_BIG:
case DRGN_OBJECT_ENCODING_UNSIGNED_BIG:
return &drgn_integer_too_big;
case DRGN_OBJECT_ENCODING_BUFFER:
case DRGN_OBJECT_ENCODING_NONE:
case DRGN_OBJECT_ENCODING_INCOMPLETE_BUFFER:
@ -1860,6 +2067,9 @@ drgn_op_add_to_pointer(struct drgn_object *res,
if (err)
return err;
break;
case DRGN_OBJECT_ENCODING_SIGNED_BIG:
case DRGN_OBJECT_ENCODING_UNSIGNED_BIG:
return &drgn_integer_too_big;
default:
return drgn_error_create(DRGN_ERROR_TYPE,
"invalid addend type for pointer arithmetic");

View File

@ -153,6 +153,15 @@ drgn_object_set_unsigned_internal(struct drgn_object *res,
const struct drgn_object_type *type,
uint64_t uvalue);
/**
* Like @ref drgn_object_set_float() but @ref drgn_object_type() was already
* called and the type is already known to be a floating-point type.
*/
struct drgn_error *
drgn_object_set_float_internal(struct drgn_object *res,
const struct drgn_object_type *type,
double fvalue);
/**
* Like @ref drgn_object_set_from_buffer() but @ref drgn_object_type() was
* already called and the bounds of the buffer have already been checked.

View File

@ -15,6 +15,7 @@
#include "../drgn.h"
// IWYU pragma: end_exports
#include "../hash_table.h"
#include "../hash_table.h"
#include "../program.h"

View File

@ -5,6 +5,7 @@
#include <math.h>
#include "drgnpy.h"
#include "../cleanup.h"
#include "../error.h"
#include "../object.h"
#include "../serialize.h"
@ -50,6 +51,43 @@ static int DrgnObject_literal(struct drgn_object *res, PyObject *literal)
return 0;
}
static void *
py_long_to_bytes_for_object_type(PyObject *value_obj,
const struct drgn_object_type *type)
{
if (!PyNumber_Check(value_obj)) {
return set_error_type_name("'%s' value must be number",
drgn_object_type_qualified(type));
}
_cleanup_pydecref_ PyObject *long_obj = PyNumber_Long(value_obj);
if (!long_obj)
return NULL;
uint64_t size = drgn_value_size(type->bit_size);
_cleanup_free_ void *buf = malloc64(size);
if (!buf) {
PyErr_NoMemory();
return NULL;
}
// _PyLong_AsByteArray() still returns the least significant bytes on
// OverflowError unless the object is negative and is_signed is false.
// So, we always pass is_signed as true.
int r = _PyLong_AsByteArray((PyLongObject *)long_obj, buf, size,
type->little_endian, true);
if (r) {
PyObject *exc_type, *exc_value, *exc_traceback;
PyErr_Fetch(&exc_type, &exc_value, &exc_traceback);
if (PyErr_GivenExceptionMatches(exc_type, PyExc_OverflowError)) {
Py_XDECREF(exc_traceback);
Py_XDECREF(exc_value);
Py_DECREF(exc_type);
} else {
PyErr_Restore(exc_type, exc_value, exc_traceback);
return NULL;
}
}
return_ptr(buf);
}
static int serialize_py_object(struct drgn_program *prog, char *buf,
uint64_t buf_bit_size, uint64_t bit_offset,
PyObject *value_obj,
@ -220,6 +258,19 @@ static int serialize_py_object(struct drgn_program *prog, char *buf,
type->little_endian);
return 0;
}
case DRGN_OBJECT_ENCODING_SIGNED_BIG:
case DRGN_OBJECT_ENCODING_UNSIGNED_BIG: {
_cleanup_free_ void *tmp =
py_long_to_bytes_for_object_type(value_obj, type);
if (!tmp)
return -1;
int src_bit_offset = 0;
if (!type->little_endian)
src_bit_offset = -type->bit_size % 8;
copy_bits(buf + bit_offset / 8, bit_offset % 8, tmp,
src_bit_offset, type->bit_size, type->little_endian);
return 0;
}
case DRGN_OBJECT_ENCODING_FLOAT: {
if (!PyNumber_Check(value_obj)) {
set_error_type_name("'%s' value must be number",
@ -270,19 +321,10 @@ static int serialize_py_object(struct drgn_program *prog, char *buf,
}
static int buffer_object_from_value(struct drgn_object *res,
struct drgn_qualified_type qualified_type,
const struct drgn_object_type *type,
PyObject *value_obj)
{
struct drgn_error *err;
struct drgn_object_type type;
err = drgn_object_type(qualified_type, 0, &type);
if (err) {
set_drgn_error(err);
return -1;
}
uint64_t size = drgn_value_size(type.bit_size);
uint64_t size = drgn_value_size(type->bit_size);
if (size > SIZE_MAX) {
PyErr_NoMemory();
return -1;
@ -301,14 +343,14 @@ static int buffer_object_from_value(struct drgn_object *res,
}
memset(buf, 0, size);
if (serialize_py_object(drgn_object_program(res), buf, type.bit_size, 0,
value_obj, &type) == -1) {
if (serialize_py_object(drgn_object_program(res), buf, type->bit_size,
0, value_obj, type) == -1) {
if (buf != value.ibuf)
free(buf);
return -1;
}
drgn_object_reinit(res, &type, DRGN_OBJECT_VALUE);
drgn_object_reinit(res, type, DRGN_OBJECT_VALUE);
res->value = value;
return 0;
}
@ -392,25 +434,15 @@ static DrgnObject *DrgnObject_new(PyTypeObject *subtype, PyObject *args,
return NULL;
}
enum drgn_object_encoding encoding =
drgn_type_object_encoding(qualified_type.type);
if (!drgn_object_encoding_is_complete(encoding)) {
err = drgn_error_incomplete_type("cannot create value with %s type",
qualified_type.type);
struct drgn_object_type object_type;
err = drgn_object_type(qualified_type, bit_field_size.uvalue,
&object_type);
if (err)
return set_drgn_error(err);
}
if (!bit_field_size.is_none &&
encoding != DRGN_OBJECT_ENCODING_SIGNED &&
encoding != DRGN_OBJECT_ENCODING_UNSIGNED) {
PyErr_SetString(PyExc_ValueError,
"bit field must be integer");
return NULL;
}
switch (encoding) {
SWITCH_ENUM(object_type.encoding,
case DRGN_OBJECT_ENCODING_BUFFER:
if (buffer_object_from_value(&obj->obj, qualified_type,
if (buffer_object_from_value(&obj->obj, &object_type,
value_obj) == -1)
return NULL;
err = NULL;
@ -433,19 +465,33 @@ static DrgnObject *DrgnObject_new(PyTypeObject *subtype, PyObject *args,
};
if (tmp.uvalue == (uint64_t)-1 && PyErr_Occurred())
return NULL;
if (encoding == DRGN_OBJECT_ENCODING_SIGNED) {
err = drgn_object_set_signed(&obj->obj,
qualified_type,
tmp.svalue,
bit_field_size.uvalue);
if (object_type.encoding == DRGN_OBJECT_ENCODING_SIGNED) {
err = drgn_object_set_signed_internal(&obj->obj,
&object_type,
tmp.svalue);
} else {
err = drgn_object_set_unsigned(&obj->obj,
qualified_type,
tmp.uvalue,
bit_field_size.uvalue);
err = drgn_object_set_unsigned_internal(&obj->obj,
&object_type,
tmp.uvalue);
}
break;
}
case DRGN_OBJECT_ENCODING_SIGNED_BIG:
case DRGN_OBJECT_ENCODING_UNSIGNED_BIG: {
_cleanup_free_ void *tmp =
py_long_to_bytes_for_object_type(value_obj,
&object_type);
if (!tmp)
return NULL;
uint64_t src_bit_offset = 0;
if (!object_type.little_endian)
src_bit_offset = -object_type.bit_size % 8;
err = drgn_object_set_from_buffer_internal(&obj->obj,
&object_type,
tmp,
src_bit_offset);
break;
}
case DRGN_OBJECT_ENCODING_FLOAT: {
if (!PyNumber_Check(value_obj)) {
return set_error_type_name("'%s' value must be number",
@ -454,13 +500,18 @@ static DrgnObject *DrgnObject_new(PyTypeObject *subtype, PyObject *args,
double fvalue = PyFloat_AsDouble(value_obj);
if (fvalue == -1.0 && PyErr_Occurred())
return NULL;
err = drgn_object_set_float(&obj->obj, qualified_type,
err = drgn_object_set_float_internal(&obj->obj,
&object_type,
fvalue);
break;
}
default:
UNREACHABLE();
}
case DRGN_OBJECT_ENCODING_NONE:
case DRGN_OBJECT_ENCODING_INCOMPLETE_BUFFER:
case DRGN_OBJECT_ENCODING_INCOMPLETE_INTEGER:
err = drgn_error_incomplete_type("cannot create value with %s type",
qualified_type.type);
break;
)
} else {
if (!qualified_type.type) {
PyErr_SetString(PyExc_ValueError,
@ -637,6 +688,19 @@ static PyObject *DrgnObject_value_impl(struct drgn_object *obj)
else
return PyLong_FromUint64(uvalue);
}
case DRGN_OBJECT_ENCODING_SIGNED_BIG:
case DRGN_OBJECT_ENCODING_UNSIGNED_BIG: {
union drgn_value value_mem;
const union drgn_value *value;
err = drgn_object_read_value(obj, &value_mem, &value);
if (err)
return set_drgn_error(err);
return _PyLong_FromByteArray((void *)value->bufp,
drgn_object_size(obj),
obj->little_endian,
obj->encoding
== DRGN_OBJECT_ENCODING_SIGNED_BIG);
}
case DRGN_OBJECT_ENCODING_FLOAT: {
double fvalue;
@ -1104,100 +1168,79 @@ static int DrgnObject_bool(DrgnObject *self)
static PyObject *DrgnObject_int(DrgnObject *self)
{
struct drgn_error *err;
union drgn_value value_mem;
const union drgn_value *value;
PyObject *ret;
if (!drgn_type_is_scalar(self->obj.type)) {
return set_error_type_name("cannot convert '%s' to int",
drgn_object_qualified_type(&self->obj));
}
err = drgn_object_read_value(&self->obj, &value_mem, &value);
SWITCH_ENUM(self->obj.encoding,
case DRGN_OBJECT_ENCODING_SIGNED:
case DRGN_OBJECT_ENCODING_UNSIGNED:
case DRGN_OBJECT_ENCODING_SIGNED_BIG:
case DRGN_OBJECT_ENCODING_UNSIGNED_BIG:
return DrgnObject_value(self);
case DRGN_OBJECT_ENCODING_FLOAT: {
double fvalue;
err = drgn_object_read_float(&self->obj, &fvalue);
if (err)
return set_drgn_error(err);
switch (self->obj.encoding) {
case DRGN_OBJECT_ENCODING_SIGNED:
ret = PyLong_FromInt64(value->svalue);
break;
case DRGN_OBJECT_ENCODING_UNSIGNED:
ret = PyLong_FromUint64(value->uvalue);
break;
case DRGN_OBJECT_ENCODING_FLOAT:
ret = PyLong_FromDouble(value->fvalue);
break;
default:
UNREACHABLE();
return PyLong_FromDouble(fvalue);
}
drgn_object_deinit_value(&self->obj, value);
return ret;
case DRGN_OBJECT_ENCODING_BUFFER:
case DRGN_OBJECT_ENCODING_NONE:
case DRGN_OBJECT_ENCODING_INCOMPLETE_BUFFER:
case DRGN_OBJECT_ENCODING_INCOMPLETE_INTEGER:
return set_error_type_name("cannot convert '%s' to int",
drgn_object_qualified_type(&self->obj));
)
}
static PyObject *DrgnObject_float(DrgnObject *self)
{
struct drgn_error *err;
union drgn_value value_mem;
const union drgn_value *value;
PyObject *ret;
if (!drgn_type_is_arithmetic(self->obj.type)) {
return set_error_type_name("cannot convert '%s' to float",
drgn_object_qualified_type(&self->obj));
}
err = drgn_object_read_value(&self->obj, &value_mem, &value);
SWITCH_ENUM(self->obj.encoding,
case DRGN_OBJECT_ENCODING_FLOAT: {
double fvalue;
err = drgn_object_read_float(&self->obj, &fvalue);
if (err)
return set_drgn_error(err);
switch (self->obj.encoding) {
case DRGN_OBJECT_ENCODING_SIGNED:
ret = PyFloat_FromDouble(value->svalue);
break;
case DRGN_OBJECT_ENCODING_UNSIGNED:
ret = PyFloat_FromDouble(value->uvalue);
break;
case DRGN_OBJECT_ENCODING_FLOAT:
ret = PyFloat_FromDouble(value->fvalue);
break;
default:
UNREACHABLE();
return PyFloat_FromDouble(fvalue);
}
drgn_object_deinit_value(&self->obj, value);
return ret;
case DRGN_OBJECT_ENCODING_SIGNED:
case DRGN_OBJECT_ENCODING_UNSIGNED:
case DRGN_OBJECT_ENCODING_SIGNED_BIG:
case DRGN_OBJECT_ENCODING_UNSIGNED_BIG: {
if (drgn_type_kind(drgn_underlying_type(self->obj.type))
!= DRGN_TYPE_POINTER) {
_cleanup_pydecref_ PyObject *value =
DrgnObject_value(self);
if (!value)
return NULL;
return PyObject_CallFunctionObjArgs((PyObject *)&PyFloat_Type,
value, NULL);
}
fallthrough;
}
case DRGN_OBJECT_ENCODING_BUFFER:
case DRGN_OBJECT_ENCODING_NONE:
case DRGN_OBJECT_ENCODING_INCOMPLETE_BUFFER:
case DRGN_OBJECT_ENCODING_INCOMPLETE_INTEGER:
return set_error_type_name("cannot convert '%s' to float",
drgn_object_qualified_type(&self->obj));
)
}
static PyObject *DrgnObject_index(DrgnObject *self)
{
struct drgn_error *err;
struct drgn_type *underlying_type;
union drgn_value value_mem;
const union drgn_value *value;
PyObject *ret;
underlying_type = drgn_underlying_type(self->obj.type);
if (!drgn_type_is_integer(underlying_type) &&
drgn_type_kind(underlying_type) != DRGN_TYPE_POINTER) {
SWITCH_ENUM(self->obj.encoding,
case DRGN_OBJECT_ENCODING_SIGNED:
case DRGN_OBJECT_ENCODING_UNSIGNED:
case DRGN_OBJECT_ENCODING_SIGNED_BIG:
case DRGN_OBJECT_ENCODING_UNSIGNED_BIG:
return DrgnObject_value(self);
case DRGN_OBJECT_ENCODING_FLOAT:
case DRGN_OBJECT_ENCODING_BUFFER:
case DRGN_OBJECT_ENCODING_NONE:
case DRGN_OBJECT_ENCODING_INCOMPLETE_BUFFER:
case DRGN_OBJECT_ENCODING_INCOMPLETE_INTEGER:
return set_error_type_name("'%s' object cannot be interpreted as an integer",
drgn_object_qualified_type(&self->obj));
}
err = drgn_object_read_value(&self->obj, &value_mem, &value);
if (err)
return set_drgn_error(err);
switch (self->obj.encoding) {
case DRGN_OBJECT_ENCODING_SIGNED:
ret = PyLong_FromInt64(value->svalue);
break;
case DRGN_OBJECT_ENCODING_UNSIGNED:
ret = PyLong_FromUint64(value->uvalue);
break;
default:
UNREACHABLE();
}
drgn_object_deinit_value(&self->obj, value);
return ret;
)
}
static PyObject *DrgnObject_round(DrgnObject *self, PyObject *args,
@ -1237,33 +1280,19 @@ static PyObject *DrgnObject_round(DrgnObject *self, PyObject *args,
#define DrgnObject_round_method(func) \
static PyObject *DrgnObject_##func(DrgnObject *self) \
{ \
struct drgn_error *err; \
union drgn_value value_mem; \
const union drgn_value *value; \
PyObject *ret; \
\
if (!drgn_type_is_arithmetic(self->obj.type)) { \
return set_error_type_name("cannot round '%s'", \
drgn_object_qualified_type(&self->obj));\
} \
\
err = drgn_object_read_value(&self->obj, &value_mem, &value); \
if (self->obj.encoding != DRGN_OBJECT_ENCODING_FLOAT) \
return DrgnObject_value(self); \
union drgn_value value_mem; \
const union drgn_value *value; \
struct drgn_error *err = \
drgn_object_read_value(&self->obj, &value_mem, &value); \
if (err) \
return set_drgn_error(err); \
\
switch (self->obj.encoding) { \
case DRGN_OBJECT_ENCODING_SIGNED: \
ret = PyLong_FromInt64(value->svalue); \
break; \
case DRGN_OBJECT_ENCODING_UNSIGNED: \
ret = PyLong_FromUint64(value->uvalue); \
break; \
case DRGN_OBJECT_ENCODING_FLOAT: \
ret = PyLong_FromDouble(func(value->fvalue)); \
break; \
default: \
UNREACHABLE(); \
} \
PyObject *ret = PyLong_FromDouble(func(value->fvalue)); \
drgn_object_deinit_value(&self->obj, value); \
return ret; \
}

View File

@ -55,11 +55,12 @@ static inline uint8_t truncate_unsigned8(uint8_t uvalue, int bit_size)
* least-significant bytes of @p dst.
*
* If `src_size > dst_size`, the extra bytes are discarded. If `src_size <
* dst_size`, the extra bytes are zero-filled.
* dst_size`, the extra bytes are filled with @p fill.
*/
static inline void copy_lsbytes(void *dst, size_t dst_size,
static inline void copy_lsbytes_fill(void *dst, size_t dst_size,
bool dst_little_endian, const void *src,
size_t src_size, bool src_little_endian)
size_t src_size, bool src_little_endian,
int fill)
{
char *d = dst;
const char *s = src;
@ -71,9 +72,9 @@ static inline void copy_lsbytes(void *dst, size_t dst_size,
for (size_t i = 0; i < size; i++)
d[i] = s[src_size - 1 - i];
}
memset(d + size, 0, dst_size - size);
memset(d + size, fill, dst_size - size);
} else {
memset(d, 0, dst_size - size);
memset(d, fill, dst_size - size);
if (src_little_endian) {
for (size_t i = dst_size - size; i < dst_size; i++)
d[i] = s[dst_size - 1 - i];
@ -83,6 +84,21 @@ static inline void copy_lsbytes(void *dst, size_t dst_size,
}
}
/**
* Copy the @p src_size least-significant bytes from @p src to the @p dst_size
* least-significant bytes of @p dst.
*
* If `src_size > dst_size`, the extra bytes are discarded. If `src_size <
* dst_size`, the extra bytes are zero-filled.
*/
static inline void copy_lsbytes(void *dst, size_t dst_size,
bool dst_little_endian, const void *src,
size_t src_size, bool src_little_endian)
{
return copy_lsbytes_fill(dst, dst_size, dst_little_endian, src,
src_size, src_little_endian, 0);
}
/**
* Return a bit mask with bits `[bit_offset, 7]` set.
*

View File

@ -1200,37 +1200,6 @@ struct drgn_error *drgn_type_bit_size(struct drgn_type *type, uint64_t *ret)
return NULL;
}
enum drgn_object_encoding drgn_type_object_encoding(struct drgn_type *type)
{
SWITCH_ENUM(drgn_type_kind(type),
case DRGN_TYPE_INT:
return (drgn_type_is_signed(type) ?
DRGN_OBJECT_ENCODING_SIGNED :
DRGN_OBJECT_ENCODING_UNSIGNED);
case DRGN_TYPE_BOOL:
case DRGN_TYPE_POINTER:
return DRGN_OBJECT_ENCODING_UNSIGNED;
case DRGN_TYPE_FLOAT:
return DRGN_OBJECT_ENCODING_FLOAT;
case DRGN_TYPE_STRUCT:
case DRGN_TYPE_UNION:
case DRGN_TYPE_CLASS:
case DRGN_TYPE_ARRAY:
return (drgn_type_is_complete(type) ?
DRGN_OBJECT_ENCODING_BUFFER :
DRGN_OBJECT_ENCODING_INCOMPLETE_BUFFER);
case DRGN_TYPE_ENUM:
if (!drgn_type_is_complete(type))
return DRGN_OBJECT_ENCODING_INCOMPLETE_INTEGER;
fallthrough;
case DRGN_TYPE_TYPEDEF:
return drgn_type_object_encoding(drgn_type_type(type).type);
case DRGN_TYPE_VOID:
case DRGN_TYPE_FUNCTION:
return DRGN_OBJECT_ENCODING_NONE;
)
}
struct drgn_error *drgn_type_error(const char *format, struct drgn_type *type)
{
struct drgn_qualified_type qualified_type = { type };

View File

@ -558,9 +558,6 @@ bool drgn_type_is_scalar(struct drgn_type *type);
struct drgn_error *drgn_type_bit_size(struct drgn_type *type,
uint64_t *ret);
/** Get the appropriate @ref drgn_object_encoding for a @ref drgn_type. */
enum drgn_object_encoding drgn_type_object_encoding(struct drgn_type *type);
/** Initialize type-related fields in a @ref drgn_program. */
void drgn_program_init_types(struct drgn_program *prog);
/** Deinitialize type-related fields in a @ref drgn_program. */

View File

@ -2409,3 +2409,123 @@ class TestPrettyPrintObject(MockProgramTestCase):
else:
type_name = type_
self.assertEqual(str(Object(self.prog, type_)), f"({type_name})<absent>")
def test_bigint(self):
segment = bytearray(16)
self.add_memory_segment(segment, virt_addr=0xFFFF0000)
for byteorder in ("little", "big"):
with self.subTest(byteorder=byteorder):
obj = Object(
self.prog,
self.prog.int_type("unsigned __int128", 16, False, byteorder),
address=0xFFFF0000,
)
for value in (
0,
0x100,
0x112233445566778899AABBCCDDEEFF,
0xFEDCBA9876543210,
):
segment[:] = value.to_bytes(16, byteorder)
self.assertEqual(str(obj), "(unsigned __int128)" + hex(value))
def test_bigint_in_array(self):
segment = bytearray(16 * 3)
self.add_memory_segment(segment, virt_addr=0xFFFF0000)
for byteorder in ("little", "big"):
with self.subTest(byteorder=byteorder):
segment[:16] = (0x112233445566778899AABBCCDDEEFF).to_bytes(
16, byteorder
)
segment[16:32] = (0xDEADBEEF).to_bytes(16, byteorder)
obj = Object(
self.prog,
self.prog.array_type(
self.prog.int_type("unsigned __int128", 16, False, byteorder), 3
),
address=0xFFFF0000,
)
self.assertEqual(
str(obj),
"(unsigned __int128 [3]){ 0x112233445566778899aabbccddeeff, 0xdeadbeef }",
)
self.assertEqual(
obj.format_(implicit_elements=True),
"(unsigned __int128 [3]){ 0x112233445566778899aabbccddeeff, 0xdeadbeef, 0x0 }",
)
def test_bigint_in_struct(self):
segment = bytearray(16 * 3)
self.add_memory_segment(segment, virt_addr=0xFFFF0000)
for byteorder in ("little", "big"):
with self.subTest(byteorder=byteorder):
segment[:16] = (0x112233445566778899AABBCCDDEEFF).to_bytes(
16, byteorder
)
segment[16:32] = (0xDEADBEEF).to_bytes(16, byteorder)
type = self.prog.int_type("unsigned __int128", 16, False, byteorder)
obj = Object(
self.prog,
self.prog.struct_type(
"foo",
16 * 3,
(
TypeMember(type, "a"),
TypeMember(type, "b", 16 * 8),
TypeMember(type, "c", 16 * 8 * 2),
),
),
address=0xFFFF0000,
)
self.assertEqual(
str(obj),
"""\
(struct foo){
.a = (unsigned __int128)0x112233445566778899aabbccddeeff,
.b = (unsigned __int128)0xdeadbeef,
.c = (unsigned __int128)0x0,
}""",
)
self.assertEqual(
obj.format_(implicit_members=False),
"""\
(struct foo){
.a = (unsigned __int128)0x112233445566778899aabbccddeeff,
.b = (unsigned __int128)0xdeadbeef,
}""",
)
def test_bigint_typedef(self):
self.add_memory_segment(
(0xDEADBEEF).to_bytes(16, "little"), virt_addr=0xFFFF0000
)
type = self.prog.typedef_type(
"__uint128_t", self.prog.int_type("unsigned __int128", 16, False)
)
self.assertEqual(
str(Object(self.prog, type, address=0xFFFF0000)),
"(__uint128_t)0xdeadbeef",
)
self.assertEqual(
str(Object(self.prog, self.prog.array_type(type, 1), address=0xFFFF0000)),
"(__uint128_t [1]){ 0xdeadbeef }",
)
self.assertEqual(
str(
Object(
self.prog,
self.prog.struct_type("foo", 16, (TypeMember(type, "a"),)),
address=0xFFFF0000,
)
),
"""\
(struct foo){
.a = (__uint128_t)0xdeadbeef,
}""",
)

View File

@ -144,7 +144,7 @@ class TestInit(MockProgramTestCase):
def _int_bits_cases(prog):
for signed in (True, False):
for byteorder in ("little", "big"):
for bit_size in range(1, 65):
for bit_size in range(1, 129):
if bit_size <= 8:
size = 1
else:
@ -263,8 +263,60 @@ class TestReference(MockProgramTestCase):
bit_offset=7,
)
def test_signed_big(self):
buffer = (-4).to_bytes(16, "little", signed=True)
self.add_memory_segment(buffer, virt_addr=0xFFFF0000)
obj = Object(
self.prog,
self.prog.int_type("__int128", 16, True),
address=0xFFFF0000,
)
self.assertIs(obj.prog_, self.prog)
self.assertFalse(obj.absent_)
self.assertEqual(obj.address_, 0xFFFF0000)
self.assertEqual(obj.bit_offset_, 0)
self.assertIsNone(obj.bit_field_size_)
self.assertEqual(obj.type_.size, 16)
self.assertEqual(obj.value_(), -4)
self.assertEqual(obj.to_bytes_(), buffer)
self.assertEqual(repr(obj), "Object(prog, '__int128', address=0xffff0000)")
self.assertIdentical(
obj.read_(),
Object(self.prog, self.prog.int_type("__int128", 16, True), value=-4),
)
def test_unsigned_big(self):
buffer = (1000).to_bytes(16, "little")
self.add_memory_segment(buffer, virt_addr=0xFFFF0000)
obj = Object(
self.prog,
self.prog.int_type("unsigned __int128", 16, False),
address=0xFFFF0000,
)
self.assertIs(obj.prog_, self.prog)
self.assertFalse(obj.absent_)
self.assertEqual(obj.address_, 0xFFFF0000)
self.assertEqual(obj.bit_offset_, 0)
self.assertIsNone(obj.bit_field_size_)
self.assertEqual(obj.type_.size, 16)
self.assertEqual(obj.value_(), 1000)
self.assertEqual(obj.to_bytes_(), buffer)
self.assertEqual(
repr(obj), "Object(prog, 'unsigned __int128', address=0xffff0000)"
)
self.assertIdentical(
obj.read_(),
Object(
self.prog,
self.prog.int_type("unsigned __int128", 16, False),
value=1000,
),
)
def test_int_bits(self):
buffer = bytearray(9)
buffer = bytearray(17)
self.add_memory_segment(buffer, virt_addr=0xFFFF0000)
for (
signed,
@ -525,30 +577,6 @@ class TestReference(MockProgramTestCase):
Object(self.prog, self.point_type, address=0xFFFF0004),
)
def test_big_int(self):
buffer = (1000).to_bytes(16, "little")
self.add_memory_segment(buffer, virt_addr=0xFFFF0000)
obj = Object(
self.prog,
self.prog.int_type("unsigned __int128", 16, False),
address=0xFFFF0000,
)
self.assertIs(obj.prog_, self.prog)
self.assertFalse(obj.absent_)
self.assertEqual(obj.address_, 0xFFFF0000)
self.assertEqual(obj.bit_offset_, 0)
self.assertIsNone(obj.bit_field_size_)
self.assertEqual(obj.type_.size, 16)
self.assertRaisesRegex(
NotImplementedError,
"integer values larger than 64 bits are not yet supported",
obj.value_,
)
self.assertEqual(obj.to_bytes_(), buffer)
self.assertEqual(
repr(obj), "Object(prog, 'unsigned __int128', address=0xffff0000)"
)
def test_bit_field_of_big_int(self):
buffer = (1000).to_bytes(4, "little")
self.add_memory_segment(buffer, virt_addr=0xFFFF0000)
@ -643,18 +671,6 @@ class TestValue(MockProgramTestCase):
self.assertEqual(obj.value_(), -8)
self.assertEqual(repr(obj), "Object(prog, 'int', value=-8, bit_field_size=4)")
value = 12345678912345678989
for bit_size in range(1, 65):
tmp = value & ((1 << bit_size) - 1)
mask = 1 << (bit_size - 1)
tmp = (tmp ^ mask) - mask
self.assertEqual(
Object(
self.prog, "long", value=value, bit_field_size=bit_size
).value_(),
tmp,
)
def test_unsigned(self):
obj = Object(self.prog, "unsigned int", value=2**32 - 1)
self.assertIs(obj.prog_, self.prog)
@ -702,6 +718,113 @@ class TestValue(MockProgramTestCase):
value & ((1 << bit_size) - 1),
)
def _test_big_int_operators(self, type):
big_obj = Object(self.prog, type, 1000)
obj = Object(self.prog, "int", 0)
for op in (
operator.lt,
operator.le,
operator.eq,
operator.ge,
operator.gt,
operator.add,
operator.and_,
operator.lshift,
operator.mod,
operator.mul,
operator.or_,
operator.rshift,
operator.sub,
operator.truediv,
operator.xor,
):
self.assertRaises(NotImplementedError, op, big_obj, obj)
self.assertRaises(NotImplementedError, op, obj, big_obj)
for op in (
operator.inv,
operator.neg,
operator.pos,
):
self.assertRaises(NotImplementedError, op, big_obj)
self.assertFalse(not big_obj)
self.assertTrue(bool(big_obj))
for op in (
operator.index,
round,
math.trunc,
math.floor,
math.ceil,
):
self.assertEqual(op(big_obj), 1000)
def test_signed_big(self):
type = self.prog.int_type("__int128", 16, True)
obj = Object(self.prog, type, -4)
self.assertIs(obj.prog_, self.prog)
self.assertIdentical(obj.type_, self.prog.int_type("__int128", 16, True))
self.assertFalse(obj.absent_)
self.assertIsNone(obj.address_)
self.assertIsNone(obj.bit_offset_)
self.assertIsNone(obj.bit_field_size_)
self.assertEqual(obj.value_(), -4)
self.assertEqual(repr(obj), "Object(prog, '__int128', value=-4)")
self.assertIdentical(Object(self.prog, type, value=2**128 - 4), obj)
self.assertIdentical(Object(self.prog, type, value=-4.6), obj)
self.assertIdentical(
Object(self.prog, type, value=2**128 + 4),
Object(self.prog, type, value=4),
)
self.assertRaisesRegex(
TypeError,
"'__int128' value must be number",
Object,
self.prog,
type,
value=b"asdf",
)
self._test_big_int_operators(type)
def test_unsigned_big(self):
type = self.prog.int_type("unsigned __int128", 16, False)
obj = Object(self.prog, type, 2**128 - 1)
self.assertIs(obj.prog_, self.prog)
self.assertIdentical(
obj.type_, self.prog.int_type("unsigned __int128", 16, False)
)
self.assertFalse(obj.absent_)
self.assertIsNone(obj.address_)
self.assertIsNone(obj.bit_offset_)
self.assertIsNone(obj.bit_field_size_)
self.assertEqual(obj.value_(), 2**128 - 1)
self.assertEqual(
repr(obj),
"Object(prog, 'unsigned __int128', value=340282366920938463463374607431768211455)",
)
self.assertIdentical(Object(self.prog, type, value=-1), obj)
self.assertIdentical(Object(self.prog, type, value=2**128 - 1), obj)
self.assertIdentical(Object(self.prog, type, value=2**129 - 1), obj)
self.assertIdentical(
Object(self.prog, type, value=0.1), Object(self.prog, type, value=0)
)
self.assertRaisesRegex(
TypeError,
"'unsigned __int128' value must be number",
Object,
self.prog,
type,
value="foo",
)
self._test_big_int_operators(type)
def test_int_bits(self):
for (
signed,
@ -940,6 +1063,57 @@ class TestValue(MockProgramTestCase):
self.assertEqual(obj.a.value_(), 1234.0)
self.assertEqual(obj.b.value_(), -3.125)
def test_compound_bit_fields(self):
a = 0xF8935CF44C45202748DE66B49BA0CBAC
b = -0xC256D5AAFFDC3179A6AC84E7154A215D
for signed in (True, False):
if signed:
def truncate(x, bit_size):
sign = 1 << (bit_size - 1)
return (x & (sign - 1)) - (x & sign)
else:
def truncate(x, bit_size):
return x & ((1 << bit_size) - 1)
for byteorder in ("little", "big"):
for bit_size in range(1, 128):
with self.subTest(
signed=signed, byteorder=byteorder, bit_size=bit_size
):
type = self.prog.int_type(
("" if signed else "unsigned ") + "__int128", 16, signed
)
obj = Object(
self.prog,
self.prog.struct_type(
None,
type.size * 2,
(
TypeMember(
Object(
self.prog, type, bit_field_size=bit_size
),
"a",
),
TypeMember(
Object(
self.prog,
type,
bit_field_size=128 - bit_size,
),
"b",
bit_size,
),
),
),
value={"a": a, "b": b},
)
self.assertEqual(obj.a.value_(), truncate(a, bit_size))
self.assertEqual(obj.b.value_(), truncate(b, 128 - bit_size))
def test_pointer(self):
obj = Object(self.prog, "int *", value=0xFFFF0000)
self.assertFalse(obj.absent_)
@ -995,29 +1169,7 @@ class TestValue(MockProgramTestCase):
ValueError, "non-scalar must be byte-aligned", obj.member_, "point"
)
def test_big_int(self):
for type in [
self.prog.int_type("unsigned __int128", 16, False),
self.prog.int_type("__int128", 16, True),
]:
self.assertRaisesRegex(
NotImplementedError,
"integer values larger than 64 bits are not yet supported",
Object,
self.prog,
type,
0,
)
self.assertRaisesRegex(
NotImplementedError,
"integer values larger than 64 bits are not yet supported",
Object.from_bytes_,
self.prog,
type,
(0).to_bytes(16, "little"),
)
def test_bit_field_of_big_int(self):
def test_small_bit_field_of_big_int(self):
obj = Object(
self.prog,
self.prog.int_type("unsigned __int128", 16, False),