Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add several common bit operations to RzIL. #3977

Merged
merged 1 commit into from
Dec 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 0 additions & 9 deletions librz/analysis/arch/ppc/ppc_il.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,6 @@
*/
#define DCACHE_LINE_SIZE 128

/**
* \brief Writes to the info log that an unimplemented instruction was encountered and returns an EMPTY() effect.
*/
#define NOT_IMPLEMENTED \
do { \
RZ_LOG_INFO("IL instruction not implemented."); \
return NULL; \
} while (0)

/**
* \brief Unsigned value \p i which is PPC_ARCH_BITS (32 or 64) wide.
*/
Expand Down
213 changes: 213 additions & 0 deletions librz/il/il_routines.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
// SPDX-FileCopyrightText: 2023 Rot127 <[email protected]>
// SPDX-License-Identifier: LGPL-3.0-only

/**
* \file Implements common bit operation perfomed on values.
*/

#include <rz_util/rz_assert.h>
#include <rz_il/rz_il_opcodes.h>
#include <rz_types.h>

/**
* \brief Extracts \p length bits from \p start of \p value and returns them as U32.
*
* Performed operation: ((value >> start) & (~0U >> (0x20 - length)));
*
* \param value The value to extract the bits from. It must be a bitvector of size 32.
* \param start The start index of the bits to extract. Passed bitvector can be of any width.
* \param length Number of bits to extract. Passed bitvector must be 32bits in size.
*
* \return A 32bit wide bitvector with the extracted value.
*/
RZ_API RZ_OWN RzILOpBitVector *rz_il_extract32(RZ_BORROW RzILOpBitVector *value, RZ_BORROW RzILOpBitVector *start, RZ_BORROW RzILOpBitVector *length) {
rz_return_val_if_fail(value && start && length, NULL);
RzILOpPure *op_RSHIFT_0 = rz_il_op_new_shiftr(rz_il_op_new_b0(), value, start);
RzILOpPure *op_SUB_4 = rz_il_op_new_sub(rz_il_op_new_bitv_from_st64(32, 0x20), length);
RzILOpPure *op_RSHIFT_5 = rz_il_op_new_shiftr(rz_il_op_new_b0(), rz_il_op_new_bitv_from_ut64(32, -1), op_SUB_4);
RzILOpPure *op_AND_6 = rz_il_op_new_log_and(op_RSHIFT_0, op_RSHIFT_5);
return op_AND_6;
}

/**
* \brief Extracts \p length bits from \p start of \p value and returns them as U64.
*
* Performed operation: ((value >> start) & (~0ULL >> (0x40 - length)));
*
* \param value The value to extract the bits from. It must be a bitvector of size 64.
* \param start The start index of the bits to extract. Passed bitvector can be of any width.
* \param length Number of bits to extract. Passed bitvector must be 32bits in size.
*
* \return A 64bit wide bitvector with the extracted value.
*/
RZ_API RZ_OWN RzILOpBitVector *rz_il_extract64(RZ_BORROW RzILOpBitVector *value, RZ_BORROW RzILOpBitVector *start, RZ_BORROW RzILOpBitVector *length) {
rz_return_val_if_fail(value && start && length, NULL);
RzILOpPure *op_RSHIFT_0 = rz_il_op_new_shiftr(rz_il_op_new_b0(), value, start);
RzILOpPure *op_SUB_4 = rz_il_op_new_sub(rz_il_op_new_bitv_from_st64(32, 0x40), length);
RzILOpPure *op_RSHIFT_5 = rz_il_op_new_shiftr(rz_il_op_new_b0(), rz_il_op_new_bitv_from_ut64(64, -1), op_SUB_4);
RzILOpPure *op_AND_6 = rz_il_op_new_log_and(op_RSHIFT_0, op_RSHIFT_5);
return op_AND_6;
}

