mirror of https://github.com/pwndbg/pwndbg.git
Add bitwise math helper functions (#2278)
* Implement bitwise math rotation operations on numbers of discrete width. Will be used in manually evaluating arm instruction offsets and shifts * fixespull/2315/head
parent
9c0dd25a13
commit
e9b7f82cc9
@ -0,0 +1,48 @@
|
||||
from __future__ import annotations
|
||||
|
||||
|
||||
def to_signed(unsigned: int, bit_width: int):
|
||||
"""
|
||||
Returns the signed number associated with the two's-complement binary representation of `unsigned`
|
||||
"""
|
||||
extract_bit = 1 << (bit_width - 1)
|
||||
return unsigned - ((unsigned & extract_bit) << 1)
|
||||
|
||||
|
||||
def logical_shift_left(n: int, shift_amt: int, bit_width: int):
|
||||
return (n << shift_amt) & ((1 << bit_width) - 1)
|
||||
|
||||
|
||||
def logical_shift_right(n: int, shift_amt: int, bit_width: int):
|
||||
"""
|
||||
`n` is truncated to the width of `bit_width` before the operation takes place.
|
||||
"""
|
||||
n = n & ((1 << bit_width) - 1)
|
||||
return n >> shift_amt
|
||||
|
||||
|
||||
def rotate_right(n: int, shift_amt: int, bit_width: int):
|
||||
"""
|
||||
`n` is truncated to the width of `bit_width` before the operation takes place.
|
||||
"""
|
||||
n = n & ((1 << bit_width) - 1)
|
||||
return ((n >> shift_amt) | (n << (bit_width - shift_amt))) & ((1 << bit_width) - 1)
|
||||
|
||||
|
||||
def arithmetic_shift_right(n: int, shift_amt: int, bit_width: int):
|
||||
"""
|
||||
This returns the value represented by the two's-complement binary representation of the final result.
|
||||
This means the result could be negative (if the top bit of the input is negative)
|
||||
|
||||
`n` is truncated to the width of `bit_width` before the operation takes place.
|
||||
"""
|
||||
n = n & ((1 << bit_width) - 1)
|
||||
|
||||
result = logical_shift_right(n, shift_amt, bit_width)
|
||||
|
||||
sign_extension_mask = (1 << (bit_width - shift_amt)) - 1
|
||||
# Replicate the sign bit if it's set
|
||||
if n & (1 << (bit_width - 1)):
|
||||
result |= ~sign_extension_mask
|
||||
|
||||
return result
|
||||
@ -0,0 +1,69 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import pwndbg.lib.disasm.helpers as bit_math
|
||||
|
||||
# We must import the function under test after all the mocks are imported
|
||||
|
||||
|
||||
def test_to_signed():
|
||||
assert bit_math.to_signed(0b0100_0000, 8) == 0b0100_0000
|
||||
assert bit_math.to_signed(0b1000_0000, 8) == -128
|
||||
|
||||
assert bit_math.to_signed(0xFFFFFFFF_FFFFFFFF, 64) == -1
|
||||
assert bit_math.to_signed(0x7FFFFFFF_FFFFFFFF, 64) == 0x7FFFFFFF_FFFFFFFF
|
||||
|
||||
assert bit_math.to_signed(0xFFFF_FFFF, 32) == -1
|
||||
assert bit_math.to_signed(0x8000_0000, 32) == -(2**31)
|
||||
|
||||
|
||||
def test_lsl():
|
||||
assert bit_math.logical_shift_left(0b1000_0000, 1, 8) == 0
|
||||
assert bit_math.logical_shift_left(0b0100_0000, 1, 8) == 0b1000_0000
|
||||
assert bit_math.logical_shift_left(0b1111_1111, 1, 8) == 0b1111_1110
|
||||
assert bit_math.logical_shift_left(0b1111_1111, 5, 8) == 0b1110_0000
|
||||
|
||||
|
||||
def test_lsr():
|
||||
assert bit_math.logical_shift_right(0b1000_0000, 1, 8) == 0b0100_0000
|
||||
assert bit_math.logical_shift_right(0b0100_0000, 1, 8) == 0b0010_0000
|
||||
assert bit_math.logical_shift_right(0b1111_1111, 1, 8) == 0b0111_1111
|
||||
assert bit_math.logical_shift_right(0b1111_1111, 5, 8) == 0b0000_0111
|
||||
# Should truncate to bit_width before operation
|
||||
assert bit_math.logical_shift_right(0b1_0000_0000, 1, 8) == 0
|
||||
|
||||
|
||||
def test_ror():
|
||||
assert bit_math.rotate_right(0b1000_0001, 1, 8) == 0b1100_0000
|
||||
assert bit_math.rotate_right(0b0100_0000, 1, 8) == 0b0010_0000
|
||||
assert bit_math.rotate_right(0b0100_0000, 4, 8) == 0b0000_0100
|
||||
assert bit_math.rotate_right(0b1111_1111, 1, 8) == 0b1111_1111
|
||||
assert bit_math.rotate_right(0b1110_1111, 5, 8) == 0b0111_1111
|
||||
|
||||
# Should truncate to bit_width before operation
|
||||
assert bit_math.rotate_right(0b1_0000_0000, 1, 8) == 0
|
||||
assert bit_math.rotate_right(0b1_0111_1111, 1, 8) == 0b1011_1111
|
||||
|
||||
|
||||
def test_asr():
|
||||
# Unsigned numbers should be the same
|
||||
assert bit_math.arithmetic_shift_right(0b0100_0000, 1, 8) == bit_math.logical_shift_right(
|
||||
0b0100_0000, 1, 8
|
||||
)
|
||||
assert bit_math.arithmetic_shift_right(0xFFFF_FF, 1, 32) == bit_math.logical_shift_right(
|
||||
0xFFFF_FF, 1, 32
|
||||
)
|
||||
assert bit_math.arithmetic_shift_right(0xFFFF_FF, 6, 32) == bit_math.logical_shift_right(
|
||||
0xFFFF_FF, 6, 32
|
||||
)
|
||||
|
||||
assert bit_math.arithmetic_shift_right(0b1000_0000, 1, 8) == -64
|
||||
assert bit_math.arithmetic_shift_right(0b1000_0000, 2, 8) == -32
|
||||
assert bit_math.arithmetic_shift_right(0b1000_0000, 7, 8) == -1
|
||||
|
||||
# Should truncate to bit_width before operation
|
||||
assert bit_math.arithmetic_shift_right(0b1_0000_0000, 1, 8) == 0
|
||||
assert bit_math.arithmetic_shift_right(0b1_0111_1111, 7, 8) == 0
|
||||
|
||||
# Unsigned number shifted
|
||||
assert bit_math.arithmetic_shift_right(0x70000000_00000000, 62, 64) == 1
|
||||
assert bit_math.arithmetic_shift_right(0x70000000_00000000, 63, 64) == 0
|
||||
Loading…
Reference in new issue