Merge remote-tracking branch

'origin/GP-877_dev747368_fixedlen_leb128_datatypes--SQUASHED
This commit is contained in:
Ryan Kurtz
2023-03-08 13:52:09 -05:00
58 changed files with 1361 additions and 1233 deletions
@@ -15,13 +15,10 @@
*/
package ghidra.app.plugin.exceptionhandlers.gcc;
import ghidra.app.plugin.exceptionhandlers.gcc.datatype.SignedLeb128DataType;
import ghidra.app.plugin.exceptionhandlers.gcc.datatype.UnsignedLeb128DataType;
import ghidra.program.model.address.*;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.*;
import ghidra.program.model.scalar.Scalar;
import ghidra.program.util.AddressTranslationException;
/**
@@ -35,9 +32,6 @@ abstract class AbstractDwarfEHDecoder implements DwarfEHDecoder {
protected static DWordDataType DWORD_DATA_TYPE = DWordDataType.dataType;
protected static QWordDataType QWORD_DATA_TYPE = QWordDataType.dataType;
protected static SignedLeb128DataType SLEB_DATA_TYPE = SignedLeb128DataType.dataType;
protected static UnsignedLeb128DataType ULEB_DATA_TYPE = UnsignedLeb128DataType.dataType;
protected final DwarfEHDataApplicationMode appMode;
protected final boolean isIndirect;
@@ -222,70 +216,6 @@ abstract class AbstractDwarfEHDecoder implements DwarfEHDecoder {
return buf.getBytes(buffer, 0);
}
/**
* Reads an unsigned LEB128-encoded value from <code>program</code> at <code>addr</code>
* @param program Program to read from
* @param addr Address to read from
* @throws MemoryAccessException if the data can't be read
*/
protected long read_leb128(Program program, Address addr) throws MemoryAccessException {
UnsignedLeb128DataType uleb = UnsignedLeb128DataType.dataType;
MemBuffer buf = new DumbMemBufferImpl(program.getMemory(), addr);
Scalar scalar =
(Scalar) uleb.getValue(buf, uleb.getDefaultSettings(), uleb.getLength(buf, -1));
return scalar.getUnsignedValue();
}
/**
* Reads an unsigned LEB128-encoded value from <code>program</code> at the address of <code>buf</code>
* @param buf Buffer to read from
* @param length Unused
* @throws MemoryAccessException if the data can't be read
*/
protected long read_leb128(MemBuffer buf, int length) throws MemoryAccessException {
UnsignedLeb128DataType uleb = UnsignedLeb128DataType.dataType;
Scalar scalar =
(Scalar) uleb.getValue(buf, uleb.getDefaultSettings(), uleb.getLength(buf, -1));
return scalar.getUnsignedValue();
}
/**
* Reads a signed LEB128-encoded value from <code>program</code> at <code>addr</code>
* @param program Program to read from
* @param addr Address to read from
* @throws MemoryAccessException if the data can't be read
*/
protected long read_sleb128(Program program, Address addr) throws MemoryAccessException {
SignedLeb128DataType sleb = SignedLeb128DataType.dataType;
MemBuffer buf = new DumbMemBufferImpl(program.getMemory(), addr);
Scalar scalar =
(Scalar) sleb.getValue(buf, sleb.getDefaultSettings(), sleb.getLength(buf, -1));
return scalar.getSignedValue();
}
/**
* Reads a signed LEB128-encoded value from <code>program</code> at the address of <code>buf</code>
* @param buf Buffer to read from
* @param length Number of bytes to read
* @param buffer Destination buffer to read into
* @throws MemoryAccessException if the data can't be read
*/
protected long read_sleb128(MemBuffer buf, int length) throws MemoryAccessException {
SignedLeb128DataType sleb = SignedLeb128DataType.dataType;
Scalar scalar =
(Scalar) sleb.getValue(buf, sleb.getDefaultSettings(), sleb.getLength(buf, -1));
return scalar.getSignedValue();
}
/**
* Get the DWARF-encoded address value as stored by the context
* @param context Stores program location and decode parameters
@@ -373,6 +303,11 @@ abstract class AbstractDwarfEHDecoder implements DwarfEHDecoder {
/**
* Decode an integer value according to parameters stored in the <code>context</code> object.
* <p>
* Implementations should duplicate the result of the call to doDecode in
* {@link DwarfDecodeContext#setDecodedValue(Object, int)}, as well as the underlying length of
* the data item that was decoded.
*
* @param context Stores program location and decode parameters
* @return the integer value
* @throws MemoryAccessException if the data can't be read
@@ -18,15 +18,13 @@ package ghidra.app.plugin.exceptionhandlers.gcc;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import ghidra.app.plugin.exceptionhandlers.gcc.datatype.SignedLeb128DataType;
import ghidra.app.plugin.exceptionhandlers.gcc.datatype.UnsignedLeb128DataType;
import ghidra.app.util.bin.LEB128Info;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.VoidDataType;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.*;
import ghidra.program.model.scalar.Scalar;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.model.mem.MemoryBufferImpl;
/**
* Generate instances of DwarfEHDecoder suitable for various pointer-encodings.
@@ -235,25 +233,15 @@ public class DwarfDecoderFactory {
Program program = context.getProgram();
Address addr = context.getAddress();
MemBuffer buf = new DumbMemBufferImpl(program.getMemory(), addr);
UnsignedLeb128DataType uleb = UnsignedLeb128DataType.dataType;
int numAvailBytes = uleb.getLength(buf, -1);
Scalar scalar = (Scalar) uleb.getValue(buf, uleb.getDefaultSettings(), numAvailBytes);
long offset = scalar.getUnsignedValue();
int readLen = uleb.getLength(buf, numAvailBytes);
context.setDecodedValue(offset, readLen);
return offset;
LEB128Info uleb128 = GccAnalysisUtils.readULEB128Info(program, addr);
context.setDecodedValue(uleb128.asLong(), uleb128.getLength());
return uleb128.asLong();
}
@Override
public DataType getDataType(Program program) {
return ULEB_DATA_TYPE;
return UnsignedLeb128DataType.dataType;
}
}
static final class DW_EH_PE_udata2_Decoder extends AbstractUnsignedDwarfEHDecoder {
@@ -407,23 +395,15 @@ public class DwarfDecoderFactory {
Program program = context.getProgram();
Address addr = context.getAddress();
MemBuffer buf = new DumbMemBufferImpl(program.getMemory(), addr);
SignedLeb128DataType sleb = SignedLeb128DataType.dataType;
LEB128Info sleb128 = GccAnalysisUtils.readSLEB128Info(program, addr);
context.setDecodedValue(sleb128.asLong(), sleb128.getLength());
int numAvailBytes = sleb.getLength(buf, -1);
Scalar scalar = (Scalar) sleb.getValue(buf, sleb.getDefaultSettings(), numAvailBytes);
long offset = scalar.getSignedValue();
int readLen = sleb.getLength(buf, numAvailBytes);
context.setDecodedValue(offset, readLen);
return offset;
return sleb128.asLong();
}
@Override
public DataType getDataType(Program program) {
return SLEB_DATA_TYPE;
return SignedLeb128DataType.dataType;
}
}
@@ -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.
@@ -16,12 +15,12 @@
*/
package ghidra.app.plugin.exceptionhandlers.gcc;
import ghidra.app.plugin.exceptionhandlers.gcc.datatype.SignedLeb128DataType;
import ghidra.app.plugin.exceptionhandlers.gcc.datatype.UnsignedLeb128DataType;
import java.io.IOException;
import ghidra.app.util.bin.*;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.*;
import ghidra.program.model.scalar.Scalar;
import ghidra.program.model.mem.MemoryAccessException;
/**
* Utility methods for use by the gcc exception handling analysis.
@@ -79,62 +78,49 @@ public class GccAnalysisUtils {
* @param buffer the array to save the bytes that were read.
* @throws MemoryAccessException if the expected number of bytes can't be read.
*/
public static void readBytes(Program program, Address addr, byte[] buffer) throws MemoryAccessException {
public static void readBytes(Program program, Address addr, byte[] buffer)
throws MemoryAccessException {
program.getMemory().getBytes(addr, buffer);
}
/**
* Reads an unsigned little endian base 128 integer from memory.
*
* @param program the program with memory to be read.
* @param addr the address in memory to begin reading the unsigned LEB128.
* @return the unsigned LEB128 integer.
* @return {@link LEB128Info} (value + metadata)
*/
public static long readULEB128(Program program, Address addr) {
UnsignedLeb128DataType uleb = UnsignedLeb128DataType.dataType;
MemBuffer buf = new DumbMemBufferImpl(program.getMemory(), addr);
Scalar scalar = (Scalar) uleb.getValue(buf, uleb.getDefaultSettings(), uleb.getLength(buf, -1));
return scalar.getUnsignedValue();
}
/**
* Gets the size of an unsigned little endian base 128 integer.
* @param program the program with memory to be read.
* @param addr the address in memory to begin reading the unsigned LEB128.
* @return the length of the unsigned LEB128 integer.
*/
public static int getULEB128Length(Program program, Address addr) {
UnsignedLeb128DataType uleb = UnsignedLeb128DataType.dataType;
MemBuffer buf = new DumbMemBufferImpl(program.getMemory(), addr);
return uleb.getLength(buf, -1);
public static LEB128Info readULEB128Info(Program program, Address addr)
throws MemoryAccessException {
LEB128Info uleb128 = readLEB128Info(program, addr, false);
return uleb128;
}
/**
* Reads an signed little endian base 128 integer from memory.
*
* @param program the program with memory to be read.
* @param addr the address in memory to begin reading the signed LEB128.
* @return the signed LEB128 integer.
* @return {@link LEB128Info} (value + metadata)
*/
public static long readSLEB128(Program program, Address addr) {
SignedLeb128DataType sleb = SignedLeb128DataType.dataType;
MemBuffer buf = new DumbMemBufferImpl(program.getMemory(), addr);
Scalar scalar = (Scalar) sleb.getValue(buf, sleb.getDefaultSettings(), sleb.getLength(buf, -1));
return scalar.getUnsignedValue();
public static LEB128Info readSLEB128Info(Program program, Address addr)
throws MemoryAccessException {
LEB128Info sleb128 = readLEB128Info(program, addr, true);
return sleb128;
}
/**
* Gets the size of a signed little endian base 128 integer.
* @param program the program with memory to be read.
* @param addr the address in memory to begin reading the signed LEB128.
* @return the length of the signed LEB128 integer.
*/
public static int getSLEB128Length(Program program, Address addr) {
SignedLeb128DataType sleb = SignedLeb128DataType.dataType;
private static LEB128Info readLEB128Info(Program program, Address addr, boolean isSigned)
throws MemoryAccessException {
try (MemoryByteProvider mbp =
new MemoryByteProvider(program.getMemory(), addr.getAddressSpace())) {
BinaryReader br = new BinaryReader(mbp, !program.getMemory().isBigEndian());
br.setPointerIndex(addr.getOffset());
MemBuffer buf = new DumbMemBufferImpl(program.getMemory(), addr);
return sleb.getLength(buf, -1);
return LEB128Info.readValue(br, isSigned);
}
catch (IOException e) {
throw new MemoryAccessException("Error reading LEB128 value at " + addr.toString(), e);
}
}
}
@@ -1,168 +0,0 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.exceptionhandlers.gcc.datatype;
import ghidra.docking.settings.*;
import ghidra.program.model.data.*;
import ghidra.program.model.mem.MemBuffer;
import ghidra.program.model.scalar.Scalar;
/**
* LEB128 is an integer compression system resulting in variable-length byte sequence.
* An abstract base class for a little endian base 128 integer data type.
*/
public abstract class AbstractLeb128DataType extends BuiltIn implements Dynamic {
/** The maximum length in bytes of a leb128 data type. */
public static final int MAX_LEB128_ENCODED_VALUE_LEN = 8;
private static final FormatSettingsDefinition FORMAT = FormatSettingsDefinition.DEF_HEX;
private static final PaddingSettingsDefinition PADDING = PaddingSettingsDefinition.DEF;
private static SettingsDefinition[] SETTINGS_DEFS = { FORMAT, PADDING };
private final boolean signed;
/**
* Base constructor for a little endian based 128 data type.
* @param name name of the leb128 data type that extends this class.
* @param signed true if it is signed. false if unsigned.
* @param dtm the data type manager to associate with this data type.
*/
public AbstractLeb128DataType(String name, boolean signed, DataTypeManager dtm) {
super(null, name, dtm);
this.signed = signed;
}
@Override
protected SettingsDefinition[] getBuiltInSettingsDefinitions() {
return SETTINGS_DEFS;
}
@Override
public String getDescription() {
return "Dwarf LEB128-Encoded Number";
}
@Override
public int getLength() {
return -1;
}
@Override
public int getLength(MemBuffer buf, int maxLength) {
if (maxLength < 1 || maxLength > MAX_LEB128_ENCODED_VALUE_LEN) {
maxLength = MAX_LEB128_ENCODED_VALUE_LEN;
}
byte[] data = new byte[maxLength];
int availBytes = buf.getBytes(data, 0);
int numRead = 0;
byte curByte = 0;
while ((numRead < availBytes) && (numRead < data.length)) {
curByte = data[numRead];
numRead++;
if ((curByte & 0x80) == 0) {
break; // End of LEB128.
}
}
return numRead;
}
@Override
public Object getValue(MemBuffer buf, Settings settings, int length) {
byte[] data = new byte[length];
if (buf.getBytes(data, 0) != length) {
return null;
}
int numRead = 0;
int shift = 0;
byte curByte = 0;
long val = 0;
if (data.length >= 1) {
do {
curByte = data[numRead];
numRead++;
val |= ((curByte & 0x7f) << shift);
shift += 7;
}
while ((curByte & 0x80) != 0 && numRead < data.length);
if (signed && ((curByte & 0x40) != 0)) {
// val |= (-1 << (shift - 7)) << 7;
val |= -1 << shift;
}
}
return new Scalar(numRead * 8, val, signed);
}
@Override
public String getRepresentation(MemBuffer buf, Settings settings, int length) {
int format = FORMAT.getFormat(settings);
boolean padded = PADDING.isPadded(settings);
int size = getLength(buf, length);
if (size <= 0 && length <= 0) {
return "??";
}
Scalar val = (Scalar) getValue(buf, settings, length);
if (val == null) {
return "??";
}
int radix;
String prefix = "";
String postfix = "";
switch (format) {
default:
case FormatSettingsDefinition.HEX:
radix = 16;
postfix = "h";
break;
case FormatSettingsDefinition.DECIMAL:
radix = 10;
break;
case FormatSettingsDefinition.BINARY:
radix = 2;
postfix = "b";
break;
case FormatSettingsDefinition.OCTAL:
radix = 8;
postfix = "o";
break;
}
String valStr = val.toString(radix, padded, true /* showSign */, prefix, "");
return valStr.toUpperCase() + postfix;
}
@Override
public DataType getReplacementBaseType() {
return ByteDataType.dataType;
}
@Override
public boolean canSpecifyLength() {
return false;
}
}
@@ -97,7 +97,7 @@ public class EhFrameHeaderSection {
DwarfEHDecoder fdeCountDecoder = getFdeCountDecoder(eh_frame_hdr);
Address fdeCountAddress = curAddress;
curAddress = processEncodedFdeCount(fdeCountAddress, fdeCountDecoder);
curAddress = markupEncodedFdeCount(fdeCountAddress, fdeCountDecoder);
int fdeTableCnt = getFdeTableCount(fdeCountAddress, curMemBlock, fdeCountDecoder);
if (fdeTableCnt > 0) {
@@ -113,8 +113,12 @@ public class EhFrameHeaderSection {
* @param curAddress address of the FDE count field
* @param fdeDecoder decoder to use in determining data type for this field
* @return the next address after the FDE count field
* @throws MemoryAccessException
*/
private Address processEncodedFdeCount(Address curAddress, DwarfEHDecoder fdeDecoder) {
private Address markupEncodedFdeCount(Address curAddress, DwarfEHDecoder fdeDecoder)
throws MemoryAccessException {
DwarfDecodeContext ctx = new DwarfDecodeContext(program, curAddress);
long unused = fdeDecoder.decode(ctx); // we only parse to get the length of the integer
/* Create the Encoded FDE count member */
DataType encDataType = fdeDecoder.getDataType(program);
@@ -126,7 +130,7 @@ public class EhFrameHeaderSection {
new SetCommentCmd(curAddress, CodeUnit.EOL_COMMENT, "Encoded FDE count");
commentCmd.applyTo(program);
curAddress = curAddress.add(encDataType.getLength());
curAddress = curAddress.add(ctx.getEncodedLength());
return curAddress;
}
@@ -153,8 +157,8 @@ public class EhFrameHeaderSection {
/* Create the encoded Exception Handler Frame Pointer */
DwarfEHDecoder frmPtrDecoder =
DwarfDecoderFactory.getDecoder(eh_frame_hdr.getEh_FramePtrEncoding());
Address frmPtrAddr =
frmPtrDecoder.decodeAddress(new DwarfDecodeContext(program, curAddress, curMemBlock));
DwarfDecodeContext ctx = new DwarfDecodeContext(program, curAddress, curMemBlock);
Address frmPtrAddr = frmPtrDecoder.decodeAddress(ctx);
program.getReferenceManager().addMemoryReference(curAddress, frmPtrAddr, RefType.DATA,
SourceType.ANALYSIS, 0);
@@ -168,7 +172,7 @@ public class EhFrameHeaderSection {
new SetCommentCmd(curAddress, CodeUnit.EOL_COMMENT, "Encoded eh_frame_ptr");
commentCmd.applyTo(program);
curAddress = curAddress.add(frmPtrDataType.getLength());
curAddress = curAddress.add(ctx.getEncodedLength());
return curAddress;
}
@@ -21,13 +21,13 @@ import java.nio.ByteOrder;
import ghidra.app.cmd.comments.SetCommentCmd;
import ghidra.app.cmd.data.CreateArrayCmd;
import ghidra.app.plugin.exceptionhandlers.gcc.*;
import ghidra.app.plugin.exceptionhandlers.gcc.datatype.*;
import ghidra.app.plugin.exceptionhandlers.gcc.datatype.DwarfEncodingModeDataType;
import ghidra.app.util.bin.LEB128Info;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressOutOfBoundsException;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.*;
import ghidra.program.model.mem.*;
import ghidra.program.model.scalar.Scalar;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.model.symbol.RefType;
import ghidra.program.model.symbol.SourceType;
import ghidra.util.Msg;
@@ -280,18 +280,16 @@ public class Cie extends GccAnalysisClass {
*/
String comment = "(CIE) Code Alignment";
UnsignedLeb128DataType uleb = UnsignedLeb128DataType.dataType;
MemBuffer buf = new DumbMemBufferImpl(program.getMemory(), addr);
int encodedLen = uleb.getLength(buf, AbstractLeb128DataType.MAX_LEB128_ENCODED_VALUE_LEN);
Object augLenObj = uleb.getValue(buf, uleb.getDefaultSettings(), encodedLen);
LEB128Info uleb128 = GccAnalysisUtils.readULEB128Info(program, addr);
codeAlignFactor = (int) ((Scalar) augLenObj).getUnsignedValue();
codeAlignFactor = (int) uleb128.asLong();
createAndCommentData(program, addr, uleb, comment, CodeUnit.EOL_COMMENT);
createAndCommentData(program, addr, UnsignedLeb128DataType.dataType, comment,
CodeUnit.EOL_COMMENT);
curSize += encodedLen;
curSize += uleb128.getLength();
return addr.add(encodedLen);
return addr.add(uleb128.getLength());
}
/**
@@ -309,18 +307,16 @@ public class Cie extends GccAnalysisClass {
*/
String comment = "(CIE) Data Alignment";
SignedLeb128DataType sleb = SignedLeb128DataType.dataType;
MemBuffer buf = new DumbMemBufferImpl(program.getMemory(), addr);
int encodedLen = sleb.getLength(buf, -1);
Object alignObj = sleb.getValue(buf, sleb.getDefaultSettings(), encodedLen);
LEB128Info sleb128 = GccAnalysisUtils.readSLEB128Info(program, addr);
dataAlignFactor = (int) ((Scalar) alignObj).getSignedValue();
dataAlignFactor = (int) sleb128.asLong();
createAndCommentData(program, addr, sleb, comment, CodeUnit.EOL_COMMENT);
createAndCommentData(program, addr, SignedLeb128DataType.dataType, comment,
CodeUnit.EOL_COMMENT);
curSize += encodedLen;
curSize += sleb128.getLength();
return addr.add(encodedLen);
return addr.add(sleb128.getLength());
}
/**
@@ -349,16 +345,14 @@ public class Cie extends GccAnalysisClass {
}
else {
UnsignedLeb128DataType uleb = UnsignedLeb128DataType.dataType;
encodedDt = uleb;
LEB128Info uleb128 = GccAnalysisUtils.readULEB128Info(program, addr);
encodedDt = UnsignedLeb128DataType.dataType;
// TODO Instead use the following data type once it can correctly determine the register.
// encodedDt = new DwarfRegisterLeb128DataType();
MemBuffer buf = new DumbMemBufferImpl(program.getMemory(), addr);
encodedLen = uleb.getLength(buf, AbstractLeb128DataType.MAX_LEB128_ENCODED_VALUE_LEN);
Object augLenObj = uleb.getValue(buf, uleb.getDefaultSettings(), encodedLen);
returnAddrRegister = (int) ((Scalar) augLenObj).getUnsignedValue();
encodedLen = uleb128.getLength();
returnAddrRegister = (int) uleb128.asLong();
}
@@ -383,18 +377,16 @@ public class Cie extends GccAnalysisClass {
*/
String comment = "(CIE) Augmentation Data Length";
UnsignedLeb128DataType uleb = UnsignedLeb128DataType.dataType;
MemBuffer buf = new DumbMemBufferImpl(program.getMemory(), addr);
int encodedLen = uleb.getLength(buf, AbstractLeb128DataType.MAX_LEB128_ENCODED_VALUE_LEN);
Object augLenObj = uleb.getValue(buf, uleb.getDefaultSettings(), encodedLen);
LEB128Info uleb128 = GccAnalysisUtils.readULEB128Info(program, addr);
augmentationDataLength = (int) ((Scalar) augLenObj).getUnsignedValue();
augmentationDataLength = (int) uleb128.asLong();
createAndCommentData(program, addr, uleb, comment, CodeUnit.EOL_COMMENT);
createAndCommentData(program, addr, UnsignedLeb128DataType.dataType, comment,
CodeUnit.EOL_COMMENT);
curSize += encodedLen;
curSize += uleb128.getLength();
return addr.add(encodedLen);
return addr.add(uleb128.getLength());
}
@@ -18,6 +18,7 @@ package ghidra.app.plugin.exceptionhandlers.gcc.structures.ehFrame;
import ghidra.app.cmd.comments.SetCommentCmd;
import ghidra.app.plugin.exceptionhandlers.gcc.GccAnalysisUtils;
import ghidra.app.util.bin.LEB128Info;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.CodeUnit;
import ghidra.program.model.listing.Program;
@@ -99,6 +100,7 @@ public class DwarfCallFrameOpcodeParser {
int operand1Len;
long operand2;
int operand2Len;
LEB128Info uleb128;
while (curr.compareTo(limit) < 0) {
@@ -124,8 +126,10 @@ public class DwarfCallFrameOpcodeParser {
break;
case DW_CFA_offset:
primaryOpcode = true;
operand1 = GccAnalysisUtils.readULEB128(program, curr);
operand1Len = GccAnalysisUtils.getULEB128Length(program, curr);
uleb128 = GccAnalysisUtils.readULEB128Info(program, curr);
operand1 = uleb128.asLong();
operand1Len = uleb128.getLength();
curr = curr.add(operand1Len);
@@ -177,12 +181,14 @@ public class DwarfCallFrameOpcodeParser {
break;
case DW_CFA_offset_extended:
operand1 = GccAnalysisUtils.readULEB128(program, curr);
operand1Len = GccAnalysisUtils.getULEB128Length(program, curr);
uleb128 = GccAnalysisUtils.readULEB128Info(program, curr);
operand1 = uleb128.asLong();
operand1Len = uleb128.getLength();
curr = curr.add(operand1Len);
operand2 = GccAnalysisUtils.readULEB128(program, curr);
operand2Len = GccAnalysisUtils.getULEB128Length(program, curr);
uleb128 = GccAnalysisUtils.readULEB128Info(program, curr);
operand2 = uleb128.asLong();
operand2Len = uleb128.getLength();
curr = curr.add(operand2Len);
sb.append(
@@ -190,36 +196,41 @@ public class DwarfCallFrameOpcodeParser {
break;
case DW_CFA_restore_extended:
operand1 = GccAnalysisUtils.readULEB128(program, curr);
operand1Len = GccAnalysisUtils.getULEB128Length(program, curr);
uleb128 = GccAnalysisUtils.readULEB128Info(program, curr);
operand1 = uleb128.asLong();
operand1Len = uleb128.getLength();
curr = curr.add(operand1Len);
sb.append("DW_CFA_restore_extended reg[" + operand1 + "]");
break;
case DW_CFA_undefined:
operand1 = GccAnalysisUtils.readULEB128(program, curr);
operand1Len = GccAnalysisUtils.getULEB128Length(program, curr);
uleb128 = GccAnalysisUtils.readULEB128Info(program, curr);
operand1 = uleb128.asLong();
operand1Len = uleb128.getLength();
curr = curr.add(operand1Len);
sb.append("DW_CFA_undefined reg[" + operand1 + "]");
break;
case DW_CFA_same_value:
operand1 = GccAnalysisUtils.readULEB128(program, curr);
operand1Len = GccAnalysisUtils.getULEB128Length(program, curr);
uleb128 = GccAnalysisUtils.readULEB128Info(program, curr);
operand1 = uleb128.asLong();
operand1Len = uleb128.getLength();
curr = curr.add(operand1Len);
sb.append("DW_CFA_same_value reg[" + operand1 + "]");
break;
case DW_CFA_register:
operand1 = GccAnalysisUtils.readULEB128(program, curr);
operand1Len = GccAnalysisUtils.getULEB128Length(program, curr);
uleb128 = GccAnalysisUtils.readULEB128Info(program, curr);
operand1 = uleb128.asLong();
operand1Len = uleb128.getLength();
curr = curr.add(operand1Len);
operand2 = GccAnalysisUtils.readULEB128(program, curr);
operand2Len = GccAnalysisUtils.getULEB128Length(program, curr);
uleb128 = GccAnalysisUtils.readULEB128Info(program, curr);
operand2 = uleb128.asLong();
operand2Len = uleb128.getLength();
curr = curr.add(operand2Len);
sb.append("DW_CFA_register reg[" + operand1 + "] reg[" + operand2 + "]");
@@ -234,28 +245,32 @@ public class DwarfCallFrameOpcodeParser {
break;
case DW_CFA_def_cfa:
operand1 = GccAnalysisUtils.readULEB128(program, curr);
operand1Len = GccAnalysisUtils.getULEB128Length(program, curr);
uleb128 = GccAnalysisUtils.readULEB128Info(program, curr);
operand1 = uleb128.asLong();
operand1Len = uleb128.getLength();
curr = curr.add(operand1Len);
operand2 = GccAnalysisUtils.readULEB128(program, curr);
operand2Len = GccAnalysisUtils.getULEB128Length(program, curr);
uleb128 = GccAnalysisUtils.readULEB128Info(program, curr);
operand2 = uleb128.asLong();
operand2Len = uleb128.getLength();
curr = curr.add(operand2Len);
sb.append("DW_CFA_def_cfa reg[" + operand1 + "] offs[" + operand2 + "]");
break;
case DW_CFA_def_cfa_register:
operand1 = GccAnalysisUtils.readULEB128(program, curr);
operand1Len = GccAnalysisUtils.getULEB128Length(program, curr);
uleb128 = GccAnalysisUtils.readULEB128Info(program, curr);
operand1 = uleb128.asLong();
operand1Len = uleb128.getLength();
curr = curr.add(operand1Len);
sb.append("DW_CFA_def_cfa_register reg[" + operand1 + "]");
break;
case DW_CFA_def_cfa_offset:
operand1 = GccAnalysisUtils.readULEB128(program, curr);
operand1Len = GccAnalysisUtils.getULEB128Length(program, curr);
uleb128 = GccAnalysisUtils.readULEB128Info(program, curr);
operand1 = uleb128.asLong();
operand1Len = uleb128.getLength();
curr = curr.add(operand1Len);
sb.append("DW_CFA_def_cfa_offset offs[" + operand1 + "]");
@@ -266,21 +281,23 @@ public class DwarfCallFrameOpcodeParser {
break;
case DW_CFA_expression:
operand1 = GccAnalysisUtils.readULEB128(program, curr);
operand1Len = GccAnalysisUtils.getULEB128Length(program, curr);
uleb128 = GccAnalysisUtils.readULEB128Info(program, curr);
operand1 = uleb128.asLong();
operand1Len = uleb128.getLength();
curr = curr.add(operand1Len);
sb.append("DW_CFA_expression reg[" + operand1 + "] BLOCK");
break;
case DW_CFA_offset_extended_sf:
operand1 = GccAnalysisUtils.readULEB128(program, curr);
operand1Len = GccAnalysisUtils.getULEB128Length(program, curr);
uleb128 = GccAnalysisUtils.readULEB128Info(program, curr);
operand1 = uleb128.asLong();
operand1Len = uleb128.getLength();
curr = curr.add(operand1Len);
operand2 = GccAnalysisUtils.readULEB128(program, curr);
operand2Len = GccAnalysisUtils.getULEB128Length(program, curr);
uleb128 = GccAnalysisUtils.readULEB128Info(program, curr);
operand2 = uleb128.asLong();
operand2Len = uleb128.getLength();
curr = curr.add(operand2Len);
sb.append("DW_CFA_offset_extended_sf reg[" + operand1 + "] offs[" +
@@ -288,52 +305,60 @@ public class DwarfCallFrameOpcodeParser {
break;
case DW_CFA_def_cfa_sf:
operand1 = GccAnalysisUtils.readULEB128(program, curr);
operand1Len = GccAnalysisUtils.getULEB128Length(program, curr);
uleb128 = GccAnalysisUtils.readULEB128Info(program, curr);
operand1 = uleb128.asLong();
operand1Len = uleb128.getLength();
curr = curr.add(operand1Len);
operand2 = GccAnalysisUtils.readULEB128(program, curr);
operand2Len = GccAnalysisUtils.getULEB128Length(program, curr);
uleb128 = GccAnalysisUtils.readULEB128Info(program, curr);
operand2 = uleb128.asLong();
operand2Len = uleb128.getLength();
curr = curr.add(operand2Len);
sb.append("DW_CFA_def_cfa_sf reg[" + operand1 + "] offs[" + operand2 + "]");
break;
case DW_CFA_def_cfa_offset_sf:
operand1 = GccAnalysisUtils.readULEB128(program, curr);
operand1Len = GccAnalysisUtils.getULEB128Length(program, curr);
uleb128 = GccAnalysisUtils.readULEB128Info(program, curr);
operand1 = uleb128.asLong();
operand1Len = uleb128.getLength();
curr = curr.add(operand1Len);
sb.append("DW_CFA_def_cfa_offset_sf offs[" + operand1 + "]");
break;
case DW_CFA_val_offset:
operand1 = GccAnalysisUtils.readULEB128(program, curr);
operand1Len = GccAnalysisUtils.getULEB128Length(program, curr);
uleb128 = GccAnalysisUtils.readULEB128Info(program, curr);
operand1 = uleb128.asLong();
operand1Len = uleb128.getLength();
curr = curr.add(operand1Len);
operand2 = GccAnalysisUtils.readULEB128(program, curr);
operand2Len = GccAnalysisUtils.getULEB128Length(program, curr);
uleb128 = GccAnalysisUtils.readULEB128Info(program, curr);
operand2 = uleb128.asLong();
operand2Len = uleb128.getLength();
curr = curr.add(operand2Len);
sb.append("DW_CFA_val_offset [" + operand1 + "] [" + operand2 + "]");
break;
case DW_CFA_val_offset_sf:
operand1 = GccAnalysisUtils.readULEB128(program, curr);
operand1Len = GccAnalysisUtils.getULEB128Length(program, curr);
uleb128 = GccAnalysisUtils.readULEB128Info(program, curr);
operand1 = uleb128.asLong();
operand1Len = uleb128.getLength();
curr = curr.add(operand1Len);
operand2 = GccAnalysisUtils.readULEB128(program, curr);
operand2Len = GccAnalysisUtils.getULEB128Length(program, curr);
uleb128 = GccAnalysisUtils.readULEB128Info(program, curr);
operand2 = uleb128.asLong();
operand2Len = uleb128.getLength();
curr = curr.add(operand2Len);
sb.append("DW_CFA_val_offset_sf [" + operand1 + "] [" + operand2 + "]");
break;
case DW_CFA_val_expression:
operand1 = GccAnalysisUtils.readULEB128(program, curr);
operand1Len = GccAnalysisUtils.getULEB128Length(program, curr);
uleb128 = GccAnalysisUtils.readULEB128Info(program, curr);
operand1 = uleb128.asLong();
operand1Len = uleb128.getLength();
curr = curr.add(operand1Len);
sb.append("DW_CFA_val_expression [" + operand1 + "] BLOCK");
@@ -20,16 +20,14 @@ import ghidra.app.cmd.data.CreateArrayCmd;
import ghidra.app.cmd.data.CreateDataCmd;
import ghidra.app.cmd.function.CreateFunctionCmd;
import ghidra.app.plugin.exceptionhandlers.gcc.*;
import ghidra.app.plugin.exceptionhandlers.gcc.datatype.UnsignedLeb128DataType;
import ghidra.app.plugin.exceptionhandlers.gcc.sections.CieSource;
import ghidra.app.plugin.exceptionhandlers.gcc.sections.DebugFrameSection;
import ghidra.app.plugin.exceptionhandlers.gcc.structures.gccexcepttable.LSDATable;
import ghidra.app.util.opinion.ElfLoader;
import ghidra.app.util.bin.LEB128Info;
import ghidra.program.model.address.*;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.*;
import ghidra.program.model.mem.*;
import ghidra.program.model.scalar.Scalar;
import ghidra.program.model.symbol.*;
import ghidra.program.model.util.CodeUnitInsertionException;
import ghidra.util.Msg;
@@ -350,18 +348,16 @@ public class FrameDescriptionEntry extends GccAnalysisClass {
*/
String comment = "(FDE) Augmentation Data Length";
UnsignedLeb128DataType uleb = UnsignedLeb128DataType.dataType;
MemBuffer buf = new DumbMemBufferImpl(program.getMemory(), addr);
int encodedLen = uleb.getLength(buf, -1);
Object augLenObj = uleb.getValue(buf, uleb.getDefaultSettings(), encodedLen);
LEB128Info uleb128 = GccAnalysisUtils.readULEB128Info(program, addr);
intAugmentationDataLength = (int) ((Scalar) augLenObj).getUnsignedValue();
intAugmentationDataLength = (int) uleb128.asLong();
createAndCommentData(program, addr, uleb, comment, CodeUnit.EOL_COMMENT);
createAndCommentData(program, addr, UnsignedLeb128DataType.dataType, comment,
CodeUnit.EOL_COMMENT);
curSize += encodedLen;
curSize += uleb128.getLength();
return addr.add(encodedLen);
return addr.add(uleb128.getLength());
}
/**
@@ -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,15 +16,13 @@
package ghidra.app.plugin.exceptionhandlers.gcc.structures.gccexcepttable;
import ghidra.app.cmd.comments.SetCommentCmd;
import ghidra.app.plugin.exceptionhandlers.gcc.GccAnalysisClass;
import ghidra.app.plugin.exceptionhandlers.gcc.RegionDescriptor;
import ghidra.app.plugin.exceptionhandlers.gcc.datatype.SignedLeb128DataType;
import ghidra.app.plugin.exceptionhandlers.gcc.*;
import ghidra.app.util.bin.LEB128Info;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.SignedLeb128DataType;
import ghidra.program.model.listing.CodeUnit;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.DumbMemBufferImpl;
import ghidra.program.model.mem.MemBuffer;
import ghidra.program.model.scalar.Scalar;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.util.task.TaskMonitor;
/**
@@ -66,8 +63,9 @@ public class LSDAActionRecord extends GccAnalysisClass {
* it as an action record.
* <br>Note: This method must get called before any of the "get..." methods.
* @param address the start (minimum address) of this action record.
* @throws MemoryAccessException
*/
public void create(Address address) {
public void create(Address address) throws MemoryAccessException {
if (address == null) {
throw new IllegalArgumentException("action record's address cannot be null.");
@@ -91,37 +89,28 @@ public class LSDAActionRecord extends GccAnalysisClass {
nextAddress = addr;
}
private Address createTypeFilter(Address addr) {
private Address createTypeFilter(Address addr) throws MemoryAccessException {
String comment = "(LSDA Action Table) Type Filter";
SignedLeb128DataType sleb = SignedLeb128DataType.dataType;
MemBuffer buf = new DumbMemBufferImpl(program.getMemory(), addr);
int encodedLen = sleb.getLength(buf, -1);
Scalar typeFilterObj = (Scalar) sleb.getValue(buf, sleb.getDefaultSettings(), encodedLen);
LEB128Info sleb128 = GccAnalysisUtils.readSLEB128Info(program, addr);
encodedLen = typeFilterObj.bitLength() / 8;
typeFilter = (int) sleb128.asLong();
typeFilter = (int) typeFilterObj.getSignedValue();
createAndCommentData(program, addr, SignedLeb128DataType.dataType, comment,
CodeUnit.EOL_COMMENT);
createAndCommentData(program, addr, sleb, comment, CodeUnit.EOL_COMMENT);
size += sleb128.getLength();
size += encodedLen;
return addr.add(encodedLen);
return addr.add(sleb128.getLength());
}
private Address createNextActionRef(Address addr) {
private Address createNextActionRef(Address addr) throws MemoryAccessException {
String comment = "(LSDA Action Table) Next-Action Reference";
SignedLeb128DataType sleb = SignedLeb128DataType.dataType;
LEB128Info sleb128 = GccAnalysisUtils.readSLEB128Info(program, addr);
MemBuffer buf = new DumbMemBufferImpl(program.getMemory(), addr);
int encodedLen = sleb.getLength(buf, -1);
Scalar nextDispObj = (Scalar) sleb.getValue(buf, sleb.getDefaultSettings(), encodedLen);
encodedLen = nextDispObj.bitLength() / 8;
displacementToNext = (int) nextDispObj.getSignedValue();
displacementToNext = (int) sleb128.asLong();
if (displacementToNext == 0) {
nextActionAddress = Address.NO_ADDRESS;
@@ -130,11 +119,12 @@ public class LSDAActionRecord extends GccAnalysisClass {
nextActionAddress = addr.add(displacementToNext);
}
createAndCommentData(program, addr, sleb, comment, CodeUnit.EOL_COMMENT);
createAndCommentData(program, addr, SignedLeb128DataType.dataType, comment,
CodeUnit.EOL_COMMENT);
size += encodedLen;
size += sleb128.getLength();
return addr.add(encodedLen);
return addr.add(sleb128.getLength());
}
/**
@@ -22,6 +22,7 @@ import ghidra.app.plugin.exceptionhandlers.gcc.RegionDescriptor;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.CodeUnit;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.util.task.TaskMonitor;
/**
@@ -59,8 +60,9 @@ public class LSDAActionTable {
* <br>Note: This method must get called before any of the "get..." methods.
* @param address the start (minimum address) of this action table.
* @param maxAddress the end (maximum address) of this action table.
* @throws MemoryAccessException
*/
public void create(Address address, Address maxAddress) {
public void create(Address address, Address maxAddress) throws MemoryAccessException {
if (address == null) {
throw new IllegalArgumentException("action record's address cannot be null.");
@@ -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.
@@ -18,13 +17,13 @@ package ghidra.app.plugin.exceptionhandlers.gcc.structures.gccexcepttable;
import ghidra.app.cmd.comments.SetCommentCmd;
import ghidra.app.plugin.exceptionhandlers.gcc.*;
import ghidra.app.plugin.exceptionhandlers.gcc.datatype.UnsignedLeb128DataType;
import ghidra.app.util.bin.LEB128Info;
import ghidra.program.model.address.*;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.UnsignedLeb128DataType;
import ghidra.program.model.listing.CodeUnit;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.*;
import ghidra.program.model.scalar.Scalar;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.model.symbol.RefType;
import ghidra.program.model.symbol.SourceType;
import ghidra.util.task.TaskMonitor;
@@ -164,25 +163,20 @@ public class LSDACallSiteRecord extends GccAnalysisClass {
return addr.add(encodedLen);
}
private Address createAction(Address addr) {
private Address createAction(Address addr) throws MemoryAccessException {
String comment = "(LSDA Call Site) Action Table Offset";
UnsignedLeb128DataType uleb = UnsignedLeb128DataType.dataType;
MemBuffer buf = new DumbMemBufferImpl(program.getMemory(), addr);
LEB128Info uleb128 = GccAnalysisUtils.readULEB128Info(program, addr);
int encodedLen = uleb.getLength(buf, -1);
Object actionObj = uleb.getValue(buf, uleb.getDefaultSettings(), encodedLen);
actionOffset = (int) ((Scalar) actionObj).getUnsignedValue();
encodedLen = ((Scalar) actionObj).bitLength() / 8;
actionOffset = (int) uleb128.asLong();
if (actionOffset == 0) {
comment += " (No action -- cleanup)";
}
createAndCommentData(program, addr, uleb, comment, CodeUnit.EOL_COMMENT);
return addr.add(encodedLen);
createAndCommentData(program, addr, UnsignedLeb128DataType.dataType, comment,
CodeUnit.EOL_COMMENT);
return addr.add(uleb128.getLength());
}
/**
@@ -17,14 +17,13 @@ package ghidra.app.plugin.exceptionhandlers.gcc.structures.gccexcepttable;
import ghidra.app.cmd.comments.SetCommentCmd;
import ghidra.app.plugin.exceptionhandlers.gcc.*;
import ghidra.app.plugin.exceptionhandlers.gcc.datatype.*;
import ghidra.app.plugin.exceptionhandlers.gcc.datatype.DwarfEncodingModeDataType;
import ghidra.app.util.bin.LEB128Info;
import ghidra.program.model.address.*;
import ghidra.program.model.data.ByteDataType;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.CodeUnit;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.*;
import ghidra.program.model.scalar.Scalar;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.model.symbol.SourceType;
import ghidra.program.model.symbol.Symbol;
import ghidra.util.task.TaskMonitor;
@@ -41,6 +40,7 @@ public class LSDAHeader extends GccAnalysisClass {
static final int OMITTED_ENCODING_TYPE = 0xFF;
/* Class Members */
private RegionDescriptor region;
@@ -140,7 +140,7 @@ public class LSDAHeader extends GccAnalysisClass {
return addr.add(BYTE_LEN);
}
private Address createTTypeOffset(Address addr) {
private Address createTTypeOffset(Address addr) throws MemoryAccessException {
String comment = "(LSDA) TType Offset";
DwarfEHDecoder decoder = DwarfDecoderFactory.getDecoder(ttypeEncoding);
@@ -149,19 +149,15 @@ public class LSDAHeader extends GccAnalysisClass {
return addr;
}
UnsignedLeb128DataType uleb = UnsignedLeb128DataType.dataType;
MemBuffer buf = new DumbMemBufferImpl(program.getMemory(), addr);
int encodedLen = uleb.getLength(buf, -1);
Object actionObj = uleb.getValue(buf, uleb.getDefaultSettings(), encodedLen);
LEB128Info uleb128 = GccAnalysisUtils.readULEB128Info(program, addr);
// this offset it based from *here*..
ttypeOffset = (int) ((Scalar) actionObj).getUnsignedValue() + curSize;
ttypeOffset = uleb128.asLong() + curSize;
createAndCommentData(program, addr, uleb, comment, CodeUnit.EOL_COMMENT);
createAndCommentData(program, addr, UnsignedLeb128DataType.dataType, comment,
CodeUnit.EOL_COMMENT);
return addr.add(encodedLen);
return addr.add(uleb128.getLength());
}
private Address createCallSiteTableEncoding(Address addr) throws MemoryAccessException {
@@ -175,24 +171,18 @@ public class LSDAHeader extends GccAnalysisClass {
return addr.add(BYTE_LEN);
}
private Address createCallSiteTableLength(Address addr) {
private Address createCallSiteTableLength(Address addr) throws MemoryAccessException {
String comment = "(LSDA) Call Site Table Length";
UnsignedLeb128DataType uleb = UnsignedLeb128DataType.dataType;
LEB128Info uleb128 = GccAnalysisUtils.readULEB128Info(program, addr);
MemBuffer buf = new DumbMemBufferImpl(program.getMemory(), addr);
callSiteTableLength = (int) uleb128.asLong();
int encodedLen = uleb.getLength(buf, AbstractLeb128DataType.MAX_LEB128_ENCODED_VALUE_LEN);
Object lenObj = uleb.getValue(buf, uleb.getDefaultSettings(), encodedLen);
callSiteTableLength = (int) (((Scalar) lenObj).getUnsignedValue());
createAndCommentData(program, addr, uleb, comment, CodeUnit.EOL_COMMENT);
curSize += encodedLen;
return addr.add(encodedLen);
createAndCommentData(program, addr, UnsignedLeb128DataType.dataType, comment,
CodeUnit.EOL_COMMENT);
curSize += uleb128.getLength();
return addr.add(uleb128.getLength());
}
/**
@@ -67,6 +67,9 @@ public class BinaryReader {
* <p>
* When reading from the BinaryReader, use "readNext" methods to consume the location where
* the object was located.
* <p>
* Implementations of this method should not return {@code null}, instead they should
* throw an IOException.
*
* @param reader {@link BinaryReader}
* @return new object
@@ -75,6 +78,26 @@ public class BinaryReader {
T get(BinaryReader reader) throws IOException;
}
/**
* Reads and returns an object from the current position in the specified input stream.
* <p>
*
* @param <T> the type of object that will be returned
*/
public interface InputStreamReaderFunction<T> {
/**
* Reads from the specified input stream and returns a new object instance.
* <p>
* Implementations of this method should not return {@code null}, instead they should
* throw an IOException.
*
* @param is an {@link InputStream} view of the BinaryReader
* @return new object
* @throws IOException if error reading
*/
T get(InputStream is) throws IOException;
}
private final ByteProvider provider;
private DataConverter converter;
private long currentIndex;
@@ -306,6 +329,18 @@ public class BinaryReader {
return currentIndex;
}
/**
* Returns an InputStream that is a live view of the BinaryReader's position.
* <p>
* Any bytes read with the stream will affect the current position of the BinaryReader, and
* any change to the BinaryReader's position will affect the next value the inputstream returns.
*
* @return {@link InputStream}
*/
public InputStream getInputStream() {
return new BinaryReaderInputStream();
}
/**
* Peeks at the next byte without incrementing
* the current index.
@@ -1034,6 +1069,22 @@ public class BinaryReader {
return (int) value;
}
/**
* Reads a variable length / unknown format integer from the current position, using the
* supplied reader function, returning it (if it fits) as a 32 bit java integer.
*
* @param func {@link InputStreamReaderFunction}
* @return signed int32
* @throws IOException if error reading or if the value does not fit into a 32 bit java int
* @throws InvalidDataException if value can not be held in a java integer
*/
public int readNextVarInt(InputStreamReaderFunction<Long> func)
throws IOException, InvalidDataException {
long value = func.get(getInputStream());
ensureInt32s(value);
return (int) value;
}
/**
* Reads a variable length / unknown format unsigned integer from the current position, using
* the supplied reader function, returning it (if it fits) as a 32 bit java integer.
@@ -1050,6 +1101,22 @@ public class BinaryReader {
return (int) value;
}
/**
* Reads a variable length / unknown format unsigned integer from the current position, using
* the supplied reader function, returning it (if it fits) as a 32 bit java integer.
*
* @param func {@link InputStreamReaderFunction}
* @return unsigned int32
* @throws IOException if error reading data
* @throws InvalidDataException if value can not be held in a java integer
*/
public int readNextUnsignedVarIntExact(InputStreamReaderFunction<Long> func)
throws IOException, InvalidDataException {
long value = func.get(getInputStream());
ensureInt32u(value);
return (int) value;
}
/**
* Reads an object from the current position, using the supplied reader function.
*
@@ -1063,12 +1130,25 @@ public class BinaryReader {
return obj;
}
/**
* Reads an object from the current position, using the supplied reader function.
*
* @param <T> type of the object that will be returned
* @param func {@link InputStreamReaderFunction} that will read and return an object
* @return new object of type T
* @throws IOException if error reading
*/
public <T> T readNext(InputStreamReaderFunction<T> func) throws IOException {
T obj = func.get(getInputStream());
return obj;
}
//-------------------------------------------------------------------------------------
private static void ensureInt32u(long value) throws InvalidDataException {
if (value < 0 || value > Integer.MAX_VALUE) {
throw new InvalidDataException(
"Value out of range for positive java 32 bit unsigned int: %s"
.formatted(Long.toUnsignedString(value)));
.formatted(Long.toUnsignedString(value)));
}
}
@@ -1079,4 +1159,17 @@ public class BinaryReader {
}
}
/**
* Adapter between this BinaryReader and a InputStream.
*/
private class BinaryReaderInputStream extends InputStream {
@Override
public int read() throws IOException {
if (!hasNext()) {
return -1;
}
return readNextUnsignedByte();
}
}
}
@@ -0,0 +1,160 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util.bin;
import java.io.IOException;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.LEB128;
import ghidra.program.model.listing.Program;
/**
* Class to hold result of reading a {@link LEB128} value, along with size and position metadata.
*/
public class LEB128Info {
/**
* Reads an unsigned LEB128 value from the BinaryReader and returns a {@link LEB128Info} instance
* that contains the value along with size and position metadata.
* <p>
* @param reader {@link BinaryReader} to read bytes from
* @return a {@link LEB128Info} instance with the read LEB128 value with metadata
* @throws IOException if an I/O error occurs or value is outside the range of a java
* 64 bit int
*/
public static LEB128Info unsigned(BinaryReader reader) throws IOException {
return readValue(reader, false);
}
/**
* Reads an signed LEB128 value from the BinaryReader and returns a {@link LEB128Info} instance
* that contains the value along with size and position metadata.
* <p>
* @param reader {@link BinaryReader} to read bytes from
* @return a {@link LEB128Info} instance with the read LEB128 value with metadata
* @throws IOException if an I/O error occurs or value is outside the range of a java
* 64 bit int
*/
public static LEB128Info signed(BinaryReader reader) throws IOException {
return readValue(reader, true);
}
private final long offset;
private final long value;
private final int byteLength;
private LEB128Info(long offset, long value, int byteLength) {
this.offset = offset;
this.value = value;
this.byteLength = byteLength;
}
/**
* Returns the value as an unsigned int32. If the actual value
* is outside the positive range of a java int (ie. 0.. {@link Integer#MAX_VALUE}),
* an exception is thrown.
*
* @return int in the range of 0 to {@link Integer#MAX_VALUE}
* @throws IOException if value is outside range
*/
public int asUInt32() throws IOException {
ensureInt32u(value);
return (int) value;
}
/**
* Returns the value as an signed int32. If the actual value
* is outside the range of a java int (ie. {@link Integer#MIN_VALUE}.. {@link Integer#MAX_VALUE}),
* an exception is thrown.
*
* @return int in the range of {@link Integer#MIN_VALUE} to {@link Integer#MAX_VALUE}
* @throws IOException if value is outside range
*/
public int asInt32() throws IOException {
ensureInt32s(value);
return (int) value;
}
/**
* Returns the value as a 64bit primitive long. Interpreting the signed-ness of the
* value will depend on the way the value was read (ie. if {@link #signed(BinaryReader)}
* vs. {@link #unsigned(BinaryReader)} was used).
*
* @return long value.
*/
public long asLong() {
return value;
}
/**
* Returns the offset of the LEB128 value in the stream it was read from.
*
* @return stream offset of the LEB128 value
*/
public long getOffset() {
return offset;
}
/**
* Returns the number of bytes that were used to store the LEB128 value in the stream
* it was read from.
*
* @return number of bytes used to store the read LEB128 value
*/
public int getLength() {
return byteLength;
}
@Override
public String toString() {
return String.format("LEB128: value: %d, offset: %d, byteLength: %d", value, offset,
byteLength);
}
/**
* Reads a LEB128 value from the BinaryReader and returns a {@link LEB128Info} instance
* that contains the value along with size and position metadata.
* <p>
* @param reader {@link BinaryReader} to read bytes from
* @param isSigned true if the value is signed
* @return a {@link LEB128Info} instance with the read LEB128 value with metadata
* @throws IOException if an I/O error occurs or value is outside the range of a java
* 64 bit int
*/
public static LEB128Info readValue(BinaryReader reader, boolean isSigned) throws IOException {
long offset = reader.getPointerIndex();
long value = LEB128.read(reader.getInputStream(), isSigned);
int size = (int) (reader.getPointerIndex() - offset);
return new LEB128Info(offset, value, size);
}
private static void ensureInt32u(long value) throws IOException {
if (value < 0 || value > Integer.MAX_VALUE) {
throw new InvalidDataException(
"Value out of range for positive java 32 bit unsigned int: %s"
.formatted(Long.toUnsignedString(value)));
}
}
private static void ensureInt32s(long value) throws IOException {
if (value < Integer.MIN_VALUE || value > Integer.MAX_VALUE) {
throw new InvalidDataException(
"Value out of range for java 32 bit signed int: %d".formatted(value));
}
}
}
@@ -76,6 +76,16 @@ public interface StructConverter {
*/
public final static DataType IBO64 = IBO64DataType.dataType;
/**
* Reusable Unsigned LEB128 dynamic length data type
*/
public static final UnsignedLeb128DataType ULEB128 = UnsignedLeb128DataType.dataType;
/**
* Reusable Signed LEB128 dynamic length data type
*/
public static final SignedLeb128DataType SLEB128 = SignedLeb128DataType.dataType;
/**
* Returns a structure datatype representing the
* contents of the implementor of this interface.
@@ -18,7 +18,7 @@ package ghidra.app.util.bin.format.dwarf.line;
import java.io.IOException;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.format.dwarf4.LEB128;
import ghidra.program.model.data.LEB128;
public class FileEntry {
private String fileName;
@@ -31,9 +31,9 @@ public class FileEntry {
if (fileName.length() == 0) {
return;
}
directoryIndex = LEB128.readAsLong(reader, false);
lastModifiedTime = LEB128.readAsLong(reader, false);
fileLengthInBytes = LEB128.readAsLong(reader, false);
directoryIndex = reader.readNext(LEB128::unsigned);
lastModifiedTime = reader.readNext(LEB128::unsigned);
fileLengthInBytes = reader.readNext(LEB128::unsigned);
}
public String getFileName() {
@@ -15,11 +15,11 @@
*/
package ghidra.app.util.bin.format.dwarf.line;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.format.dwarf4.LEB128;
import java.io.IOException;
import ghidra.app.util.bin.BinaryReader;
import ghidra.program.model.data.LEB128;
public final class StatementProgramInstructions {
//Standard Opcodes
@@ -93,7 +93,7 @@ public final class StatementProgramInstructions {
}
private void executeExtended(int opcode) throws IOException {
long length = LEB128.readAsLong(reader, false);
long length = reader.readNext(LEB128::unsigned);
long oldIndex = reader.getPointerIndex();
int extendedOpcode = reader.readNextByte();
@@ -123,22 +123,22 @@ public final class StatementProgramInstructions {
break;
}
case DW_LNS_advance_pc: {
long value = LEB128.readAsLong(reader, false);
long value = reader.readNext(LEB128::unsigned);
machine.address += (value * prologue.getMinimumInstructionLength());
break;
}
case DW_LNS_advance_line: {
long value = LEB128.readAsLong(reader, false);
long value = reader.readNext(LEB128::unsigned);
machine.line += value;
break;
}
case DW_LNS_set_file: {
long value = LEB128.readAsLong(reader, false);
long value = reader.readNext(LEB128::unsigned);
machine.file = (int) value;
break;
}
case DW_LNS_set_column: {
long value = LEB128.readAsLong(reader, false);
long value = reader.readNext(LEB128::unsigned);
machine.column = (int) value;
break;
}
@@ -15,16 +15,18 @@
*/
package ghidra.app.util.bin.format.dwarf4;
import java.util.*;
import java.io.IOException;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.format.dwarf4.encoding.DWARFChildren;
import ghidra.app.util.bin.format.dwarf4.encoding.DWARFTag;
import ghidra.app.util.bin.format.dwarf4.next.DWARFProgram;
import ghidra.program.model.data.LEB128;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
import java.io.IOException;
import java.util.*;
/**
* This class represents the 'schema' for a DWARF DIE record.
* <p>
@@ -43,11 +45,11 @@ public class DWARFAbbreviation
TaskMonitor monitor)
throws IOException, CancelledException {
int ac = LEB128.readAsUInt32(reader);
int ac = reader.readNextUnsignedVarIntExact(LEB128::unsigned);
if (ac == 0) {
return null;
}
int tag = LEB128.readAsUInt32(reader);
int tag = reader.readNextUnsignedVarIntExact(LEB128::unsigned);
DWARFChildren hasChildren = DWARFChildren.find((int) reader.readNextByte());
// Read each attribute specification until attribute and its value is 0
@@ -15,11 +15,12 @@
*/
package ghidra.app.util.bin.format.dwarf4;
import java.io.IOException;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.format.dwarf4.encoding.DWARFAttribute;
import ghidra.app.util.bin.format.dwarf4.encoding.DWARFForm;
import java.io.IOException;
import ghidra.program.model.data.LEB128;
/**
* Information about a single DWARF attribute.
@@ -38,8 +39,9 @@ public class DWARFAttributeSpecification {
* @throws IOException
*/
public static DWARFAttributeSpecification read(BinaryReader reader) throws IOException {
int attribute = LEB128.readAsUInt32(reader);
DWARFForm attributeForm = DWARFForm.find(LEB128.readAsUInt32(reader));
int attribute = reader.readNextUnsignedVarIntExact(LEB128::unsigned);
DWARFForm attributeForm =
DWARFForm.find(reader.readNextUnsignedVarIntExact(LEB128::unsigned));
return attribute != 0 && attributeForm != DWARFForm.NULL
? new DWARFAttributeSpecification(attribute, attributeForm) : null;
@@ -27,6 +27,7 @@ import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.format.dwarf4.DWARFUtil.LengthResult;
import ghidra.app.util.bin.format.dwarf4.encoding.DWARFAttribute;
import ghidra.app.util.bin.format.dwarf4.next.DWARFProgram;
import ghidra.program.model.data.LEB128;
public class DWARFLine {
private long unit_length;
@@ -226,9 +227,9 @@ public class DWARFLine {
// This entry exists only if the length of the string is more than 0
if (this.name.length() > 0) {
this.directory_index = LEB128.readAsLong(reader, false);
this.modification_time = LEB128.readAsLong(reader, false);
this.length = LEB128.readAsLong(reader, false);
this.directory_index = reader.readNext(LEB128::unsigned);
this.modification_time = reader.readNext(LEB128::unsigned);
this.length = reader.readNext(LEB128::unsigned);
}
}
@@ -15,13 +15,15 @@
*/
package ghidra.app.util.bin.format.dwarf4;
import java.io.IOException;
import java.util.*;
import java.io.IOException;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.format.dwarf4.attribs.*;
import ghidra.app.util.bin.format.dwarf4.encoding.DWARFAttribute;
import ghidra.app.util.bin.format.dwarf4.encoding.DWARFTag;
import ghidra.program.model.data.LEB128;
/**
* A DWARF Debug Info Entry is a collection of {@link DWARFAttributeValue attributes}
@@ -60,7 +62,7 @@ public class DebugInfoEntry {
public static DebugInfoEntry read(BinaryReader reader, DWARFCompilationUnit unit,
DWARFAttributeFactory attributeFactory) throws IOException {
long offset = reader.getPointerIndex();
int abbreviationCode = LEB128.readAsUInt32(reader);
int abbreviationCode = reader.readNextUnsignedVarIntExact(LEB128::unsigned);
// Check for terminator DIE
if (abbreviationCode == 0) {
@@ -1,299 +0,0 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util.bin.format.dwarf4;
import java.io.IOException;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.ByteArrayProvider;
/**
* Class to hold result of reading a LEB128 value, along with size and position metadata.
* <p>
* Note: If a LEB128 value that would result in a native value longer than 64bits is attempted to
* be read, an {@link IOException} will be thrown, and the stream's position will be left at the last read byte.
* <p>
* If this was a valid (but overly large) LEB128, the caller's stream will be left still pointing to LEB data.
* <p>
*/
public class LEB128 {
private final long offset;
private final long value;
private final int byteLength;
private LEB128(long offset, long value, int byteLength) {
this.offset = offset;
this.value = value;
this.byteLength = byteLength;
}
/**
* Returns the value as an unsigned int32. If the actual value
* is outside the positive range of a java int (ie. 0.. {@link Integer#MAX_VALUE}),
* an exception is thrown.
*
* @return int in the range of 0 to {@link Integer#MAX_VALUE}
* @throws IOException if value is outside range
*/
public int asUInt32() throws IOException {
ensureInt32u(value);
return (int) value;
}
/**
* Returns the value as an signed int32. If the actual value
* is outside the range of a java int (ie. {@link Integer#MIN_VALUE}.. {@link Integer#MAX_VALUE}),
* an exception is thrown.
*
* @return int in the range of {@link Integer#MIN_VALUE} to {@link Integer#MAX_VALUE}
* @throws IOException if value is outside range
*/
public int asInt32() throws IOException {
ensureInt32s(value);
return (int) value;
}
/**
* Returns the value as a 64bit primitive long. Interpreting the signed-ness of the
* value will depend on the way the value was read (ie. if {@link #readSignedValue(BinaryReader)}
* vs. {@link #readUnsignedValue(BinaryReader)} was used).
*
* @return long value.
*/
public long asLong() {
return value;
}
/**
* Returns the offset of the LEB128 value in the stream it was read from.
*
* @return stream offset of the LEB128 value
*/
public long getOffset() {
return offset;
}
/**
* Returns the number of bytes that were used to store the LEB128 value in the stream
* it was read from.
*
* @return number of bytes used to store the read LEB128 value
*/
public int getLength() {
return byteLength;
}
@Override
public String toString() {
return String.format("LEB128: value: %d, offset: %d, byteLength: %d", value, offset,
byteLength);
}
/**
* Reads a LEB128 value from the BinaryReader and returns a {@link LEB128} instance
* that contains the value along with size and position metadata.
* <p>
* See {@link #readAsLong(BinaryReader, boolean)}.
*
* @param reader {@link BinaryReader} to read bytes from
* @param isSigned true if the value is signed
* @return a {@link LEB128} instance with the read LEB128 value with metadata
* @throws IOException if an I/O error occurs or value is outside the range of a java
* 64 bit int
*/
public static LEB128 readValue(BinaryReader reader, boolean isSigned) throws IOException {
long offset = reader.getPointerIndex();
long value = LEB128.readAsLong(reader, isSigned);
int size = (int) (reader.getPointerIndex() - offset);
return new LEB128(offset, value, size);
}
/**
* Reads an unsigned LEB128 value from the BinaryReader and returns a {@link LEB128} instance
* that contains the value along with size and position metadata.
* <p>
* See {@link #readAsLong(BinaryReader, boolean)}.
*
* @param reader {@link BinaryReader} to read bytes from
* @return a {@link LEB128} instance with the read LEB128 value with metadata
* @throws IOException if an I/O error occurs or value is outside the range of a java
* 64 bit int
*/
public static LEB128 readUnsignedValue(BinaryReader reader) throws IOException {
return readValue(reader, false);
}
/**
* Reads an signed LEB128 value from the BinaryReader and returns a {@link LEB128} instance
* that contains the value along with size and position metadata.
* <p>
* See {@link #readAsLong(BinaryReader, boolean)}.
*
* @param reader {@link BinaryReader} to read bytes from
* @return a {@link LEB128} instance with the read LEB128 value with metadata
* @throws IOException if an I/O error occurs or value is outside the range of a java
* 64 bit int
*/
public static LEB128 readSignedValue(BinaryReader reader) throws IOException {
return readValue(reader, true);
}
/**
* Reads a LEB128 signed number from the BinaryReader and returns it as a java 32 bit int.
* <p>
* If the value of the number can not fit in the int type, an {@link IOException} will
* be thrown.
*
* @param reader {@link BinaryReader} to read bytes from
* @return signed int32 value
* @throws IOException if error reading bytes or value is outside the
* range of a signed int32
*/
public static int readAsInt32(BinaryReader reader) throws IOException {
long tmp = readAsLong(reader, true);
ensureInt32s(tmp);
return (int) tmp;
}
/**
* Reads a LEB128 unsigned number from the BinaryReader and returns it as a java 32 bit int.
* <p>
* If the value of the number can not fit in the positive range of the int type,
* an {@link IOException} will be thrown.
*
* @param reader {@link BinaryReader} to read bytes from
* @return unsigned int32 value 0..Integer.MAX_VALUE
* @throws IOException if error reading bytes or value is outside the
* positive range of a java 32 bit int (ie. 0..Integer.MAX_VALUE)
*/
public static int readAsUInt32(BinaryReader reader) throws IOException {
long tmp = readAsLong(reader, false);
ensureInt32u(tmp);
return (int) tmp;
}
/**
* Reads a LEB128 number from the BinaryReader and returns it as a java 64 bit long int.
* <p>
* Large unsigned integers that use all 64 bits are be returned in a java native
* 'long' type, which is signed. It is up to the caller to treat the value as unsigned.
* <p>
* Large integers that use more than 64 bits will cause an IOException to be thrown.
* <p>
* @param reader {@link BinaryReader} to read bytes from
* @param isSigned true if the value is signed
* @return long integer value. Caller must treat it as unsigned if isSigned parameter was
* set to false
* @throws IOException if an I/O error occurs or value is outside the range of a java
* 64 bit int
*/
public static long readAsLong(BinaryReader reader, boolean isSigned) throws IOException {
int nextByte = 0;
int shift = 0;
long value = 0;
while (true) {
nextByte = reader.readNextUnsignedByte();
if (shift == 70 || (isSigned == false && shift == 63 && nextByte > 1)) {
throw new IOException(
"Unsupported LEB128 value, too large to fit in 64bit java long variable");
}
// must cast to long before shifting otherwise shift values greater than 32 cause problems
value |= ((long) (nextByte & 0x7F)) << shift;
shift += 7;
if ((nextByte & 0x80) == 0) {
break;
}
}
if ((isSigned) && (shift < Long.SIZE) && ((nextByte & 0x40) != 0)) {
// 0x40 is the new 'high' sign bit since 0x80 is the continuation flag
value |= (-1L << shift);
}
return value;
}
/**
* Reads a LEB128 number from the BinaryReader and returns it as a java 64 bit long int.
* <p>
* Large unsigned integers that use all 64 bits are be returned in a java native
* 'long' type, which is signed. It is up to the caller to treat the value as unsigned.
* <p>
* Large integers that use more than 64 bits will cause an IOException to be thrown.
* <p>
* @param reader {@link BinaryReader} to read bytes from
* @return long integer value
* @throws IOException if an I/O error occurs or value is outside the range of a java
* 64 bit int
*/
public static long readUnsignedAsLong(BinaryReader reader) throws IOException {
return readAsLong(reader, false);
}
/**
* Decodes a LEB128 number from a byte array and returns it as a long.
* <p>
* See {@link #readAsLong(BinaryReader, boolean)}.
*
* @param bytes the bytes representing the LEB128 number
* @param isSigned true if the value is signed
* @return long integer value. Caller must treat it as unsigned if isSigned parameter was
* set to false
* @throws IOException if error reading bytes or value is outside the
* range of a java 64 bit int
*/
public static long decode(byte[] bytes, boolean isSigned) throws IOException {
return decode(bytes, 0, isSigned);
}
/**
* Decodes a LEB128 number from a byte array and returns it as a long.
* <p>
* See {@link #readAsLong(BinaryReader, boolean)}.
*
* @param bytes the bytes representing the LEB128 number
* @param offset offset in byte array of where to start reading bytes
* @param isSigned true if the value is signed
* @return long integer value. Caller must treat it as unsigned if isSigned parameter was
* set to false
* @throws IOException if error reading bytes or value is outside the
* range of a java 64 bit int
*/
public static long decode(byte[] bytes, int offset, boolean isSigned) throws IOException {
ByteArrayProvider bap = new ByteArrayProvider(bytes);
BinaryReader br = new BinaryReader(bap, true);
br.setPointerIndex(offset);
return readAsLong(br, isSigned);
}
private static void ensureInt32u(long value) throws IOException {
if (value < 0 || value > Integer.MAX_VALUE) {
throw new IOException("LEB128 value out of range for java 32 bit unsigned int: " +
Long.toUnsignedString(value));
}
}
private static void ensureInt32s(long value) throws IOException {
if (value < Integer.MIN_VALUE || value > Integer.MAX_VALUE) {
throw new IOException(
"LEB128 value out of range for java 32 bit signed int: " +
Long.toString(value));
}
}
}
@@ -18,10 +18,12 @@ package ghidra.app.util.bin.format.dwarf4.attribs;
import java.io.IOException;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.format.dwarf4.*;
import ghidra.app.util.bin.format.dwarf4.DWARFCompilationUnit;
import ghidra.app.util.bin.format.dwarf4.DWARFUtil;
import ghidra.app.util.bin.format.dwarf4.encoding.DWARFForm;
import ghidra.app.util.bin.format.dwarf4.next.DWARFProgram;
import ghidra.app.util.bin.format.dwarf4.next.StringTable;
import ghidra.program.model.data.LEB128;
import ghidra.util.NumberUtil;
/**
@@ -73,7 +75,7 @@ public class DWARFAttributeFactory {
return new DWARFNumericAttribute(uoffset + unit.getStartOffset());
}
case DW_FORM_ref_udata: {
long uoffset = LEB128.readAsLong(reader, false);
long uoffset = reader.readNext(LEB128::unsigned);
return new DWARFNumericAttribute(uoffset + unit.getStartOffset());
}
@@ -103,7 +105,7 @@ public class DWARFAttributeFactory {
return new DWARFBlobAttribute(reader.readNextByteArray(length));
}
case DW_FORM_block: {
int length = LEB128.readAsUInt32(reader);
int length = reader.readNextUnsignedVarIntExact(LEB128::unsigned);
if (length < 0 || length > MAX_BLOCK4_SIZE) {
throw new IOException("Invalid/bad dw_form_block size: " + length);
}
@@ -121,12 +123,12 @@ public class DWARFAttributeFactory {
case DW_FORM_data8:
return new DWARFNumericAttribute(reader.readNextLong());
case DW_FORM_sdata:
return new DWARFNumericAttribute(LEB128.readAsLong(reader, true));
return new DWARFNumericAttribute(reader.readNext(LEB128::signed));
case DW_FORM_udata:
return new DWARFNumericAttribute(LEB128.readAsLong(reader, false));
return new DWARFNumericAttribute(reader.readNext(LEB128::unsigned));
case DW_FORM_exprloc: {
int length = LEB128.readAsUInt32(reader);
int length = reader.readNextUnsignedVarIntExact(LEB128::unsigned);
if (length < 0 || length > MAX_BLOCK4_SIZE) {
throw new IOException("Invalid/bad dw_form_exprloc size: " + length);
}
@@ -157,7 +159,8 @@ public class DWARFAttributeFactory {
// Indirect Form
case DW_FORM_indirect:
DWARFForm formValue = DWARFForm.find(LEB128.readAsUInt32(reader));
DWARFForm formValue =
DWARFForm.find(reader.readNextUnsignedVarIntExact(LEB128::unsigned));
DWARFAttributeValue value = read(reader, unit, formValue);
return new DWARFIndirectAttribute(value, formValue);
@@ -15,12 +15,14 @@
*/
package ghidra.app.util.bin.format.dwarf4.expression;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.io.IOException;
import ghidra.app.util.bin.*;
import ghidra.app.util.bin.format.dwarf4.*;
import ghidra.program.model.data.LEB128;
import ghidra.util.NumericUtilities;
/**
@@ -137,9 +139,9 @@ public class DWARFExpression {
case U_LONG:
return reader.readNextLong(); /* & there is no mask for ulong */
case S_LEB128:
return LEB128.readAsLong(reader, true);
return reader.readNext(LEB128::signed);
case U_LEB128:
return LEB128.readAsLong(reader, false);
return reader.readNext(LEB128::unsigned);
case SIZED_BLOB:
throw new IOException("Can't read SIZED_BLOB as a Long value");
case DWARF_INT:
@@ -17,12 +17,10 @@ package ghidra.app.util.bin.format.elf;
import javax.help.UnsupportedOperationException;
import ghidra.app.plugin.exceptionhandlers.gcc.datatype.AbstractLeb128DataType;
import ghidra.docking.settings.Settings;
import ghidra.docking.settings.SettingsDefinition;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.DataTypeManager;
import ghidra.program.model.data.*;
/**
* <code>AndroidElfRelocationData</code> provides a dynamic LEB128 data
@@ -33,7 +31,7 @@ import ghidra.program.model.data.DataTypeManager;
* component instance. This functionality relies on the 1:1 relationship
* between this dynamic datatype and the single component which references it.
*/
class AndroidElfRelocationData extends AbstractLeb128DataType {
class AndroidElfRelocationData extends SignedLeb128DataType {
private final long relocationOffset;
@@ -44,7 +42,7 @@ class AndroidElfRelocationData extends AbstractLeb128DataType {
* @param relocationOffset relocation offset associated with component.
*/
AndroidElfRelocationData(DataTypeManager dtm, long relocationOffset) {
super("sleb128", true, dtm);
super(dtm);
this.relocationOffset = relocationOffset;
}
@@ -54,21 +52,11 @@ class AndroidElfRelocationData extends AbstractLeb128DataType {
throw new UnsupportedOperationException("may not be cloned");
}
@Override
public String getMnemonic(Settings settings) {
return name;
}
@Override
public String getDescription() {
return "Android Packed Relocation Data for ELF";
}
@Override
public String getDefaultLabelPrefix() {
return "sleb128";
}
@Override
protected SettingsDefinition[] getBuiltInSettingsDefinitions() {
return null;
@@ -15,13 +15,13 @@
*/
package ghidra.app.util.bin.format.elf;
import java.io.IOException;
import java.util.ArrayList;
import java.io.IOException;
import javax.help.UnsupportedOperationException;
import ghidra.app.util.bin.*;
import ghidra.app.util.bin.format.elf.AndroidElfRelocationTableDataType.LEB128Info;
import ghidra.docking.settings.Settings;
import ghidra.program.model.data.*;
import ghidra.program.model.mem.MemBuffer;
@@ -78,13 +78,15 @@ class AndroidElfRelocationGroup extends DynamicDataType {
ArrayList<DataTypeComponent> list = new ArrayList<>();
LEB128Info sleb128 = LEB128Info.parse(reader, true);
long groupSize = sleb128.value;
list.add(sleb128.getComponent(this, list.size(), "group_size", null));
LEB128Info sleb128 = reader.readNext(LEB128Info::signed);
long groupSize = sleb128.asLong();
list.add(AndroidElfRelocationTableDataType.getLEB128Component(sleb128, this,
list.size(), "group_size", null));
sleb128 = LEB128Info.parse(reader, true);
long groupFlags = sleb128.value;
list.add(sleb128.getComponent(this, list.size(), "group_flags", null));
sleb128 = reader.readNext(LEB128Info::signed);
long groupFlags = sleb128.asLong();
list.add(AndroidElfRelocationTableDataType.getLEB128Component(sleb128, this,
list.size(), "group_flags", null));
boolean groupedByInfo = (groupFlags & RELOCATION_GROUPED_BY_INFO_FLAG) != 0;
boolean groupedByDelta = (groupFlags & RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG) != 0;
@@ -93,23 +95,26 @@ class AndroidElfRelocationGroup extends DynamicDataType {
long groupOffsetDelta = 0;
if (groupedByDelta) {
sleb128 = LEB128Info.parse(reader, true);
groupOffsetDelta = sleb128.value;
sleb128 = reader.readNext(LEB128Info::signed);
groupOffsetDelta = sleb128.asLong();
long minOffset = baseRelocOffset + groupOffsetDelta;
String rangeStr = "First relocation offset: 0x" + Long.toHexString(minOffset);
list.add(sleb128.getComponent(this, list.size(), "group_offsetDelta", rangeStr));
list.add(AndroidElfRelocationTableDataType.getLEB128Component(sleb128, this,
list.size(), "group_offsetDelta", rangeStr));
}
if (groupedByInfo) {
sleb128 = LEB128Info.parse(reader, true);
list.add(sleb128.getComponent(this, list.size(), "group_info", null));
sleb128 = reader.readNext(LEB128Info::signed);
list.add(AndroidElfRelocationTableDataType.getLEB128Component(sleb128, this,
list.size(), "group_info", null));
}
if (groupedByAddend && groupHasAddend) {
sleb128 = LEB128Info.parse(reader, true);
list.add(sleb128.getComponent(this, list.size(), "group_addend", null));
sleb128 = reader.readNext(LEB128Info::signed);
list.add(AndroidElfRelocationTableDataType.getLEB128Component(sleb128, this,
list.size(), "group_addend", null));
}
long relocOffset = baseRelocOffset;
@@ -124,26 +129,26 @@ class AndroidElfRelocationGroup extends DynamicDataType {
relocOffset += groupOffsetDelta;
}
else {
sleb128 = LEB128Info.parse(reader, true);
sleb128 = reader.readNext(LEB128Info::signed);
long baseOffset = relocOffset;
relocOffset += sleb128.value;
relocOffset += sleb128.asLong();
DataTypeComponent dtc = new ReadOnlyDataTypeComponent(
new AndroidElfRelocationOffset(dataMgr, baseOffset, relocOffset), this,
sleb128.byteLength, list.size(), sleb128.offset, "reloc_offset_" + i,
null);
sleb128.getLength(), list.size(), (int) sleb128.getOffset(),
"reloc_offset_" + i, null);
list.add(dtc);
}
if (!groupedByInfo) {
sleb128 = LEB128Info.parse(reader, true);
list.add(sleb128.getComponent(this, list.size(), "reloc_info_" + i, null,
relocOffset));
sleb128 = reader.readNext(LEB128Info::signed);
list.add(AndroidElfRelocationTableDataType.getLEB128Component(sleb128, this,
list.size(), "reloc_info_" + i, null, relocOffset));
}
if (groupHasAddend && !groupedByAddend) {
sleb128 = LEB128Info.parse(reader, true);
list.add(sleb128.getComponent(this, list.size(), "reloc_addend_" + i, null,
relocOffset));
sleb128 = reader.readNext(LEB128Info::signed);
list.add(AndroidElfRelocationTableDataType.getLEB128Component(sleb128, this,
list.size(), "reloc_addend_" + i, null, relocOffset));
}
}
}
@@ -17,14 +17,12 @@ package ghidra.app.util.bin.format.elf;
import javax.help.UnsupportedOperationException;
import ghidra.app.plugin.exceptionhandlers.gcc.datatype.AbstractLeb128DataType;
import ghidra.app.util.opinion.ElfLoader;
import ghidra.docking.settings.Settings;
import ghidra.docking.settings.SettingsDefinition;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.DataTypeManager;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.MemBuffer;
import ghidra.program.model.mem.Memory;
@@ -15,12 +15,11 @@
*/
package ghidra.app.util.bin.format.elf;
import java.io.IOException;
import java.util.ArrayList;
import ghidra.app.plugin.exceptionhandlers.gcc.datatype.SignedLeb128DataType;
import java.io.IOException;
import ghidra.app.util.bin.*;
import ghidra.app.util.bin.format.dwarf4.LEB128;
import ghidra.docking.settings.Settings;
import ghidra.program.model.data.*;
import ghidra.program.model.mem.MemBuffer;
@@ -64,41 +63,6 @@ public class AndroidElfRelocationTableDataType extends DynamicDataType {
return "";
}
static class LEB128Info {
final int offset;
final long value;
final int byteLength;
private LEB128Info(int offset, long value, int byteLength) {
this.offset = offset;
this.value = value;
this.byteLength = byteLength;
}
DataTypeComponent getComponent(DynamicDataType parent, int ordinal, String name,
String comment, long relocOffset) {
return new ReadOnlyDataTypeComponent(
new AndroidElfRelocationData(parent.getDataTypeManager(), relocOffset), parent,
byteLength, ordinal, offset, name, comment);
}
DataTypeComponent getComponent(DynamicDataType parent, int ordinal, String name,
String comment) {
return new ReadOnlyDataTypeComponent(
new SignedLeb128DataType(parent.getDataTypeManager()), parent, byteLength, ordinal,
offset, name, comment);
}
static LEB128Info parse(BinaryReader reader, boolean signed) throws IOException {
long nextPos = reader.getPointerIndex();
long value = LEB128.readAsLong(reader, signed);
long pos = reader.getPointerIndex();
int size = (int) (pos - nextPos);
return new LEB128Info((int) nextPos, value, size);
}
}
@Override
protected DataTypeComponent[] getAllComponents(MemBuffer buf) {
@@ -118,13 +82,13 @@ public class AndroidElfRelocationTableDataType extends DynamicDataType {
null));
reader.setPointerIndex(4);
LEB128Info sleb128 = LEB128Info.parse(reader, true);
long remainingRelocations = sleb128.value;
list.add(sleb128.getComponent(this, list.size(), "reloc_count", null));
LEB128Info sleb128 = reader.readNext(LEB128Info::signed);
long remainingRelocations = sleb128.asLong();
list.add(getLEB128Component(sleb128, this, list.size(), "reloc_count", null));
sleb128 = LEB128Info.parse(reader, true);
long baseRelocOffset = sleb128.value;
list.add(sleb128.getComponent(this, list.size(), "reloc_baseOffset", null));
sleb128 = reader.readNext(LEB128Info::signed);
long baseRelocOffset = sleb128.asLong();
list.add(getLEB128Component(sleb128, this, list.size(), "reloc_baseOffset", null));
int groupIndex = 0;
long groupRelocOffset = baseRelocOffset;
@@ -132,7 +96,7 @@ public class AndroidElfRelocationTableDataType extends DynamicDataType {
// NOTE: assumes 2-GByte MemBuffer limit
int offset = (int) reader.getPointerIndex();
long groupSize = LEB128.readAsLong(reader, true);
long groupSize = reader.readNext(LEB128Info::signed).asLong();
if (groupSize > remainingRelocations) {
Msg.debug(this, "Group relocation count " + groupSize +
" exceeded total count " + remainingRelocations);
@@ -166,4 +130,18 @@ public class AndroidElfRelocationTableDataType extends DynamicDataType {
}
}
public static DataTypeComponent getLEB128Component(LEB128Info leb128, DynamicDataType parent,
int ordinal, String name, String comment, long relocOffset) {
return new ReadOnlyDataTypeComponent(
new AndroidElfRelocationData(parent.getDataTypeManager(), relocOffset), parent,
leb128.getLength(), ordinal, (int) leb128.getOffset(), name, comment);
}
public static DataTypeComponent getLEB128Component(LEB128Info leb128, DynamicDataType parent,
int ordinal, String name, String comment) {
return new ReadOnlyDataTypeComponent(
new SignedLeb128DataType(parent.getDataTypeManager()), parent, leb128.getLength(),
ordinal, (int) leb128.getOffset(), name, comment);
}
}
@@ -20,9 +20,7 @@ import java.util.ArrayList;
import java.util.List;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.format.dwarf4.LEB128;
import ghidra.program.model.data.ArrayDataType;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.*;
import ghidra.util.Msg;
/**
@@ -197,8 +195,8 @@ public class ElfRelocationTable implements ElfFileSection {
try {
int relocationIndex = 0;
long remainingRelocations = LEB128.readAsLong(reader, true); // reloc_count
long offset = LEB128.readAsLong(reader, true); // reloc_baseOffset
long remainingRelocations = reader.readNext(LEB128::signed); // reloc_count
long offset = reader.readNext(LEB128::signed); // reloc_baseOffset
while (remainingRelocations > 0) {
@@ -206,7 +204,7 @@ public class ElfRelocationTable implements ElfFileSection {
long addend = 0;
// group_size
long groupSize = LEB128.readAsLong(reader, true);
long groupSize = reader.readNext(LEB128::signed);
if (groupSize > remainingRelocations) {
elfHeader.logError("Group relocation count " + groupSize +
" exceeded total count " + remainingRelocations);
@@ -214,7 +212,7 @@ public class ElfRelocationTable implements ElfFileSection {
}
// group_flags
long groupFlags = LEB128.readAsLong(reader, true);
long groupFlags = reader.readNext(LEB128::signed);
boolean groupedByInfo =
(groupFlags & AndroidElfRelocationGroup.RELOCATION_GROUPED_BY_INFO_FLAG) != 0;
boolean groupedByDelta = (groupFlags &
@@ -225,28 +223,29 @@ public class ElfRelocationTable implements ElfFileSection {
(groupFlags & AndroidElfRelocationGroup.RELOCATION_GROUP_HAS_ADDEND_FLAG) != 0;
// group_offsetDelta (optional)
long groupOffsetDelta = groupedByDelta ? LEB128.readAsLong(reader, true) : 0;
long groupOffsetDelta = groupedByDelta ? reader.readNext(LEB128::signed) : 0;
// group_info (optional)
long groupRInfo = groupedByInfo ? LEB128.readAsLong(reader, true) : 0;
long groupRInfo = groupedByInfo ? reader.readNext(LEB128::signed) : 0;
if (groupedByAddend && groupHasAddend) {
// group_addend (optional)
addend += LEB128.readAsLong(reader, true);
addend += reader.readNext(LEB128::signed);
}
for (int i = 0; i < groupSize; i++) {
// reloc_offset (optional)
offset += groupedByDelta ? groupOffsetDelta : LEB128.readAsLong(reader, true);
offset +=
groupedByDelta ? groupOffsetDelta : reader.readNext(LEB128::signed);
// reloc_info (optional)
long info = groupedByInfo ? groupRInfo : LEB128.readAsLong(reader, true);
long info = groupedByInfo ? groupRInfo : reader.readNext(LEB128::signed);
long rAddend = 0;
if (groupHasAddend) {
if (!groupedByAddend) {
// reloc_addend (optional)
addend += LEB128.readAsLong(reader, true);
addend += reader.readNext(LEB128::signed);
}
rAddend = addend;
}
@@ -25,7 +25,6 @@ import java.nio.charset.StandardCharsets;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.MemoryByteProvider;
import ghidra.app.util.bin.format.dwarf4.LEB128;
import ghidra.app.util.bin.format.elf.info.ElfInfoItem;
import ghidra.framework.options.Options;
import ghidra.program.model.address.Address;
@@ -330,7 +329,7 @@ public class GoBuildInfo implements ElfInfoItem {
* @throws IOException if error
*/
private static byte[] varlenBytes(BinaryReader reader) throws IOException {
int strLen = reader.readNextUnsignedVarIntExact(LEB128::readUnsignedAsLong);
int strLen = reader.readNextUnsignedVarIntExact(LEB128::unsigned);
byte[] bytes = reader.readNextByteArray(strLen);
return bytes;
}
@@ -15,15 +15,17 @@
*/
package ghidra.app.util.bin.format.macho.commands;
import static ghidra.app.util.bin.format.macho.commands.DyldInfoCommandConstants.*;
import static ghidra.app.util.bin.format.macho.commands.DyldInfoCommandConstants.EXPORT_SYMBOL_FLAGS_REEXPORT;
import static ghidra.app.util.bin.format.macho.commands.DyldInfoCommandConstants.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER;
import java.io.IOException;
import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.io.IOException;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.format.dwarf4.LEB128;
import ghidra.program.model.data.LEB128;
/**
* Mach-O export trie
@@ -105,31 +107,31 @@ public class ExportTrie {
private LinkedList<Node> parseNode(String name, int offset) throws IOException {
LinkedList<Node> children = new LinkedList<>();
reader.setPointerIndex(base + offset);
int terminalSize = LEB128.readAsUInt32(reader);
int terminalSize = reader.readNextUnsignedVarIntExact(LEB128::unsigned);
long childrenIndex = reader.getPointerIndex() + terminalSize;
if (terminalSize != 0) {
long flags = LEB128.readAsLong(reader, false);
long flags = reader.readNext(LEB128::unsigned);
long address = 0;
long other = 0;
String importName = null;
if ((flags & EXPORT_SYMBOL_FLAGS_REEXPORT) != 0) {
other = LEB128.readAsLong(reader, false); // dylib ordinal
other = reader.readNext(LEB128::unsigned); // dylib ordinal
importName = reader.readNextAsciiString();
}
else {
address = LEB128.readAsLong(reader, false);
address = reader.readNext(LEB128::unsigned);
if ((flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER) != 0) {
other = LEB128.readAsLong(reader, false);
other = reader.readNext(LEB128::unsigned);
}
}
ExportEntry export = new ExportEntry(name, address, flags, other, importName);
exports.add(export);
}
reader.setPointerIndex(childrenIndex);
int numChildren = LEB128.readAsUInt32(reader);
int numChildren = reader.readNextUnsignedVarIntExact(LEB128::unsigned);
for (int i = 0; i < numChildren; i++) {
String childName = reader.readNextAsciiString();
int childOffset = LEB128.readAsUInt32(reader);
int childOffset = reader.readNextUnsignedVarIntExact(LEB128::unsigned);
children.add(new Node(childName, childOffset));
}
return children;
@@ -15,14 +15,15 @@
*/
package ghidra.app.util.bin.format.macho.commands;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.io.IOException;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.ByteProvider;
import ghidra.app.util.bin.format.dwarf4.LEB128;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.LEB128;
/**
* Represents a LC_FUNCTION_STARTS command.
@@ -80,7 +81,7 @@ public class FunctionStartsCommand extends LinkEditDataCommand {
List<Long> offsets = new ArrayList<>();
while (true) {
long offset = LEB128.readAsLong(reader, false);
long offset = reader.readNext(LEB128::unsigned);
if (offset == 0) {
break;
}
@@ -274,6 +274,23 @@ public class BinaryReaderTest {
}
}
@Test
public void testUint32Max() throws IOException {
BinaryReader br = br(true, 0xff, 0xff, 0xff, 0x7f, 0xff);
int value = br.readNextUnsignedIntExact();
Assert.assertEquals(Integer.MAX_VALUE, value);
}
@Test(expected = IOException.class)
public void testUint32Overflow() throws IOException {
// Test uint32 max overflow with 0xff_ff_ff_ff
BinaryReader br = br(true, 0xff, 0xff, 0xff, 0xff, 0xff);
int value = br.readNextUnsignedIntExact();
Assert.fail(
"Should not be able to read a value that is larger than what can fit in java 32 bit int: " +
Integer.toUnsignedString(value));
}
// ------------------------------------------------------------------------------------
// UTF-16 Unicode String
// ------------------------------------------------------------------------------------
@@ -546,4 +563,5 @@ public class BinaryReaderTest {
BinaryReader br = br(true, -22, -87, 'A', 0);
assertEquals("\ufffdA" /* unicode replacement char, 'A'*/, br.readUtf8String(0));
}
}
@@ -17,9 +17,7 @@ package ghidra.file.formats.android.dex.format;
import java.io.IOException;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverter;
import ghidra.app.util.bin.format.dwarf4.LEB128;
import ghidra.app.util.bin.*;
import ghidra.program.model.data.*;
import ghidra.util.exception.DuplicateNameException;
@@ -30,7 +28,7 @@ public class AnnotationElement implements StructConverter {
private EncodedValue value;
public AnnotationElement(BinaryReader reader) throws IOException {
LEB128 leb128 = LEB128.readUnsignedValue(reader);
LEB128Info leb128 = reader.readNext(LEB128Info::unsigned);
nameIndex = leb128.asUInt32();
nameIndexLength = leb128.getLength();
@@ -50,22 +48,16 @@ public class AnnotationElement implements StructConverter {
DataType encodeValueDataType = value.toDataType();
String name =
"annotation_element" + "_" + nameIndexLength + "_" + encodeValueDataType.getName();
"annotation_element_%d_%s".formatted(nameIndexLength, encodeValueDataType.getName());
Structure structure = new StructureDataType(name, 0);
structure.add(new ArrayDataType(BYTE, nameIndexLength, BYTE.getLength()), "nameIndex",
null);
structure.add(ULEB128, nameIndexLength, "nameIndex", null);
structure.add(encodeValueDataType, "value", null);
structure.setCategoryPath(new CategoryPath("/dex/annotation_element"));
// try {
// structure.setName( name + "_" + structure.getLength( ) );
// }
// catch ( Exception e ) {
// // ignore
// }
return structure;
}
}
@@ -15,19 +15,12 @@
*/
package ghidra.file.formats.android.dex.format;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.*;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverter;
import ghidra.app.util.bin.format.dwarf4.LEB128;
import ghidra.program.model.data.ArrayDataType;
import ghidra.program.model.data.CategoryPath;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.Structure;
import ghidra.program.model.data.StructureDataType;
import java.io.IOException;
import ghidra.app.util.bin.*;
import ghidra.program.model.data.*;
import ghidra.util.exception.DuplicateNameException;
public class ClassDataItem implements StructConverter {
@@ -48,19 +41,19 @@ public class ClassDataItem implements StructConverter {
private List<EncodedMethod> virtualMethods = new ArrayList<>();
public ClassDataItem(BinaryReader reader, DexHeader dexHeader) throws IOException {
LEB128 leb128 = LEB128.readUnsignedValue(reader);
LEB128Info leb128 = reader.readNext(LEB128Info::unsigned);
staticFieldsSize = leb128.asUInt32();
staticFieldsSizeLength = leb128.getLength();
leb128 = LEB128.readUnsignedValue(reader);
leb128 = reader.readNext(LEB128Info::unsigned);
instanceFieldsSize = leb128.asUInt32();
instanceFieldsSizeLength = leb128.getLength();
leb128 = LEB128.readUnsignedValue(reader);
leb128 = reader.readNext(LEB128Info::unsigned);
directMethodsSize = leb128.asUInt32();
directMethodsSizeLength = leb128.getLength();
leb128 = LEB128.readUnsignedValue(reader);
leb128 = reader.readNext(LEB128Info::unsigned);
virtualMethodsSize = leb128.asUInt32();
virtualMethodsSizeLength = leb128.getLength();
@@ -137,18 +130,13 @@ public class ClassDataItem implements StructConverter {
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
// int unique = 0;
String name =
"class_data_item" + "_" + staticFieldsSizeLength + "_" + instanceFieldsSizeLength +
"_" + directMethodsSizeLength + "_" + virtualMethodsSizeLength;
String name = "class_data_item_%d_%d_%d_%d".formatted(staticFieldsSizeLength,
instanceFieldsSizeLength, directMethodsSizeLength, virtualMethodsSizeLength);
Structure structure = new StructureDataType(name, 0);
structure.add(new ArrayDataType(BYTE, staticFieldsSizeLength, BYTE.getLength()),
"static_fields", null);
structure.add(new ArrayDataType(BYTE, instanceFieldsSizeLength, BYTE.getLength()),
"instance_fields", null);
structure.add(new ArrayDataType(BYTE, directMethodsSizeLength, BYTE.getLength()),
"direct_methods", null);
structure.add(new ArrayDataType(BYTE, virtualMethodsSizeLength, BYTE.getLength()),
"virtual_methods", null);
structure.add(ULEB128, staticFieldsSizeLength, "static_fields", null);
structure.add(ULEB128, instanceFieldsSizeLength, "instance_fields", null);
structure.add(ULEB128, directMethodsSizeLength, "direct_methods", null);
structure.add(ULEB128, virtualMethodsSizeLength, "virtual_methods", null);
// int index = 0;
// for ( EncodedField field : staticFields ) {
// DataType dataType = field.toDataType( );
@@ -17,9 +17,7 @@ package ghidra.file.formats.android.dex.format;
import java.io.IOException;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverter;
import ghidra.app.util.bin.format.dwarf4.LEB128;
import ghidra.app.util.bin.*;
import ghidra.program.model.data.*;
import ghidra.util.exception.DuplicateNameException;
@@ -34,11 +32,11 @@ public class DebugInfoItem implements StructConverter {
private byte[] stateMachineOpcodes;
public DebugInfoItem(BinaryReader reader) throws IOException {
LEB128 leb128 = LEB128.readUnsignedValue(reader);
LEB128Info leb128 = reader.readNext(LEB128Info::unsigned);
lineStart = leb128.asUInt32();
lineStartLength = leb128.getLength();
leb128 = LEB128.readUnsignedValue(reader);
leb128 = reader.readNext(LEB128Info::unsigned);
parametersSize = leb128.asUInt32();
parametersSizeLength = leb128.getLength();
@@ -46,7 +44,7 @@ public class DebugInfoItem implements StructConverter {
parameterNamesLengths = new int[parametersSize];
for (int i = 0; i < parametersSize; ++i) {
leb128 = LEB128.readUnsignedValue(reader);
leb128 = reader.readNext(LEB128Info::unsigned);
parameterNames[i] = leb128.asUInt32() - 1;// uleb128p1
parameterNamesLengths[i] = leb128.getLength();
@@ -102,16 +100,12 @@ public class DebugInfoItem implements StructConverter {
Structure structure = new StructureDataType(builder.toString(), 0);
structure.add(new ArrayDataType(BYTE, lineStartLength, BYTE.getLength()), "line_start",
null);
structure.add(new ArrayDataType(BYTE, parametersSizeLength, BYTE.getLength()),
"parameters_size", null);
structure.add(ULEB128, lineStartLength, "line_start", null);
structure.add(ULEB128, parametersSizeLength, "parameters_size", null);
for (int i = 0; i < parametersSize; ++i) {
ArrayDataType dataType =
new ArrayDataType(BYTE, parameterNamesLengths[i], BYTE.getLength());
structure.add(dataType, "parameter_" + i, null);
builder.append(dataType.getLength() + "");
structure.add(ULEB128, parameterNamesLengths[i], "parameter_" + i, null);
builder.append("%d".formatted(parameterNamesLengths[i]));
}
ArrayDataType stateMachineArray =
@@ -18,7 +18,7 @@ package ghidra.file.formats.android.dex.format;
import java.io.IOException;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.format.dwarf4.LEB128;
import ghidra.program.model.data.LEB128;
class DebugInfoStateMachineReader {
private static final int MAX_SIZE = 0x10000; // 64k
@@ -35,44 +35,44 @@ class DebugInfoStateMachineReader {
return (int) (reader.getPointerIndex() - start);//done!
}
case DebugStateMachineOpCodes.DBG_ADVANCE_PC: {
LEB128.readAsUInt32(reader);
reader.readNext(LEB128::unsigned);
break;
}
case DebugStateMachineOpCodes.DBG_ADVANCE_LINE: {
LEB128.readAsUInt32(reader);
reader.readNext(LEB128::unsigned);
break;
}
case DebugStateMachineOpCodes.DBG_START_LOCAL: {
int register = LEB128.readAsUInt32(reader);
int register = reader.readNextUnsignedVarIntExact(LEB128::unsigned);
//TODO uleb128p1
int name = LEB128.readAsUInt32(reader);
int name = reader.readNextUnsignedVarIntExact(LEB128::unsigned);
//TODO uleb128p1
int type = LEB128.readAsUInt32(reader);
int type = reader.readNextUnsignedVarIntExact(LEB128::unsigned);
break;
}
case DebugStateMachineOpCodes.DBG_START_LOCAL_EXTENDED: {
int register = LEB128.readAsUInt32(reader);
int register = reader.readNextUnsignedVarIntExact(LEB128::unsigned);
//TODO uleb128p1
int name = LEB128.readAsUInt32(reader);
int name = reader.readNextUnsignedVarIntExact(LEB128::unsigned);
//TODO uleb128p1
int type = LEB128.readAsUInt32(reader);
int type = reader.readNextUnsignedVarIntExact(LEB128::unsigned);
//TODO uleb128p1
int signature = LEB128.readAsUInt32(reader);
int signature = reader.readNextUnsignedVarIntExact(LEB128::unsigned);
break;
}
case DebugStateMachineOpCodes.DBG_END_LOCAL: {
int register = LEB128.readAsUInt32(reader);
int register = reader.readNextUnsignedVarIntExact(LEB128::unsigned);
break;
}
case DebugStateMachineOpCodes.DBG_RESTART_LOCAL: {
int register = LEB128.readAsUInt32(reader);
int register = reader.readNextUnsignedVarIntExact(LEB128::unsigned);
break;
}
case DebugStateMachineOpCodes.DBG_SET_PROLOGUE_END: {
@@ -83,7 +83,7 @@ class DebugInfoStateMachineReader {
}
case DebugStateMachineOpCodes.DBG_SET_FILE: {
//TODO uleb128p1
int name = LEB128.readAsUInt32(reader);
int name = reader.readNextUnsignedVarIntExact(LEB128::unsigned);
break;
}
default: {
@@ -15,12 +15,11 @@
*/
package ghidra.file.formats.android.dex.format;
import java.io.IOException;
import java.util.*;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverter;
import ghidra.app.util.bin.format.dwarf4.LEB128;
import java.io.IOException;
import ghidra.app.util.bin.*;
import ghidra.program.model.data.*;
import ghidra.util.exception.DuplicateNameException;
@@ -33,11 +32,11 @@ public class EncodedAnnotation implements StructConverter {
private List<AnnotationElement> elements = new ArrayList<>();
public EncodedAnnotation(BinaryReader reader) throws IOException {
LEB128 leb128 = LEB128.readUnsignedValue(reader);
LEB128Info leb128 = reader.readNext(LEB128Info::unsigned);
typeIndex = leb128.asUInt32();
typeIndexLength = leb128.getLength();
leb128 = LEB128.readUnsignedValue(reader);
leb128 = reader.readNext(LEB128Info::unsigned);
size = leb128.asUInt32();
sizeLength = leb128.getLength();
@@ -68,9 +67,8 @@ public class EncodedAnnotation implements StructConverter {
Structure structure = new StructureDataType(builder.toString(), 0);
structure.add(new ArrayDataType(BYTE, typeIndexLength, BYTE.getLength()), "typeIndex",
null);
structure.add(new ArrayDataType(BYTE, sizeLength, BYTE.getLength()), "size", null);
structure.add(ULEB128, typeIndexLength, "typeIndex", null);
structure.add(ULEB128, sizeLength, "size", null);
int index = 0;
for (AnnotationElement element : elements) {
@@ -15,13 +15,12 @@
*/
package ghidra.file.formats.android.dex.format;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverter;
import ghidra.app.util.bin.format.dwarf4.LEB128;
import java.io.IOException;
import ghidra.app.util.bin.*;
import ghidra.program.model.data.*;
import ghidra.util.exception.DuplicateNameException;
@@ -33,7 +32,7 @@ public class EncodedArray implements StructConverter {
private byte[] values;
public EncodedArray(BinaryReader reader) throws IOException {
LEB128 leb128 = LEB128.readUnsignedValue(reader);
LEB128Info leb128 = reader.readNext(LEB128Info::unsigned);
size = leb128.asUInt32();
sizeLength = leb128.getLength();
@@ -57,7 +56,7 @@ public class EncodedArray implements StructConverter {
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
Structure structure = new StructureDataType("encoded_array_" + values.length, 0);
structure.add(new ArrayDataType(BYTE, sizeLength, BYTE.getLength()), "size", null);
structure.add(ULEB128, sizeLength, "size", null);
if (values.length > 0) {
structure.add(new ArrayDataType(BYTE, values.length, BYTE.getLength()), "values", null);
}
@@ -15,13 +15,12 @@
*/
package ghidra.file.formats.android.dex.format;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverter;
import ghidra.app.util.bin.format.dwarf4.LEB128;
import java.io.IOException;
import ghidra.app.util.bin.*;
import ghidra.program.model.data.*;
import ghidra.util.exception.DuplicateNameException;
@@ -34,7 +33,7 @@ public class EncodedCatchHandler implements StructConverter {
private int catchAllAddressLength;
public EncodedCatchHandler(BinaryReader reader) throws IOException {
LEB128 leb128 = LEB128.readSignedValue(reader);
LEB128Info leb128 = reader.readNext(LEB128Info::signed);
size = leb128.asInt32();
sizeLength = leb128.getLength();
@@ -43,7 +42,7 @@ public class EncodedCatchHandler implements StructConverter {
}
if (size <= 0) {// This element is only present if size is non-positive.
leb128 = LEB128.readUnsignedValue(reader);
leb128 = reader.readNext(LEB128Info::unsigned);
catchAllAddress = leb128.asUInt32();
catchAllAddressLength = leb128.getLength();
}
@@ -79,10 +78,10 @@ public class EncodedCatchHandler implements StructConverter {
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
StringBuilder builder = new StringBuilder();
builder.append("encoded_catch_handler_" + sizeLength + "_" + catchAllAddressLength + "_" +
handlers.size());
builder.append("encoded_catch_handler_%d_%d_%d".formatted(sizeLength, catchAllAddressLength,
handlers.size()));
Structure structure = new StructureDataType(builder.toString(), 0);
structure.add(new ArrayDataType(BYTE, sizeLength, BYTE.getLength()), "size", null);
structure.add(ULEB128, sizeLength, "size", null);
int index = 0;
for (EncodedTypeAddressPair pair : handlers) {
DataType dataType = pair.toDataType();
@@ -90,8 +89,7 @@ public class EncodedCatchHandler implements StructConverter {
builder.append(pair.getDataTypeIdString());
}
if (size <= 0) {// This element is only present if size is non-positive.
structure.add(new ArrayDataType(BYTE, catchAllAddressLength, BYTE.getLength()),
"catch_all_addr", null);
structure.add(ULEB128, catchAllAddressLength, "catch_all_addr", null);
}
structure.setCategoryPath(new CategoryPath("/dex/encoded_catch_handler"));
try {
@@ -15,13 +15,12 @@
*/
package ghidra.file.formats.android.dex.format;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverter;
import ghidra.app.util.bin.format.dwarf4.LEB128;
import java.io.IOException;
import ghidra.app.util.bin.*;
import ghidra.program.model.data.*;
import ghidra.util.exception.DuplicateNameException;
@@ -32,7 +31,7 @@ public class EncodedCatchHandlerList implements StructConverter {
private List<EncodedCatchHandler> handlers = new ArrayList<>();
public EncodedCatchHandlerList(BinaryReader reader) throws IOException {
LEB128 leb128 = LEB128.readUnsignedValue(reader);
LEB128Info leb128 = reader.readNext(LEB128Info::unsigned);
size = leb128.asUInt32();
sizeLength = leb128.getLength();
@@ -57,7 +56,7 @@ public class EncodedCatchHandlerList implements StructConverter {
// int unique = 0;
String name = "encoded_catch_handler_list" + "_" + sizeLength;
Structure structure = new StructureDataType(name, 0);
structure.add(new ArrayDataType(BYTE, sizeLength, BYTE.getLength()), "size", null);
structure.add(ULEB128, sizeLength, "size", null);
// int index = 0;
// for ( EncodedCatchHandler handler : handlers ) {
// DataType dataType = handler.toDataType( );
@@ -17,9 +17,7 @@ package ghidra.file.formats.android.dex.format;
import java.io.IOException;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverter;
import ghidra.app.util.bin.format.dwarf4.LEB128;
import ghidra.app.util.bin.*;
import ghidra.program.model.data.*;
import ghidra.util.exception.DuplicateNameException;
@@ -35,12 +33,12 @@ public class EncodedField implements StructConverter {
public EncodedField(BinaryReader reader) throws IOException {
LEB128 leb128 = LEB128.readUnsignedValue(reader);
LEB128Info leb128 = reader.readNext(LEB128Info::unsigned);
_fileOffset = leb128.getOffset();
fieldIndexDifference = leb128.asUInt32();
fieldIndexDifferenceLength = leb128.getLength();
leb128 = LEB128.readUnsignedValue(reader);
leb128 = reader.readNext(LEB128Info::unsigned);
accessFlags = leb128.asUInt32();
accessFlagsLength = leb128.getLength();
}
@@ -59,12 +57,11 @@ public class EncodedField implements StructConverter {
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
String name = "encoded_field_" + fieldIndexDifferenceLength + "_" + accessFlagsLength;
String name =
"encoded_field_%d_%d".formatted(fieldIndexDifferenceLength, accessFlagsLength);
Structure structure = new StructureDataType(name, 0);
structure.add(new ArrayDataType(BYTE, fieldIndexDifferenceLength, BYTE.getLength()),
"field_idx_diff", null);
structure.add(new ArrayDataType(BYTE, accessFlagsLength, BYTE.getLength()), "accessFlags",
null);
structure.add(ULEB128, fieldIndexDifferenceLength, "field_idx_diff", null);
structure.add(ULEB128, accessFlagsLength, "accessFlags", null);
structure.setCategoryPath(new CategoryPath("/dex/encoded_field"));
return structure;
}
@@ -17,17 +17,11 @@ package ghidra.file.formats.android.dex.format;
import java.io.IOException;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverter;
import ghidra.app.util.bin.format.dwarf4.LEB128;
import ghidra.app.util.bin.*;
import ghidra.file.formats.android.cdex.CDexCodeItem;
import ghidra.file.formats.android.cdex.CDexHeader;
import ghidra.file.formats.android.dex.util.DexUtil;
import ghidra.program.model.data.ArrayDataType;
import ghidra.program.model.data.CategoryPath;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.Structure;
import ghidra.program.model.data.StructureDataType;
import ghidra.program.model.data.*;
import ghidra.util.exception.DuplicateNameException;
/**
@@ -51,16 +45,16 @@ public class EncodedMethod implements StructConverter {
public EncodedMethod(BinaryReader reader, DexHeader dexHeader) throws IOException {
LEB128 leb128 = LEB128.readUnsignedValue(reader);
LEB128Info leb128 = reader.readNext(LEB128Info::unsigned);
_fileOffset = leb128.getOffset();
methodIndexDifference = leb128.asUInt32();
methodIndexDifferenceLength = leb128.getLength();
leb128 = LEB128.readUnsignedValue(reader);
leb128 = reader.readNext(LEB128Info::unsigned);
accessFlags = leb128.asUInt32();
accessFlagsLength = leb128.getLength();
leb128 = LEB128.readUnsignedValue(reader);
leb128 = reader.readNext(LEB128Info::unsigned);
codeOffset = leb128.asUInt32();
codeOffsetLength = leb128.getLength();
@@ -115,15 +109,12 @@ public class EncodedMethod implements StructConverter {
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
String name = "encoded_method_" + methodIndexDifferenceLength + "_" + accessFlagsLength +
"_" + codeOffsetLength;
String name = "encoded_method_%d_%d_%d".formatted(methodIndexDifferenceLength,
accessFlagsLength, codeOffsetLength);
Structure structure = new StructureDataType(name, 0);
structure.add(new ArrayDataType(BYTE, methodIndexDifferenceLength, BYTE.getLength()),
"method_idx_diff", null);
structure.add(new ArrayDataType(BYTE, accessFlagsLength, BYTE.getLength()), "access_flags",
null);
structure.add(new ArrayDataType(BYTE, codeOffsetLength, BYTE.getLength()), "code_off",
null);
structure.add(ULEB128, methodIndexDifferenceLength, "method_idx_diff", null);
structure.add(ULEB128, accessFlagsLength, "access_flags", null);
structure.add(ULEB128, codeOffsetLength, "code_off", null);
structure.setCategoryPath(new CategoryPath("/dex/encoded_method"));
return structure;
}
@@ -17,9 +17,7 @@ package ghidra.file.formats.android.dex.format;
import java.io.IOException;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverter;
import ghidra.app.util.bin.format.dwarf4.LEB128;
import ghidra.app.util.bin.*;
import ghidra.program.model.data.*;
import ghidra.util.exception.DuplicateNameException;
@@ -32,11 +30,11 @@ public class EncodedTypeAddressPair implements StructConverter {
private int addressLength;// in bytes
public EncodedTypeAddressPair(BinaryReader reader) throws IOException {
LEB128 leb128 = LEB128.readUnsignedValue(reader);
LEB128Info leb128 = reader.readNext(LEB128Info::unsigned);
typeIndex = leb128.asUInt32();
typeIndexLength = leb128.getLength();
leb128 = LEB128.readUnsignedValue(reader);
leb128 = reader.readNext(LEB128Info::unsigned);
address = leb128.asUInt32();
addressLength = leb128.getLength();
}
@@ -60,9 +58,9 @@ public class EncodedTypeAddressPair implements StructConverter {
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
Structure structure = new StructureDataType(
"encoded_type_addr_pair_" + typeIndexLength + "_" + addressLength, 0);
structure.add(new ArrayDataType(BYTE, typeIndexLength, BYTE.getLength()), "type_idx", null);
structure.add(new ArrayDataType(BYTE, addressLength, BYTE.getLength()), "addr", null);
"encoded_type_addr_pair_%d_%d".formatted(typeIndexLength, addressLength), 0);
structure.add(ULEB128, typeIndexLength, "type_idx", null);
structure.add(ULEB128, addressLength, "addr", null);
structure.setCategoryPath(new CategoryPath("/dex/encoded_type_addr_pair"));
return structure;
}
@@ -18,9 +18,7 @@ package ghidra.file.formats.android.dex.format;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverter;
import ghidra.app.util.bin.format.dwarf4.LEB128;
import ghidra.app.util.bin.*;
import ghidra.file.formats.android.dex.util.DexUtil;
import ghidra.program.model.data.*;
import ghidra.util.exception.AssertException;
@@ -42,9 +40,10 @@ public class StringDataItem implements StructConverter {
reader = reader.clone(DexUtil.adjustOffset(stringItem.getStringDataOffset(), dexHeader));
LEB128 leb128 = LEB128.readUnsignedValue(reader);
LEB128Info leb128 = reader.readNext(LEB128Info::unsigned);
stringLength = leb128.asUInt32();
lebLength = leb128.getLength();
long nullTermIndex =
getIndexOfByteValue(reader, reader.getPointerIndex(), MAX_STRING_LEN, (byte) 0);
actualLength = (int) (nullTermIndex - reader.getPointerIndex() + 1);
@@ -72,7 +71,7 @@ public class StringDataItem implements StructConverter {
@Override
public DataType toDataType() {
Structure structure = new StructureDataType("string_data_item_" + actualLength, 0);
structure.add(new ArrayDataType(BYTE, lebLength, BYTE.getLength()), "utf16_size", null);
structure.add(ULEB128, lebLength, "utf16_size", null);
structure.add(UTF8, actualLength, "data", null);
try {
structure.setCategoryPath(new CategoryPath("/dex/string_data_item"));
@@ -28,6 +28,7 @@ public class FormatSettingsDefinition implements EnumSettingsDefinition {
//NOTE: if these strings change, the XML needs to changed also...
private static final String[] choices = { "hex", "decimal", "binary", "octal", "char" };
private static final String[] valuePostfix = { "h", "", "b", "o", "" };
private static final int[] radix = { 16, 10, 2, 8, 0 };
protected static final String FORMAT = "format";
@@ -52,7 +53,8 @@ public class FormatSettingsDefinition implements EnumSettingsDefinition {
* Returns the format based on the specified settings
*
* @param settings the instance settings or null for default value.
* @return the format value (HEX, DECIMAL, BINARY, OCTAL, CHAR)
* @return the format value (HEX, DECIMAL, BINARY, OCTAL, CHAR), or HEX if invalid
* data in the FORMAT settings value
*/
public int getFormat(Settings settings) {
if (settings == null) {
@@ -76,12 +78,18 @@ public class FormatSettingsDefinition implements EnumSettingsDefinition {
* @return the format radix
*/
public int getRadix(Settings settings) {
try {
return radix[getFormat(settings)];
}
catch (ArrayIndexOutOfBoundsException e) {
return 16;
}
return radix[getFormat(settings)];
}
/**
* Returns a descriptive string suffix that should be appended after converting a value
* using the radix returned by {@link #getRadix(Settings)}.
*
* @param settings the instance settings
* @return string suffix, such as "h" for HEX, "o" for octal
*/
public String getRepresentationPostfix(Settings settings) {
return valuePostfix[getFormat(settings)];
}
@Override
@@ -0,0 +1,124 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.program.model.data;
import java.io.IOException;
import java.io.InputStream;
import ghidra.docking.settings.*;
import ghidra.program.model.mem.MemBuffer;
import ghidra.program.model.scalar.Scalar;
import ghidra.util.classfinder.ClassTranslator;
/**
* An abstract base class for a LEB128 variable length integer data type.
* <p>
* See {@link LEB128}.
*/
public abstract class AbstractLeb128DataType extends BuiltIn implements Dynamic {
/* package */ static final FormatSettingsDefinition FORMAT = FormatSettingsDefinition.DEF_HEX;
private static SettingsDefinition[] SETTINGS_DEFS = { FORMAT };
private final boolean signed;
/**
* Base constructor for a little endian based 128 data type.
* @param name name of the leb128 data type that extends this class.
* @param signed true if it is signed. false if unsigned.
* @param dtm the data type manager to associate with this data type.
*/
public AbstractLeb128DataType(String name, boolean signed, DataTypeManager dtm) {
super(null, name, dtm);
this.signed = signed;
}
@Override
protected SettingsDefinition[] getBuiltInSettingsDefinitions() {
return SETTINGS_DEFS;
}
@Override
public int getLength() {
return -1;
}
@Override
public int getLength(MemBuffer buf, int maxLength) {
if (maxLength < 0) {
maxLength = LEB128.MAX_SUPPORTED_LENGTH;
}
try (InputStream is = buf.getInputStream(0, maxLength)) {
return LEB128.getLength(is);
}
catch (IOException e) {
return -1;
}
}
@Override
public Class<?> getValueClass(Settings settings) {
return Scalar.class;
}
@Override
public Object getValue(MemBuffer buf, Settings settings, int maxLength) {
if (maxLength < 0) {
maxLength = LEB128.MAX_SUPPORTED_LENGTH;
}
try (InputStream is = buf.getInputStream(0, maxLength)) {
long val = LEB128.read(is, signed);
return new Scalar(64 - Long.numberOfLeadingZeros(val), val, signed);
}
catch (IOException e) {
return null; // memory error, or more than 10 bytes long
}
}
@Override
public String getRepresentation(MemBuffer buf, Settings settings, int length) {
Scalar val = (Scalar) getValue(buf, settings, length);
if (val == null) {
return "??";
}
int radix = FORMAT.getRadix(settings);
String postfix = FORMAT.getRepresentationPostfix(settings);
String valStr = val.toString(radix, false, signed, "", "");
return valStr.toUpperCase() + postfix;
}
@Override
public DataType getReplacementBaseType() {
return ByteDataType.dataType;
}
@Override
public boolean canSpecifyLength() {
return true;
}
@Override
public String getDefaultLabelPrefix() {
return name;
}
}
@@ -15,9 +15,10 @@
*/
package ghidra.program.model.data;
import java.net.URL;
import java.util.Collection;
import java.net.URL;
import ghidra.docking.settings.Settings;
import ghidra.docking.settings.SettingsDefinition;
import ghidra.program.model.mem.MemBuffer;
@@ -290,16 +291,18 @@ public interface DataType {
public URL getDocs();
/**
* Get the interpreted data value in the form of the appropriate Object for this DataType.
* This method must return a value consistent with {@link #getValueClass(Settings)}.
* Returns the interpreted data value as an instance of the
* {@link #getValueClass(Settings) advertised value class}.
* <p>
* For instance, if this datatype is a {@link Pointer} an Address object or null should be returned.
* A Byte, returns a {@link Scalar} object.
* For instance, {@link Pointer} data types should return an Address object (or null), or
* integer data types should return a {@link Scalar} object.
*
* @param buf the data buffer.
* @param buf the data buffer
* @param settings the settings to use.
* @param length the number of bytes to get the value from.
* @return the data Object.
* @param length indicates the maximum number of bytes that may be consumed by a
* {@link Dynamic} datatype, otherwise this value is ignored. A value of -1 may be specified
* to allow a Dynamic datatype to determine the length based upon the actual data bytes
* @return the data object, or null if data is invalid
*/
public Object getValue(MemBuffer buf, Settings settings, int length);
@@ -0,0 +1,153 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.program.model.data;
import java.io.*;
/**
* Logic for reading LEB128 values.
* <p>
* LEB128 is a variable length integer encoding that uses 7 bits per byte, with the high bit
* being reserved as a continuation flag, with the least significant bytes coming first
* (<b>L</b>ittle <b>E</b>ndian <B>B</b>ase <b>128</b>).
* <p>
* This implementation only supports reading values that decode to at most 64 bits (to fit into
* a java long).
* <p>
* When reading a value, you must already know if it was written as a signed or unsigned value to
* be able to decode it correctly.
*/
public class LEB128 {
/**
* Max number of bytes that is supported by the deserialization code.
*/
public static final int MAX_SUPPORTED_LENGTH = 10;
/**
* Reads an unsigned LEB128 variable length integer from the stream.
*
* @param is {@link InputStream} to get bytes from
* @return leb128 value, as a long
* @throws IOException if an I/O error occurs or decoded value is outside the range of a java
* 64 bit int (or it used more than {@value #MAX_SUPPORTED_LENGTH} bytes to be encoded), or
* there is an error or EOF getting a byte from the InputStream before reaching the end of the
* encoded value
*/
public static long unsigned(InputStream is) throws IOException {
return read(is, false);
}
/**
* Reads a signed LEB128 variable length integer from the stream.
*
* @param is {@link InputStream} to get bytes from
* @return leb128 value, as a long
* @throws IOException if an I/O error occurs or decoded value is outside the range of a java
* 64 bit int (or it used more than {@value #MAX_SUPPORTED_LENGTH} bytes to be encoded), or
* there is an error or EOF getting a byte from the InputStream before reaching the end of the
* encoded value
*/
public static long signed(InputStream is) throws IOException {
return read(is, true);
}
/**
* Reads a LEB128 number from the stream and returns it as a java 64 bit long int.
* <p>
* Large unsigned integers that use all 64 bits are returned in a java native
* 'long' type, which is signed. It is up to the caller to treat the value as unsigned.
* <p>
* Large integers that use more than 64 bits will cause an IOException to be thrown.
* <p>
* @param is {@link InputStream} to get bytes from
* @param isSigned true if the value is signed
* @return long integer value. Caller must treat it as unsigned if isSigned parameter was
* set to false
* @throws IOException if an I/O error occurs or decoded value is outside the range of a java
* 64 bit int (or it used more than {@value #MAX_SUPPORTED_LENGTH} bytes to be encoded), or
* there is an error or EOF getting a byte from the InputStream before reaching the end of the
* encoded value
*/
public static long read(InputStream is, boolean isSigned) throws IOException {
int nextByte = 0;
int shift = 0;
long value = 0;
while (true) {
nextByte = is.read();
if (nextByte < 0) {
throw new EOFException();
}
if (shift == 70 || (isSigned == false && shift == 63 && nextByte > 1)) {
throw new IOException(
"Unsupported LEB128 value, too large to fit in 64bit java long variable");
}
// must cast to long before shifting otherwise shift values greater than 32 cause problems
value |= ((long) (nextByte & 0x7F)) << shift;
shift += 7;
if ((nextByte & 0x80) == 0) {
break;
}
}
if ((isSigned) && (shift < Long.SIZE) && ((nextByte & 0x40) != 0)) {
// 0x40 is the new 'high' sign bit since 0x80 is the continuation flag.
// bitwise-or in all the sign-extension bits we need for the value
value |= (-1L << shift);
}
return value;
}
/**
* Returns the length of the variable length LEB128 value.
*
* @param is InputStream to get bytes from
* @return length of the LEB128 value, or -1 if the end of the value is not found
* @throws IOException if error getting next byte from stream
*/
public static int getLength(InputStream is) throws IOException {
int length = 0;
int nextByte;
while ((nextByte = is.read()) >= 0 && length < MAX_SUPPORTED_LENGTH) {
length++;
if ((nextByte & 0x80) == 0) {
return length;
}
}
return -1;
}
/**
* Decodes a LEB128 number from a byte array and returns it as a long.
* <p>
* See {@link #read(InputStream, boolean)}
*
* @param bytes the bytes representing the LEB128 number
* @param offset offset in byte array of where to start reading bytes
* @param isSigned true if the value is signed
* @return long integer value. Caller must treat it as unsigned if isSigned parameter was
* set to false
* @throws IOException if array offset is invalid, decoded value is outside the range of a java
* 64 bit int (or it used more than {@value #MAX_SUPPORTED_LENGTH} bytes to be encoded), or
* the end of the array was reached before reaching the end of the encoded value
*/
public static long decode(byte[] bytes, int offset, boolean isSigned) throws IOException {
InputStream is = new ByteArrayInputStream(bytes, offset, bytes.length - offset);
return read(is, isSigned);
}
}
@@ -13,16 +13,18 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.exceptionhandlers.gcc.datatype;
package ghidra.program.model.data;
import ghidra.docking.settings.Settings;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.DataTypeManager;
import ghidra.util.classfinder.ClassTranslator;
/**
* A Signed Little Endian Base 128 integer data type.
*/
public class SignedLeb128DataType extends AbstractLeb128DataType {
static {
ClassTranslator.put("ghidra.app.plugin.exceptionhandlers.gcc.datatype.SignedLeb128DataType",
SignedLeb128DataType.class.getName());
}
/** A statically defined SignedLeb128DataType instance.*/
public final static SignedLeb128DataType dataType = new SignedLeb128DataType();
@@ -50,18 +52,8 @@ public class SignedLeb128DataType extends AbstractLeb128DataType {
return new SignedLeb128DataType(dtm);
}
@Override
public String getMnemonic(Settings settings) {
return name;
}
@Override
public String getDescription() {
return "Signed Dwarf LEB128-Encoded Number";
}
@Override
public String getDefaultLabelPrefix() {
return "sleb128";
return "Signed LEB128-Encoded Number";
}
}

Some files were not shown because too many files have changed in this diff Show More