/**
* \brief Extracts \p length bits from \p start from \p value and returns them as S64. The extracted value is sign extended.
*
* Performed operation: (((st64) (value << 0x40 - length - start)) >> 0x40 - length);
*
* \param value The value to extract the bits from. It must be a bitvector of size 64.
* \param start The start index of the bits to extract. Passed bitvector must be 32bits in size.
* \param length Number of bits to extract. Passed bitvector must be 32bits in size.
*
* \return A 64bit wide sign extended bitvector with the extracted value.
*/
RZ_API RZ_OWN RzILOpBitVector *rz_il_sextract64(RZ_BORROW RzILOpBitVector *value, RZ_BORROW RzILOpBitVector *start, RZ_BORROW RzILOpBitVector *length) {
rz_return_val_if_fail(value && start && length, NULL);
RzILOpPure *op_SUB_1 = rz_il_op_new_sub(rz_il_op_new_bitv_from_st64(32, 0x40), length);
RzILOpPure *op_SUB_2 = rz_il_op_new_sub(op_SUB_1, start);
RzILOpPure *op_LSHIFT_3 = rz_il_op_new_shiftl(rz_il_op_new_b0(), value, op_SUB_2);
RzILOpPure *op_SUB_6 = rz_il_op_new_sub(rz_il_op_new_bitv_from_st64(32, 0x40), rz_il_op_pure_dup(length));
RzILOpPure *op_RSHIFT_7 = rz_il_op_new_shiftr_arith(rz_il_op_new_cast(64, rz_il_op_new_b0(), op_LSHIFT_3), op_SUB_6);
return op_RSHIFT_7;
}

/**
* \brief Deposits \p fieldval in \p value. The \p fieldval is inserted at \p start until \p start + \p length.
*
* Performed operation: ((value & (~((~0ULL >> (0x40 - length)) << start))) | ((fieldval << start) & ((~0ULL >> (0x40 - length)) << start)));
*
* \param value The value to deposit \p fieldval into. It must be a bitvector of size 64.
* \param start The start index to deposit \p fieldval into. Passed bitvector can be of any width.
* \param length Number of bits to deposit. Passed bitvector must be 32bits in size.
* \param fieldval The bits to deposit into \p value into. It must be a bitvector of size 64.
*
* \return \p value where bits[start:length] are replaced with \p fieldval
*/
RZ_API RZ_OWN RzILOpBitVector *rz_il_deposit64(RZ_BORROW RzILOpBitVector *value, RZ_BORROW RzILOpBitVector *start, RZ_BORROW RzILOpBitVector *length, RZ_BORROW RzILOpBitVector *fieldval) {
rz_return_val_if_fail(value && start && length && fieldval, NULL);
RzILOpPure *op_SUB_4 = rz_il_op_new_sub(rz_il_op_new_bitv_from_st64(32, 0x40), length);
RzILOpPure *op_RSHIFT_5 = rz_il_op_new_shiftr(rz_il_op_new_b0(), rz_il_op_new_bitv_from_ut64(64, -1), op_SUB_4);
RzILOpPure *op_LSHIFT_6 = rz_il_op_new_shiftl(rz_il_op_new_b0(), op_RSHIFT_5, start);
RzILOpPure *op_NOT_8 = rz_il_op_new_log_not(op_LSHIFT_6);
RzILOpPure *op_AND_9 = rz_il_op_new_log_and(value, op_NOT_8);
RzILOpPure *op_LSHIFT_10 = rz_il_op_new_shiftl(rz_il_op_new_b0(), fieldval, rz_il_op_pure_dup(start));
RzILOpPure *op_AND_11 = rz_il_op_new_log_and(op_LSHIFT_10, rz_il_op_pure_dup(op_LSHIFT_6));
RzILOpPure *op_OR_12 = rz_il_op_new_log_or(op_AND_9, op_AND_11);
return op_OR_12;
}

