summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author android-build-team Robot <android-build-team-robot@google.com> 2020-06-02 07:13:00 +0000
committer android-build-team Robot <android-build-team-robot@google.com> 2020-06-02 07:13:00 +0000
commitc57334a08e04ebdec7919e604b05e7fa2ceb9a05 (patch)
treefb6abb08148000f9e1b59aead2ff4b27dd9bcb72
parent786170d33819d7e6f5c063503ce3751ac04fc1d8 (diff)
parent0d8a211fa279fca639434b3a4ec987adaaa1e664 (diff)
Snap for 6549967 from 0d8a211fa279fca639434b3a4ec987adaaa1e664 to mainline-release
Change-Id: Iaa4badbbaa8c846eb11385a969a02043fc355872
-rw-r--r--dexlayout/dex_writer.cc7
-rw-r--r--libdexfile/dex/standard_dex_file.cc9
-rw-r--r--libdexfile/dex/standard_dex_file.h4
-rw-r--r--runtime/class_linker.cc13
-rw-r--r--runtime/mirror/class-inl.h6
-rw-r--r--runtime/mirror/dex_cache-inl.h1
-rw-r--r--runtime/native/dalvik_system_VMRuntime.cc10
-rw-r--r--test/180-native-default-method/build30
-rw-r--r--test/180-native-default-method/expected.txt1
-rw-r--r--test/180-native-default-method/info.txt3
-rw-r--r--test/180-native-default-method/jasmin/TestClass.j25
-rw-r--r--test/180-native-default-method/jasmin/TestInterface.j19
-rw-r--r--test/180-native-default-method/src/Main.java32
13 files changed, 144 insertions, 16 deletions
diff --git a/dexlayout/dex_writer.cc b/dexlayout/dex_writer.cc
index 268abe4415..7f05ae89fb 100644
--- a/dexlayout/dex_writer.cc
+++ b/dexlayout/dex_writer.cc
@@ -794,8 +794,11 @@ void DexWriter::WriteHeader(Stream* stream) {
StandardDexFile::Header header;
if (CompactDexFile::IsMagicValid(header_->Magic())) {
StandardDexFile::WriteMagic(header.magic_);
- // TODO: Should we write older versions based on the feature flags?
- StandardDexFile::WriteCurrentVersion(header.magic_);
+ if (header_->SupportDefaultMethods()) {
+ StandardDexFile::WriteCurrentVersion(header.magic_);
+ } else {
+ StandardDexFile::WriteVersionBeforeDefaultMethods(header.magic_);
+ }
} else {
// Standard dex -> standard dex, just reuse the same header.
static constexpr size_t kMagicAndVersionLen =
diff --git a/libdexfile/dex/standard_dex_file.cc b/libdexfile/dex/standard_dex_file.cc
index 9c4cb45f8b..1f1bc19ae2 100644
--- a/libdexfile/dex/standard_dex_file.cc
+++ b/libdexfile/dex/standard_dex_file.cc
@@ -32,9 +32,9 @@ const uint8_t StandardDexFile::kDexMagicVersions[StandardDexFile::kNumDexVersion
{'0', '3', '7', '\0'},
// Dex version 038: Android "O" and beyond.
{'0', '3', '8', '\0'},
- // Dex verion 039: Android "P" and beyond.
+ // Dex version 039: Android "P" and beyond.
{'0', '3', '9', '\0'},
- // Dex verion 040: beyond Android "10" (previously known as Android "Q").
+ // Dex version 040: beyond Android "10" (previously known as Android "Q").
{'0', '4', '0', '\0'},
};
@@ -48,6 +48,11 @@ void StandardDexFile::WriteCurrentVersion(uint8_t* magic) {
magic + kDexMagicSize);
}
+
+void StandardDexFile::WriteVersionBeforeDefaultMethods(uint8_t* magic) {
+ std::copy_n(kDexMagicVersions[0u], kDexVersionLen, magic + kDexMagicSize);
+}
+
bool StandardDexFile::IsMagicValid(const uint8_t* magic) {
return (memcmp(magic, kDexMagic, sizeof(kDexMagic)) == 0);
}
diff --git a/libdexfile/dex/standard_dex_file.h b/libdexfile/dex/standard_dex_file.h
index 3af36f6791..25cf62a417 100644
--- a/libdexfile/dex/standard_dex_file.h
+++ b/libdexfile/dex/standard_dex_file.h
@@ -83,6 +83,10 @@ class StandardDexFile : public DexFile {
// Write the current version, note that the input is the address of the magic.
static void WriteCurrentVersion(uint8_t* magic);
+ // Write the last version before default method support,
+ // note that the input is the address of the magic.
+ static void WriteVersionBeforeDefaultMethods(uint8_t* magic);
+
static const uint8_t kDexMagic[kDexMagicSize];
static constexpr size_t kNumDexVersions = 5;
static const uint8_t kDexMagicVersions[kNumDexVersions][kDexVersionLen];
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 6550c9bb40..cf2eb0312f 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -6423,6 +6423,19 @@ bool ClassLinker::LinkVirtualMethods(
ArtMethod* m = klass->GetVirtualMethodDuringLinking(i, image_pointer_size_);
m->SetMethodIndex(i);
if (!m->IsAbstract()) {
+ // If the dex file does not support default methods, throw ClassFormatError.
+ // This check is necessary to protect from odd cases, such as native default
+ // methods, that the dex file verifier permits for old dex file versions. b/157170505
+ // FIXME: This should be `if (!m->GetDexFile()->SupportsDefaultMethods())` but we're
+ // currently running CTS tests for default methods with dex file version 035 which
+ // does not support default methods. So, we limit this to native methods. b/157718952
+ if (m->IsNative()) {
+ DCHECK(!m->GetDexFile()->SupportsDefaultMethods());
+ ThrowClassFormatError(klass.Get(),
+ "Dex file does not support default method '%s'",
+ m->PrettyMethod().c_str());
+ return false;
+ }
m->SetAccessFlags(m->GetAccessFlags() | kAccDefault);
has_defaults = true;
}
diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h
index ef82d2345e..6a5317c1b6 100644
--- a/runtime/mirror/class-inl.h
+++ b/runtime/mirror/class-inl.h
@@ -131,7 +131,7 @@ inline uint32_t Class::GetVirtualMethodsStartOffset() {
template<VerifyObjectFlags kVerifyFlags>
inline ArraySlice<ArtMethod> Class::GetDirectMethodsSlice(PointerSize pointer_size) {
- DCHECK(IsLoaded() || IsErroneous());
+ DCHECK(IsLoaded() || IsErroneous()) << GetStatus();
return GetDirectMethodsSliceUnchecked(pointer_size);
}
@@ -144,7 +144,7 @@ inline ArraySlice<ArtMethod> Class::GetDirectMethodsSliceUnchecked(PointerSize p
template<VerifyObjectFlags kVerifyFlags>
inline ArraySlice<ArtMethod> Class::GetDeclaredMethodsSlice(PointerSize pointer_size) {
- DCHECK(IsLoaded() || IsErroneous());
+ DCHECK(IsLoaded() || IsErroneous()) << GetStatus();
return GetDeclaredMethodsSliceUnchecked(pointer_size);
}
@@ -157,7 +157,7 @@ inline ArraySlice<ArtMethod> Class::GetDeclaredMethodsSliceUnchecked(PointerSize
template<VerifyObjectFlags kVerifyFlags>
inline ArraySlice<ArtMethod> Class::GetDeclaredVirtualMethodsSlice(PointerSize pointer_size) {
- DCHECK(IsLoaded() || IsErroneous());
+ DCHECK(IsLoaded() || IsErroneous()) << GetStatus();
return GetDeclaredVirtualMethodsSliceUnchecked(pointer_size);
}
diff --git a/runtime/mirror/dex_cache-inl.h b/runtime/mirror/dex_cache-inl.h
index 4f23273065..d5e1362ada 100644
--- a/runtime/mirror/dex_cache-inl.h
+++ b/runtime/mirror/dex_cache-inl.h
@@ -164,6 +164,7 @@ inline Class* DexCache::GetResolvedType(dex::TypeIndex type_idx) {
inline void DexCache::SetResolvedType(dex::TypeIndex type_idx, ObjPtr<Class> resolved) {
DCHECK(resolved != nullptr);
+ DCHECK(resolved->IsResolved()) << resolved->GetStatus();
// TODO default transaction support.
// Use a release store for SetResolvedType. This is done to prevent other threads from seeing a
// class but not necessarily seeing the loaded members like the static fields array.
diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc
index bb3ff117bd..efaa3d938a 100644
--- a/runtime/native/dalvik_system_VMRuntime.cc
+++ b/runtime/native/dalvik_system_VMRuntime.cc
@@ -399,7 +399,6 @@ static void PreloadDexCachesResolveString(
if (string == nullptr) {
return;
}
- // LOG(INFO) << "VMRuntime.preloadDexCaches resolved string=" << utf8;
dex_cache->SetResolvedString(string_idx, string);
}
@@ -419,17 +418,10 @@ static void PreloadDexCachesResolveType(Thread* self,
ObjPtr<mirror::Class> klass = (class_name[1] == '\0')
? linker->LookupPrimitiveClass(class_name[0])
: linker->LookupClass(self, class_name, nullptr);
- if (klass == nullptr) {
+ if (klass == nullptr || !klass->IsResolved()) {
return;
}
- // LOG(INFO) << "VMRuntime.preloadDexCaches resolved klass=" << class_name;
dex_cache->SetResolvedType(type_idx, klass);
- // Skip uninitialized classes because filled static storage entry implies it is initialized.
- if (!klass->IsInitialized()) {
- // LOG(INFO) << "VMRuntime.preloadDexCaches uninitialized klass=" << class_name;
- return;
- }
- // LOG(INFO) << "VMRuntime.preloadDexCaches static storage klass=" << class_name;
}
// Based on ClassLinker::ResolveField.
diff --git a/test/180-native-default-method/build b/test/180-native-default-method/build
new file mode 100644
index 0000000000..3963fd30b4
--- /dev/null
+++ b/test/180-native-default-method/build
@@ -0,0 +1,30 @@
+#!/bin/bash
+#
+# Copyright 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# make us exit on a failure
+set -e
+
+./default-build "$@"
+
+if [[ $@ != *"--jvm"* ]]; then
+ # Change the generated dex file to have a v35 magic number if it is version 39
+ if test -f classes.dex && head -c 7 classes.dex | grep -q 039; then
+ # place ascii value '035' into the classes.dex file starting at byte 4.
+ printf '035' | dd status=none conv=notrunc of=classes.dex bs=1 seek=4 count=3
+ rm -f $TEST_NAME.jar
+ zip $TEST_NAME.jar classes.dex
+ fi
+fi
diff --git a/test/180-native-default-method/expected.txt b/test/180-native-default-method/expected.txt
new file mode 100644
index 0000000000..b0aad4deb5
--- /dev/null
+++ b/test/180-native-default-method/expected.txt
@@ -0,0 +1 @@
+passed
diff --git a/test/180-native-default-method/info.txt b/test/180-native-default-method/info.txt
new file mode 100644
index 0000000000..0cba4ebd2e
--- /dev/null
+++ b/test/180-native-default-method/info.txt
@@ -0,0 +1,3 @@
+Regression test for DCHECK() failure for copying a default native method from
+an interface to a class implementing that interface. The default native method
+should result in ClassFormatError before we reach that DCHECK().
diff --git a/test/180-native-default-method/jasmin/TestClass.j b/test/180-native-default-method/jasmin/TestClass.j
new file mode 100644
index 0000000000..fddd99b820
--- /dev/null
+++ b/test/180-native-default-method/jasmin/TestClass.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2020 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class public TestClass
+.super java/lang/Object
+.implements TestInterface
+
+.method public <init>()V
+ .limit stack 1
+ .limit locals 1
+ aload_0
+ invokespecial java/lang/Object/<init>()V
+ return
+.end method
diff --git a/test/180-native-default-method/jasmin/TestInterface.j b/test/180-native-default-method/jasmin/TestInterface.j
new file mode 100644
index 0000000000..080474e566
--- /dev/null
+++ b/test/180-native-default-method/jasmin/TestInterface.j
@@ -0,0 +1,19 @@
+; Copyright (C) 2020 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.interface public TestInterface
+.super java/lang/Object
+
+.method public native foo()V
+.end method
diff --git a/test/180-native-default-method/src/Main.java b/test/180-native-default-method/src/Main.java
new file mode 100644
index 0000000000..4b2704b9ec
--- /dev/null
+++ b/test/180-native-default-method/src/Main.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+ public static void main(String args[]) {
+ try {
+ // Regression test for default native methods that should cause ClassFormatException
+ // if they pass the dex file verification, i.e. for old dex file versions.
+ // We previously did not handle this case properly and failed a DCHECK() for
+ // a non-interface class creating a copied method that was native. b/157170505
+ Class.forName("TestClass");
+ throw new Error("UNREACHABLE");
+ } catch (ClassFormatError expected) {
+ System.out.println("passed");
+ } catch (Throwable unexpected) {
+ unexpected.printStackTrace();
+ }
+ }
+}