Add support for 64-bit oat files

This commit is contained in:
Ben Gruver 2015-10-17 14:47:10 -07:00
parent e202aeede5
commit b8e255f389
2 changed files with 103 additions and 64 deletions

View File

@ -97,6 +97,23 @@ public class BaseDexBuffer {
(((long)buf[offset+7]) << 56); (((long)buf[offset+7]) << 56);
} }
public int readLongAsSmallUint(int offset) {
byte[] buf = this.buf;
offset += baseOffset;
long result = (buf[offset] & 0xff) |
((buf[offset+1] & 0xff) << 8) |
((buf[offset+2] & 0xff) << 16) |
((buf[offset+3] & 0xffL) << 24) |
((buf[offset+4] & 0xffL) << 32) |
((buf[offset+5] & 0xffL) << 40) |
((buf[offset+6] & 0xffL) << 48) |
(((long)buf[offset+7]) << 56);
if (result < 0 || result > Integer.MAX_VALUE) {
throw new ExceptionWithContext("Encountered out-of-range ulong at offset 0x%x", offset);
}
return (int)result;
}
public int readInt(int offset) { public int readInt(int offset) {
byte[] buf = this.buf; byte[] buf = this.buf;
offset += baseOffset; offset += baseOffset;

View File

@ -49,7 +49,7 @@ import java.util.List;
public class OatFile extends BaseDexBuffer { public class OatFile extends BaseDexBuffer {
private static final byte[] ELF_MAGIC = new byte[] { 0x7f, 'E', 'L', 'F' }; private static final byte[] ELF_MAGIC = new byte[] { 0x7f, 'E', 'L', 'F' };
private static final byte[] OAT_MAGIC = new byte[] { 'o', 'a', 't', '\n' }; private static final byte[] OAT_MAGIC = new byte[] { 'o', 'a', 't', '\n' };
private static final int ELF_HEADER_SIZE = 52; private static final int MIN_ELF_HEADER_SIZE = 52;
// These are the "known working" versions that I have manually inspected the source for. // These are the "known working" versions that I have manually inspected the source for.
// Later version may or may not work, depending on what changed. // Later version may or may not work, depending on what changed.
@ -60,18 +60,27 @@ public class OatFile extends BaseDexBuffer {
public static final int SUPPORTED = 1; public static final int SUPPORTED = 1;
public static final int UNKNOWN = 2; public static final int UNKNOWN = 2;
private final boolean is64bit;
@Nonnull private final OatHeader oatHeader; @Nonnull private final OatHeader oatHeader;
@Nonnull private final Opcodes opcodes; @Nonnull private final Opcodes opcodes;
public OatFile(@Nonnull byte[] buf) { public OatFile(@Nonnull byte[] buf) {
super(buf); super(buf);
if (buf.length < ELF_HEADER_SIZE) { if (buf.length < MIN_ELF_HEADER_SIZE) {
throw new NotAnOatFileException(); throw new NotAnOatFileException();
} }
verifyMagic(buf); verifyMagic(buf);
if (buf[4] == 1) {
is64bit = false;
} else if (buf[4] == 2) {
is64bit = true;
} else {
throw new InvalidOatFileException(String.format("Invalid word-size value: %x", buf[5]));
}
OatHeader oatHeader = null; OatHeader oatHeader = null;
SymbolTable symbolTable = getSymbolTable(); SymbolTable symbolTable = getSymbolTable();
for (Symbol symbol: symbolTable.getSymbols()) { for (Symbol symbol: symbolTable.getSymbols()) {
@ -258,9 +267,18 @@ public class OatFile extends BaseDexBuffer {
@Nonnull @Nonnull
private List<SectionHeader> getSections() { private List<SectionHeader> getSections() {
final int offset = readSmallUint(32); final int offset;
final int entrySize = readUshort(46); final int entrySize;
final int entryCount = readUshort(48); final int entryCount;
if (is64bit) {
offset = readLongAsSmallUint(40);
entrySize = readUshort(58);
entryCount = readUshort(60);
} else {
offset = readSmallUint(32);
entrySize = readUshort(46);
entryCount = readUshort(48);
}
if (offset + (entrySize * entryCount) > buf.length) { if (offset + (entrySize * entryCount) > buf.length) {
throw new InvalidOatFileException("The ELF section headers extend past the end of the file"); throw new InvalidOatFileException("The ELF section headers extend past the end of the file");
@ -271,7 +289,11 @@ public class OatFile extends BaseDexBuffer {
if (index < 0 || index >= entryCount) { if (index < 0 || index >= entryCount) {
throw new IndexOutOfBoundsException(); throw new IndexOutOfBoundsException();
} }
return new SectionHeader(offset + (index * entrySize)); if (is64bit) {
return new SectionHeader64Bit(offset + (index * entrySize));
} else {
return new SectionHeader32Bit(offset + (index * entrySize));
}
} }
@Override public int size() { @Override public int size() {
@ -304,43 +326,35 @@ public class OatFile extends BaseDexBuffer {
} }
} }
private class SectionHeader { private abstract class SectionHeader {
private final int offset; protected final int offset;
public static final int TYPE_DYNAMIC_SYMBOL_TABLE = 11; public static final int TYPE_DYNAMIC_SYMBOL_TABLE = 11;
public SectionHeader(int offset) { this.offset = offset; }
@Nonnull public String getName() { return getSectionNameStringTable().getString(readSmallUint(offset)); }
public int getType() { return readInt(offset + 4); }
public abstract long getAddress();
public abstract int getOffset();
public abstract int getSize();
public abstract int getLink();
public abstract int getEntrySize();
}
public SectionHeader(int offset) { private class SectionHeader32Bit extends SectionHeader {
this.offset = offset; public SectionHeader32Bit(int offset) { super(offset); }
} @Override public long getAddress() { return readInt(offset + 12) & 0xFFFFFFFFL; }
@Override public int getOffset() { return readSmallUint(offset + 16); }
@Override public int getSize() { return readSmallUint(offset + 20); }
@Override public int getLink() { return readSmallUint(offset + 24); }
@Override public int getEntrySize() { return readSmallUint(offset + 36); }
}
@Nonnull private class SectionHeader64Bit extends SectionHeader {
public String getName() { public SectionHeader64Bit(int offset) { super(offset); }
return getSectionNameStringTable().getString(readSmallUint(offset)); @Override public long getAddress() { return readLong(offset + 16); }
} @Override public int getOffset() { return readLongAsSmallUint(offset + 24); }
@Override public int getSize() { return readLongAsSmallUint(offset + 32); }
public int getType() { @Override public int getLink() { return readSmallUint(offset + 40); }
return readInt(offset + 4); @Override public int getEntrySize() { return readLongAsSmallUint(offset + 56); }
}
public long getAddress() {
return readInt(offset + 12) & 0xFFFFFFFFL;
}
public int getOffset() {
return readSmallUint(offset + 16);
}
public int getSize() {
return readSmallUint(offset + 20);
}
public int getLink() {
return readSmallUint(offset + 24);
}
public int getEntrySize() {
return readSmallUint(offset + 36);
}
} }
class SymbolTable { class SymbolTable {
@ -371,7 +385,11 @@ public class OatFile extends BaseDexBuffer {
if (index < 0 || index >= entryCount) { if (index < 0 || index >= entryCount) {
throw new IndexOutOfBoundsException(); throw new IndexOutOfBoundsException();
} }
return new Symbol(offset + index * entrySize); if (is64bit) {
return new Symbol64(offset + index * entrySize);
} else {
return new Symbol32(offset + index * entrySize);
}
} }
@Override public int size() { @Override public int size() {
@ -380,29 +398,13 @@ public class OatFile extends BaseDexBuffer {
}; };
} }
public class Symbol { public abstract class Symbol {
private final int offset; protected final int offset;
public Symbol(int offset) { this.offset = offset; }
public Symbol(int offset) { @Nonnull public abstract String getName();
this.offset = offset; public abstract long getValue();
} public abstract int getSize();
public abstract int getSectionIndex();
@Nonnull
public String getName() {
return stringTable.getString(readSmallUint(offset));
}
public int getValue() {
return readSmallUint(offset + 4);
}
public int getSize() {
return readSmallUint(offset + 8);
}
public int getSectionIndex() {
return readUshort(offset + 14);
}
public int getFileOffset() { public int getFileOffset() {
SectionHeader sectionHeader; SectionHeader sectionHeader;
@ -427,6 +429,26 @@ public class OatFile extends BaseDexBuffer {
return (int)fileOffset; return (int)fileOffset;
} }
} }
public class Symbol32 extends Symbol {
public Symbol32(int offset) { super(offset); }
@Nonnull
public String getName() { return stringTable.getString(readSmallUint(offset)); }
public long getValue() { return readSmallUint(offset + 4); }
public int getSize() { return readSmallUint(offset + 8); }
public int getSectionIndex() { return readUshort(offset + 14); }
}
public class Symbol64 extends Symbol {
public Symbol64(int offset) { super(offset); }
@Nonnull
public String getName() { return stringTable.getString(readSmallUint(offset)); }
public long getValue() { return readLong(offset + 8); }
public int getSize() { return readLongAsSmallUint(offset + 16); }
public int getSectionIndex() { return readUshort(offset + 6); }
}
} }
private class StringTable { private class StringTable {