Skip to content

Commit

Permalink
Add implicit_convert()
Browse files Browse the repository at this point in the history
There are two main types of conversions in C:

- Explicit conversions, i.e., casts.
- Implicit conversions, which are applied for variable assignments,
  assigning arguments to function parameters, return values, etc. These
  have stricter rules about what types can be converted.

drgn currently only has a cast() function to do an explicit conversion.
This adds an implicit_convert() function to do an implicit conversion.
The most obvious use case is allowing helpers to type check their
parameters and perform the same conversions that happen in C (e.g,
automatically converting `void *` to the appropriate pointer type but
rejecting other pointer types):

  >>> def foo(task: Object):
  ...     task = implicit_convert("struct task_struct *", task)
  ...
  >>> foo(Object(prog, "void *", 0))
  >>> foo(Object(prog, "struct mm_struct *", 0))
  Traceback (most recent call last):
    ...
  TypeError: cannot convert 'struct mm_struct *' to incompatible type 'struct task_struct *'

I haven't measured the performance overhead of doing this yet, so I'm
not going to add this to existing helpers yet. But, I do have another
use case for type checking function calls coming up soon.

Signed-off-by: Omar Sandoval <[email protected]>
  • Loading branch information
osandov committed Sep 5, 2024
1 parent fd474f0 commit d0ce699
Show file tree
Hide file tree
Showing 12 changed files with 1,198 additions and 84 deletions.
55 changes: 47 additions & 8 deletions _drgn.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -1690,18 +1690,48 @@ def NULL(prog: Program, type: Union[str, Type]) -> Object:

def cast(type: Union[str, Type], obj: Object) -> Object:
"""
Get the value of the given object casted to another type.
Get the value of an object explicitly casted to another type.
Objects with a scalar type (integer, boolean, enumerated, floating-point,
or pointer) can be casted to a different scalar type. Other objects can
only be casted to the same type. This always results in a value object. See
also :func:`drgn.reinterpret()`.
This uses the programming language's rules for explicit conversions, like
the cast operator.
>>> cast("unsigned int", Object(prog, "float", 2.0))
(unsigned int)2
>>> cast("void *", Object(prog, "int", 0))
(void *)0x0
See also :func:`implicit_convert()` for implicit conversions (which usually
do stricter type checking) and :func:`reinterpret()` for reinterpreting the
raw memory of an object.
:param type: Type to cast to.
:param obj: Object to cast.
:return: Casted object. This is always a value object.
:raises TypeError: if casting *obj* to *type* is not allowed
"""
...

def implicit_convert(type: Union[str, Type], obj: Object) -> Object:
"""
Get the value of an object implicitly converted to another type.
This uses the programming language's rules for implicit conversions, like
when assigning to a variable or passing arguments to a function call.
>>> implicit_convert("unsigned int", Object(prog, "float", 2.0))
(unsigned int)2
>>> implicit_convert("void *", Object(prog, "int", 0))
Traceback (most recent call last):
...
TypeError: cannot convert 'int' to incompatible type 'void *'
See also :func:`cast()` for explicit conversions and :func:`reinterpret()`
for reinterpreting the raw memory of an object.
:param type: Type to convert to.
:param obj: Object to convert.
:return: Converted object. This is always a value object.
:raises TypeError: if converting *obj* to *type* is not allowed
"""
...