/**
* \brief Deposits \p fieldval in \p value. The \p fieldval is inserted at \p start until \p start + \p length.
*
* Performed operation: ((value & (~((~0U >> (0x20 - length)) << start))) | ((fieldval << start) & ((~0U >> (0x20 - length)) << start)));
*
* \param value The value to deposit \p fieldval into. It must be a bitvector of size 32.
* \param start The start index to deposit \p fieldval into. Passed bitvector can be of any width.
* \param length Number of bits to deposit. Passed bitvector must be 32bits in size.
* \param fieldval The bits to deposit into \p value into. It must be a bitvector of size 32.
*
* \return \p value where bits[start:length] are replaced with \p fieldval.
*/
RZ_API RZ_OWN RzILOpBitVector *rz_il_deposit32(RZ_BORROW RzILOpBitVector *value, RZ_BORROW RzILOpBitVector *start, RZ_BORROW RzILOpBitVector *length, RZ_BORROW RzILOpBitVector *fieldval) {
rz_return_val_if_fail(value && start && length && fieldval, NULL);
RzILOpPure *op_SUB_4 = rz_il_op_new_sub(rz_il_op_new_bitv_from_st64(32, 0x20), length);
RzILOpPure *op_RSHIFT_5 = rz_il_op_new_shiftr(rz_il_op_new_b0(), rz_il_op_new_bitv_from_ut64(32, -1), op_SUB_4);
RzILOpPure *op_LSHIFT_6 = rz_il_op_new_shiftl(rz_il_op_new_b0(), op_RSHIFT_5, start);
RzILOpPure *op_NOT_8 = rz_il_op_new_log_not(op_LSHIFT_6);
RzILOpPure *op_AND_9 = rz_il_op_new_log_and(value, op_NOT_8);
RzILOpPure *op_LSHIFT_10 = rz_il_op_new_shiftl(rz_il_op_new_b0(), fieldval, rz_il_op_pure_dup(start));
RzILOpPure *op_AND_11 = rz_il_op_new_log_and(op_LSHIFT_10, rz_il_op_pure_dup(op_LSHIFT_6));
RzILOpPure *op_OR_12 = rz_il_op_new_log_or(op_AND_9, op_AND_11);
return op_OR_12;
}

/**
* \brief Performes a byte swap of \p t.
*
* Perfomed operation: (((((st32) t) & 0xff00) >> 0x8) | ((((st32) t) & 0xff) << 0x8));
*
* \param t A 16bit wide bitvector for which to swap the bytes.
*
* \return The bitvector \p t with swapped bytes.
*/
RZ_API RZ_OWN RzILOpBitVector *rz_il_bswap16(RZ_BORROW RzILOpBitVector *t) {
rz_return_val_if_fail(t, NULL);
RzILOpPure *op_AND_2 = rz_il_op_new_log_and(rz_il_op_new_cast(32, rz_il_op_new_b0(), t), rz_il_op_new_bitv_from_st64(32, 0xff00));
RzILOpPure *op_RSHIFT_4 = rz_il_op_new_shiftr_arith(op_AND_2, rz_il_op_new_bitv_from_st64(32, 8));
RzILOpPure *op_AND_7 = rz_il_op_new_log_and(rz_il_op_new_cast(32, rz_il_op_new_b0(), rz_il_op_pure_dup(t)), rz_il_op_new_bitv_from_st64(32, 0xff));
RzILOpPure *op_LSHIFT_9 = rz_il_op_new_shiftl(rz_il_op_new_b0(), op_AND_7, rz_il_op_new_bitv_from_st64(32, 8));
RzILOpPure *op_OR_10 = rz_il_op_new_log_or(op_RSHIFT_4, op_LSHIFT_9);
return rz_il_op_new_cast(16, rz_il_op_new_b0(), op_OR_10);
}

