Add support for the throw-verification-error opcode

This commit is contained in:
Ben Gruver 2011-10-09 14:52:00 -04:00
parent cb3e0ea38a
commit bbe539f2d2
10 changed files with 280 additions and 10 deletions

View File

@ -31,6 +31,7 @@ package org.jf.baksmali.Adaptors.Format;
import org.jf.baksmali.Adaptors.MethodItem;
import org.jf.baksmali.Adaptors.ReferenceFormatter;
import org.jf.baksmali.Adaptors.RegisterFormatter;
import org.jf.dexlib.Code.Format.Instruction20bc;
import org.jf.util.IndentingWriter;
import org.jf.baksmali.Renderers.LongRenderer;
import org.jf.dexlib.Code.*;
@ -84,6 +85,13 @@ public class InstructionMethodItem<T extends Instruction> extends MethodItem {
writer.write(", ");
writeSecondRegister(writer);
return true;
case Format20bc:
writeOpcode(writer);
writer.write(' ');
writeVerificationErrorType(writer);
writer.write(", ");
writeReference(writer);
return true;
case Format20t:
case Format30t:
writeOpcode(writer);
@ -305,4 +313,9 @@ public class InstructionMethodItem<T extends Instruction> extends MethodItem {
Item item = ((InstructionWithReference)instruction).getReferencedItem();
ReferenceFormatter.writeReference(writer, item);
}
protected void writeVerificationErrorType(IndentingWriter writer) throws IOException {
VerificationErrorType validationErrorType = ((Instruction20bc)instruction).getValidationErrorType();
writer.write(validationErrorType.getName());
}
}

View File

