diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/BinaryReader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/BinaryReader.java index 139e3e4691..a5205d76c5 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/BinaryReader.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/BinaryReader.java @@ -93,12 +93,7 @@ public class BinaryReader { * @param isLittleEndian true for little-endian and false for big-endian */ public void setLittleEndian(boolean isLittleEndian) { - if (isLittleEndian) { - converter = new LittleEndianDataConverter(); - } - else { - converter = new BigEndianDataConverter(); - } + converter = DataConverter.getInstance(!isLittleEndian); } /** @@ -664,7 +659,7 @@ public class BinaryReader { } /** - * Returns the LONG at index. + * Returns the signed LONG at index. * @param index the index where the LONG begins * @return the LONG * @exception IOException if an I/O error occurs @@ -674,6 +669,34 @@ public class BinaryReader { return converter.getLong(bytes); } + /** + * Returns the signed value of the integer (of the specified length) at the specified offset. + * + * @param index offset the offset from the membuffers origin (the address that it is set at) + * @param len the number of bytes that the integer occupies. Valid values are 1 (byte), 2 (short), + * 4 (int), 8 (long) + * @return value of requested length, with sign bit extended, in a long + * @throws IOException + */ + public long readValue(long index, int len) throws IOException { + byte[] bytes = provider.readBytes(index, len); + return converter.getSignedValue(bytes, len); + } + + /** + * Returns the unsigned value of the integer (of the specified length) at the specified offset. + * + * @param index offset the offset from the membuffers origin (the address that it is set at) + * @param len the number of bytes that the integer occupies. Valid values are 1 (byte), 2 (short), + * 4 (int), 8 (long) + * @return unsigned value of requested length, in a long + * @throws IOException + */ + public long readUnsignedValue(long index, int len) throws IOException { + byte[] bytes = provider.readBytes(index, len); + return converter.getValue(bytes, len); + } + /** * Returns the BYTE array of nElements * starting at index. diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/dyld/AbstractClassicProcessor.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/dyld/AbstractClassicProcessor.java index 1eef5e3272..ea7547f437 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/dyld/AbstractClassicProcessor.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/dyld/AbstractClassicProcessor.java @@ -58,8 +58,7 @@ public abstract class AbstractClassicProcessor { Address address = defaultAddressSpace.getAddress(addressValue); - DataConverter converter = - language.isBigEndian() ? new BigEndianDataConverter() : new LittleEndianDataConverter(); + DataConverter converter = DataConverter.getInstance(language.isBigEndian()); Symbol symbol = getSymbol(nList); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/dyld/AbstractDyldInfoState.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/dyld/AbstractDyldInfoState.java index 924f3bc94c..633a718779 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/dyld/AbstractDyldInfoState.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/dyld/AbstractDyldInfoState.java @@ -58,8 +58,7 @@ abstract public class AbstractDyldInfoState { long offset = symbol.getAddress().getOffset(); - DataConverter converter = program.getLanguage().isBigEndian() ? new BigEndianDataConverter() - : new LittleEndianDataConverter(); + DataConverter converter = DataConverter.getInstance(program.getLanguage().isBigEndian()); byte[] bytes = (program.getDefaultPointerSize() == 8) ? converter.getBytes(offset) : converter.getBytes((int) offset); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/ImportDataDirectory.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/ImportDataDirectory.java index 2499be4385..5eb36e9e0e 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/ImportDataDirectory.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/ImportDataDirectory.java @@ -40,7 +40,7 @@ public class ImportDataDirectory extends DataDirectory { private ImportInfo[] imports; ExportDataDirectory exportDirectory; - DataConverter conv = new LittleEndianDataConverter(); + DataConverter conv = LittleEndianDataConverter.INSTANCE; static ImportDataDirectory createImportDataDirectory(NTHeader ntHeader, FactoryBundledWithBinaryReader reader) throws IOException { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/datatype/microsoft/GUID.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/datatype/microsoft/GUID.java index e31b9afae8..0d7d073536 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/datatype/microsoft/GUID.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/datatype/microsoft/GUID.java @@ -167,10 +167,7 @@ public class GUID { } private DataConverter getDataConverter(MemBuffer buf) { - if (buf.isBigEndian()) { - return new BigEndianDataConverter(); - } - return new LittleEndianDataConverter(); + return DataConverter.getInstance(buf.isBigEndian()); } @Override diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/datatype/microsoft/GuidDataType.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/datatype/microsoft/GuidDataType.java index b76cf818f4..45dc99d0dd 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/datatype/microsoft/GuidDataType.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/datatype/microsoft/GuidDataType.java @@ -110,9 +110,7 @@ public class GuidDataType extends BuiltIn { long[] data = new long[4]; boolean isBigEndian = ENDIAN.isBigEndian(settings, buf); - DataConverter conv = - isBigEndian ? (DataConverter) new BigEndianDataConverter() - : (DataConverter) new LittleEndianDataConverter(); + DataConverter conv = DataConverter.getInstance(isBigEndian); if (buf.getBytes(bytes, 0) != bytes.length) { if (guidName != null) { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/datatype/microsoft/GuidUtil.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/datatype/microsoft/GuidUtil.java index 3f409c6f06..e4ca174231 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/datatype/microsoft/GuidUtil.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/datatype/microsoft/GuidUtil.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -211,9 +210,7 @@ public class GuidUtil { byte[] bytes = new byte[16]; long[] data = new long[4]; boolean isBigEndian = program.getMemory().isBigEndian(); - DataConverter conv = - isBigEndian ? (DataConverter) new BigEndianDataConverter() - : (DataConverter) new LittleEndianDataConverter(); + DataConverter conv = DataConverter.getInstance(isBigEndian); try { program.getMemory().getBytes(address, bytes); @@ -254,9 +251,7 @@ public class GuidUtil { long[] data = new long[4]; int[] versionData = new int[2]; boolean isBigEndian = program.getMemory().isBigEndian(); - DataConverter conv = - isBigEndian ? (DataConverter) new BigEndianDataConverter() - : (DataConverter) new LittleEndianDataConverter(); + DataConverter conv = DataConverter.getInstance(isBigEndian); try { program.getMemory().getBytes(address, bytes); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/MachoLoader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/MachoLoader.java index 5f24f6c3e2..c28e63056a 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/MachoLoader.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/MachoLoader.java @@ -53,7 +53,7 @@ public class MachoLoader extends AbstractLibrarySupportLoader { // Efficient check to fail fast byte[] magicBytes = provider.readBytes(0, 4); - if (!MachConstants.isMagic(new LittleEndianDataConverter().getInt(magicBytes))) { + if (!MachConstants.isMagic(LittleEndianDataConverter.INSTANCE.getInt(magicBytes))) { return loadSpecs; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/MachoProgramBuilder.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/MachoProgramBuilder.java index b7352b15e6..85e3398b8b 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/MachoProgramBuilder.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/MachoProgramBuilder.java @@ -1385,8 +1385,7 @@ public class MachoProgramBuilder { } private DataConverter getDataConverter() { - DataConverter dc = program.getLanguage().isBigEndian() ? new BigEndianDataConverter() - : new LittleEndianDataConverter(); + DataConverter dc = DataConverter.getInstance(program.getLanguage().isBigEndian()); return dc; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/MzLoader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/MzLoader.java index b7d1c29bcc..c0bbad1a86 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/MzLoader.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/MzLoader.java @@ -52,7 +52,7 @@ public class MzLoader extends AbstractLibrarySupportLoader { private final static byte MOVW_DS_OPCODE = (byte) 0xba; private static final long MIN_BYTE_LENGTH = 4; - private DataConverter converter = new LittleEndianDataConverter(); + private DataConverter converter = LittleEndianDataConverter.INSTANCE; @Override public int getTierPriority() { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/OmfLoader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/OmfLoader.java index 25ab45d399..1d2a0f7cc8 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/OmfLoader.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/OmfLoader.java @@ -33,7 +33,7 @@ import ghidra.program.model.listing.*; import ghidra.program.model.mem.*; import ghidra.program.model.symbol.*; import ghidra.program.model.util.CodeUnitInsertionException; -import ghidra.util.*; +import ghidra.util.DataConverter; import ghidra.util.exception.CancelledException; import ghidra.util.exception.InvalidInputException; import ghidra.util.task.TaskMonitor; @@ -180,13 +180,7 @@ public class OmfLoader extends AbstractLibrarySupportLoader { ArrayList fixups = header.getFixups(); OmfFixupRecord.FixupState state = new OmfFixupRecord.FixupState(header, externsyms, program.getLanguage()); - DataConverter converter; - if (header.isLittleEndian()) { - converter = new LittleEndianDataConverter(); - } - else { - converter = new BigEndianDataConverter(); - } + DataConverter converter = DataConverter.getInstance(!header.isLittleEndian()); for (OmfFixupRecord fixup : fixups) { state.currentFixupRecord = fixup; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/PeLoader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/PeLoader.java index b8aa0339a4..ae17ad45b7 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/PeLoader.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/PeLoader.java @@ -319,7 +319,7 @@ public class PeLoader extends AbstractPeDebugLoader { space.getAddress(originalImageBase + brdd.getVirtualAddress() + brdd.getSize())); AddressRange headerRange = new AddressRangeImpl(space.getAddress(originalImageBase), space.getAddress(originalImageBase + optionalHeader.getSizeOfHeaders())); - DataConverter conv = new LittleEndianDataConverter(); + DataConverter conv = LittleEndianDataConverter.INSTANCE; for (BaseRelocation reloc : relocs) { if (monitor.isCancelled()) { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/program/util/ProgramMemoryUtil.java b/Ghidra/Features/Base/src/main/java/ghidra/program/util/ProgramMemoryUtil.java index fb4e1e4276..735b27d5d4 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/program/util/ProgramMemoryUtil.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/program/util/ProgramMemoryUtil.java @@ -521,16 +521,9 @@ public class ProgramMemoryUtil { int addrSize = toAddress.getSize(); - DataConverter dataConverter; + DataConverter dataConverter = DataConverter.getInstance(memory.isBigEndian()); byte[] addressBytes = new byte[addrSize / 8]; - if (isBigEndian) { - dataConverter = new BigEndianDataConverter(); - } - else { - dataConverter = new LittleEndianDataConverter(); - } - if (toAddress instanceof SegmentedAddress) { // Only search for offset (exclude segment) addressBytes = new byte[2]; diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/select/flow/FollowFlowProgramBuilder.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/select/flow/FollowFlowProgramBuilder.java index ce13faf240..40b9bc33c6 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/select/flow/FollowFlowProgramBuilder.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/select/flow/FollowFlowProgramBuilder.java @@ -195,9 +195,7 @@ public class FollowFlowProgramBuilder extends ProgramBuilder { private void setupProgram() throws Exception { - dataConverter = - (getProgram().getMemory().isBigEndian()) ? new BigEndianDataConverter() - : new LittleEndianDataConverter(); + dataConverter = DataConverter.getInstance(getProgram().getMemory().isBigEndian()); createMemory(".text", "0x0", 0x1000); createMemory(".data", "0x5000", 0x1000); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/util/BigEndianConverterTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/util/BigEndianConverterTest.java index c0a4c427e0..47c71e8c00 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/util/BigEndianConverterTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/util/BigEndianConverterTest.java @@ -15,18 +15,14 @@ */ package ghidra.util; -import static org.junit.Assert.assertEquals; +import static org.junit.Assert.*; import java.math.BigInteger; import java.util.Arrays; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; +import org.junit.*; import ghidra.test.AbstractGhidraHeadedIntegrationTest; -import ghidra.util.BigEndianDataConverter; -import ghidra.util.DataConverter; /** * @@ -38,7 +34,7 @@ import ghidra.util.DataConverter; */ public class BigEndianConverterTest extends AbstractGhidraHeadedIntegrationTest { private byte[] b; - private DataConverter dc; + private DataConverter dc = BigEndianDataConverter.INSTANCE; /** * Constructor for BigEndianConverterTest. @@ -54,7 +50,6 @@ public class BigEndianConverterTest extends AbstractGhidraHeadedIntegrationTest for (int i = 0; i < b.length; i++) { b[i] = (byte) i; } - dc = new BigEndianDataConverter(); } @Test @@ -75,6 +70,10 @@ public class BigEndianConverterTest extends AbstractGhidraHeadedIntegrationTest assertEquals(0x000102L, dc.getValue(b, 3)); assertEquals(0x0001020304050607L, dc.getValue(b, 8)); + assertEquals(0x0001L, dc.getSignedValue(b, 2)); + assertEquals(0x000102L, dc.getSignedValue(b, 3)); + assertEquals(0x0001020304050607L, dc.getSignedValue(b, 8)); + assertEquals(0x0203L, dc.getValue(b, 2, 2)); assertEquals(0x020304L, dc.getValue(b, 2, 3)); assertEquals(0x0203040506070809L, dc.getValue(b, 2, 8)); @@ -83,15 +82,21 @@ public class BigEndianConverterTest extends AbstractGhidraHeadedIntegrationTest assertEquals(0x04050607, dc.getBigInteger(b, 4, 4, true).intValue()); assertEquals(0x0405060708090a0bL, dc.getBigInteger(b, 4, 8, true).longValue()); - BigInteger bint = - dc.getBigInteger(new byte[] { 0x01, 0x02, (byte) 0xff, 0x03 }, 2, 2, true); + BigInteger bint = dc.getBigInteger(bytes(0x01, 0x02, 0xff, 0x03), 2, 2, true); assertEquals((short) 0xff03, bint.shortValue());// -253 assertEquals(0xffffff03, bint.intValue()); - bint = dc.getBigInteger(new byte[] { 0x01, 0x02, (byte) 0xff, 0x03 }, 2, 2, false); + bint = dc.getBigInteger(bytes(0x01, 0x02, 0xff, 0x03), 2, 2, false); assertEquals((short) 0xff03, bint.shortValue()); assertEquals(0x0000ff03, bint.intValue()); + } + @Test + public void testGetSignedValues() { + assertEquals(Integer.MIN_VALUE, dc.getSignedValue(bytes(0x80, 00, 00, 00), 4)); + assertEquals(-0x800000L, dc.getSignedValue(bytes(0x80, 00, 00, 00), 3)); + + assertEquals(-256, dc.getSignedValue(bytes(0xFF, 00, 00, 00), 2)); } @Test diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/util/LittleEndianConverterTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/util/LittleEndianConverterTest.java index e8bb76d143..b15f6fe4c4 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/util/LittleEndianConverterTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/util/LittleEndianConverterTest.java @@ -15,14 +15,12 @@ */ package ghidra.util; -import static org.junit.Assert.assertEquals; +import static org.junit.Assert.*; import java.math.BigInteger; import java.util.Arrays; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; +import org.junit.*; import ghidra.test.AbstractGhidraHeadedIntegrationTest; @@ -36,7 +34,7 @@ import ghidra.test.AbstractGhidraHeadedIntegrationTest; */ public class LittleEndianConverterTest extends AbstractGhidraHeadedIntegrationTest { private byte[] b; - private DataConverter dc; + private DataConverter dc = LittleEndianDataConverter.INSTANCE; /** * Constructor for BigEndianConverterTest. @@ -52,7 +50,6 @@ public class LittleEndianConverterTest extends AbstractGhidraHeadedIntegrationTe for (int i = 0; i < b.length; i++) { b[i] = (byte) i; } - dc = new LittleEndianDataConverter(); } @Test @@ -73,6 +70,10 @@ public class LittleEndianConverterTest extends AbstractGhidraHeadedIntegrationTe assertEquals(0x020100L, dc.getValue(b, 3)); assertEquals(0x0706050403020100L, dc.getValue(b, 8)); + assertEquals(0x0100L, dc.getSignedValue(b, 2)); + assertEquals(0x020100L, dc.getSignedValue(b, 3)); + assertEquals(0x0706050403020100L, dc.getSignedValue(b, 8)); + assertEquals(0x0302L, dc.getValue(b, 2, 2)); assertEquals(0x040302L, dc.getValue(b, 2, 3)); assertEquals(0x0908070605040302L, dc.getValue(b, 2, 8)); @@ -92,6 +93,14 @@ public class LittleEndianConverterTest extends AbstractGhidraHeadedIntegrationTe } + @Test + public void testGetSignedValues() { + assertEquals(Integer.MIN_VALUE, dc.getSignedValue(bytes(0, 00, 00, 0x80), 4)); + assertEquals(-0x800000L, dc.getSignedValue(bytes(0, 00, 0x80, 00), 3)); + + assertEquals(-256, dc.getSignedValue(bytes(0, 0xFF, 00, 00), 2)); + } + @Test public void testPut() { byte[] b2 = new byte[12]; diff --git a/Ghidra/Features/Base/src/test/java/ghidra/program/database/code/CodeManager64Test.java b/Ghidra/Features/Base/src/test/java/ghidra/program/database/code/CodeManager64Test.java index da5a06a77f..f8d55d5522 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/program/database/code/CodeManager64Test.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/program/database/code/CodeManager64Test.java @@ -15,7 +15,7 @@ */ package ghidra.program.database.code; -import static org.junit.Assert.assertEquals; +import static org.junit.Assert.*; import org.junit.*; @@ -133,14 +133,6 @@ public class CodeManager64Test extends AbstractGenericTest { } - private byte[] bytes(int... v) { - byte[] byteArray = new byte[v.length]; - for (int i = 0; i < v.length; i++) { - byteArray[i] = (byte) v[i]; - } - return byteArray; - } - private Address addr(long l) { return space.getAddress(l); } diff --git a/Ghidra/Features/Base/src/test/java/ghidra/program/database/code/CodeManagerTest.java b/Ghidra/Features/Base/src/test/java/ghidra/program/database/code/CodeManagerTest.java index 3072045605..3c720527e6 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/program/database/code/CodeManagerTest.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/program/database/code/CodeManagerTest.java @@ -638,14 +638,6 @@ public class CodeManagerTest extends AbstractGenericTest { assertEquals(addr(0x0101), referencesFrom[0].getToAddress()); } - private byte[] bytes(int... v) { - byte[] byteArray = new byte[v.length]; - for (int i = 0; i < v.length; i++) { - byteArray[i] = (byte) v[i]; - } - return byteArray; - } - @Test public void testGetDataAt() throws Exception { listing.createData(addr(0x1740), DefaultDataType.dataType, 1); diff --git a/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/FileByteBlock.java b/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/FileByteBlock.java index bcd8b249a2..43c5e9e30f 100644 --- a/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/FileByteBlock.java +++ b/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/FileByteBlock.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,7 +35,7 @@ class FileByteBlock implements ByteBlock { FileByteBlock(byte[] b) { buf = b; - converter = new LittleEndianDataConverter(); + converter = LittleEndianDataConverter.INSTANCE; } /* (non-Javadoc) @@ -153,12 +152,7 @@ class FileByteBlock implements ByteBlock { public void setBigEndian(boolean bigEndian) { if (this.bigEndian != bigEndian) { this.bigEndian = bigEndian; - if (bigEndian) { - converter = new BigEndianDataConverter(); - } - else { - converter = new LittleEndianDataConverter(); - } + converter = DataConverter.getInstance(bigEndian); } } diff --git a/Ghidra/Features/ByteViewer/src/test/java/ghidra/app/plugin/core/byteviewer/FileByteBlockTest.java b/Ghidra/Features/ByteViewer/src/test/java/ghidra/app/plugin/core/byteviewer/FileByteBlockTest.java index 5f20054843..0768aec172 100644 --- a/Ghidra/Features/ByteViewer/src/test/java/ghidra/app/plugin/core/byteviewer/FileByteBlockTest.java +++ b/Ghidra/Features/ByteViewer/src/test/java/ghidra/app/plugin/core/byteviewer/FileByteBlockTest.java @@ -68,7 +68,7 @@ public class FileByteBlockTest extends AbstractGenericTest { @Test public void testGetInt() throws Exception { block.setBigEndian(true); - DataConverter conv = new BigEndianDataConverter(); + DataConverter conv = BigEndianDataConverter.INSTANCE; for (int i = 0; i < 20; i++) { byte[] b = new byte[4]; @@ -77,7 +77,7 @@ public class FileByteBlockTest extends AbstractGenericTest { } block.setBigEndian(false); - conv = new LittleEndianDataConverter(); + conv = LittleEndianDataConverter.INSTANCE; for (int i = 0; i < 12; i++) { byte[] b = new byte[4]; System.arraycopy(buf, i + 4, b, 0, b.length); @@ -89,7 +89,7 @@ public class FileByteBlockTest extends AbstractGenericTest { @Test public void testGetLong() throws Exception { block.setBigEndian(true); - DataConverter conv = new BigEndianDataConverter(); + DataConverter conv = BigEndianDataConverter.INSTANCE; for (int i = 0; i < 10; i++) { byte[] b = new byte[8]; @@ -104,7 +104,7 @@ public class FileByteBlockTest extends AbstractGenericTest { } block.setBigEndian(false); - conv = new LittleEndianDataConverter(); + conv = LittleEndianDataConverter.INSTANCE; for (int i = 0; i < 10; i++) { byte[] b = new byte[8]; System.arraycopy(buf, i + 8, b, 0, b.length); @@ -141,7 +141,7 @@ public class FileByteBlockTest extends AbstractGenericTest { @Test public void testSetInt() throws Exception { block.setBigEndian(true); - DataConverter conv = new BigEndianDataConverter(); + DataConverter conv = BigEndianDataConverter.INSTANCE; byte[] b = new byte[4]; System.arraycopy(buf, 35, b, 0, b.length); @@ -158,7 +158,7 @@ public class FileByteBlockTest extends AbstractGenericTest { } block.setBigEndian(false); - conv = new LittleEndianDataConverter(); + conv = LittleEndianDataConverter.INSTANCE; b = new byte[4]; System.arraycopy(buf, 35, b, 0, b.length); @@ -179,7 +179,7 @@ public class FileByteBlockTest extends AbstractGenericTest { @Test public void testSetLong() throws Exception { block.setBigEndian(true); - DataConverter conv = new BigEndianDataConverter(); + DataConverter conv = BigEndianDataConverter.INSTANCE; byte[] b = new byte[8]; System.arraycopy(buf, 35, b, 0, b.length); @@ -196,7 +196,7 @@ public class FileByteBlockTest extends AbstractGenericTest { } block.setBigEndian(false); - conv = new LittleEndianDataConverter(); + conv = LittleEndianDataConverter.INSTANCE; b = new byte[8]; System.arraycopy(buf, 35, b, 0, b.length); diff --git a/Ghidra/Features/FileFormats/ghidra_scripts/MachoProcessBindScript.java b/Ghidra/Features/FileFormats/ghidra_scripts/MachoProcessBindScript.java index c55454f44e..6b650f2e26 100644 --- a/Ghidra/Features/FileFormats/ghidra_scripts/MachoProcessBindScript.java +++ b/Ghidra/Features/FileFormats/ghidra_scripts/MachoProcessBindScript.java @@ -395,9 +395,7 @@ public class MachoProcessBindScript extends GhidraScript { long offset = symbol.getAddress().getOffset(); - DataConverter converter = currentProgram.getLanguage().isBigEndian() ? - new BigEndianDataConverter() : - new LittleEndianDataConverter(); + DataConverter converter = DataConverter.getInstance(currentProgram.getLanguage().isBigEndian()); if ( currentProgram.getDefaultPointerSize() == 8 ) { setBytes( getAddress(), converter.getBytes( offset ) ); diff --git a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/bplist/NSObject.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/bplist/NSObject.java index 24bfdc08c7..007fec307f 100644 --- a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/bplist/NSObject.java +++ b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/bplist/NSObject.java @@ -32,7 +32,7 @@ public abstract class NSObject implements StructConverter { /** * All data is stored BIG ENDIAN in a binary plist. */ - protected DataConverter converter = new BigEndianDataConverter( ); + protected DataConverter converter = BigEndianDataConverter.INSTANCE; public abstract String getType(); diff --git a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/dyldcache/FixupMacho32bitArmOffsets.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/dyldcache/FixupMacho32bitArmOffsets.java index d52284f5f4..a1f91b1c64 100644 --- a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/dyldcache/FixupMacho32bitArmOffsets.java +++ b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/dyldcache/FixupMacho32bitArmOffsets.java @@ -28,7 +28,7 @@ import ghidra.util.exception.DuplicateNameException; import ghidra.util.task.TaskMonitor; public class FixupMacho32bitArmOffsets { - private DataConverter converter = new LittleEndianDataConverter(); + private DataConverter converter = LittleEndianDataConverter.INSTANCE; public InputStream fix(GFile file, long offsetAdjustment, ByteProvider provider, TaskMonitor monitor) throws IOException, MachException { diff --git a/Ghidra/Framework/FileSystem/src/main/java/ghidra/framework/store/local/ItemSerializer.java b/Ghidra/Framework/FileSystem/src/main/java/ghidra/framework/store/local/ItemSerializer.java index 0d70420826..21af4d3864 100644 --- a/Ghidra/Framework/FileSystem/src/main/java/ghidra/framework/store/local/ItemSerializer.java +++ b/Ghidra/Framework/FileSystem/src/main/java/ghidra/framework/store/local/ItemSerializer.java @@ -167,7 +167,7 @@ public class ItemSerializer { inputStream.skip(MAGIC_NUMBER_POS); byte[] magicBytes = new byte[MAGIC_NUMBER_SIZE]; inputStream.read(magicBytes); - BigEndianDataConverter dc = new BigEndianDataConverter(); + BigEndianDataConverter dc = BigEndianDataConverter.INSTANCE; long magic = dc.getLong(magicBytes); return (magic == MAGIC_NUMBER); } diff --git a/Ghidra/Framework/Generic/src/main/java/generic/test/AbstractGTest.java b/Ghidra/Framework/Generic/src/main/java/generic/test/AbstractGTest.java index 2d946568e8..1ed6cf4501 100644 --- a/Ghidra/Framework/Generic/src/main/java/generic/test/AbstractGTest.java +++ b/Ghidra/Framework/Generic/src/main/java/generic/test/AbstractGTest.java @@ -325,6 +325,21 @@ public abstract class AbstractGTest { return testName.getMethodName(); } + /** + * Friendly way to create an array of bytes with static values. + * + * @param unsignedBytes var-args list of unsigned byte values (ie. 0..255) + * @return array of bytes + */ + public static byte[] bytes(int... unsignedBytes) { + byte[] result = new byte[unsignedBytes.length]; + for (int i = 0; i < unsignedBytes.length; i++) { + result[i] = (byte) unsignedBytes[i]; + } + return result; + } + + //================================================================================================== // Wait Methods //================================================================================================== diff --git a/Ghidra/Framework/Generic/src/main/java/ghidra/util/BigEndianDataConverter.java b/Ghidra/Framework/Generic/src/main/java/ghidra/util/BigEndianDataConverter.java index c57240201e..c533ff08dc 100644 --- a/Ghidra/Framework/Generic/src/main/java/ghidra/util/BigEndianDataConverter.java +++ b/Ghidra/Framework/Generic/src/main/java/ghidra/util/BigEndianDataConverter.java @@ -16,6 +16,7 @@ package ghidra.util; import java.math.BigInteger; +import java.util.Objects; /** * Helper class to convert a byte array to Java primitives and primitives to a @@ -27,42 +28,26 @@ import java.math.BigInteger; public class BigEndianDataConverter implements DataConverter { public static final BigEndianDataConverter INSTANCE = new BigEndianDataConverter(); - /** - * - */ private static final long serialVersionUID = 1L; /** - * Constructor for BigEndianDataConverter. + * Don't use this constructor to create new instances of this class. Use the static {@link #INSTANCE} instead. */ public BigEndianDataConverter() { + // empty } - /** - * @see DataConverter#getShort(byte[]) - */ - public final short getShort(byte[] b) { - return getShort(b, 0); - } - - /** - * @see DataConverter#getShort(byte[], int) - */ + @Override public short getShort(byte[] b, int offset) { + Objects.checkFromIndexSize(offset, Short.BYTES, b.length); + return (short) (((b[offset] & 0xff) << 8) | (b[offset + 1] & 0xff)); } - /** - * @see DataConverter#getInt(byte[]) - */ - public final int getInt(byte[] b) { - return getInt(b, 0); - } - - /** - * @see DataConverter#getInt(byte[], int) - */ + @Override public int getInt(byte[] b, int offset) { + Objects.checkFromIndexSize(offset, Integer.BYTES, b.length); + int v = b[offset]; for (int i = 1; i < 4; i++) { v = (v << 8) | (b[offset + i] & 0xff); @@ -70,17 +55,10 @@ public class BigEndianDataConverter implements DataConverter { return v; } - /** - * @see DataConverter#getLong(byte[]) - */ - public final long getLong(byte[] b) { - return getLong(b, 0); - } - - /** - * @see DataConverter#getLong(byte[], int) - */ + @Override public long getLong(byte[] b, int offset) { + Objects.checkFromIndexSize(offset, Long.BYTES, b.length); + long v = b[offset]; for (int i = 1; i < 8; i++) { v = (v << 8) | (b[offset + i] & 0xff); @@ -88,20 +66,11 @@ public class BigEndianDataConverter implements DataConverter { return v; } - /** - * @see ghidra.util.DataConverter#getValue(byte[], int) - */ - public long getValue(byte[] b, int size) { - return getValue(b, 0, size); - } - - /** - * @see ghidra.util.DataConverter#getValue(byte[], int, int) - */ + @Override public long getValue(byte[] b, int offset, int size) { - if (size > 8) { - throw new IndexOutOfBoundsException("size exceeds sizeof long: " + size); - } + Objects.checkFromIndexSize(offset, size, b.length); + Objects.checkIndex(size, Long.BYTES + 1); + long val = 0; for (int i = 0; i < size; i++) { val = (val << 8) | (b[offset + i] & 0xff); @@ -109,16 +78,10 @@ public class BigEndianDataConverter implements DataConverter { return val; } - @Override - public final BigInteger getBigInteger(byte[] b, int size, boolean signed) { - return getBigInteger(b, 0, size, signed); - } - @Override public final BigInteger getBigInteger(byte[] b, int offset, int size, boolean signed) { - if ((size + offset) > b.length) { - throw new IndexOutOfBoundsException("insufficient bytes"); - } + Objects.checkFromIndexSize(offset, size, b.length); + if (offset != 0 || size != b.length) { int index = 0; if (!signed && b[offset] < 0) { @@ -132,39 +95,25 @@ public class BigEndianDataConverter implements DataConverter { } else if (!signed && b[0] < 0) { // keep unsigned - prepend 0 byte - byte[] bytes = new byte[size+1]; + byte[] bytes = new byte[size + 1]; System.arraycopy(b, 0, bytes, 1, size); b = bytes; } return new BigInteger(b); } - /** - * @see DataConverter#getBytes(short, byte[]) - */ - public final void getBytes(short value, byte[] b) { - getBytes(value, b, 0); - } + @Override + public void putShort(byte[] b, int offset, short value) { + Objects.checkFromIndexSize(offset, Short.BYTES, b.length); - /** - * @see DataConverter#getBytes(short, byte[], int) - */ - public void getBytes(short value, byte[] b, int offset) { b[offset] = (byte) (value >> 8); b[offset + 1] = (byte) (value & 0xff); } - /** - * @see DataConverter#getBytes(int, byte[]) - */ - public final void getBytes(int value, byte[] b) { - getBytes(value, b, 0); - } + @Override + public void putInt(byte[] b, int offset, int value) { + Objects.checkFromIndexSize(offset, Integer.BYTES, b.length); - /** - * @see DataConverter#getBytes(int, byte[], int) - */ - public void getBytes(int value, byte[] b, int offset) { b[offset + 3] = (byte) (value); for (int i = 2; i >= 0; i--) { value >>= 8; @@ -172,113 +121,20 @@ public class BigEndianDataConverter implements DataConverter { } } - /** - * @see DataConverter#getBytes(long, byte[]) - */ - public final void getBytes(long value, byte[] b) { - getBytes(value, 8, b, 0); - } + @Override + public void putValue(long value, int size, byte[] b, int offset) { + Objects.checkFromIndexSize(offset, size, b.length); + Objects.checkIndex(size, Long.BYTES + 1); - /** - * @see DataConverter#getBytes(long, byte[], int) - */ - public void getBytes(long value, byte[] b, int offset) { - getBytes(value, 8, b, offset); - } - - /** - * @see ghidra.util.DataConverter#getBytes(long, int, byte[], int) - */ - public void getBytes(long value, int size, byte[] b, int offset) { for (int i = size - 1; i >= 0; i--) { b[offset + i] = (byte) value; value >>= 8; } } - /** - * @see ghidra.util.DataConverter#putInt(byte[], int, int) - */ - public final void putInt(byte[] b, int offset, int value) { - getBytes(value, b, offset); - } - - /** - * @see ghidra.util.DataConverter#putInt(byte[], int) - */ - public final void putInt(byte[] b, int value) { - getBytes(value, b); - } - - /** - * @see ghidra.util.DataConverter#putLong(byte[], int, long) - */ - public final void putLong(byte[] b, int offset, long value) { - getBytes(value, b, offset); - } - - /** - * @see ghidra.util.DataConverter#putLong(byte[], long) - */ - public final void putLong(byte[] b, long value) { - getBytes(value, b); - } - - /** - * @see ghidra.util.DataConverter#putShort(byte[], int, short) - */ - public final void putShort(byte[] b, int offset, short value) { - getBytes(value, b, offset); - } - - /** - * @see ghidra.util.DataConverter#putShort(byte[], short) - */ - public final void putShort(byte[] b, short value) { - getBytes(value, b); - } - - /** - * @see ghidra.util.DataConverter#getBytes(int) - */ - public byte[] getBytes(int value) { - byte[] bytes = new byte[4]; - getBytes(value, bytes); - return bytes; - } - - /** - * @see ghidra.util.DataConverter#getBytes(long) - */ - public byte[] getBytes(long value) { - byte[] bytes = new byte[8]; - getBytes(value, bytes); - return bytes; - } - - /** - * @see ghidra.util.DataConverter#getBytes(short) - */ - public byte[] getBytes(short value) { - byte[] bytes = new byte[2]; - getBytes(value, bytes); - return bytes; - } - - @Override - public byte[] getBytes(BigInteger value, int size) { - byte[] bytes = new byte[size]; - putBigInteger(bytes, 0, size, value); - return bytes; - } - - @Override - public void getBytes(BigInteger value, int size, byte[] b, int offset) { - putBigInteger(b, offset, size, value); - } - @Override public void putBigInteger(byte[] b, int offset, int size, BigInteger value) { + Objects.checkFromIndexSize(offset, size, b.length); int fillIndex = offset; // start fill from MSB int srcIndex; @@ -300,9 +156,4 @@ public class BigEndianDataConverter implements DataConverter { System.arraycopy(valBytes, srcIndex, b, fillIndex, fillCnt); } - @Override - public void putBigInteger(byte[] b, int size, BigInteger value) { - putBigInteger(b, 0, size, value); - } - } diff --git a/Ghidra/Framework/Generic/src/main/java/ghidra/util/DataConverter.java b/Ghidra/Framework/Generic/src/main/java/ghidra/util/DataConverter.java index 3f5b764463..cb639935fe 100644 --- a/Ghidra/Framework/Generic/src/main/java/ghidra/util/DataConverter.java +++ b/Ghidra/Framework/Generic/src/main/java/ghidra/util/DataConverter.java @@ -19,277 +19,492 @@ import java.io.Serializable; import java.math.BigInteger; /** - * - * Defines methods to convert byte arrays to a specific primitive Java types, - * and to populate byte arrays from primitive Java types. - * + * Stateless helper classes with static singleton instances that contain methods to convert + * Java numeric types to and from their raw form in a byte array. + *

* */ public interface DataConverter extends Serializable { + /** + * Returns the correct DataConverter static instance for the requested endian-ness. + * + * @param isBigEndian boolean flag, true means big endian + * @return static DataConverter instance + */ public static DataConverter getInstance(boolean isBigEndian) { return isBigEndian ? BigEndianDataConverter.INSTANCE : LittleEndianDataConverter.INSTANCE; } /** - * Get the short value from the given byte array. - * @param b array containing bytes - * @throws IndexOutOfBoundsException if byte array size is - * less than 2. + * Returns the endianess of this DataConverter instance. + * + * @return boolean flag, true means big-endian */ - public short getShort(byte[] b); + default boolean isBigEndian() { + return this instanceof BigEndianDataConverter; + } /** * Get the short value from the given byte array. * @param b array containing bytes + * @return signed short value from the beginning of the specified array + * @throws IndexOutOfBoundsException if byte array size is less than 2. + */ + default short getShort(byte[] b) { + return getShort(b, 0); + } + + /** + * Get the short value from the given byte array. + * + * @param b array containing bytes * @param offset offset into byte array for getting the short - * @throws IndexOutOfBoundsException if byte array size is - * less than offset+2. + * @return signed short value + * @throws IndexOutOfBoundsException if byte array size is less than offset+2 */ - public short getShort(byte[] b, int offset); + short getShort(byte[] b, int offset); /** * Get the int value from the given byte array. + * * @param b array containing bytes - * @throws IndexOutOfBoundsException if byte array size is - * less than 4. + * @return signed int value from the beginning of the specified array + * @throws IndexOutOfBoundsException if byte array size is less than 4 */ - public int getInt(byte[] b); + default int getInt(byte[] b) { + return getInt(b, 0); + } /** * Get the int value from the given byte array. + * * @param b array containing bytes * @param offset offset into byte array for getting the int - * @throws IndexOutOfBoundsException if byte array size is - * less than offset+4. + * @return signed int value + * @throws IndexOutOfBoundsException if byte array size is less than offset+4 */ - public int getInt(byte[] b, int offset); + int getInt(byte[] b, int offset); /** * Get the long value from the given byte array. + * * @param b array containing bytes - * @throws IndexOutOfBoundsException if byte array size is - * less than 8. + * @return signed long value from the beginning of the specified array + * @throws IndexOutOfBoundsException if byte array size is less than 8 */ - public long getLong(byte[] b); + default long getLong(byte[] b) { + return getLong(b, 0); + } /** * Get the long value from the given byte array. + * * @param b array containing bytes * @param offset offset into byte array for getting the long - * @throws IndexOutOfBoundsException if byte array size is - * less than offset+8. + * @return signed long value + * @throws IndexOutOfBoundsException if byte array size is less than offset+8 */ - public long getLong(byte[] b, int offset); + long getLong(byte[] b, int offset); + + /** + * Get the unsigned value from the given byte array using the specified + * integer size, returned as a long. + *

+ * Values with a size less than sizeof(long) will not have their sign bit + * extended and therefore will appear as an 'unsigned' value. + *

+ * Casting the 'unsigned' long value to the correctly sized smaller + * java primitive will cause the value to appear as a signed value. + *

+ * Values of size 8 (ie. longs) will be signed. + * + * @param b array containing bytes + * @param size number of bytes (1 - 8) to use from array at offset 0 + * @return unsigned value from the beginning of the specified array + * @throws IndexOutOfBoundsException if byte array size is less than specified size + */ + default long getValue(byte[] b, int size) { + return getValue(b, 0, size); + } + + /** + * Get the unsigned value from the given byte array using the specified + * integer size, returned as a long. + *

+ * Values with a size less than sizeof(long) will not have their sign bit + * extended and therefore will appear as an 'unsigned' value. + *

+ * Casting the 'unsigned' long value to the correctly sized smaller + * java primitive will cause the value to appear as a signed value. + *

+ * Values of size 8 (ie. longs) will be signed. + * + * @param b array containing bytes + * @param size number of bytes (1 - 8) to use from array + * @param offset offset into byte array for getting the long + * @return unsigned value + * @throws IndexOutOfBoundsException if byte array size is + * less than offset+size or size is greater than 8 (sizeof long) + */ + long getValue(byte[] b, int offset, int size); + + /** + * Get the signed value from the given byte array using the specified + * integer size, returned as a long. + *

+ * Values with a size less than sizeof(long) will have their sign bit + * extended. + * + * @param b array containing bytes + * @param size number of bytes (1 - 8) to use from array at offset 0 + * @return signed value from the beginning of the specified array + * @throws IndexOutOfBoundsException if byte array size is less than specified size + */ + default long getSignedValue(byte[] b, int size) { + return getSignedValue(b, 0, size); + } + + /** + * Get the signed value from the given byte array using the specified + * integer size, returned as a long. + *

+ * Values with a size less than sizeof(long) will have their sign bit + * extended. + * + * @param b array containing bytes + * @param size number of bytes (1 - 8) to use from array + * @param offset offset into byte array for getting the long + * @return signed value + * @throws IndexOutOfBoundsException if byte array size is + * less than offset+size or size is greater than 8 (sizeof long) + */ + default long getSignedValue(byte[] b, int offset, int size) { + long val = getValue(b, offset, size); + + int shiftBits = (8 /*sizeof(long)*/ - size) * 8; + + // this little bit of magic will sign-extend the value + val = val << shiftBits; + val = val >> shiftBits; + return val; + } /** * Get the value from the given byte array using the specified size. + * * @param b array containing bytes * @param size number of bytes to use from array at offset 0 + * @param signed boolean flag indicating the value is signed + * @return {@link BigInteger} with value * @throws IndexOutOfBoundsException if byte array size is - * less than size. + * less than size */ - public long getValue(byte[] b, int size); + default BigInteger getBigInteger(byte[] b, int size, boolean signed) { + return getBigInteger(b, 0, size, signed); + } /** * Get the value from the given byte array using the specified size. + * * @param b array containing bytes * @param size number of bytes to use from array * @param offset offset into byte array for getting the long + * @param signed boolean flag indicating the value is signed + * @return {@link BigInteger} with value * @throws IndexOutOfBoundsException if byte array size is - * less than offset+size or size is greater than 8 (sizeof long). + * less than offset+size */ - public long getValue(byte[] b, int offset, int size); + BigInteger getBigInteger(byte[] b, int offset, int size, boolean signed); + + //------------------------------------------------------------------------------- /** - * Get the value from the given byte array using the specified size. - * @param b array containing bytes - * @param size number of bytes to use from array at offset 0 - * @throws IndexOutOfBoundsException if byte array size is - * less than size. + * Converts the short value to an array of bytes. + * + * @param value short value to be converted + * @return array of bytes */ - public BigInteger getBigInteger(byte[] b, int size, boolean signed); + default byte[] getBytes(short value) { + byte[] bytes = new byte[2]; + getBytes(value, bytes); + return bytes; + } /** - * Get the value from the given byte array using the specified size. - * @param b array containing bytes - * @param size number of bytes to use from array - * @param offset offset into byte array for getting the long - * @throws IndexOutOfBoundsException if byte array size is - * less than offset+size. + * Converts the int value to an array of bytes. + * + * @param value int value to be converted + * @return array of bytes */ - public BigInteger getBigInteger(byte[] b, int offset, int size, boolean signed); + default byte[] getBytes(int value) { + byte[] bytes = new byte[4]; + getBytes(value, bytes); + return bytes; + } + + /** + * Converts the long value to an array of bytes. + * + * @param value long value to be converted + * @return array of bytes + */ + default byte[] getBytes(long value) { + byte[] bytes = new byte[8]; + getBytes(value, bytes); + return bytes; + } + + /** + * Converts the value to an array of bytes. + * + * @param value value to be converted + * @param size value size in bytes + * @return array of bytes + */ + default byte[] getBytes(BigInteger value, int size) { + byte[] bytes = new byte[size]; + putBigInteger(bytes, 0, size, value); + return bytes; + } + + //------------------------------------------------------------------------------- + + /** + * Writes a short value into a byte array. + * + * @param b array to contain the bytes + * @param value the short value + * @throws IndexOutOfBoundsException if byte array is too small to hold the value + */ + default void putShort(byte[] b, short value) { + putShort(b, 0, value); + } + + /** + * Writes a short value into the byte array at the given offset + * + * @param b array to contain the bytes + * @param offset the offset into the byte array to store the value + * @param value the short value + * @throws IndexOutOfBoundsException if offset is too large or byte array + * is too small to hold the value + */ + void putShort(byte[] b, int offset, short value); + + /** + * Writes a int value into a byte array. + *

+ * See {@link #getBytes(int, byte[])} + * + * @param b array to contain the bytes + * @param value the int value + * @throws IndexOutOfBoundsException if byte array is too small to hold the value + */ + default void putInt(byte[] b, int value) { + putInt(b, 0, value); + } + + /** + * Writes a int value into the byte array at the given offset. + *

+ * See {@link #getBytes(int, byte[], int)} + * + * @param b array to contain the bytes + * @param offset the offset into the byte array to store the value + * @param value the int value + * @throws IndexOutOfBoundsException if offset is too large or byte array + * is too small to hold the value + */ + void putInt(byte[] b, int offset, int value); + + /** + * Writes a long value into a byte array. + *

+ * See {@link #getBytes(long, byte[])} + * + * @param b array to contain the bytes + * @param value the long value + * @throws IndexOutOfBoundsException if byte array is too small to hold the value + */ + default void putLong(byte[] b, long value) { + putLong(b, 0, value); + } + + /** + * Writes a long value into the byte array at the given offset + *

+ * See {@link #getBytes(long, byte[], int)} + * + * @param b array to contain the bytes + * @param offset the offset into the byte array to store the value + * @param value the long value + * @throws IndexOutOfBoundsException if offset is too large or byte array + * is too small to hold the value + */ + default void putLong(byte[] b, int offset, long value) { + putValue(value, Long.BYTES, b, offset); + } + + /** + * Converts the given value to bytes using the number of least significant bytes + * specified by size. + *

+ * + * @param value value to convert to bytes + * @param size number of least significant bytes of value to be written to the byte array + * @param b byte array to store bytes + * @param offset offset into byte array to put the bytes + * @throws IndexOutOfBoundsException if (offset+size)>b.length + */ + void putValue(long value, int size, byte[] b, int offset); + + /** + * Writes a value of specified size into the byte array at the given offset. + *

+ * See {@link #getBytes(BigInteger, int, byte[], int)} + * + * @param b array to contain the bytes at offset 0 + * @param size number of bytes to be written + * @param value BigInteger value to convert + * @throws IndexOutOfBoundsException if byte array is less than specified size + */ + default void putBigInteger(byte[] b, int size, BigInteger value) { + putBigInteger(b, 0, size, value); + } + + /** + * Writes a value of specified size into the byte array at the given offset + *

+ * See {@link #getBytes(BigInteger, int, byte[], int)} + * + * @param b array to contain the bytes + * @param offset the offset into the byte array to store the value + * @param size number of bytes to be written + * @param value BigInteger value to convert + * @throws IndexOutOfBoundsException if (offset+size)>b.length + */ + public void putBigInteger(byte[] b, int offset, int size, BigInteger value); + + //-------------------------------------------------------------------------------- /** * Converts the given value to bytes. + * See {@link #putShort(byte[], short)} * @param value value to convert to bytes * @param b byte array to store bytes * @throws IndexOutOfBoundsException if b.length is not at least * 2. */ - public void getBytes(short value, byte[] b); + default void getBytes(short value, byte[] b) { + getBytes(value, b, 0); + } /** * Converts the given value to bytes. + *

+ * See {@link #putShort(byte[], int, short)} + * * @param value value to convert to bytes * @param b byte array to store bytes * @param offset offset into byte array to put the bytes * @throws IndexOutOfBoundsException if (offset+2)>b.length */ - public void getBytes(short value, byte[] b, int offset); + default void getBytes(short value, byte[] b, int offset) { + putShort(b, offset, value); + } /** * Converts the given value to bytes. + *

+ * See {@link #putInt(byte[], int)} + * * @param value value to convert to bytes * @param b byte array to store bytes * @throws IndexOutOfBoundsException if b.length is not at least * 4. */ - public void getBytes(int value, byte[] b); + default void getBytes(int value, byte[] b) { + getBytes(value, b, 0); + } /** * Converts the given value to bytes. + *

+ * See {@link #putInt(byte[], int)} + * * @param value value to convert to bytes * @param b byte array to store bytes * @param offset offset into byte array to put the bytes * @throws IndexOutOfBoundsException if (offset+4)>b.length */ - public void getBytes(int value, byte[] b, int offset); + default void getBytes(int value, byte[] b, int offset) { + putInt(b, offset, value); + } /** * Converts the given value to bytes. + *

+ * See {@link #putLong(byte[], long)} + * * @param value value to convert to bytes * @param b byte array to store bytes * @throws IndexOutOfBoundsException if b.length is not at least * 8. */ - public void getBytes(long value, byte[] b); + default void getBytes(long value, byte[] b) { + getBytes(value, b, 0); + } /** * Converts the given value to bytes. + *

+ * See {@link #putLong(byte[], long)} + * * @param value value to convert to bytes * @param b byte array to store bytes * @param offset offset into byte array to put the bytes * @throws IndexOutOfBoundsException if (offset+8)>b.length */ - public void getBytes(long value, byte[] b, int offset); + default void getBytes(long value, byte[] b, int offset) { + putLong(b, offset, value); + } /** * Converts the given value to bytes using the number of least significant bytes * specified by size. + *

+ * See {@link #putValue(long, int, byte[], int)} + * + * @param value value to convert to bytes + * @param size number of least significant bytes of value to be written to the byte array + * @param b byte array to store bytes + * @param offset offset into byte array to put the bytes + * @throws IndexOutOfBoundsException if (offset+size)>b.length + */ + default void getBytes(long value, int size, byte[] b, int offset) { + putValue(value, size, b, offset); + } + + /** + * Converts the given value to bytes using the number of least significant bytes + * specified by size. + *

+ * See {@link #putBigInteger(byte[], int, BigInteger)} + * * @param value value to convert to bytes * @param size number of least significant bytes of value to be written to the byte array * @param b byte array to store bytes * @param offset offset into byte array to put the bytes * @throws IndexOutOfBoundsException if (offset+size)>b.length. */ - public void getBytes(long value, int size, byte[] b, int offset); - - /** - * Converts the given value to bytes using the number of least significant bytes - * specified by size. - * @param value value to convert to bytes - * @param size number of least significant bytes of value to be written to the byte array - * @param b byte array to store bytes - * @param offset offset into byte array to put the bytes - * @throws IndexOutOfBoundsException if (offset+size)>b.length. - */ - public void getBytes(BigInteger value, int size, byte[] b, int offset); - - /** - * Converts the short value to an array of bytes. - * @param value short value to be converted - * @return array of bytes - */ - public byte[] getBytes(short value); - - /** - * Converts the int value to an array of bytes. - * @param value int value to be converted - * @return array of bytes - */ - public byte[] getBytes(int value); - - /** - * Converts the long value to an array of bytes. - * @param value long value to be converted - * @return array of bytes - */ - public byte[] getBytes(long value); - - /** - * Converts the value to an array of bytes. - * @param value value to be converted - * @param size value size in bytes - * @return array of bytes - */ - public byte[] getBytes(BigInteger value, int size); - - /** - * Writes a short value into a byte array. - * @param b array to contain the bytes; - * @param value the short value - */ - public void putShort(byte[] b, short value); - - /** - * Writes a short value into the byte array at the given offset - * @param b array to contain the bytes; - * @param offset the offset into the byte array to store the value. - * @param value the short value - */ - public void putShort(byte[] b, int offset, short value); - - /** - * Writes a int value into a byte array. - * @param b array to contain the bytes; - * @param value the int value - */ - public void putInt(byte[] b, int value); - - /** - * Writes a int value into the byte array at the given offset - * @param b array to contain the bytes; - * @param offset the offset into the byte array to store the value. - * @param value the int value - */ - public void putInt(byte[] b, int offset, int value); - - /** - * Writes a long value into a byte array. - * @param b array to contain the bytes; - * @param value the long value - */ - public void putLong(byte[] b, long value); - - /** - * Writes a long value into the byte array at the given offset - * @param b array to contain the bytes; - * @param offset the offset into the byte array to store the value. - * @param value the long value - */ - public void putLong(byte[] b, int offset, long value); - - /** - * Writes a value of specified size into the byte array at the given offset - * @param b array to contain the bytes at offset 0; - * @param size number of bytes to be written - * @param value - */ - public void putBigInteger(byte[] b, int size, BigInteger value); - - /** - * Writes a value of specified size into the byte array at the given offset - * @param b array to contain the bytes; - * @param offset the offset into the byte array to store the value. - * @param size number of bytes to be written - * @param value - */ - public void putBigInteger(byte[] b, int offset, int size, BigInteger value); + default void getBytes(BigInteger value, int size, byte[] b, int offset) { + putBigInteger(b, offset, size, value); + } /** * Swap the least-significant bytes (based upon size) - * @param val value whoose bytes are to be swapped + * @param val value whose bytes are to be swapped * @param size number of least significant bytes to be swapped * @return value with bytes swapped (any high-order bytes beyond size will be 0) */ diff --git a/Ghidra/Framework/Generic/src/main/java/ghidra/util/LittleEndianDataConverter.java b/Ghidra/Framework/Generic/src/main/java/ghidra/util/LittleEndianDataConverter.java index afe90fbe4b..bfe95173af 100644 --- a/Ghidra/Framework/Generic/src/main/java/ghidra/util/LittleEndianDataConverter.java +++ b/Ghidra/Framework/Generic/src/main/java/ghidra/util/LittleEndianDataConverter.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,6 +16,7 @@ package ghidra.util; import java.math.BigInteger; +import java.util.Objects; /** * @@ -25,43 +25,29 @@ import java.math.BigInteger; */ public class LittleEndianDataConverter implements DataConverter { - public static LittleEndianDataConverter INSTANCE = new LittleEndianDataConverter(); - /** - * - */ + public static final LittleEndianDataConverter INSTANCE = new LittleEndianDataConverter(); + private static final long serialVersionUID = 1L; /** - * Constructor for BigEndianDataConverter. + * Don't use this constructor to create new instances of this class. Use the static {@link #INSTANCE} instead + * or {@link DataConverter#getInstance(Endian)} */ public LittleEndianDataConverter() { + // empty } - /** - * @see DataConverter#getShort(byte[]) - */ - public final short getShort(byte[] b) { - return getShort(b, 0); - } - - /** - * @see DataConverter#getShort(byte[], int) - */ + @Override public short getShort(byte[] b, int offset) { + Objects.checkFromIndexSize(offset, Short.BYTES, b.length); + return (short) (((b[offset + 1] & 0xff) << 8) | (b[offset] & 0xff)); } - /** - * @see DataConverter#getInt(byte[]) - */ - public final int getInt(byte[] b) { - return getInt(b, 0); - } - - /** - * @see DataConverter#getInt(byte[], int) - */ + @Override public int getInt(byte[] b, int offset) { + Objects.checkFromIndexSize(offset, Integer.BYTES, b.length); + int v = b[offset + 3]; for (int i = 2; i >= 0; i--) { v = (v << 8) | (b[offset + i] & 0xff); @@ -69,17 +55,10 @@ public class LittleEndianDataConverter implements DataConverter { return v; } - /** - * @see DataConverter#getLong(byte[]) - */ - public final long getLong(byte[] b) { - return getLong(b, 0); - } - - /** - * @see DataConverter#getLong(byte[], int) - */ + @Override public long getLong(byte[] b, int offset) { + Objects.checkFromIndexSize(offset, Long.BYTES, b.length); + long v = b[offset + 7]; for (int i = 6; i >= 0; i--) { v = (v << 8) | (b[offset + i] & 0xff); @@ -87,20 +66,11 @@ public class LittleEndianDataConverter implements DataConverter { return v; } - /** - * @see ghidra.util.DataConverter#getValue(byte[], int) - */ - public long getValue(byte[] b, int size) { - return getValue(b, 0, size); - } - - /** - * @see ghidra.util.DataConverter#getValue(byte[], int, int) - */ + @Override public long getValue(byte[] b, int offset, int size) { - if (size > 8) { - throw new IndexOutOfBoundsException("size exceeds sizeof long: " + size); - } + Objects.checkFromIndexSize(offset, size, b.length); + Objects.checkIndex(size, Long.BYTES + 1); + long val = 0; for (int i = size - 1; i >= 0; i--) { val = (val << 8) | (b[offset + i] & 0xff); @@ -109,15 +79,9 @@ public class LittleEndianDataConverter implements DataConverter { } @Override - public final BigInteger getBigInteger(byte[] b, int size, boolean signed) { - return getBigInteger(b, 0, size, signed); - } + public BigInteger getBigInteger(byte[] b, int offset, int size, boolean signed) { + Objects.checkFromIndexSize(offset, size, b.length); - @Override - public final BigInteger getBigInteger(byte[] b, int offset, int size, boolean signed) { - if ((size + offset) > b.length) { - throw new IndexOutOfBoundsException("insufficient bytes"); - } int msbIndex = 0; if (!signed) { // prepend 0 byte @@ -132,32 +96,18 @@ public class LittleEndianDataConverter implements DataConverter { return new BigInteger(bytes); } - /** - * @see DataConverter#getBytes(short, byte[]) - */ - public final void getBytes(short value, byte[] b) { - getBytes(value, b, 0); - } + @Override + public void putShort(byte[] b, int offset, short value) { + Objects.checkFromIndexSize(offset, Short.BYTES, b.length); - /** - * @see DataConverter#getBytes(short, byte[], int) - */ - public void getBytes(short value, byte[] b, int offset) { b[offset + 1] = (byte) (value >> 8); b[offset] = (byte) (value & 0xff); } - /** - * @see DataConverter#getBytes(int, byte[]) - */ - public final void getBytes(int value, byte[] b) { - getBytes(value, b, 0); - } + @Override + public void putInt(byte[] b, int offset, int value) { + Objects.checkFromIndexSize(offset, Integer.BYTES, b.length); - /** - * @see DataConverter#getBytes(int, byte[], int) - */ - public void getBytes(int value, byte[] b, int offset) { b[offset] = (byte) (value); for (int i = 1; i < 4; i++) { value >>= 8; @@ -165,113 +115,20 @@ public class LittleEndianDataConverter implements DataConverter { } } - /** - * @see DataConverter#getBytes(long, byte[]) - */ - public final void getBytes(long value, byte[] b) { - getBytes(value, 8, b, 0); - } + @Override + public void putValue(long value, int size, byte[] b, int offset) { + Objects.checkFromIndexSize(offset, size, b.length); + Objects.checkIndex(size, Long.BYTES + 1); - /** - * @see DataConverter#getBytes(long, byte[], int) - */ - public void getBytes(long value, byte[] b, int offset) { - getBytes(value, 8, b, offset); - } - - /** - * @see ghidra.util.DataConverter#getBytes(long, int, byte[], int) - */ - public void getBytes(long value, int size, byte[] b, int offset) { for (int i = 0; i < size; i++) { b[offset + i] = (byte) value; value >>= 8; } } - /** - * @see ghidra.util.DataConverter#putInt(byte[], int, int) - */ - public final void putInt(byte[] b, int offset, int value) { - getBytes(value, b, offset); - } - - /** - * @see ghidra.util.DataConverter#putInt(byte[], int) - */ - public final void putInt(byte[] b, int value) { - getBytes(value, b); - } - - /** - * @see ghidra.util.DataConverter#putLong(byte[], int, long) - */ - public final void putLong(byte[] b, int offset, long value) { - getBytes(value, b, offset); - } - - /** - * @see ghidra.util.DataConverter#putLong(byte[], long) - */ - public final void putLong(byte[] b, long value) { - getBytes(value, b); - } - - /** - * @see ghidra.util.DataConverter#putShort(byte[], int, short) - */ - public final void putShort(byte[] b, int offset, short value) { - getBytes(value, b, offset); - } - - /** - * @see ghidra.util.DataConverter#putShort(byte[], short) - */ - public final void putShort(byte[] b, short value) { - getBytes(value, b); - } - - /** - * @see ghidra.util.DataConverter#getBytes(int) - */ - public byte[] getBytes(int value) { - byte[] bytes = new byte[4]; - getBytes(value, bytes); - return bytes; - } - - /** - * @see ghidra.util.DataConverter#getBytes(long) - */ - public byte[] getBytes(long value) { - byte[] bytes = new byte[8]; - getBytes(value, bytes); - return bytes; - } - - /** - * @see ghidra.util.DataConverter#getBytes(short) - */ - public byte[] getBytes(short value) { - byte[] bytes = new byte[2]; - getBytes(value, bytes); - return bytes; - } - - @Override - public byte[] getBytes(BigInteger value, int size) { - byte[] bytes = new byte[size]; - putBigInteger(bytes, 0, size, value); - return bytes; - } - - @Override - public void getBytes(BigInteger value, int size, byte[] b, int offset) { - putBigInteger(b, offset, size, value); - } - @Override public void putBigInteger(byte[] b, int offset, int size, BigInteger value) { + Objects.checkFromIndexSize(offset, size, b.length); int fillIndex = offset + size - 1; // start fill from MSB int srcIndex; @@ -292,9 +149,4 @@ public class LittleEndianDataConverter implements DataConverter { } } - @Override - public void putBigInteger(byte[] b, int size, BigInteger value) { - putBigInteger(b, 0, size, value); - } - } diff --git a/Ghidra/Framework/Generic/src/main/java/ghidra/util/NumericUtilities.java b/Ghidra/Framework/Generic/src/main/java/ghidra/util/NumericUtilities.java index e0de71c902..28442d9eb8 100644 --- a/Ghidra/Framework/Generic/src/main/java/ghidra/util/NumericUtilities.java +++ b/Ghidra/Framework/Generic/src/main/java/ghidra/util/NumericUtilities.java @@ -29,6 +29,7 @@ import util.CollectionUtils; public final class NumericUtilities { public static final BigInteger MAX_UNSIGNED_LONG = new BigInteger("ffffffffffffffff", 16); public static final BigInteger MAX_SIGNED_LONG = new BigInteger("7fffffffffffffff", 16); + public static final long MAX_UNSIGNED_INT32_AS_LONG = 0xffffffffL; private final static String HEX_PREFIX_X = "0X"; private final static String HEX_PREFIX_x = "0x"; @@ -228,14 +229,26 @@ public final class NumericUtilities { return buf.toString(); } - public final static BigInteger unsignedLongToBigInteger(long value) { + /** + * Converts a unsigned long value, which is currently stored in a + * java signed long, into a {@link BigInteger}. + *

+ * In other words, the full 64 bits of the primitive java signed + * long is being used to store an unsigned value. This + * method converts this into a positive BigInteger value. + * + * @param value java unsigned long value stuffed into a + * java signed long + * @return new {@link BigInteger} with the positive value of the unsigned long value + */ + public static BigInteger unsignedLongToBigInteger(long value) { if (value >= 0) { return BigInteger.valueOf(value); } return MAX_UNSIGNED_LONG.add(BigInteger.valueOf(value + 1)); } - public final static long bigIntegerToUnsignedLong(BigInteger value) { + public static long bigIntegerToUnsignedLong(BigInteger value) { if (value.compareTo(MAX_SIGNED_LONG) <= 0) { return value.longValue(); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/emulator/Emulator.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/emulator/Emulator.java index a243e26581..70212d57e7 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/emulator/Emulator.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/emulator/Emulator.java @@ -172,8 +172,7 @@ public class Emulator { * @param restore if true restore modified registers within the register space only */ private void initRegisters(boolean restore) { - DataConverter conv = - language.isBigEndian() ? new BigEndianDataConverter() : new LittleEndianDataConverter(); + DataConverter conv = DataConverter.getInstance(language.isBigEndian()); Set keys = mstate.getKeys(); for (String key : keys) { List vals = mstate.getVals(key); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/references/RefList.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/references/RefList.java index c83275494b..2ecfa8d0b3 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/references/RefList.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/references/RefList.java @@ -31,7 +31,7 @@ abstract class RefList extends DatabaseObject { static volatile int BIG_REFLIST_THRESHOLD = 1700; - protected static DataConverter converter = new BigEndianDataConverter(); + protected static DataConverter converter = BigEndianDataConverter.INSTANCE; protected Address address; protected RecordAdapter adapter; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/GenericAddress.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/GenericAddress.java index fcc240820c..6d4b295404 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/GenericAddress.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/GenericAddress.java @@ -327,7 +327,7 @@ public class GenericAddress implements Address { public String toString(boolean showAddressSpace, int minNumDigits) { boolean stackFormat = false; - StringBuffer buf = new StringBuffer(); + StringBuilder buf = new StringBuilder(); if (addrSpace.isStackSpace()) { stackFormat = true; buf.append("Stack["); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/PointerDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/PointerDataType.java index a0cc360ad3..a1be4f0947 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/PointerDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/PointerDataType.java @@ -24,6 +24,7 @@ import ghidra.program.model.address.*; import ghidra.program.model.listing.*; import ghidra.program.model.mem.*; import ghidra.program.model.symbol.*; +import ghidra.util.DataConverter; /** * Basic implementation for a pointer dataType @@ -287,7 +288,7 @@ public class PointerDataType extends BuiltIn implements Pointer { @Override public String getDescription() { - StringBuffer sbuf = new StringBuffer(); + StringBuilder sbuf = new StringBuilder(); if (length > 0) { sbuf.append(Integer.toString(8 * length)); sbuf.append("-bit "); @@ -342,6 +343,7 @@ public class PointerDataType extends BuiltIn implements Pointer { if (buf.getAddress() instanceof SegmentedAddress) { try { + // NOTE: conversion assumes a little-endian space return getSegmentedAddressValue(buf, size); } catch (AddressOutOfBoundsException e) { @@ -362,21 +364,7 @@ public class PointerDataType extends BuiltIn implements Pointer { return null; } - boolean isBigEndian = buf.isBigEndian(); // ENDIAN.isBigEndian(settings, buf); - - if (!isBigEndian) { - byte[] flipped = new byte[size]; - for (int i = 0; i < size; i++) { - flipped[i] = bytes[size - i - 1]; - } - bytes = flipped; - } - - // Use long when possible - long val = 0; - for (byte b : bytes) { - val = (val << 8) + (b & 0x0ffL); - } + long val = DataConverter.getInstance(buf.isBigEndian()).getValue(bytes, size); try { return targetSpace.getAddress(val, true); @@ -387,22 +375,27 @@ public class PointerDataType extends BuiltIn implements Pointer { return null; } + /** + * Read segmented address from memory. + * NOTE: little-endian memory assumed. + * @param buf memory buffer associated with a segmented-address space + * positioned at start of address value to be read + * @param dataLen pointer-length (2 and 4-byte pointers supported) + * @return address value returned as segmented Address object or null + * for unsupported pointer length or meory access error occurs. + */ private static Address getSegmentedAddressValue(MemBuffer buf, int dataLen) { SegmentedAddress a = (SegmentedAddress) buf.getAddress(); int segment = a.getSegment(); int offset = 0; try { switch (dataLen) { - case 1: - offset = buf.getByte(0) & 0xff; + case 2: // near pointer + offset = (int) buf.getVarLengthUnsignedInt(0, dataLen); break; - case 2: - offset = buf.getShort(0) & 0xffff; - break; - case 4: - case 8: - segment = buf.getShort(0) & 0xffff; - offset = buf.getShort(2) & 0xffff; + case 4: // far pointer + segment = buf.getUnsignedShort(0); + offset = buf.getUnsignedShort(2); break; default: return null; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ShiftedAddressDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ShiftedAddressDataType.java index e678b18d1b..3bdecbf70c 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ShiftedAddressDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ShiftedAddressDataType.java @@ -20,6 +20,7 @@ import ghidra.docking.settings.SettingsDefinition; import ghidra.program.model.address.*; import ghidra.program.model.lang.ProcessorContext; import ghidra.program.model.mem.MemBuffer; +import ghidra.util.DataConverter; import ghidra.util.classfinder.ClassTranslator; /** @@ -105,21 +106,7 @@ public class ShiftedAddressDataType extends BuiltIn { return null; } - boolean isBigEndian = buf.isBigEndian(); // ENDIAN.isBigEndian(settings, buf); - - if (!isBigEndian) { - byte[] flipped = new byte[size]; - for (int i = 0; i < size; i++) { - flipped[i] = bytes[size - i - 1]; - } - bytes = flipped; - } - - // Use long when possible - long val = 0; - for (byte b : bytes) { - val = (val << 8) + (b & 0x0ffL); - } + long val = DataConverter.getInstance(buf.isBigEndian()).getValue(bytes, size); val = val << shift; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/ByteMemBufferImpl.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/ByteMemBufferImpl.java index 882f632ae9..dc6dee7627 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/ByteMemBufferImpl.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/ByteMemBufferImpl.java @@ -18,8 +18,7 @@ package ghidra.program.model.mem; import java.math.BigInteger; import ghidra.program.model.address.Address; -import ghidra.util.GhidraBigEndianDataConverter; -import ghidra.util.GhidraLittleEndianDataConverter; +import ghidra.util.GhidraDataConverter; /** * Simple byte buffer implementation of the memBuffer. Since there is no @@ -29,9 +28,9 @@ import ghidra.util.GhidraLittleEndianDataConverter; */ public class ByteMemBufferImpl implements MemBuffer { + private final GhidraDataConverter converter; private byte[] bytes; private Address addr; - private final boolean isBigEndian; /** * Construct a ByteMemBufferImpl object @@ -42,23 +41,7 @@ public class ByteMemBufferImpl implements MemBuffer { public ByteMemBufferImpl(Address addr, byte[] bytes, boolean isBigEndian) { this.addr = addr; this.bytes = bytes; - this.isBigEndian = isBigEndian; - } - - /** - * Convenience constructor using varargs for specifying byte values. - * @param addr the address to associate with the bytes - * @param isBigEndian true for BigEndian, false for LittleEndian. - * @param byteValues varargs for specifying the individual byte values. The int argument - * will be truncated to a byte value. - */ - public ByteMemBufferImpl(Address addr, boolean isBigEndian, int... byteValues) { - this.addr = addr; - this.isBigEndian = isBigEndian; - bytes = new byte[byteValues.length]; - for (int i = 0; i < bytes.length; i++) { - bytes[i] = (byte) byteValues[i]; - } + this.converter = GhidraDataConverter.getInstance(isBigEndian); } /** @@ -96,42 +79,30 @@ public class ByteMemBufferImpl implements MemBuffer { System.arraycopy(bytes, offset, b, 0, len); return len; } - + @Override public boolean isBigEndian() { - return isBigEndian; + return converter.isBigEndian(); } @Override public short getShort(int offset) throws MemoryAccessException { - if (isBigEndian) { - return GhidraBigEndianDataConverter.INSTANCE.getShort(this, offset); - } - return GhidraLittleEndianDataConverter.INSTANCE.getShort(this, offset); + return converter.getShort(this, offset); } @Override public int getInt(int offset) throws MemoryAccessException { - if (isBigEndian) { - return GhidraBigEndianDataConverter.INSTANCE.getInt(this, offset); - } - return GhidraLittleEndianDataConverter.INSTANCE.getInt(this, offset); + return converter.getInt(this, offset); } @Override public long getLong(int offset) throws MemoryAccessException { - if (isBigEndian) { - return GhidraBigEndianDataConverter.INSTANCE.getLong(this, offset); - } - return GhidraLittleEndianDataConverter.INSTANCE.getLong(this, offset); + return converter.getLong(this, offset); } @Override public BigInteger getBigInteger(int offset, int size, boolean signed) throws MemoryAccessException { - if (isBigEndian) { - return GhidraBigEndianDataConverter.INSTANCE.getBigInteger(this, offset, size, signed); - } - return GhidraLittleEndianDataConverter.INSTANCE.getBigInteger(this, offset, size, signed); + return converter.getBigInteger(this, offset, size, signed); } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/util/GhidraDataConverter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/util/GhidraDataConverter.java index 3bfa5a3dc1..df8b22596d 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/util/GhidraDataConverter.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/util/GhidraDataConverter.java @@ -23,37 +23,54 @@ import ghidra.program.model.mem.MemoryAccessException; public interface GhidraDataConverter extends DataConverter { /** - * Generate a little-endian short value by invoking buf.getBytes at the specified offset. - * @param buf - * @param offset - * @return little-endian short value + * Returns the correct GhidraDataConverter static instance for the requested endian-ness. + * + * @param isBigEndian boolean flag, true means big endian + * @return static GhidraDataConverter instance + */ + public static GhidraDataConverter getInstance(boolean isBigEndian) { + return isBigEndian ? GhidraBigEndianDataConverter.INSTANCE + : GhidraLittleEndianDataConverter.INSTANCE; + } + + /** + * Generate a short value by invoking buf.getBytes at the specified offset. + * + * @param buf MemBuffer source of bytes + * @param offset offset in mem buffer to read + * @return short value * @throws MemoryAccessException if failed to read 2-bytes at the specified offset */ public short getShort(MemBuffer buf, int offset) throws MemoryAccessException; /** - * Generate a little-endian int value by invoking buf.getBytes at the specified offset. - * @param buf - * @param offset - * @return little-endian int value + * Generate a int value by invoking buf.getBytes at the specified offset. + * + * @param buf MemBuffer source of bytes + * @param offset offset in mem buffer to read + * @return int value * @throws MemoryAccessException if failed to read 4-bytes at the specified offset */ public int getInt(MemBuffer buf, int offset) throws MemoryAccessException; /** - * Generate a little-endian long value by invoking buf.getBytes at the specified offset. - * @param buf - * @param offset - * @return little-endian long value + * Generate a long value by invoking buf.getBytes at the specified offset. + * + * @param buf MemBuffer source of bytes + * @param offset offset in mem buffer to read + * @return long value * @throws MemoryAccessException if failed to read 8-bytes at the specified offset */ public long getLong(MemBuffer buf, int offset) throws MemoryAccessException; /** - * Generate a little-endian BigInteger value by invoking buf.getBytes at the specified offset. - * @param buf - * @param offset - * @return little-endian BigInteger value + * Generate a BigInteger value by invoking buf.getBytes at the specified offset. + * + * @param buf MemBuffer source of bytes + * @param offset offset in mem buffer to read + * @param size number of bytes + * @param signed boolean flag + * @return BigInteger value * @throws MemoryAccessException if failed to read specified number of bytes * at the specified offset */ diff --git a/Ghidra/Framework/SoftwareModeling/src/test.slow/java/ghidra/program/model/data/FloatDataTypeTest.java b/Ghidra/Framework/SoftwareModeling/src/test.slow/java/ghidra/program/model/data/FloatDataTypeTest.java index b5746ebad9..2f91569cab 100644 --- a/Ghidra/Framework/SoftwareModeling/src/test.slow/java/ghidra/program/model/data/FloatDataTypeTest.java +++ b/Ghidra/Framework/SoftwareModeling/src/test.slow/java/ghidra/program/model/data/FloatDataTypeTest.java @@ -15,21 +15,19 @@ */ package ghidra.program.model.data; -import static org.junit.Assert.assertEquals; +import static org.junit.Assert.*; import org.junit.Test; import generic.test.AbstractGTest; import ghidra.program.model.mem.ByteMemBufferImpl; +import ghidra.util.LittleEndianDataConverter; public class FloatDataTypeTest extends AbstractGTest { private byte[] getBytes(long value, int size) { byte[] bytes = new byte[size]; - for (int i = 0; i < size; i++) { - bytes[i] = (byte) value; - value >>= 8; - } + LittleEndianDataConverter.INSTANCE.getBytes(value, size, bytes, 0); return bytes; } diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/ArrayStringableTest.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/ArrayStringableTest.java index 5575159165..c0bc34dee0 100644 --- a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/ArrayStringableTest.java +++ b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/ArrayStringableTest.java @@ -26,12 +26,8 @@ import ghidra.program.model.mem.ByteMemBufferImpl; public class ArrayStringableTest extends AbstractGTest { private ByteMemBufferImpl mb(boolean isBE, int... values) { - byte[] bytes = new byte[values.length]; - for (int i = 0; i < values.length; i++) { - bytes[i] = (byte) values[i]; - } GenericAddressSpace gas = new GenericAddressSpace("test", 32, AddressSpace.TYPE_RAM, 1); - return new ByteMemBufferImpl(gas.getAddress(0), bytes, isBE); + return new ByteMemBufferImpl(gas.getAddress(0), bytes(values), isBE); } private SettingsBuilder newset() { diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/BitFieldDataTypeTest.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/BitFieldDataTypeTest.java index fa3849e927..33cfb26aec 100644 --- a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/BitFieldDataTypeTest.java +++ b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/BitFieldDataTypeTest.java @@ -15,7 +15,7 @@ */ package ghidra.program.model.data; -import static org.junit.Assert.assertEquals; +import static org.junit.Assert.*; import org.junit.Test; @@ -157,8 +157,9 @@ public class BitFieldDataTypeTest extends AbstractGTest { assertEquals("5", getDecimalRepresentation(unsignedBitField(4, 0), 0x55)); } - private String getRepresentation(BitFieldDataType bitField, int... bytes) throws Exception { - MemBuffer membuf = membuf(bytes); + private String getRepresentation(BitFieldDataType bitField, int... unsignedBytes) + throws Exception { + MemBuffer membuf = membuf(unsignedBytes); return bitField.getRepresentation(membuf, null, 4); } @@ -184,8 +185,8 @@ public class BitFieldDataTypeTest extends AbstractGTest { return new BitFieldDataType(UnsignedIntegerDataType.dataType, size, offset); } - private MemBuffer membuf(int... bytes) throws Exception { - return new ByteMemBufferImpl(null, true, bytes); + private MemBuffer membuf(int... unsignedBytes) throws Exception { + return new ByteMemBufferImpl(null, bytes(unsignedBytes), true); } } diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/CharDataTypesRenderTest.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/CharDataTypesRenderTest.java index 46af0c325b..619913cacc 100644 --- a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/CharDataTypesRenderTest.java +++ b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/CharDataTypesRenderTest.java @@ -36,12 +36,8 @@ import ghidra.program.model.mem.ByteMemBufferImpl; public class CharDataTypesRenderTest extends AbstractGTest { private ByteMemBufferImpl mb(boolean isBE, int... values) { - byte[] bytes = new byte[values.length]; - for (int i = 0; i < values.length; i++) { - bytes[i] = (byte) values[i]; - } GenericAddressSpace gas = new GenericAddressSpace("test", 32, AddressSpace.TYPE_RAM, 1); - return new ByteMemBufferImpl(gas.getAddress(0), bytes, isBE); + return new ByteMemBufferImpl(gas.getAddress(0), bytes(values), isBE); } private SettingsBuilder newset() { diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/Float10DataTypeTest.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/Float10DataTypeTest.java index 6884b47b34..6ec505aae9 100644 --- a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/Float10DataTypeTest.java +++ b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/Float10DataTypeTest.java @@ -29,53 +29,53 @@ public class Float10DataTypeTest extends AbstractGTest { @Test public void testGetValue() { - byte[] bytes = new byte[] { 0x7f, (byte) 0xff, 0, 0, 0, 0, 0, 0, 0, 0 }; // 0x7fff0000000000000000 = +infinity + byte[] bytes = bytes(0x7f, 0xff, 0, 0, 0, 0, 0, 0, 0, 0); // 0x7fff0000000000000000 = +infinity Object value = Float10DataType.dataType.getValue(new ByteMemBufferImpl(null, bytes, true), null, 10); Assert.assertEquals(FloatFormat.BIG_POSITIVE_INFINITY, value); - bytes = new byte[] { (byte) 0xff, (byte) 0xff, 0, 0, 0, 0, 0, 0, 0, 0 }; // 0xffff0000000000000000 = -infinity + bytes = bytes(0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0); // 0xffff0000000000000000 = -infinity value = Float10DataType.dataType.getValue(new ByteMemBufferImpl(null, bytes, true), null, 10); Assert.assertEquals(FloatFormat.BIG_NEGATIVE_INFINITY, value); - bytes = new byte[] { (byte) 0x7f, (byte) 0xff, (byte) 0x80, 0, 0, 0, 0, 0, 0, 0 }; // 0x7fff8000000000000000 = NaN + bytes = bytes(0x7f, 0xff, 0x80, 0, 0, 0, 0, 0, 0, 0); // 0x7fff8000000000000000 = NaN value = Float10DataType.dataType.getValue(new ByteMemBufferImpl(null, bytes, true), null, 10); Assert.assertEquals(FloatFormat.BIG_NaN, value); // Really small values - bytes = new byte[] { 0, 1, (byte) 0x80, 0, 0, 0, 0, 0, 0, 0 }; // 0x00018000000000000000 = approaches 0 + bytes = bytes(0, 1, 0x80, 0, 0, 0, 0, 0, 0, 0); // 0x00018000000000000000 = approaches 0 value = Float10DataType.dataType.getValue(new ByteMemBufferImpl(null, bytes, true), null, 10); Assert.assertEquals("5.04315471466814026E-4932", value.toString()); - bytes = new byte[] { (byte) 0x80, 1, (byte) 0x80, 0, 0, 0, 0, 0, 0, 0 }; // 0x00018000000000000000 = approaches 0 + bytes = bytes(0x80, 1, 0x80, 0, 0, 0, 0, 0, 0, 0); // 0x00018000000000000000 = approaches 0 value = Float10DataType.dataType.getValue(new ByteMemBufferImpl(null, bytes, true), null, 10); Assert.assertEquals("-5.04315471466814026E-4932", value.toString()); // Really big values - bytes = new byte[] { (byte) 0x7f, (byte) 0xfe, (byte) 0x80, 0, 0, 0, 0, 0, 0, 0 }; // 0x7ffe8000000000000000 = approaches +infinity + bytes = bytes(0x7f, 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0); // 0x7ffe8000000000000000 = approaches +infinity value = Float10DataType.dataType.getValue(new ByteMemBufferImpl(null, bytes, true), null, 10); Assert.assertEquals("8.92298621517923824E+4931", value.toString()); - bytes = new byte[] { (byte) 0xff, (byte) 0xfe, (byte) 0x80, 0, 0, 0, 0, 0, 0, 0 }; // 0x7ffe8000000000000000 = approaches -infinity + bytes = bytes(0xff, 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0); // 0x7ffe8000000000000000 = approaches -infinity value = Float10DataType.dataType.getValue(new ByteMemBufferImpl(null, bytes, true), null, 10); Assert.assertEquals("-8.92298621517923824E+4931", value.toString()); // Values within the range of Double - bytes = new byte[] { 0x40, 1, 0x20, 0, 0, 0, 0, 0, 0, 0 }; // 0x40002000000000000000 = approaches -infinity + bytes = bytes(0x40, 1, 0x20, 0, 0, 0, 0, 0, 0, 0); // 0x40002000000000000000 = approaches -infinity value = Float10DataType.dataType.getValue(new ByteMemBufferImpl(null, bytes, true), null, 10); Assert.assertEquals(BigDecimal.valueOf(4.5), value); - bytes = new byte[] { (byte) 0xc0, 1, 0x20, 0, 0, 0, 0, 0, 0, 0 }; // 0x40002000000000000000 = approaches -infinity + bytes = bytes(0xc0, 1, 0x20, 0, 0, 0, 0, 0, 0, 0); // 0x40002000000000000000 = approaches -infinity value = Float10DataType.dataType.getValue(new ByteMemBufferImpl(null, bytes, true), null, 10); Assert.assertEquals(BigDecimal.valueOf(-4.5), value); diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/StringDataTypeTest.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/StringDataTypeTest.java index b9236c20c5..ffedbe1231 100644 --- a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/StringDataTypeTest.java +++ b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/StringDataTypeTest.java @@ -59,12 +59,8 @@ public class StringDataTypeTest extends AbstractGTest { } private ByteMemBufferImpl mb(boolean isBE, int... values) { - byte[] bytes = new byte[values.length]; - for (int i = 0; i < values.length; i++) { - bytes[i] = (byte) values[i]; - } GenericAddressSpace gas = new GenericAddressSpace("test", 32, AddressSpace.TYPE_RAM, 1); - return new ByteMemBufferImpl(gas.getAddress(0), bytes, isBE); + return new ByteMemBufferImpl(gas.getAddress(0), bytes(values), isBE); } private SettingsBuilder newset() { diff --git a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/ViewStringsPluginScreenShots.java b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/ViewStringsPluginScreenShots.java index b5bc175d2f..abe168b13d 100644 --- a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/ViewStringsPluginScreenShots.java +++ b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/ViewStringsPluginScreenShots.java @@ -80,14 +80,6 @@ public class ViewStringsPluginScreenShots extends GhidraScreenShotGenerator { } - private static byte[] bytes(int... intValues) { - byte[] result = new byte[intValues.length]; - for (int i = 0; i < intValues.length; i++) { - result[i] = (byte) (intValues[i]); - } - return result; - } - @Test public void testDefined_String_Table() { ViewStringsProvider provider = showProvider(ViewStringsProvider.class);