/**
* \brief Performes a byte swap of \p t.
*
* Perfomed operation:
* ((t & ((ut32) 0xff)) << 0x18)
* | ((t & ((ut32) 0xff00)) << 0x8))
* | ((t & ((ut32) 0xff0000)) >> 0x8))
* | ((t & ((ut32) 0xff000000)) >> 0x18));
*
* \param t A 32bit wide bitvector for which to swap the bytes.
*
* \return The bitvector \p t with swapped bytes.
*/
RZ_API RZ_OWN RzILOpBitVector *rz_il_bswap32(RZ_BORROW RzILOpBitVector *t) {
rz_return_val_if_fail(t, NULL);
RzILOpPure *op_AND_2 = rz_il_op_new_log_and(t, rz_il_op_new_cast(32, rz_il_op_new_b0(), rz_il_op_new_bitv_from_st64(32, 0xff)));
RzILOpPure *op_LSHIFT_4 = rz_il_op_new_shiftl(rz_il_op_new_b0(), op_AND_2, rz_il_op_new_bitv_from_st64(32, 24));
RzILOpPure *op_AND_7 = rz_il_op_new_log_and(rz_il_op_pure_dup(t), rz_il_op_new_cast(32, rz_il_op_new_b0(), rz_il_op_new_bitv_from_st64(32, 0xff00)));
RzILOpPure *op_LSHIFT_9 = rz_il_op_new_shiftl(rz_il_op_new_b0(), op_AND_7, rz_il_op_new_bitv_from_st64(32, 8));
RzILOpPure *op_OR_10 = rz_il_op_new_log_or(op_LSHIFT_4, op_LSHIFT_9);
RzILOpPure *op_AND_13 = rz_il_op_new_log_and(rz_il_op_pure_dup(t), rz_il_op_new_cast(32, rz_il_op_new_b0(), rz_il_op_new_bitv_from_st64(32, 0xff0000)));
RzILOpPure *op_RSHIFT_15 = rz_il_op_new_shiftr(rz_il_op_new_b0(), op_AND_13, rz_il_op_new_bitv_from_st64(32, 8));
RzILOpPure *op_OR_16 = rz_il_op_new_log_or(op_OR_10, op_RSHIFT_15);
RzILOpPure *op_AND_19 = rz_il_op_new_log_and(rz_il_op_pure_dup(t), rz_il_op_new_cast(32, rz_il_op_new_b0(), rz_il_op_new_bitv_from_st64(32, 0xff000000)));
RzILOpPure *op_RSHIFT_21 = rz_il_op_new_shiftr(rz_il_op_new_b0(), op_AND_19, rz_il_op_new_bitv_from_st64(32, 24));
RzILOpPure *op_OR_22 = rz_il_op_new_log_or(op_OR_16, op_RSHIFT_21);
return op_OR_22;
}