@ -36,6 +36,7 @@ public enum Format {
Format11n(Instruction11n.Factory, 2),
Format11x(Instruction11x.Factory, 2),
Format12x(Instruction12x.Factory, 2),
Format20bc(Instruction20bc.Factory, 4),
Format20t(Instruction20t.Factory, 4),
Format21c(Instruction21c.Factory, 4),
Format21h(Instruction21h.Factory, 4),

View File

@ -0,0 +1,100 @@
/*
* [The "BSD licence"]
* Copyright (c) 2011 Ben Gruver
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.jf.dexlib.Code.Format;
import org.jf.dexlib.*;
import org.jf.dexlib.Code.*;
import org.jf.dexlib.Util.AnnotatedOutput;
import org.jf.dexlib.Util.NumberUtils;
public class Instruction20bc extends InstructionWithReference {
public static final Instruction.InstructionFactory Factory = new Factory();
private VerificationErrorType validationErrorType;
public Instruction20bc(Opcode opcode, VerificationErrorType validationErrorType, Item referencedItem) {
super(opcode, referencedItem, getReferenceType(referencedItem));
this.validationErrorType = validationErrorType;
}
private static ReferenceType getReferenceType(Item item) {
if (item instanceof TypeIdItem) {
return ReferenceType.type;
}
if (item instanceof FieldIdItem) {
return ReferenceType.field;
}
if (item instanceof MethodIdItem) {
return ReferenceType.method;
}
return null;
}
private Instruction20bc(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) {
super(dexFile, opcode, buffer, bufferIndex);
short val = NumberUtils.decodeUnsignedByte(buffer[bufferIndex+1]);
validationErrorType = VerificationErrorType.getValidationErrorType(val & 0x3f);
}
protected ReferenceType readReferenceType(Opcode opcode, byte[] buffer, int bufferIndex) {
short val = NumberUtils.decodeUnsignedByte(buffer[bufferIndex+1]);
short referenceType = (short)(val >> 6);
return ReferenceType.fromValidationErrorReferenceType(referenceType);
}
@Override
public Format getFormat() {
return Format.Format20bc;
}
@Override
protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) {
if(opcode == Opcode.CONST_STRING && getReferencedItem().getIndex() > 0xFFFF) {
throw new RuntimeException("String offset is too large for const-string. Use string-const/jumbo instead.");
}
out.writeByte(opcode.value);
out.writeByte((this.getReferenceType().getValidationErrorReferenceType() << 6) &
validationErrorType.getValue());
out.writeShort(getReferencedItem().getIndex());
}
public VerificationErrorType getValidationErrorType() {
return validationErrorType;
}
private static class Factory implements Instruction.InstructionFactory {
public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) {
return new Instruction20bc(dexFile, opcode, buffer, bufferIndex);
}
}
}

View File

@ -33,17 +33,27 @@ import org.jf.dexlib.Util.NumberUtils;
public abstract class InstructionWithReference extends Instruction {
private Item referencedItem;
private ReferenceType referenceType;
protected InstructionWithReference(Opcode opcode, Item referencedItem) {
super(opcode);
this.referencedItem = referencedItem;
this.referenceType = opcode.referenceType;
checkReferenceType();
}
protected InstructionWithReference(Opcode opcode, Item referencedItem, ReferenceType referenceType) {
super(opcode);
this.referencedItem = referencedItem;
this.referenceType = referenceType;
checkReferenceType();
}
protected InstructionWithReference(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) {
super(opcode);
int itemIndex = getReferencedItemIndex(buffer, bufferIndex);
this.referenceType = readReferenceType(opcode, buffer, bufferIndex);
int itemIndex = getReferencedItemIndex(buffer, bufferIndex);
lookupReferencedItem(dexFile, opcode, itemIndex);
}
@ -51,12 +61,20 @@ public abstract class InstructionWithReference extends Instruction {
return NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 2);
}
public ReferenceType getReferenceType() {
return referenceType;
}
public Item getReferencedItem() {
return referencedItem;
}
protected ReferenceType readReferenceType(Opcode opcode, byte[] buffer, int bufferIndex) {
return opcode.referenceType;
}
private void lookupReferencedItem(DexFile dexFile, Opcode opcode, int itemIndex) {
switch (opcode.referenceType) {
switch (referenceType) {
case field:
referencedItem = dexFile.FieldIdsSection.getItemByIndex(itemIndex);
return;
@ -73,7 +91,7 @@ public abstract class InstructionWithReference extends Instruction {
private void checkReferenceType() {
switch (opcode.referenceType) {
switch (referenceType) {
case field:
if (!(referencedItem instanceof FieldIdItem)) {
throw new RuntimeException(referencedItem.getClass().getSimpleName() +

View File

@ -263,6 +263,7 @@ public enum Opcode
SGET_WIDE_VOLATILE((byte)0xea, "sget-wide-volatile", ReferenceType.field, Format.Format21c, Opcode.ODEX_ONLY | Opcode.ODEXED_STATIC_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
SPUT_WIDE_VOLATILE((byte)0xeb, "sput-wide-volatile", ReferenceType.field, Format.Format21c, Opcode.ODEX_ONLY | Opcode.ODEXED_STATIC_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
THROW_VERIFICATION_ERROR((byte)0xed, "throw-verification-error", ReferenceType.none, Format.Format20bc, Opcode.ODEX_ONLY | Opcode.CAN_THROW),
EXECUTE_INLINE((byte)0xee, "execute-inline", ReferenceType.none, Format.Format35ms, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
EXECUTE_INLINE_RANGE((byte)0xef, "execute-inline/range", ReferenceType.none, Format.Format3rms, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
INVOKE_DIRECT_EMPTY((byte)0xf0, "invoke-direct-empty", ReferenceType.method, Format.Format35s, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),

View File

@ -32,11 +32,17 @@ import org.jf.dexlib.*;
public enum ReferenceType
{
string,
type,
field,
method,
none;
string(-1),
type(0),
field(1),
method(2),
none(-1);
private int validationErrorReferenceType;
private ReferenceType(int validationErrorReferenceType) {
this.validationErrorReferenceType = validationErrorReferenceType;
}
public boolean checkItem(Item item) {
switch (this) {
@ -48,9 +54,27 @@ public enum ReferenceType
return item instanceof FieldIdItem;
case method:
return item instanceof MethodIdItem;
case none:
return item == null;
}
return false;
}
public static ReferenceType fromValidationErrorReferenceType(int validationErrorReferenceType) {
switch (validationErrorReferenceType) {
case 0:
return type;
case 1:
return field;
case 2:
return method;
}
return null;
}
public int getValidationErrorReferenceType() {
if (validationErrorReferenceType == -1) {
throw new RuntimeException("This reference type cannot be referenced from a throw-validation-error" +
" instruction");
}
return validationErrorReferenceType;
}
}

View File

@ -0,0 +1,83 @@
/*
* [The "BSD licence"]
* Copyright (c) 2011 Ben Gruver
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.jf.dexlib.Code;
public enum VerificationErrorType {
None(0, "no-error"),
Generic(1, "generic-error"),
NoClass(2, "no-such-class"),
NoField(3, "no-such-field"),
NoMethod(4, "no-such-method"),
AccessClass(5, "illegal-class-access"),
AccessField(6, "illegal-field-access"),
AccessMethod(7, "illegal-method-access"),
ClassChange(8, "class-change-error"),
Instantiation(9, "instantiation-error");
private int value;
private String name;
private VerificationErrorType(int value, String name) {
this.value = value;
this.name = name;
}
public int getValue() {
return value;
}
public String getName() {
return name;
}
public static VerificationErrorType getValidationErrorType(int validationErrorType) {
switch (validationErrorType) {
case 0:
return None;
case 1:
return Generic;
case 2:
return NoClass;
case 3:
return NoField;
case 4:
return NoMethod;
case 5:
return AccessClass;
case 6:
return AccessField;
case 7:
return AccessMethod;
case 8:
return ClassChange;
case 9:
return Instantiation;
}
return null;
}
}

View File

@ -331,6 +331,18 @@ ACCESS_SPEC
| 'volatile'
| 'transient';
VERIFICATION_ERROR_TYPE
: 'no-error'
| 'generic-error'
| 'no-such-class'
| 'no-such-field'
| 'no-such-method'
| 'illegal-class-access'
| 'illegal-field-access'
| 'illegal-method-access'
| 'class-change-error'
| 'instantiation-error';
VTABLE_OFFSET
: 'vtable@0x' HEX_DIGIT+;
@ -434,6 +446,9 @@ INSTRUCTION_FORMAT12x
| 'div-double/2addr'
| 'rem-double/2addr';
INSTRUCTION_FORMAT20bc
: 'throw-verification-error';
INSTRUCTION_FORMAT20t
: 'goto/16';

View File

@ -419,6 +419,7 @@ the indicated type OR an identifier, depending on the context*/
simple_name
: SIMPLE_NAME
| ACCESS_SPEC -> SIMPLE_NAME[$ACCESS_SPEC]
| VERIFICATION_ERROR_TYPE -> SIMPLE_NAME[$VERIFICATION_ERROR_TYPE]
| POSITIVE_INTEGER_LITERAL -> SIMPLE_NAME[$POSITIVE_INTEGER_LITERAL]
| NEGATIVE_INTEGER_LITERAL -> SIMPLE_NAME[$NEGATIVE_INTEGER_LITERAL]
| INTEGER_LITERAL -> SIMPLE_NAME[$INTEGER_LITERAL]
@ -684,6 +685,11 @@ instruction returns [int size]
| //e.g. move v1 v2
instruction_format12x REGISTER COMMA REGISTER {$size = Format.Format12x.size;}
-> ^(I_STATEMENT_FORMAT12x[$start, "I_STATEMENT_FORMAT12x"] instruction_format12x REGISTER REGISTER)
| //e.g. throw-verification-error generic-error, Lsome/class;
INSTRUCTION_FORMAT20bc VERIFICATION_ERROR_TYPE COMMA (CLASS_DESCRIPTOR | fully_qualified_field | fully_qualified_method)
{
throwOdexedInstructionException(input, $INSTRUCTION_FORMAT20bc.text);
}
| //e.g. goto/16 endloop:
INSTRUCTION_FORMAT20t label_ref_or_offset {$size = Format.Format20t.size;}
-> ^(I_STATEMENT_FORMAT20t[$start, "I_STATEMENT_FORMAT20t"] INSTRUCTION_FORMAT20t label_ref_or_offset)

View File

@ -364,6 +364,11 @@ Type = {PrimitiveType} | {ClassDescriptor} | {ArrayDescriptor}
return newToken(ACCESS_SPEC);
}
"no-error" | "generic-error" | "no-such-class" | "no-such-field" | "no-such-method" | "illegal-class-access" |
"illegal-field-access" | "illegal-method-access" | "class-change-error" | "instantiation-error" {
return newToken(VERIFICATION_ERROR_TYPE);
}
"vtable@0x" {HexDigit}+ { return newToken(VTABLE_OFFSET); }
"field@0x" {HexDigit}+ { return newToken(FIELD_OFFSET); }
@ -407,6 +412,10 @@ Type = {PrimitiveType} | {ClassDescriptor} | {ArrayDescriptor}
return newToken(INSTRUCTION_FORMAT12x);
}
"throw-verification-error" {
return newToken(INSTRUCTION_FORMAT20bc);
}
"goto/16" {
return newToken(INSTRUCTION_FORMAT20t);
}