Expand All @@ -1710,15 +1740,24 @@ def reinterpret(type: Union[str, Type], obj: Object) -> Object:
Get the representation of an object reinterpreted as another type.
This reinterprets the raw memory of the object, so an object can be
reinterpreted as any other type. Reinterpreting a reference results in a
reference, and reinterpreting a value results in a value. See also
:func:`drgn.cast()`.
reinterpreted as any other type.
>>> reinterpret("unsigned int", Object(prog, "float", 2.0))
(unsigned int)1073741824
.. note::
You usually want :func:`cast()` or :func:`implicit_convert()` instead,
which convert the *value* of an object instead of its in-memory
representation.
:param type: Type to reinterpret as.
:param obj: Object to reinterpret.
:return: Reinterpreted object. If *obj* is a reference object, then this is
a reference object. If *obj* is a value object, then this is a value
object.
:raises OutOfBoundsError: if *obj* is a value object and *type* is larger
than *obj*
"""
...

Expand Down
1 change: 1 addition & 0 deletions docs/api_reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ Objects
.. drgndoc:: Object
.. drgndoc:: NULL
.. drgndoc:: cast
.. drgndoc:: implicit_convert
.. drgndoc:: reinterpret
.. drgndoc:: container_of

Expand Down
2 changes: 2 additions & 0 deletions drgn/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@
filename_matches,
get_default_prog,
host_platform,
implicit_convert,
offsetof,
program_from_core_dump,
program_from_kernel,
Expand Down Expand Up @@ -140,6 +141,7 @@
"filename_matches",
"get_default_prog",
"host_platform",
"implicit_convert",
"offsetof",
"program_from_core_dump",
"program_from_kernel",
Expand Down
41 changes: 29 additions & 12 deletions libdrgn/drgn.h
Original file line number Diff line number Diff line change
Expand Up @@ -2083,36 +2083,53 @@ struct drgn_error *drgn_format_object(const struct drgn_object *obj,
*/

/**
* Set a @ref drgn_object to the value of an object casted to a another type.
* Set a @ref drgn_object to the value of an object explicitly casted to a
* another type.
*
* Objects with a scalar type can be casted to a different scalar type. Other
* objects can only be casted to the same type. @p res is always set to a value
* object.
* This uses the programming language's rules for explicit conversions, like the
* cast operator.
*
* @sa drgn_object_reinterpret()
* @sa drgn_object_implicit_convert(), drgn_object_reinterpret()
*
* @param[out] res Object to set.
* @param[out] res Object to set. Always set to a value object.
* @param[in] qualified_type New type.
* @param[in] obj Object to read.
* @param[in] obj Object to cast.
* @return @c NULL on success, non-@c NULL on error.
*/
struct drgn_error *drgn_object_cast(struct drgn_object *res,
struct drgn_qualified_type qualified_type,
const struct drgn_object *obj);

/**
* Set a @ref drgn_object to the value of an object implicitly converted to a
* another type.
*
* This uses the programming language's rules for implicit conversions, like
* when assigning to a variable or passing arguments to a function call.
*
* @sa drgn_object_cast(), drgn_object_reinterpret()
*
* @param[out] res Object to set. Always set to a value object.
* @param[in] qualified_type New type.
* @param[in] obj Object to convert.
* @return @c NULL on success, non-@c NULL on error.
*/
struct drgn_error *
drgn_object_implicit_convert(struct drgn_object *res,
struct drgn_qualified_type qualified_type,
const struct drgn_object *obj);

/**
* Set a @ref drgn_object to the representation of an object reinterpreted as
* another type.
*
* This reinterprets the raw memory of the object, so an object can be
* reinterpreted as any other type.
*
* If @c obj is a value, then @c res is set to a value; if @c obj is a
* reference, then @c res is set to a reference.
*
* @sa drgn_object_cast()
* @sa drgn_object_cast(), drgn_object_implicit_convert()
*
* @param[out] res Object to set.
* @param[out] res Object to set. If @p obj is a value, set to a value. If @p
* obj is a reference, set to a reference.
* @param[in] qualified_type New type.
* @param[in] obj Object to reinterpret.
* @return @c NULL on success, non-@c NULL on error.
Expand Down
1 change: 1 addition & 0 deletions libdrgn/language.h
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ struct drgn_language {
*/
drgn_float_literal_fn *float_literal;
drgn_cast_op *op_cast;
drgn_cast_op *op_implicit_convert;
drgn_bool_op *op_bool;
drgn_cmp_op *op_cmp;
drgn_binary_op *op_add;
Expand Down
Loading

0 comments on commit d0ce699

Please sign in to comment.