Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 1 addition & 27 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -31,32 +31,6 @@
apply plugin: 'idea'

version = '3.0.9'
def jcommanderVersion = ''

if (!('release' in gradle.startParameter.taskNames)) {
// we compile against 1.48 normally, to match what's in AOSP, but switch to a newer version
// for release, because it has some fixes required when running on Android
jcommanderVersion = 'com.beust:jcommander:1.48'

def versionSuffix
try {
def git = org.eclipse.jgit.api.Git.open(file('.'))
def head = git.getRepository().getRef('HEAD')
versionSuffix = head.getObjectId().abbreviate(8).name()

if (!git.status().call().clean) {
versionSuffix += '-dirty'
}
} catch (Exception) {
// In case we can't get the commit for some reason,
// just use -dev
versionSuffix = 'dev'
}

version += "-${versionSuffix}"
} else {
jcommanderVersion = 'com.beust:jcommander:1.64'
}

// Note: please don't use this. This is strictly for the official releases
// that are posted on, e.g. the bitbucket download page.
Expand Down Expand Up @@ -108,7 +82,7 @@ subprojects {
proguard_gradle: 'com.guardsquare:proguard-gradle:7.1.0',
dx: 'com.google.android.tools:dx:1.7',
gson: 'com.google.code.gson:gson:2.3.1',
jcommander: jcommanderVersion
jcommander: 'org.jcommander:jcommander:1.85'
]
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ protected int getTriesSize() {
}

