blob: 87829fe4fdf0de06a6970928702d4505f4e45d06 [file] [log] [blame]
// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.dex;
import static com.android.tools.r8.dex.Constants.DEX_FILE_MAGIC_PREFIX;
import com.android.tools.r8.ProgramResource;
import com.android.tools.r8.ResourceException;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.DexVersion;
import com.android.tools.r8.utils.StringUtils;
import java.io.IOException;
import java.nio.BufferUnderflowException;
import java.nio.ByteOrder;
import java.util.Optional;
/**
* {@link BinaryReader} for Dex content.
*/
public class DexReader extends BinaryReader {
private final DexVersion version;
public DexReader(ProgramResource resource) throws ResourceException, IOException {
super(resource);
version = parseMagic(buffer, 0);
}
/**
* Returns a File that contains the bytes provided as argument. Used for testing.
*
* @param bytes contents of the file
*/
DexReader(Origin origin, byte[] bytes) {
super(origin, bytes);
version = parseMagic(buffer, 0);
}
DexReader(Origin origin, byte[] bytes, int offset) {
super(origin, bytes);
version = parseMagic(buffer, offset);
}
// Parse the magic header and determine the dex file version.
private DexVersion parseMagic(CompatByteBuffer buffer, int offset) {
try {
buffer.get();
buffer.rewind();
} catch (BufferUnderflowException e) {
throw new CompilationError("Dex file is empty", origin);
}
int index = offset;
for (byte prefixByte : DEX_FILE_MAGIC_PREFIX) {
byte actualByte = buffer.get(index++);
if (actualByte != prefixByte) {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(
"Dex file has invalid header, expected "
+ prefixByte
+ " got "
+ actualByte
+ ". Next bytes are ");
for (int i = 0; i < 10; i++) {
if (buffer.hasRemaining()) {
stringBuilder.append(StringUtils.hexString(buffer.get(), 2));
stringBuilder.append(",");
}
}
throw new CompilationError(stringBuilder.toString(), origin);
}
}
char versionByte0 = (char) buffer.get(index++);
char versionByte1 = (char) buffer.get(index++);
char versionByte2 = (char) buffer.get(index++);
Optional<DexVersion> maybeVersion =
DexVersion.getDexVersion(versionByte0, versionByte1, versionByte2);
if (!maybeVersion.isPresent()) {
throw new CompilationError(
"Unsupported DEX file version: "
+ versionByte0
+ versionByte1
+ versionByte2,
origin);
}
if (buffer.get(index++) != '\0') {
throw new CompilationError("Dex file has invalid header", origin);
}
return maybeVersion.get();
}
@Override
void setByteOrder() {
// Make sure we set the right endian for reading.
buffer.order(ByteOrder.LITTLE_ENDIAN);
int endian = buffer.getInt(Constants.ENDIAN_TAG_OFFSET);
if (endian == Constants.REVERSE_ENDIAN_CONSTANT) {
buffer.order(ByteOrder.BIG_ENDIAN);
} else {
if (endian != Constants.ENDIAN_CONSTANT) {
throw new CompilationError("Unable to determine endianess for reading dex file.");
}
}
}
DexVersion getDexVersion() {
return version;
}
}