diff --git a/baksmali/src/main/java/org/jf/baksmali/Adaptors/Debug/LineNumberMethodItem.java b/baksmali/src/main/java/org/jf/baksmali/Adaptors/Debug/LineNumberMethodItem.java index 96dbd8d9..91473bdc 100644 --- a/baksmali/src/main/java/org/jf/baksmali/Adaptors/Debug/LineNumberMethodItem.java +++ b/baksmali/src/main/java/org/jf/baksmali/Adaptors/Debug/LineNumberMethodItem.java @@ -48,7 +48,7 @@ public class LineNumberMethodItem extends DebugMethodItem { @Override public boolean writeTo(IndentingWriter writer) throws IOException { writer.write(".line "); - writer.printSignedIntAsDec(lineNumber); + writer.printUnsignedIntAsDec(lineNumber); return true; } } diff --git a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/BaseDexReader.java b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/BaseDexReader.java index 85ae4c32..71c563fe 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/BaseDexReader.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/BaseDexReader.java @@ -128,6 +128,46 @@ public class BaseDexReader { return result; } + /** + * Reads a "big" uleb128 that can legitimately be > 2^31. The value is returned as a signed integer, with the + * expected semantics of re-interpreting an unsigned value as a signed value. + * + * @return The unsigned value, reinterpreted as a signed int + */ + public int readBigUleb128() { + int end = offset; + int currentByteValue; + int result; + byte[] buf = dexBuf.buf; + + result = buf[end++] & 0xff; + if (result > 0x7f) { + currentByteValue = buf[end++] & 0xff; + result = (result & 0x7f) | ((currentByteValue & 0x7f) << 7); + if (currentByteValue > 0x7f) { + currentByteValue = buf[end++] & 0xff; + result |= (currentByteValue & 0x7f) << 14; + if (currentByteValue > 0x7f) { + currentByteValue = buf[end++] & 0xff; + result |= (currentByteValue & 0x7f) << 21; + if (currentByteValue > 0x7f) { + currentByteValue = buf[end++]; + + // MSB shouldn't be set on last byte + if (currentByteValue < 0) { + throw new ExceptionWithContext( + "Invalid uleb128 integer encountered at offset 0x%x", offset); + } + result |= currentByteValue << 28; + } + } + } + } + + offset = end; + return result; + } + public void skipUleb128() { int end = offset; byte currentByteValue; diff --git a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/util/DebugInfo.java b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/util/DebugInfo.java index d74addf6..ffa4c20d 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/util/DebugInfo.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/util/DebugInfo.java @@ -99,8 +99,7 @@ public abstract class DebugInfo implements Iterable { @Override public Iterator iterator() { DexReader reader = dexFile.readerAt(debugInfoOffset); - // TODO: this unsigned value could legitimally be > MAX_INT - final int lineNumberStart = reader.readSmallUleb128(); + final int lineNumberStart = reader.readBigUleb128(); int registerCount = methodImpl.getRegisterCount(); //TODO: does dalvik allow references to invalid registers? diff --git a/dexlib2/src/main/java/org/jf/dexlib2/iface/debug/LineNumber.java b/dexlib2/src/main/java/org/jf/dexlib2/iface/debug/LineNumber.java index daa9d0a5..8b860d15 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/iface/debug/LineNumber.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/iface/debug/LineNumber.java @@ -32,5 +32,11 @@ package org.jf.dexlib2.iface.debug; public interface LineNumber extends DebugItem { + /** + * The line number associated with this code address. This value should be treated as an unsigned integer, with + * negative values indicating a value > 2^31, using the usual two's complement semantics. + * + * @return The line number associated with this code address. + */ int getLineNumber(); } diff --git a/util/src/main/java/org/jf/util/IndentingWriter.java b/util/src/main/java/org/jf/util/IndentingWriter.java index 517b4b9d..95d6c320 100644 --- a/util/src/main/java/org/jf/util/IndentingWriter.java +++ b/util/src/main/java/org/jf/util/IndentingWriter.java @@ -192,6 +192,26 @@ public class IndentingWriter extends Writer { writeLine(buffer, bufferIndex, 24-bufferIndex); } + public void printSignedLongAsDec(long value) throws IOException { + int bufferIndex = 23; + + if (value < 0) { + value *= -1; + write('-'); + } + + do { + long digit = value % 10; + buffer[bufferIndex--] = (char)(digit + '0'); + + value = value / 10; + } while (value != 0); + + bufferIndex++; + + writeLine(buffer, bufferIndex, 24-bufferIndex); + } + public void printSignedIntAsDec(int value) throws IOException { int bufferIndex = 15; @@ -211,4 +231,14 @@ public class IndentingWriter extends Writer { writeLine(buffer, bufferIndex, 16-bufferIndex); } + + public void printUnsignedIntAsDec(int value) throws IOException { + int bufferIndex = 15; + + if (value < 0) { + printSignedLongAsDec(value & 0xFFFFFFFFL); + } else { + printSignedIntAsDec(value); + } + } } diff --git a/util/src/test/java/org/jf/util/IndentingWriterTest.java b/util/src/test/java/org/jf/util/IndentingWriterTest.java new file mode 100644 index 00000000..457262b5 --- /dev/null +++ b/util/src/test/java/org/jf/util/IndentingWriterTest.java @@ -0,0 +1,51 @@ +/* + * Copyright 2013, Google Inc. + * All rights reserved. + * + * 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 Inc. 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 org.jf.util; + +import junit.framework.Assert; +import org.junit.Test; + +import java.io.IOException; +import java.io.StringWriter; + +public class IndentingWriterTest { + @Test + public void testPrintSignedLongAsDec() throws IOException { + StringWriter stringWriter = new StringWriter(); + IndentingWriter writer = new IndentingWriter(stringWriter); + + writer.printUnsignedIntAsDec(-1); + writer.close(); + + Assert.assertEquals("4294967295", stringWriter.toString()); + } +}