mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-05-25 12:12:40 +08:00
Merge remote-tracking branch 'origin/GP-3166_dev747368_fix_Scalar_bittwiddling--SQUASHED'
This commit is contained in:
+3
-2
@@ -339,9 +339,10 @@ public class ScalarSearchModel extends AddressBasedTableModel<ScalarRowObject> {
|
||||
return (o1.isSigned() ? 1 : -1);
|
||||
}
|
||||
|
||||
return o1.compareTo(o2);
|
||||
return o1.isSigned()
|
||||
? Long.compare(o1.getSignedValue(), o2.getSignedValue())
|
||||
: Long.compareUnsigned(o1.getUnsignedValue(), o2.getUnsignedValue());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private abstract class AbstractScalarValueRenderer extends AbstractGColumnRenderer<Scalar> {
|
||||
|
||||
+59
-210
@@ -18,96 +18,76 @@ package ghidra.program.model.scalar;
|
||||
import java.math.BigInteger;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* The Scalar defines a immutable fixed bit signed integer.
|
||||
* Bit operations on a Scalar expect Scalar to act as a number in the
|
||||
* two's complement format. Scalar was designed to be used as an
|
||||
* offset (difference between two Addresses), an arithmetic operand,
|
||||
* and also potentially for simulating registers.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If an operation varies depending on whether the Scalar is
|
||||
* treated as signed or unsigned, there are usally two version such as
|
||||
* multiply and unsignedMultiply. Please note that this means that
|
||||
* the Comparable interface treats the number as signed.
|
||||
* </p>
|
||||
* The Scalar defines a immutable integer stored in an arbitrary number of bits (0..64), along
|
||||
* with a preferred signed-ness attribute.
|
||||
*/
|
||||
public class Scalar implements Comparable<Scalar> {
|
||||
private static final long[] BITMASKS = new long[65];
|
||||
|
||||
static {
|
||||
// populate the BITMASKS for each possible bit length
|
||||
// up to 64
|
||||
long value = 1;
|
||||
for (int i = 1; i < 65; ++i) {
|
||||
BITMASKS[i] = value;
|
||||
value = (value << 1) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
private byte bitLength;
|
||||
private long value;
|
||||
private boolean signed;
|
||||
public class Scalar {
|
||||
private final long value;
|
||||
private final byte bitLength;
|
||||
private final byte unusedBits; // complement of bitLength
|
||||
private final boolean signed;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param bitLength number of bits
|
||||
* @param value value of the scalar
|
||||
* @param signed true for a signed value, false for an unsigned value.
|
||||
*/
|
||||
public Scalar(int bitLength, long value, boolean signed) {
|
||||
this.signed = signed;
|
||||
if (!(bitLength == 0 && value == 0) && (bitLength < 1 || bitLength > 64)) {
|
||||
throw new IllegalArgumentException("Bit length must be >= 1 and <= 64");
|
||||
}
|
||||
this.bitLength = (byte) bitLength;
|
||||
|
||||
this.value = value & BITMASKS[bitLength];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if scalar was created as a signed value
|
||||
*/
|
||||
public boolean isSigned() {
|
||||
return signed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor a new signed scalar object.
|
||||
* @param bitLength number of bits
|
||||
* @param value value of the scalar
|
||||
* Construct a new signed scalar object.
|
||||
*
|
||||
* @param bitLength number of bits, valid values are 1..64, or 0 if value is also 0
|
||||
* @param value value of the scalar, any bits that are set above bitLength will be ignored
|
||||
*/
|
||||
public Scalar(int bitLength, long value) {
|
||||
this(bitLength, value, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value as signed.
|
||||
* Construct a new scalar.
|
||||
*
|
||||
* @param bitLength number of bits, valid values are 1..64, or 0 if value is also 0
|
||||
* @param value value of the scalar, any bits that are set above bitLength will be ignored
|
||||
* @param signed true for a signed value, false for an unsigned value.
|
||||
*/
|
||||
public Scalar(int bitLength, long value, boolean signed) {
|
||||
if (!(bitLength == 0 && value == 0) && (bitLength < 1 || bitLength > 64)) {
|
||||
throw new IllegalArgumentException("Bit length must be >= 1 and <= 64");
|
||||
}
|
||||
this.signed = signed;
|
||||
this.bitLength = (byte) bitLength;
|
||||
this.unusedBits = (byte)(64 /*sizeof(long)*8*/ - bitLength);
|
||||
this.value = (value << unusedBits) >>> unusedBits; // eliminate upper bits that are outside bitLength
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if scalar was created as a signed value
|
||||
*
|
||||
* @return boolean true if this scalar was created as a signed value, false if was created as
|
||||
* unsigned
|
||||
*/
|
||||
public boolean isSigned() {
|
||||
return signed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value as a signed long, where the highest bit of the value, if set, will be
|
||||
* extended to fill the remaining bits of a java long.
|
||||
*
|
||||
* @return signed value
|
||||
*/
|
||||
public long getSignedValue() {
|
||||
if (value == 0) { // just in case the bitLength is 0
|
||||
return 0;
|
||||
}
|
||||
if (testBit(bitLength - 1)) {
|
||||
return (value | (~BITMASKS[bitLength]));
|
||||
}
|
||||
return (value << unusedBits) >> unusedBits; // if value has highbit set, sign extend it
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value as an unsigned long.
|
||||
*
|
||||
* @return unsigned value
|
||||
*/
|
||||
public long getUnsignedValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value as unsigned.
|
||||
*/
|
||||
public long getUnsignedValue() {
|
||||
if (value == 0) { // just in case the bitLength is 0
|
||||
return 0;
|
||||
}
|
||||
return (value & BITMASKS[bitLength]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value as a signed value if it was created signed, otherwise the value is
|
||||
* returned as an unsigned value
|
||||
* Returns the value in its preferred signed-ness. See {@link #getSignedValue()} and
|
||||
* {@link #getUnsignedValue()}.
|
||||
*
|
||||
* @return value, as either signed or unsigned, depending on how this instance was created
|
||||
*/
|
||||
public long getValue() {
|
||||
return signed ? getSignedValue() : value;
|
||||
@@ -115,6 +95,8 @@ public class Scalar implements Comparable<Scalar> {
|
||||
|
||||
/**
|
||||
* Returns the BigInteger representation of the value.
|
||||
*
|
||||
* @return new BigInteger representation of the value
|
||||
*/
|
||||
public BigInteger getBigInteger() {
|
||||
int signum = (signed && testBit(bitLength - 1)) ? -1 : 1;
|
||||
@@ -134,18 +116,6 @@ public class Scalar implements Comparable<Scalar> {
|
||||
return new BigInteger(signum, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Creates a new Scalar of the same size as this scalar but with the
|
||||
* given value
|
||||
*
|
||||
* @param newValue the Scalar value which will be used to initialize
|
||||
* the new Scalar.
|
||||
* @return the new Scalar.
|
||||
*/
|
||||
public Scalar newScalar(long newValue) {
|
||||
return new Scalar(bitLength, newValue, signed);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Returns a byte array representing this Scalar. The size of
|
||||
* the byte array is the number of bytes required to hold the
|
||||
@@ -164,9 +134,6 @@ public class Scalar implements Comparable<Scalar> {
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) {
|
||||
@@ -195,34 +162,7 @@ public class Scalar implements Comparable<Scalar> {
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return (int) (value ^ (value >>> 32));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.lang.Comparable#compareTo(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public int compareTo(Scalar other) {
|
||||
if (bitLength == 64 || other.bitLength == 64) {
|
||||
return getBigInteger().compareTo(other.getBigInteger());
|
||||
}
|
||||
long v = getValue() - other.getValue();
|
||||
if (v > 0) {
|
||||
return 1;
|
||||
}
|
||||
else if (v < 0) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Adds the integer n to <code>this</code>.
|
||||
* Computes (<code>this = this + n</code>).
|
||||
* @param n the value to add to this scalars value to produce a new scalar.
|
||||
*/
|
||||
public Scalar add(long n) {
|
||||
return new Scalar(bitLength, (value + n) & BITMASKS[bitLength]);
|
||||
return Long.hashCode(value);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -237,94 +177,6 @@ public class Scalar implements Comparable<Scalar> {
|
||||
return bitLength;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>The bit number n in this Scalar is set to zero. Computes
|
||||
* (this = this & ~(1<<n)). Bits are numbered 0..bitlength()-1
|
||||
* with 0 being the least significant bit.</p>
|
||||
* @param n the bit to clear in this scalar.
|
||||
*
|
||||
* @throws IndexOutOfBoundsException if n >= bitLength().
|
||||
*/
|
||||
public Scalar clearBit(int n) {
|
||||
if (n < 0 || n > bitLength - 1) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
return new Scalar(bitLength, value & ~(1 << n));
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>The bit number n in this Scalar is flipped. Computes
|
||||
* (this = this ^ (1<<n)). Bits are numbered 0..bitlength()-1
|
||||
* with 0 being the least significant bit.</p>
|
||||
* @param n the bit to flip.
|
||||
* @throws IndexOutOfBoundsException if n >= bitLength().
|
||||
*/
|
||||
public Scalar flipBit(int n) {
|
||||
if (n < 0 || n > bitLength - 1) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
return new Scalar(bitLength, value ^ (1 << n));
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>The bit number n in this Scalar is set to one. Computes
|
||||
* (this = this | (1<<n)). Bits are numbered 0..bitlength()-1
|
||||
* with 0 being the least significant bit.</p>
|
||||
*
|
||||
* @param n the bit to set.
|
||||
* @throws IndexOutOfBoundsException if n >= bitLength().
|
||||
*/
|
||||
public Scalar setBit(int n) {
|
||||
if (n < 0 || n > bitLength - 1) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
return new Scalar(bitLength, value | (1 << n));
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Sets <code>this = this << n</code>.</p>
|
||||
* @param n the number of bits to shift left.
|
||||
* @throws ArithmeticException if n < 0.
|
||||
*/
|
||||
public Scalar shiftLeft(int n) {
|
||||
if (n < 0 || n > bitLength - 1) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
return new Scalar(bitLength, value << n);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Sets <code>this = this >> n</code> using 0 as the fill bit.</p>
|
||||
* @param n the number of bits to shift right.
|
||||
* @throws ArithmeticException if n < 0.
|
||||
*/
|
||||
public Scalar shiftRight(int n) {
|
||||
if (n < 0 || n > bitLength - 1) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
return new Scalar(bitLength, value >>> n);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Sets <code>this = this >> n</code> replicating the sign bit.</p>
|
||||
* @param n the number of bits to arithmetically shift.
|
||||
* @throws ArithmeticException if n < 0.
|
||||
*/
|
||||
public Scalar shiftRightSign(int n) {
|
||||
if (n < 0 || n > bitLength - 1) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
return new Scalar(bitLength, value >> n);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Sets <code>this = this - n</code>.</p>
|
||||
* @param n the value to subtract from this scalar to produce a new scalar.
|
||||
*/
|
||||
public Scalar subtract(long n) {
|
||||
return add(-n);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Returns true if and only if the designated bit is set to one.
|
||||
* Computes ((this & (1<<n)) != 0). Bits are numbered
|
||||
@@ -339,7 +191,7 @@ public class Scalar implements Comparable<Scalar> {
|
||||
if (n < 0 || n > bitLength - 1) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
return (value & (1 << n)) != 0;
|
||||
return (value & (1L << n)) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -419,9 +271,6 @@ public class Scalar implements Comparable<Scalar> {
|
||||
return new String(buf);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return toString(16, false, true, "0x", "");
|
||||
|
||||
+88
-2
@@ -15,15 +15,30 @@
|
||||
*/
|
||||
package ghidra.program.model.scalar;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import generic.test.AbstractGenericTest;
|
||||
import ghidra.util.NumericUtilities;
|
||||
|
||||
public class ScalarTest extends AbstractGenericTest {
|
||||
|
||||
public ScalarTest() {
|
||||
super();
|
||||
@Test
|
||||
public void testScalar0bits() {
|
||||
// test special allowance for 0-bitlength scalars that have a 0 value
|
||||
assertEquals(0, new Scalar(0, 0).getUnsignedValue());
|
||||
assertEquals(0, new Scalar(0, 0).getSignedValue());
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testScalar0bitsFailure() {
|
||||
// test special allowance for 0-bitlength scalars that don't have a 0 value
|
||||
new Scalar(0, 1 /* any non-zero intial value */);
|
||||
fail("Scalar ctor should fail when asked to create a 0 bitLength scalar that isn't 0 value");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -137,4 +152,75 @@ public class ScalarTest extends AbstractGenericTest {
|
||||
Assert.assertEquals(Byte.MAX_VALUE, s.getUnsignedValue());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIgnoreExtraBits() {
|
||||
// tests that extra bits in the long initial value are ignored when creating value
|
||||
assertEquals(0x22, new Scalar(8, 0x1122).getValue());
|
||||
assertEquals(0x1122, new Scalar(16, 0x1122).getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLeftShiftByMoreThan32() {
|
||||
// tests that the impl doesn't mess up when dealing with masks / bits that are larger
|
||||
// than what can be held in a 32bit int. (eg. 1 << 33 is an error, needs to be 1L << 33)
|
||||
assertEquals(55, new Scalar(32, 55).getSignedValue());
|
||||
assertEquals(55, new Scalar(33, 55).getSignedValue()); // would fail if shift-by-32 error
|
||||
|
||||
assertEquals(0x8000, new Scalar(32, 0x8000).getSignedValue());
|
||||
assertEquals(0x8000, new Scalar(48, 0x8000).getSignedValue()); // would fail if shift-by-32 error
|
||||
|
||||
assertEquals(0x40000000L, new Scalar(32, 0x40000000L).getSignedValue());
|
||||
assertEquals(0x40000000L, new Scalar(63, 0x40000000L).getSignedValue()); // would fail
|
||||
|
||||
assertFalse(new Scalar(64, 0x1_0000_0000L).testBit(31));
|
||||
assertTrue(new Scalar(64, 0x1_0000_0000L).testBit(32));
|
||||
assertFalse(new Scalar(64, 0x1_0000_0000L).testBit(33));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetBigInt() {
|
||||
assertEquals(BigInteger.valueOf(-1), new Scalar(8, 0xffL).getBigInteger());
|
||||
assertEquals(BigInteger.valueOf(-1), new Scalar(32, 0xffffffffL).getBigInteger());
|
||||
assertEquals(BigInteger.valueOf(-1), new Scalar(64, -1L).getBigInteger());
|
||||
|
||||
assertEquals(BigInteger.valueOf(Byte.MIN_VALUE), new Scalar(8, 0x80L).getBigInteger());
|
||||
assertEquals(BigInteger.valueOf(Short.MIN_VALUE), new Scalar(16, 0x8000L).getBigInteger());
|
||||
assertEquals(BigInteger.valueOf(Integer.MIN_VALUE),
|
||||
new Scalar(32, 0x80000000L).getBigInteger());
|
||||
assertEquals(BigInteger.valueOf(Long.MIN_VALUE),
|
||||
new Scalar(64, Long.MIN_VALUE).getBigInteger());
|
||||
|
||||
assertEquals(BigInteger.valueOf(0x80L), new Scalar(9, 0x80L).getBigInteger());
|
||||
assertEquals(BigInteger.valueOf(0x8000L), new Scalar(17, 0x8000L).getBigInteger());
|
||||
assertEquals(BigInteger.valueOf(0x8000_0000L),
|
||||
new Scalar(33, 0x8000_0000L).getBigInteger());
|
||||
assertEquals(BigInteger.valueOf(0x1_8000_0000L),
|
||||
new Scalar(34, 0x1_8000_0000L).getBigInteger());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTestBit() {
|
||||
Scalar scalar = new Scalar(64, -1, true);
|
||||
for (int bitnum = 0; bitnum < 64; bitnum++) {
|
||||
assertTrue(scalar.testBit(bitnum));
|
||||
}
|
||||
|
||||
// test top half of int64 are 0's, bottom half are 1's
|
||||
scalar = new Scalar(64, NumericUtilities.MAX_UNSIGNED_INT32_AS_LONG);
|
||||
for (int bitnum = 0; bitnum < 32; bitnum++) {
|
||||
assertTrue(scalar.testBit(bitnum));
|
||||
}
|
||||
for (int bitnum = 32; bitnum < 64; bitnum++) {
|
||||
assertFalse(scalar.testBit(bitnum));
|
||||
}
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testScalar0bitsTestBit() {
|
||||
Scalar scalar = new Scalar(0, 0);
|
||||
scalar.testBit(0);
|
||||
fail("Scalar testBit(0) should fail");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user