@Override
protected int getDebugOffset() {
public int getDebugOffset() {
CDexBackedDexFile cdexFile = ((CDexBackedDexFile) dexFile);

int debugTableItemOffset = (method.methodIndex / 16) * 4;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,13 +104,14 @@ protected DexBackedDexFile(@Nullable Opcodes opcodes,
boolean verifyMagic,
int header_offset) {
if (offset < 0 || offset > buf.length) {
throw new IndexOutOfBoundsException(
throw new DexUtil.InvalidFile(
String.format("Invalid offset %d for buffer of length %d", offset, buf.length));
}
if (header_offset < 0 || buf.length - offset < header_offset) {
throw new IndexOutOfBoundsException(String.format(
"Invalid header_offset %d for buffer of length %d at offset %d",
header_offset, buf.length, offset));
throw new DexUtil.InvalidFile(
String.format(
"Invalid header_offset %d for buffer of length %d at offset %d",
header_offset, buf.length, offset));
}

dexBuffer = new DexBuffer(buf, offset);
Expand Down Expand Up @@ -179,6 +180,16 @@ protected DexBackedDexFile(@Nullable Opcodes opcodes,
if (container_off != header_offset) {
throw new DexUtil.InvalidFile(String.format("Unexpected container offset in header"));
}

if (mapOffset < 0 || mapOffset > containerSize - 4) {
throw new DexUtil.InvalidFile(String.format("Invalid mapOffset %d", mapOffset));
}

// Eagerly read mapSize to validate map bounds
int mapSize = dexBuffer.readSmallUint(mapOffset);
if (mapOffset + 4 + mapSize * MapItem.ITEM_SIZE > containerSize) {
throw new DexUtil.InvalidFile("Map extends beyond container bounds");
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ public int size() {
return Collections.emptyList();
}

protected int getDebugOffset() {
public int getDebugOffset() {
return dexFile.getDataBuffer().readInt(codeOffset + CodeItem.DEBUG_INFO_OFFSET);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -330,18 +330,19 @@ public String getKeyValue(@Nonnull String key) {
int keyEndOffset = offset;

String k = new String(buf, keyStartOffset, keyEndOffset - keyStartOffset);
int valueStartOffset = ++offset;
while (offset < endOffset && buf[offset] != '\0') {
offset++;
}
if (offset >= endOffset) {
throw new InvalidOatFileException("Oat file contains truncated key value store");
}

if (k.equals(key)) {
int valueStartOffset = ++offset;
while (offset < endOffset && buf[offset] != '\0') {
offset++;
}
if (offset >= endOffset) {
throw new InvalidOatFileException("Oat file contains truncated key value store");
}
int valueEndOffset = offset;
return new String(buf, valueStartOffset, valueEndOffset - valueStartOffset);
return new String(buf, valueStartOffset, offset - valueStartOffset);
}
offset++;

offset++; // Move past the null terminator of the value
}
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
package com.android.tools.smali.dexlib2.dexbacked;

import com.android.tools.smali.dexlib2.Opcodes;
import com.android.tools.smali.dexlib2.iface.DexFile;
import com.android.tools.smali.dexlib2.dexbacked.DexBackedDexFile.NotADexFile;
import com.android.tools.smali.dexlib2.iface.MultiDexContainer;
import com.android.tools.smali.dexlib2.util.DexUtil;
import com.android.tools.smali.dexlib2.util.DexUtil.InvalidFile;
Expand Down Expand Up @@ -76,6 +76,7 @@ public interface ZipDexEntry extends DexEntry<DexBackedDexFile> {

private final File zipFilePath;
@Nullable private final Opcodes opcodes;
private final boolean ignoreInvalid;
private Map<String, ZipDexEntry> entries;

/**
Expand All @@ -84,8 +85,20 @@ public interface ZipDexEntry extends DexEntry<DexBackedDexFile> {
* @param zipFilePath The path to the zip file
*/
public ZipDexContainer(@Nonnull File zipFilePath, @Nullable Opcodes opcodes) {
this(zipFilePath, opcodes, true);
}

/**
* Constructs a new ZipDexContainer for the given zip file
*
* @param zipFilePath The path to the zip file
* @param ignoreInvalid If true, invalid dex entries will be silently ignored
*/
public ZipDexContainer(
@Nonnull File zipFilePath, @Nullable Opcodes opcodes, boolean ignoreInvalid) {
this.zipFilePath = zipFilePath;
this.opcodes = opcodes;
this.ignoreInvalid = ignoreInvalid;
}

/**
Expand Down Expand Up @@ -144,40 +157,54 @@ private void loadDexFilesFromZipEntry(@Nonnull ZipFile zipFile, @Nonnull ZipEntr
try (InputStream inputStream = zipFile.getInputStream(entry)) {
byte[] buf = InputStreamUtil.toByteArray(inputStream);
for (int offset = 0, i = 0; offset < buf.length; i++) {
DexBackedDexFile dex = new DexBackedDexFile(opcodes, buf, 0, true, offset);
boolean hasSingleEntry = dex.isDexContainerFirstEntry() &&
dex.isDexContainerLastEntry();
String entryName = entry.getName() + (hasSingleEntry ? "" : ("/" + i));
ZipDexEntry dexEntry = new ZipDexEntry() {
@Nonnull
@Override
public String getEntryName() {
return entryName;
}

@Nonnull
@Override
public DexBackedDexFile getDexFile() {
return dex;
}

@Nonnull
@Override
public MultiDexContainer<DexBackedDexFile> getContainer() {
return ZipDexContainer.this;
}

@Override
public long getCrc() {
return entryCrc;
}
};
entries.put(entryName, dexEntry);
offset += dex.getFileSize();
if (dex.isDexContainerLastEntry()) {
break;
}
};
final int headerOffset = offset;
boolean isLast;
int fileSize;
try {
DexBackedDexFile dex = new DexBackedDexFile(opcodes, buf, 0, true, headerOffset);
isLast = dex.isDexContainerLastEntry();
fileSize = dex.getFileSize();
} catch (NotADexFile | InvalidFile | UnsupportedFile ex) {
if (ignoreInvalid) {
return;
}
isLast = true;
fileSize = buf.length - offset;
}
final String entryName = entry.getName() + (i == 0 && isLast ? "" : ("/" + i));
ZipDexEntry dexEntry =
new ZipDexEntry() {
@Nonnull
@Override
public String getEntryName() {
return entryName;
}

// throws InvalidFile if the dex file has an invalid header.
// throws UnsupportedFile if the dex file version is not supported.
@Nonnull
@Override
public DexBackedDexFile getDexFile() {
return new DexBackedDexFile(opcodes, buf, 0, true, headerOffset);
}

@Nonnull
@Override
public MultiDexContainer<DexBackedDexFile> getContainer() {
return ZipDexContainer.this;
}

@Override
public long getCrc() {
return entryCrc;
}
};
entries.put(entryName, dexEntry);
offset += fileSize;
if (isLast) {
return;
}
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,10 @@ public class BuilderClassDef extends BaseTypeReference implements ClassDef {
methods = Collections.emptyList();
}
if (staticFields == null) {
staticFields = Collections.emptySortedSet();
staticFields = Collections.unmodifiableSortedSet(new java.util.TreeSet<>());
}
if (instanceFields == null) {
instanceFields = Collections.emptySortedSet();
instanceFields = Collections.unmodifiableSortedSet(new java.util.TreeSet<>());
}

this.type = type;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ public Set<ImmutableItem> toSet(@Nullable final Iterable<? extends Item> iterabl
public SortedSet<ImmutableItem> toSortedSet(@Nonnull Comparator<? super ImmutableItem> comparator,
@Nullable final Iterable<? extends Item> iterable) {
if (iterable == null) {
return Collections.emptySortedSet();
return unmodifiableSortedSet(new java.util.TreeSet<ImmutableItem>(comparator));
}

boolean needsCopy = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public class ImmutableUtils {

@Nonnull public static <T> SortedSet<T> nullToEmptySortedSet(@Nullable SortedSet<T> set) {
if (set == null) {
return Collections.emptySortedSet();
return unmodifiableSortedSet(new java.util.TreeSet<T>());
}
return unmodifiableSortedSet(set);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ public static String repeat(String string, int count) {
}

if (count <= 1) {
if (count >= 0) {
if (count < 0) {
throw new IllegalArgumentException("invalid count: " + count);
}
return (count == 0) ? "" : string;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* Copyright 2026, Google LLC
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Google LLC nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "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 COPYRIGHT
* OWNER OR CONTRIBUTORS 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 com.android.tools.smali.util;

import org.junit.Assert;
import org.junit.Test;

public class StringUtilsTest {
@Test
public void testRepeat() {
Assert.assertEquals("", StringUtils.repeat("a", 0));
Assert.assertEquals("a", StringUtils.repeat("a", 1));
Assert.assertEquals("aa", StringUtils.repeat("a", 2));
Assert.assertEquals("aaaaa", StringUtils.repeat("a", 5));

Assert.assertEquals("", StringUtils.repeat("", 0));
Assert.assertEquals("", StringUtils.repeat("", 1));
Assert.assertEquals("", StringUtils.repeat("", 5));
}

@Test(expected = IllegalArgumentException.class)
public void testRepeatNegative() {
StringUtils.repeat("a", -1);
}
}
11 changes: 7 additions & 4 deletions smali/run_antlr.sh
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ if [[ -z "$ANTLR_JARS" ]]; then
exit 1
fi

# cd to the script directory so we can use relative paths
cd "$SCRIPT_DIR"

function run_antlr() {
CLASSPATH="$ANTLR_JARS" java 2>&1 "$ANTLR_MAIN" "$@"
}
Expand Down Expand Up @@ -100,12 +103,12 @@ function cleanup_tokens {
[[ -f "$TOKEN_FILE" ]] && rm "$TOKEN_FILE"
}

generate_file "$SCRIPT_DIR/src/main/java/com/android/tools/smali/smali/smaliParser.java" "$SCRIPT_DIR/src/main/antlr/smaliParser.g"
generate_file "$SCRIPT_DIR/src/main/java/com/android/tools/smali/smali/smaliTreeWalker.java" "$SCRIPT_DIR/src/main/antlr/smaliTreeWalker.g"
generate_file "src/main/java/com/android/tools/smali/smali/smaliParser.java" "src/main/antlr/smaliParser.g"
generate_file "src/main/java/com/android/tools/smali/smali/smaliTreeWalker.java" "src/main/antlr/smaliTreeWalker.g"

# Clean up the tokens, no longer necessary once the tree walker is generated
cleanup_tokens "$SCRIPT_DIR/src/main/java/com/android/tools/smali/smali/smaliParser.java"
cleanup_tokens "$SCRIPT_DIR/src/main/java/com/android/tools/smali/smali/smaliTreeWalker.java"
cleanup_tokens "src/main/java/com/android/tools/smali/smali/smaliParser.java"
cleanup_tokens "src/main/java/com/android/tools/smali/smali/smaliTreeWalker.java"

# Uncomment to run interactively
#run_antlr "$@"
9 changes: 6 additions & 3 deletions smali/run_jflex.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@ EXPECTED_JFLEX_VERSION_STR="This is JFlex $EXPECTED_JFLEX_VERSION"
SCRIPT_DIR=`dirname $0`
echo $SCRIPT_DIR

TOP_PATH="$SCRIPT_DIR/../../.."
# cd to the script directory so we can use relative paths
cd "$SCRIPT_DIR"

TOP_PATH="../../.."

# Run the java jar when 'JFLEX' is not in the environment
function exec_jar_jflex {
Expand Down Expand Up @@ -52,10 +55,10 @@ then
exit 1
fi

JAVA_FILE=$SCRIPT_DIR/src/main/java/com/android/tools/smali/smali/smaliFlexLexer.java
JAVA_FILE=src/main/java/com/android/tools/smali/smali/smaliFlexLexer.java
rm -f "$JAVA_FILE"

"$JFLEX" --nobak -d "$SCRIPT_DIR/src/main/java/com/android/tools/smali/smali" "$SCRIPT_DIR/src/main/jflex/smaliLexer.jflex"
"$JFLEX" --nobak -d "src/main/java/com/android/tools/smali/smali" "src/main/jflex/smaliLexer.jflex"

# delete trailing space from end of each line to make gerrit happy
sed 's/[ ]*$//' "$JAVA_FILE" > "$JAVA_FILE.tmp"
Expand Down
Loading