/**
* \brief Performes a byte swap of \p t.
*
* Perfomed operation:
* ((t & 0xff) << 0x38)
* | ((t & 0xff00) << 0x28))
* | ((t & 0xff0000) << 0x18))
* | ((t & 0xff000000) << 0x8))
* | ((t & 0xff00000000) >> 0x8))
* | ((t & 0xff0000000000) >> 0x18))
* | ((t & 0xff000000000000) >> 0x28))
* | ((t & 0xff00000000000000) >> 0x38));
*
* \param t A 64bit wide bitvector for which to swap the bytes.
*
* \return The bitvector \p t with swapped bytes.
*/
RZ_API RZ_OWN RzILOpBitVector *rz_il_bswap64(RZ_BORROW RzILOpBitVector *t) {
RzILOpPure *op_AND_1 = rz_il_op_new_log_and(t, rz_il_op_new_bitv_from_ut64(64, 0xff));
RzILOpPure *op_LSHIFT_3 = rz_il_op_new_shiftl(rz_il_op_new_b0(), op_AND_1, rz_il_op_new_bitv_from_st64(32, 0x38));
RzILOpPure *op_AND_5 = rz_il_op_new_log_and(rz_il_op_pure_dup(t), rz_il_op_new_bitv_from_ut64(64, 0xff00));
RzILOpPure *op_LSHIFT_7 = rz_il_op_new_shiftl(rz_il_op_new_b0(), op_AND_5, rz_il_op_new_bitv_from_st64(32, 0x28));
RzILOpPure *op_OR_8 = rz_il_op_new_log_or(op_LSHIFT_3, op_LSHIFT_7);
RzILOpPure *op_AND_10 = rz_il_op_new_log_and(rz_il_op_pure_dup(t), rz_il_op_new_bitv_from_ut64(64, 0xff0000));
RzILOpPure *op_LSHIFT_12 = rz_il_op_new_shiftl(rz_il_op_new_b0(), op_AND_10, rz_il_op_new_bitv_from_st64(32, 24));
RzILOpPure *op_OR_13 = rz_il_op_new_log_or(op_OR_8, op_LSHIFT_12);
RzILOpPure *op_AND_15 = rz_il_op_new_log_and(rz_il_op_pure_dup(t), rz_il_op_new_bitv_from_ut64(64, 0xff000000));
RzILOpPure *op_LSHIFT_17 = rz_il_op_new_shiftl(rz_il_op_new_b0(), op_AND_15, rz_il_op_new_bitv_from_st64(32, 8));
RzILOpPure *op_OR_18 = rz_il_op_new_log_or(op_OR_13, op_LSHIFT_17);
RzILOpPure *op_AND_20 = rz_il_op_new_log_and(rz_il_op_pure_dup(t), rz_il_op_new_bitv_from_ut64(64, 0xff00000000));
RzILOpPure *op_RSHIFT_22 = rz_il_op_new_shiftr(rz_il_op_new_b0(), op_AND_20, rz_il_op_new_bitv_from_st64(32, 8));
RzILOpPure *op_OR_23 = rz_il_op_new_log_or(op_OR_18, op_RSHIFT_22);
RzILOpPure *op_AND_25 = rz_il_op_new_log_and(rz_il_op_pure_dup(t), rz_il_op_new_bitv_from_ut64(64, 0xff0000000000));
RzILOpPure *op_RSHIFT_27 = rz_il_op_new_shiftr(rz_il_op_new_b0(), op_AND_25, rz_il_op_new_bitv_from_st64(32, 24));
RzILOpPure *op_OR_28 = rz_il_op_new_log_or(op_OR_23, op_RSHIFT_27);
RzILOpPure *op_AND_30 = rz_il_op_new_log_and(rz_il_op_pure_dup(t), rz_il_op_new_bitv_from_ut64(64, 0xff000000000000));
RzILOpPure *op_RSHIFT_32 = rz_il_op_new_shiftr(rz_il_op_new_b0(), op_AND_30, rz_il_op_new_bitv_from_st64(32, 0x28));
RzILOpPure *op_OR_33 = rz_il_op_new_log_or(op_OR_28, op_RSHIFT_32);
RzILOpPure *op_AND_35 = rz_il_op_new_log_and(rz_il_op_pure_dup(t), rz_il_op_new_bitv_from_ut64(64, 0xff00000000000000));
RzILOpPure *op_RSHIFT_37 = rz_il_op_new_shiftr(rz_il_op_new_b0(), op_AND_35, rz_il_op_new_bitv_from_st64(32, 0x38));
RzILOpPure *op_OR_38 = rz_il_op_new_log_or(op_OR_33, op_RSHIFT_37);
return op_OR_38;
}
1 change: 1 addition & 0 deletions librz/il/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ rz_il_sources = [
'il_export_json.c',
'il_graph.c',
'il_opcodes.c',
'il_routines.c',
'il_reg.c',
'il_validate.c',
'il_vm.c',
Expand Down
15 changes: 15 additions & 0 deletions librz/include/rz_il/rz_il_opbuilder_begin.h
Original file line number Diff line number Diff line change
Expand Up @@ -170,4 +170,19 @@
#define JMP(tgt) rz_il_op_new_jmp(tgt)
#define GOTO(lbl) rz_il_op_new_goto(lbl)

#define NOT_IMPLEMENTED \
do { \
RZ_LOG_INFO("IL instruction not implemented."); \
return NULL; \
} while (0)

#define EXTRACT32(value, start, length) rz_il_extract32(value, start, length)
#define EXTRACT64(value, start, length) rz_il_extract64(value, start, length)
#define SEXTRACT64(value, start, length) rz_il_sextract64(value, start, length)
#define DEPOSIT32(value, start, length, fieldval) rz_il_deposit32(value, start, length, fieldval)
#define DEPOSIT64(value, start, length, fieldval) rz_il_deposit64(value, start, length, fieldval)
#define BSWAP16(t) rz_il_bswap16(t)
#define BSWAP32(t) rz_il_bswap32(t)
#define BSWAP64(t) rz_il_bswap64(t)

#endif
11 changes: 11 additions & 0 deletions librz/include/rz_il/rz_il_opbuilder_end.h
Original file line number Diff line number Diff line change
Expand Up @@ -133,4 +133,15 @@
#undef JMP
#undef GOTO

#undef NOT_IMPLEMENTED

#undef EXTRACT32
#undef EXTRACT64
#undef SEXTRACT64
#undef DEPOSIT32
#undef DEPOSIT64
#undef BSWAP16
#undef BSWAP32
#undef BSWAP64

#undef RZ_IL_OPBUILDER_BEGIN_H
9 changes: 9 additions & 0 deletions librz/include/rz_il/rz_il_opcodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -769,6 +769,15 @@ RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_frootn(RzFloatRMode rmode, RZ_NONNULL Rz
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_fpown(RzFloatRMode rmode, RZ_NONNULL RzILOpFloat *x, RZ_NONNULL RzILOpBitVector *n);
RZ_API RZ_OWN RzILOpFloat *rz_il_op_new_fcompound(RzFloatRMode rmode, RZ_NONNULL RzILOpFloat *x, RZ_NONNULL RzILOpBitVector *n);

RZ_API RZ_OWN RzILOpBitVector *rz_il_extract32(RZ_BORROW RzILOpBitVector *value, RZ_BORROW RzILOpBitVector *start, RZ_BORROW RzILOpBitVector *length);
RZ_API RZ_OWN RzILOpBitVector *rz_il_extract64(RZ_BORROW RzILOpBitVector *value, RZ_BORROW RzILOpBitVector *start, RZ_BORROW RzILOpBitVector *length);
RZ_API RZ_OWN RzILOpBitVector *rz_il_sextract64(RZ_BORROW RzILOpBitVector *value, RZ_BORROW RzILOpBitVector *start, RZ_BORROW RzILOpBitVector *length);
RZ_API RZ_OWN RzILOpBitVector *rz_il_deposit64(RZ_BORROW RzILOpBitVector *value, RZ_BORROW RzILOpBitVector *start, RZ_BORROW RzILOpBitVector *length, RZ_BORROW RzILOpBitVector *fieldval);
RZ_API RZ_OWN RzILOpBitVector *rz_il_deposit32(RZ_BORROW RzILOpBitVector *value, RZ_BORROW RzILOpBitVector *start, RZ_BORROW RzILOpBitVector *length, RZ_BORROW RzILOpBitVector *fieldval);
RZ_API RZ_OWN RzILOpBitVector *rz_il_bswap16(RZ_BORROW RzILOpBitVector *t);
RZ_API RZ_OWN RzILOpBitVector *rz_il_bswap32(RZ_BORROW RzILOpBitVector *t);
RZ_API RZ_OWN RzILOpBitVector *rz_il_bswap64(RZ_BORROW RzILOpBitVector *t);
Comment on lines +772 to +779
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'm very ok with this, but i wonder if we should just implement the BitVector version directly instead of adding so much IL. because if we do, rz_il_bswapXX can become just rz_il_bswap, same for the others

Copy link
Member Author

@Rot127 Rot127 Nov 30, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this would be better. I did it this way because it was low effort with the compiler and don't really have the time currently to implement them for the bitvectors.
Could we open an issue about it? Since the RzIL API for those ones will stay the same anyways


///////////////////////////////
// Opcodes of type 'a effect //

Expand Down
1 change: 1 addition & 0 deletions test/unit/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ if get_option('enable_tests')
'il_reg',
'il_validate',
'il_vm',
'il_helpers',
'intervaltree',
'io',
'io_ihex',
Expand Down
Loading
Loading